diff options
| author | Dan Allen <dan.j.allen@gmail.com> | 2018-04-21 01:01:13 -0600 |
|---|---|---|
| committer | Dan Allen <dan.j.allen@gmail.com> | 2018-04-22 23:40:05 -0600 |
| commit | b48709271c2c0f7a7bb7b5a999f12ae6c4f46251 (patch) | |
| tree | 8a007cd2b7b8dc762088dda8b156b6769f86624e | |
| parent | 6ceda95d640d677ec15fb4ecd02451b7671b3259 (diff) | |
resolves #2298 add numbering for parts
- number parts using roman numerals if the partnums attribute is set
- move method to convert integer to roman to Helpers module
- assign the value "Part" to the part-refsig attribute by default
- define the part-label attribute in the attributes-en.adoc data file
- add support for partnums and sectnums=all to create_section helper in extensions
- add partnums and sectnums=all scenarios to test for create_section helper
| -rw-r--r-- | data/locale/attributes-en.adoc | 3 | ||||
| -rw-r--r-- | features/xref.feature | 141 | ||||
| -rw-r--r-- | lib/asciidoctor/converter/html5.rb | 5 | ||||
| -rw-r--r-- | lib/asciidoctor/document.rb | 2 | ||||
| -rw-r--r-- | lib/asciidoctor/extensions.rb | 25 | ||||
| -rw-r--r-- | lib/asciidoctor/helpers.rb | 17 | ||||
| -rw-r--r-- | lib/asciidoctor/parser.rb | 47 | ||||
| -rw-r--r-- | lib/asciidoctor/section.rb | 8 | ||||
| -rw-r--r-- | test/extensions_test.rb | 6 | ||||
| -rw-r--r-- | test/sections_test.rb | 25 |
10 files changed, 229 insertions, 50 deletions
diff --git a/data/locale/attributes-en.adoc b/data/locale/attributes-en.adoc index c4132c37..9457a501 100644 --- a/data/locale/attributes-en.adoc +++ b/data/locale/attributes-en.adoc @@ -11,7 +11,8 @@ ifdef::listing-caption[:listing-caption: Listing] //:manname-title: Name :note-caption: Note -:part-refsig: Part +:part-label: Part +:part-refsig: {part-label} ifdef::preface-title[:preface-title: Preface] :section-refsig: Section :table-caption: Table diff --git a/features/xref.feature b/features/xref.feature index 9a5974ee..bd4215a6 100644 --- a/features/xref.feature +++ b/features/xref.feature @@ -263,6 +263,147 @@ Feature: Cross References |to find a complete list of features. """ + Scenario: Create a full cross reference to a numbered part + Given the AsciiDoc source + """ + :doctype: book + :sectnums: + :partnums: + :xrefstyle: full + + [preface] + = Preface + + See <<p1>> for an introduction to the language. + + [#p1] + = Language + + == Syntax + + This chapter covers the syntax. + """ + When it is converted to html + Then the result should contain the HTML structure + """ + |See + a<> href='#p1' Part I, “Language” + |for an introduction to the language. + """ + + Scenario: Create a short cross reference to a numbered part + Given the AsciiDoc source + """ + :doctype: book + :sectnums: + :partnums: + :xrefstyle: short + + [preface] + = Preface + + See <<p1>> for an introduction to the language. + + [#p1] + = Language + + == Syntax + + This chapter covers the syntax. + """ + When it is converted to html + Then the result should contain the HTML structure + """ + |See + a<> href='#p1' Part I + |for an introduction to the language. + """ + + Scenario: Create a basic cross reference to a numbered part + Given the AsciiDoc source + """ + :doctype: book + :sectnums: + :partnums: + :xrefstyle: basic + + [preface] + = Preface + + See <<p1>> for an introduction to the language. + + [#p1] + = Language + + == Syntax + + This chapter covers the syntax. + """ + When it is converted to html + Then the result should contain the HTML structure + """ + |See + a<> href='#p1' Language + |for an introduction to the language. + """ + + Scenario: Create a basic cross reference to an unnumbered part + Given the AsciiDoc source + """ + :doctype: book + :sectnums: + :xrefstyle: full + + [preface] + = Preface + + See <<p1>> for an introduction to the language. + + [#p1] + = Language + + == Syntax + + This chapter covers the syntax. + """ + When it is converted to html + Then the result should contain the HTML structure + """ + |See + a<> href='#p1' Language + |for an introduction to the language. + """ + + @wip + Scenario: Create a cross reference to a part using a custom chapter reference signifier + Given the AsciiDoc source + """ + :doctype: book + :sectnums: + :partnums: + :xrefstyle: full + :part-refsig: P + + [preface] + = Preface + + See <<p1>> for an introduction to the language. + + [#p1] + = Language + + == Syntax + + This chapter covers the syntax. + """ + When it is converted to html + Then the result should contain the HTML structure + """ + |See + a<> href='#p1' P I, “Language” + |for an introduction to the language. + """ + Scenario: Create a full cross reference to a numbered appendix Given the AsciiDoc source """ diff --git a/lib/asciidoctor/converter/html5.rb b/lib/asciidoctor/converter/html5.rb index 1bb199d2..5d51e52c 100644 --- a/lib/asciidoctor/converter/html5.rb +++ b/lib/asciidoctor/converter/html5.rb @@ -322,11 +322,10 @@ MathJax.Hub.Config({ def section node if (level = node.level) == 0 - title = node.title sect0 = true + title = node.numbered && level <= (node.document.attr 'sectnumlevels', 3).to_i ? %(#{node.sectnum} #{node.title}) : node.title else - num = node.numbered && !node.caption && level <= (node.document.attr 'sectnumlevels', 3).to_i ? %(#{node.sectnum} ) : '' - title = %(#{num}#{node.captioned_title}) + title = node.numbered && !node.caption && level <= (node.document.attr 'sectnumlevels', 3).to_i ? %(#{node.sectnum} #{node.title}) : node.captioned_title end if node.id id_attr = %( id="#{id = node.id}") diff --git a/lib/asciidoctor/document.rb b/lib/asciidoctor/document.rb index 9e609943..77254e60 100644 --- a/lib/asciidoctor/document.rb +++ b/lib/asciidoctor/document.rb @@ -363,7 +363,7 @@ class Document < AbstractBlock attrs['toc-title'] = 'Table of Contents' #attrs['preface-title'] = 'Preface' attrs['section-refsig'] = 'Section' - #attrs['part-refsig'] = 'Part' + attrs['part-refsig'] = 'Part' attrs['chapter-refsig'] = 'Chapter' attrs['appendix-caption'] = attrs['appendix-refsig'] = 'Appendix' attrs['untitled-label'] = 'Untitled' diff --git a/lib/asciidoctor/extensions.rb b/lib/asciidoctor/extensions.rb index 0c9b0cf2..c77b4e54 100644 --- a/lib/asciidoctor/extensions.rb +++ b/lib/asciidoctor/extensions.rb @@ -116,16 +116,17 @@ module Extensions # Returns a [Section] node with all properties properly initialized. def create_section parent, title, attrs, opts = {} doc = parent.document - doctype, level = doc.doctype, (opts[:level] || parent.level + 1) + book = (doctype = doc.doctype) == 'book' + level = opts[:level] || parent.level + 1 if (style = attrs.delete 'style') - if style == 'abstract' && doctype == 'book' + if book && style == 'abstract' sectname, level = 'chapter', 1 else sectname, special = style, true level = 1 if level == 0 end - elsif doctype == 'book' - sectname = level == 0 ? 'part' : (level == 1 ? 'chapter' : 'section') + elsif book + sectname = level == 0 ? 'part' : (level > 1 ? 'section' : 'chapter') elsif doctype == 'manpage' && (title.casecmp 'synopsis') == 0 sectname, special = 'synopsis', true else @@ -135,12 +136,20 @@ module Extensions sect.title, sect.sectname = title, sectname if special sect.special = true - sect.numbered = true if opts.fetch :numbered, (style == 'appendix') - elsif opts.fetch :numbered, (level > 0 && (doc.attributes.key? 'sectnums')) - sect.numbered = sect.special ? (parent.context == :section && parent.numbered) : true + if opts.fetch :numbered, (style == 'appendix') + sect.numbered = true + elsif !(opts.key? :numbered) && (doc.attr? 'sectnums', 'all') + sect.numbered = book && level == 1 ? :chapter : true + end + elsif level > 0 + if opts.fetch :numbered, (doc.attr? 'sectnums') + sect.numbered = sect.special ? parent.numbered && true : true + end + else + sect.numbered = true if opts.fetch :numbered, (book && (doc.attr? 'partnums')) end unless (id = attrs.delete 'id') == false - sect.id = attrs['id'] = id || ((doc.attributes.key? 'sectids') ? (Section.generate_id sect.title, doc) : nil) + sect.id = attrs['id'] = id || ((doc.attr? 'sectids') ? (Section.generate_id sect.title, doc) : nil) end sect.update_attributes attrs sect diff --git a/lib/asciidoctor/helpers.rb b/lib/asciidoctor/helpers.rb index f79b707f..6c4dacbf 100644 --- a/lib/asciidoctor/helpers.rb +++ b/lib/asciidoctor/helpers.rb @@ -209,5 +209,22 @@ module Helpers end end end + + ROMAN_NUMERALS = { + 'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, 'XC' => 90, + 'L' => 50, 'XL' => 40, 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1 + } + + # Converts an integer to a Roman numeral. + # + # val - the [Integer] value to convert + # + # Returns the [String] roman numeral for this integer + def self.int_to_roman val + ROMAN_NUMERALS.map {|l, i| + repeat, val = val.divmod i + l * repeat + }.join + end end end diff --git a/lib/asciidoctor/parser.rb b/lib/asciidoctor/parser.rb index 36dab24a..114c5fb1 100644 --- a/lib/asciidoctor/parser.rb +++ b/lib/asciidoctor/parser.rb @@ -70,11 +70,6 @@ class Parser 'a' => :asciidoc } - ROMAN_NUMERALS = ::Hash[ - 'M', 1000, 'CM', 900, 'D', 500, 'CD', 400, 'C', 100, 'XC', 90, - 'L', 50, 'XL', 40, 'X', 10, 'IX', 9, 'V', 5, 'IV', 4, 'I', 1 - ] - # Public: Make sure the Parser object doesn't get initialized. # # Raises RuntimeError if this constructor is invoked. @@ -1533,6 +1528,7 @@ class Parser # Returns the section [Block] def self.initialize_section reader, parent, attributes = {} document = parent.document + book = (doctype = document.doctype) == 'book' source_location = reader.cursor if document.sourcemap sect_style = attributes[1] sect_id, sect_reftext, sect_title, sect_level, sect_atx = parse_section_title reader, document, attributes['id'] @@ -1544,26 +1540,23 @@ class Parser end if sect_style - if sect_style == 'abstract' && document.doctype == 'book' + if book && sect_style == 'abstract' sect_name, sect_level = 'chapter', 1 else sect_name, sect_special = sect_style, true sect_level = 1 if sect_level == 0 sect_numbered = sect_style == 'appendix' end - else - case document.doctype - when 'book' - sect_name = sect_level == 0 ? 'part' : (sect_level == 1 ? 'chapter' : 'section') - when 'manpage' - if (sect_title.casecmp 'synopsis') == 0 - sect_name, sect_special = 'synopsis', true - else - sect_name = 'section' - end + elsif book + sect_name = sect_level == 0 ? 'part' : (sect_level > 1 ? 'section' : 'chapter') + elsif doctype == 'manpage' + if (sect_title.casecmp 'synopsis') == 0 + sect_name, sect_special = 'synopsis', true else sect_name = 'section' end + else + sect_name = 'section' end section = Section.new parent, sect_level, false @@ -1573,11 +1566,13 @@ class Parser if sect_numbered section.numbered = true elsif document.attributes['sectnums'] == 'all' - section.numbered = sect_level == 1 && document.doctype == 'book' ? :chapter : true + section.numbered = book && sect_level == 1 ? :chapter : true end - elsif sect_level > 0 && (document.attributes.key? 'sectnums') + elsif document.attributes['sectnums'] && sect_level > 0 # NOTE a special section here is guaranteed to be nested in another section section.numbered = section.special ? parent.numbered && true : true + elsif book && sect_level == 0 && document.attributes['partnums'] + section.numbered = true end # generate an ID if one was not embedded or specified as anchor above section title @@ -2206,13 +2201,13 @@ class Parser marker = 'A.' when :lowerroman if validate - expected = int_to_roman_numeral(ordinal + 1).downcase + expected = Helpers.int_to_roman(ordinal + 1).downcase actual = marker.chop # remove trailing ) end marker = 'i)' when :upperroman if validate - expected = int_to_roman_numeral(ordinal + 1) + expected = Helpers.int_to_roman(ordinal + 1) actual = marker.chop # remove trailing ) end marker = 'I)' @@ -2725,17 +2720,5 @@ class Parser def self.sanitize_attribute_name(name) name.gsub(InvalidAttributeNameCharsRx, '').downcase end - - # Internal: Converts an integer to a Roman numeral. - # - # value - The integer to convert - # - # Returns the String Roman numeral for this integer - def self.int_to_roman_numeral value - ROMAN_NUMERALS.map {|l, i| - repeat, value = value.divmod i - l * repeat - }.join - end end end diff --git a/lib/asciidoctor/section.rb b/lib/asciidoctor/section.rb index ffeff65a..1ad20e86 100644 --- a/lib/asciidoctor/section.rb +++ b/lib/asciidoctor/section.rb @@ -44,12 +44,12 @@ class Section < AbstractBlock # opts - An optional Hash of options (default: {}) def initialize parent = nil, level = nil, numbered = true, opts = {} super parent, :section, opts - if parent && parent != @document + if Section === parent @level, @special = level || (parent.level + 1), parent.special else @level, @special = level || 1, false end - @numbered = numbered && @level > 0 + @numbered = numbered @index = 0 end @@ -110,7 +110,9 @@ class Section < AbstractBlock # Returns the section number as a String def sectnum(delimiter = '.', append = nil) append ||= (append == false ? '' : delimiter) - if @level > 1 && @parent != @document + if @level == 0 + %(#{Helpers.int_to_roman @number}#{append}) + elsif @level > 1 && Section === @parent %(#{@parent.sectnum(delimiter)}#{@number}#{append}) else %(#{@number}#{append}) diff --git a/test/extensions_test.rb b/test/extensions_test.rb index c9e73420..e7195c6a 100644 --- a/test/extensions_test.rb +++ b/test/extensions_test.rb @@ -1241,15 +1241,17 @@ sect::[%s] { '' => ['chapter', 1, false, true, '_section_title'], 'level=0' => ['part', 0, false, false, '_section_title'], + 'level=0,alt' => ['part', 0, false, true, '_section_title', { 'partnums' => '' }], 'level=0,style=appendix' => ['appendix', 1, true, true, '_section_title'], 'style=appendix' => ['appendix', 1, true, true, '_section_title'], 'style=glossary' => ['glossary', 1, true, false, '_section_title'], + 'style=glossary,alt' => ['glossary', 1, true, :chapter, '_section_title', { 'sectnums' => 'all' }], 'style=abstract' => ['chapter', 1, false, true, '_section_title'], 'id=section-title' => ['chapter', 1, false, true, 'section-title'], 'id=false' => ['chapter', 1, false, true, nil] - }.each do |attrlist, (expect_sectname, expect_level, expect_special, expect_numbered, expect_id)| + }.each do |attrlist, (expect_sectname, expect_level, expect_special, expect_numbered, expect_id, extra_attrs)| input = input_tpl % attrlist - document_from_string input, :safe => :server + document_from_string input, :safe => :server, :attributes => extra_attrs assert_equal expect_sectname, sect.sectname assert_equal expect_level, sect.level assert_equal expect_special, sect.special diff --git a/test/sections_test.rb b/test/sections_test.rb index 7ca13906..dfdea842 100644 --- a/test/sections_test.rb +++ b/test/sections_test.rb @@ -1354,6 +1354,31 @@ text assert_xpath '//h3[@id="_section_2_2"][starts-with(text(), "2.2. ")]', output, 1 end + test 'should number parts when doctype is book and partnums attributes is set' do + input = <<-EOS += Book Title +:doctype: book +:sectnums: +:partnums: + += Language + +== Syntax + +content + += Processor + +== CLI + +content + EOS + + output = render_string input + assert_xpath '//h1[@id="_language"][text() = "I. Language"]', output, 1 + assert_xpath '//h1[@id="_processor"][text() = "II. Processor"]', output, 1 + end + test 'blocks should have level' do input = <<-EOS = Title |
