summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Allen <dan.j.allen@gmail.com>2018-04-21 01:01:13 -0600
committerDan Allen <dan.j.allen@gmail.com>2018-04-22 23:40:05 -0600
commitb48709271c2c0f7a7bb7b5a999f12ae6c4f46251 (patch)
tree8a007cd2b7b8dc762088dda8b156b6769f86624e
parent6ceda95d640d677ec15fb4ecd02451b7671b3259 (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.adoc3
-rw-r--r--features/xref.feature141
-rw-r--r--lib/asciidoctor/converter/html5.rb5
-rw-r--r--lib/asciidoctor/document.rb2
-rw-r--r--lib/asciidoctor/extensions.rb25
-rw-r--r--lib/asciidoctor/helpers.rb17
-rw-r--r--lib/asciidoctor/parser.rb47
-rw-r--r--lib/asciidoctor/section.rb8
-rw-r--r--test/extensions_test.rb6
-rw-r--r--test/sections_test.rb25
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, &#8220;Language&#8221;
+ |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, &#8220;Language&#8221;
+ |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