asciidoctor 2.0.12 → 2.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +142 -22
- data/LICENSE +1 -1
- data/README-de.adoc +15 -6
- data/README-fr.adoc +14 -8
- data/README-jp.adoc +15 -6
- data/README-zh_CN.adoc +14 -5
- data/README.adoc +135 -125
- data/asciidoctor.gemspec +4 -11
- data/data/locale/attributes-be.adoc +23 -0
- data/data/locale/attributes-it.adoc +4 -4
- data/data/locale/attributes-nl.adoc +6 -6
- data/data/locale/attributes-th.adoc +23 -0
- data/data/locale/attributes-vi.adoc +23 -0
- data/data/reference/syntax.adoc +14 -7
- data/data/stylesheets/asciidoctor-default.css +51 -52
- data/lib/asciidoctor.rb +12 -12
- data/lib/asciidoctor/abstract_block.rb +4 -4
- data/lib/asciidoctor/abstract_node.rb +10 -9
- data/lib/asciidoctor/attribute_list.rb +6 -6
- data/lib/asciidoctor/block.rb +6 -6
- data/lib/asciidoctor/cli/invoker.rb +0 -1
- data/lib/asciidoctor/cli/options.rb +27 -26
- data/lib/asciidoctor/convert.rb +1 -0
- data/lib/asciidoctor/converter.rb +5 -3
- data/lib/asciidoctor/converter/docbook5.rb +45 -26
- data/lib/asciidoctor/converter/html5.rb +89 -69
- data/lib/asciidoctor/converter/manpage.rb +113 -86
- data/lib/asciidoctor/converter/template.rb +11 -12
- data/lib/asciidoctor/document.rb +44 -51
- data/lib/asciidoctor/extensions.rb +10 -12
- data/lib/asciidoctor/helpers.rb +3 -6
- data/lib/asciidoctor/list.rb +2 -6
- data/lib/asciidoctor/load.rb +13 -11
- data/lib/asciidoctor/logging.rb +10 -8
- data/lib/asciidoctor/parser.rb +135 -150
- data/lib/asciidoctor/path_resolver.rb +3 -3
- data/lib/asciidoctor/reader.rb +72 -71
- data/lib/asciidoctor/rx.rb +4 -3
- data/lib/asciidoctor/substitutors.rb +117 -117
- data/lib/asciidoctor/syntax_highlighter.rb +8 -11
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +2 -1
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +1 -1
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +6 -5
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +33 -26
- data/lib/asciidoctor/table.rb +17 -19
- data/lib/asciidoctor/timings.rb +3 -3
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +10 -11
- data/man/asciidoctor.adoc +8 -7
- metadata +14 -67
data/lib/asciidoctor/logging.rb
CHANGED
@@ -20,10 +20,10 @@ class Logger < ::Logger
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class BasicFormatter < Formatter
|
23
|
-
|
23
|
+
SEVERITY_LABEL_SUBSTITUTES = { 'WARN' => 'WARNING', 'FATAL' => 'FAILED' }
|
24
24
|
|
25
25
|
def call severity, _, progname, msg
|
26
|
-
%(#{progname}: #{
|
26
|
+
%(#{progname}: #{SEVERITY_LABEL_SUBSTITUTES[severity] || severity}: #{::String === msg ? msg : msg.inspect}#{LF})
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -35,7 +35,7 @@ class Logger < ::Logger
|
|
35
35
|
end
|
36
36
|
|
37
37
|
class MemoryLogger < ::Logger
|
38
|
-
|
38
|
+
SEVERITY_SYMBOL_BY_VALUE = (Severity.constants false).map {|c| [(Severity.const_get c), c] }.to_h
|
39
39
|
|
40
40
|
attr_reader :messages
|
41
41
|
|
@@ -45,8 +45,8 @@ class MemoryLogger < ::Logger
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def add severity, message = nil, progname = nil
|
48
|
-
message
|
49
|
-
@messages << { severity:
|
48
|
+
message ||= block_given? ? yield : progname
|
49
|
+
@messages << { severity: SEVERITY_SYMBOL_BY_VALUE[severity || UNKNOWN], message: message }
|
50
50
|
true
|
51
51
|
end
|
52
52
|
|
@@ -59,7 +59,7 @@ class MemoryLogger < ::Logger
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def max_severity
|
62
|
-
empty? ? nil : @messages.map {|m| Severity.const_get m[:severity]
|
62
|
+
empty? ? nil : @messages.map {|m| Severity.const_get m[:severity] }.max
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -89,6 +89,7 @@ module LoggerManager
|
|
89
89
|
@logger ||= (@logger_class.new pipe)
|
90
90
|
end
|
91
91
|
|
92
|
+
# Returns the specified Logger
|
92
93
|
def logger= new_logger
|
93
94
|
@logger = new_logger || (@logger_class.new $stderr)
|
94
95
|
end
|
@@ -110,9 +111,10 @@ module Logging
|
|
110
111
|
# into - The Class that includes the {Logging} module
|
111
112
|
#
|
112
113
|
# Returns nothing
|
113
|
-
|
114
|
+
def self.included into
|
114
115
|
into.extend Logging
|
115
|
-
end
|
116
|
+
end
|
117
|
+
private_class_method :included # use separate declaration for Ruby 2.0.x
|
116
118
|
|
117
119
|
def logger
|
118
120
|
LoggerManager.logger
|
data/lib/asciidoctor/parser.rb
CHANGED
@@ -119,7 +119,7 @@ class Parser
|
|
119
119
|
# returns the Hash of orphan block attributes captured above the header
|
120
120
|
def self.parse_document_header(reader, document)
|
121
121
|
# capture lines of block-level metadata and plow away comment lines that precede first block
|
122
|
-
block_attrs = parse_block_metadata_lines reader, document
|
122
|
+
block_attrs = reader.skip_blank_lines ? (parse_block_metadata_lines reader, document) : {}
|
123
123
|
doc_attrs = document.attributes
|
124
124
|
|
125
125
|
# special case, block title is not allowed above document title,
|
@@ -144,7 +144,10 @@ class Parser
|
|
144
144
|
l0_section_title = nil
|
145
145
|
else
|
146
146
|
document.title = l0_section_title
|
147
|
-
doc_attrs['doctitle'] = doctitle_attr_val = document.
|
147
|
+
if (doc_attrs['doctitle'] = doctitle_attr_val = document.sub_specialchars l0_section_title).include? ATTR_REF_HEAD
|
148
|
+
# QUESTION should we defer substituting attributes until the end of the header? or should we substitute again if necessary?
|
149
|
+
doc_attrs['doctitle'] = doctitle_attr_val = document.sub_attributes doctitle_attr_val, attribute_missing: 'skip'
|
150
|
+
end
|
148
151
|
end
|
149
152
|
document.header.source_location = source_location if source_location
|
150
153
|
# default to compat-mode if document has setext doctitle
|
@@ -215,9 +218,6 @@ class Parser
|
|
215
218
|
name_section = initialize_section reader, document, {}
|
216
219
|
name_section_buffer = (reader.read_lines_until break_on_blank_lines: true, skip_line_comments: true).map {|l| l.lstrip }.join ' '
|
217
220
|
if ManpageNamePurposeRx =~ name_section_buffer
|
218
|
-
doc_attrs['manname-title'] ||= name_section.title
|
219
|
-
doc_attrs['manname-id'] = name_section.id if name_section.id
|
220
|
-
doc_attrs['manpurpose'] = $2
|
221
221
|
if (manname = $1).include? ATTR_REF_HEAD
|
222
222
|
manname = document.sub_attributes manname
|
223
223
|
end
|
@@ -226,8 +226,14 @@ class Parser
|
|
226
226
|
else
|
227
227
|
mannames = [manname]
|
228
228
|
end
|
229
|
+
if (manpurpose = $2).include? ATTR_REF_HEAD
|
230
|
+
manpurpose = document.sub_attributes manpurpose
|
231
|
+
end
|
232
|
+
doc_attrs['manname-title'] ||= name_section.title
|
233
|
+
doc_attrs['manname-id'] = name_section.id if name_section.id
|
229
234
|
doc_attrs['manname'] = manname
|
230
235
|
doc_attrs['mannames'] = mannames
|
236
|
+
doc_attrs['manpurpose'] = manpurpose
|
231
237
|
if document.backend == 'manpage'
|
232
238
|
doc_attrs['docname'] = manname
|
233
239
|
doc_attrs['outfilesuffix'] = %(.#{manvolnum})
|
@@ -327,7 +333,7 @@ class Parser
|
|
327
333
|
if current_level == 0
|
328
334
|
part = book
|
329
335
|
elsif current_level == 1 && section.special
|
330
|
-
# NOTE technically preface
|
336
|
+
# NOTE technically preface sections are only permitted in the book doctype
|
331
337
|
unless (sectname = section.sectname) == 'appendix' || sectname == 'preface' || sectname == 'abstract'
|
332
338
|
expected_next_level = nil
|
333
339
|
end
|
@@ -415,9 +421,6 @@ class Parser
|
|
415
421
|
|
416
422
|
(intro || section).blocks << new_block
|
417
423
|
attributes.clear
|
418
|
-
#else
|
419
|
-
# # don't clear attributes if we don't find a block because they may
|
420
|
-
# # be trailing attributes that didn't get associated with a block
|
421
424
|
end
|
422
425
|
end
|
423
426
|
|
@@ -452,7 +455,7 @@ class Parser
|
|
452
455
|
# of a section that need to get transfered to the next section
|
453
456
|
# see "trailing block attributes transfer to the following section" in
|
454
457
|
# test/attributes_test.rb for an example
|
455
|
-
[section
|
458
|
+
[section == parent ? nil : section, attributes.merge]
|
456
459
|
end
|
457
460
|
|
458
461
|
# Public: Parse and return the next Block at the Reader's current location
|
@@ -820,8 +823,8 @@ class Parser
|
|
820
823
|
if comma_idx > 0
|
821
824
|
language = (language.slice 0, comma_idx).strip
|
822
825
|
attributes['linenums'] = '' if comma_idx < ll - 4
|
823
|
-
|
824
|
-
attributes['linenums'] = ''
|
826
|
+
elsif ll > 4
|
827
|
+
attributes['linenums'] = ''
|
825
828
|
end
|
826
829
|
else
|
827
830
|
language = language.lstrip
|
@@ -902,9 +905,7 @@ class Parser
|
|
902
905
|
# FIXME title and caption should be assigned when block is constructed (though we need to handle all cases)
|
903
906
|
if attributes['title']
|
904
907
|
block.title = block_title = attributes.delete 'title'
|
905
|
-
|
906
|
-
block.assign_caption (attributes.delete 'caption')
|
907
|
-
end
|
908
|
+
block.assign_caption attributes.delete 'caption' if CAPTION_ATTRIBUTE_NAMES[block.context]
|
908
909
|
end
|
909
910
|
# TODO eventually remove the style attribute from the attributes hash
|
910
911
|
#block.style = attributes.delete 'style'
|
@@ -966,17 +967,12 @@ class Parser
|
|
966
967
|
# special case for fenced code blocks
|
967
968
|
if Compliance.markdown_syntax && (tip.start_with? '`')
|
968
969
|
if tip_len == 4
|
969
|
-
if tip == '````'
|
970
|
-
return
|
971
|
-
elsif (tip = tip.chop) == '```'
|
972
|
-
line = tip
|
973
|
-
line_len = tip_len = 3
|
974
|
-
else
|
970
|
+
if tip == '````' || (tip = tip.chop) != '```'
|
975
971
|
return
|
976
972
|
end
|
977
|
-
|
978
|
-
|
979
|
-
|
973
|
+
line = tip
|
974
|
+
line_len = tip_len = 3
|
975
|
+
elsif tip != '```'
|
980
976
|
return
|
981
977
|
end
|
982
978
|
elsif tip_len == 3
|
@@ -994,9 +990,10 @@ class Parser
|
|
994
990
|
# if terminator is false, that means the all the lines in the reader should be parsed
|
995
991
|
# NOTE could invoke filter in here, before and after parsing
|
996
992
|
def self.build_block(block_context, content_model, terminator, parent, reader, attributes, options = {})
|
997
|
-
|
993
|
+
case content_model
|
994
|
+
when :skip
|
998
995
|
skip_processing, parse_as_content_model = true, :simple
|
999
|
-
|
996
|
+
when :raw
|
1000
997
|
skip_processing, parse_as_content_model = false, :simple
|
1001
998
|
else
|
1002
999
|
skip_processing, parse_as_content_model = false, content_model
|
@@ -1025,14 +1022,15 @@ class Parser
|
|
1025
1022
|
block_reader = Reader.new reader.read_lines_until(terminator: terminator, skip_processing: skip_processing, context: block_context, cursor: :at_mark), block_cursor
|
1026
1023
|
end
|
1027
1024
|
|
1028
|
-
|
1025
|
+
case content_model
|
1026
|
+
when :verbatim
|
1029
1027
|
tab_size = (attributes['tabsize'] || parent.document.attributes['tabsize']).to_i
|
1030
1028
|
if (indent = attributes['indent'])
|
1031
1029
|
adjust_indentation! lines, indent.to_i, tab_size
|
1032
1030
|
elsif tab_size > 0
|
1033
1031
|
adjust_indentation! lines, -1, tab_size
|
1034
1032
|
end
|
1035
|
-
|
1033
|
+
when :skip
|
1036
1034
|
# QUESTION should we still invoke process method if extension is specified?
|
1037
1035
|
return
|
1038
1036
|
end
|
@@ -1293,7 +1291,8 @@ class Parser
|
|
1293
1291
|
has_text = true
|
1294
1292
|
list_item = ListItem.new(list_block, (item_text = match[2]))
|
1295
1293
|
list_item.source_location = reader.cursor if list_block.document.sourcemap
|
1296
|
-
|
1294
|
+
case list_type
|
1295
|
+
when :ulist
|
1297
1296
|
list_item.marker = sibling_trait
|
1298
1297
|
if item_text.start_with?('[')
|
1299
1298
|
if style && style == 'bibliography'
|
@@ -1311,13 +1310,13 @@ class Parser
|
|
1311
1310
|
list_item.text = item_text.slice(4, item_text.length)
|
1312
1311
|
end
|
1313
1312
|
end
|
1314
|
-
|
1313
|
+
when :olist
|
1315
1314
|
sibling_trait, implicit_style = resolve_ordered_list_marker(sibling_trait, (ordinal = list_block.items.size), true, reader)
|
1316
1315
|
list_item.marker = sibling_trait
|
1317
1316
|
if ordinal == 0 && !style
|
1318
1317
|
# using list level makes more sense, but we don't track it
|
1319
|
-
# basing style on marker level is compliant with AsciiDoc
|
1320
|
-
list_block.style = implicit_style || (
|
1318
|
+
# basing style on marker level is compliant with AsciiDoc.py
|
1319
|
+
list_block.style = implicit_style || (ORDERED_LIST_STYLES[sibling_trait.length - 1] || 'arabic').to_s
|
1321
1320
|
end
|
1322
1321
|
if item_text.start_with?('[[') && LeadingInlineAnchorRx =~ item_text
|
1323
1322
|
catalog_inline_anchor $1, $2, list_item, reader
|
@@ -1443,95 +1442,89 @@ class Parser
|
|
1443
1442
|
# FIXME to be AsciiDoc compliant, we shouldn't break if style in attribute line is "literal" (i.e., [literal])
|
1444
1443
|
elsif dlist && continuation != :active && (BlockAttributeLineRx.match? this_line)
|
1445
1444
|
break
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true) {|line| is_sibling_list_item? line, list_type, sibling_trait }
|
1458
|
-
else
|
1459
|
-
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1460
|
-
end
|
1461
|
-
continuation = :inactive
|
1462
|
-
# let block metadata play out until we find the block
|
1463
|
-
elsif (BlockTitleRx.match? this_line) || (BlockAttributeLineRx.match? this_line) || (AttributeEntryRx.match? this_line)
|
1464
|
-
buffer << this_line
|
1445
|
+
elsif continuation == :active && !this_line.empty?
|
1446
|
+
# literal paragraphs have special considerations (and this is one of
|
1447
|
+
# two entry points into one)
|
1448
|
+
# if we don't process it as a whole, then a line in it that looks like a
|
1449
|
+
# list item will throw off the exit from it
|
1450
|
+
if LiteralParagraphRx.match? this_line
|
1451
|
+
reader.unshift_line this_line
|
1452
|
+
if dlist
|
1453
|
+
# we may be in an indented list disguised as a literal paragraph
|
1454
|
+
# so we need to make sure we don't slurp up a legitimate sibling
|
1455
|
+
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true) {|line| is_sibling_list_item? line, list_type, sibling_trait }
|
1465
1456
|
else
|
1466
|
-
|
1467
|
-
within_nested_list = true
|
1468
|
-
if nested_list_type == :dlist && $3.nil_or_empty?
|
1469
|
-
# get greedy again
|
1470
|
-
has_text = false
|
1471
|
-
end
|
1472
|
-
end
|
1473
|
-
buffer << this_line
|
1474
|
-
continuation = :inactive
|
1457
|
+
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1475
1458
|
end
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1459
|
+
continuation = :inactive
|
1460
|
+
# let block metadata play out until we find the block
|
1461
|
+
elsif (BlockTitleRx.match? this_line) || (BlockAttributeLineRx.match? this_line) || (AttributeEntryRx.match? this_line)
|
1462
|
+
buffer << this_line
|
1463
|
+
else
|
1464
|
+
if (nested_list_type = (within_nested_list ? [:dlist] : NESTABLE_LIST_CONTEXTS).find {|ctx| ListRxMap[ctx].match? this_line })
|
1465
|
+
within_nested_list = true
|
1466
|
+
if nested_list_type == :dlist && $3.nil_or_empty?
|
1467
|
+
# get greedy again
|
1468
|
+
has_text = false
|
1469
|
+
end
|
1483
1470
|
end
|
1471
|
+
buffer << this_line
|
1472
|
+
continuation = :inactive
|
1473
|
+
end
|
1474
|
+
elsif prev_line && prev_line.empty?
|
1475
|
+
# advance to the next line of content
|
1476
|
+
if this_line.empty?
|
1477
|
+
# stop reading if we reach eof
|
1478
|
+
break unless (this_line = reader.skip_blank_lines && reader.read_line)
|
1479
|
+
# stop reading if we hit a sibling list item
|
1480
|
+
break if is_sibling_list_item? this_line, list_type, sibling_trait
|
1481
|
+
end
|
1484
1482
|
|
1485
|
-
|
1486
|
-
|
1483
|
+
if this_line == LIST_CONTINUATION
|
1484
|
+
detached_continuation = buffer.size
|
1485
|
+
buffer << this_line
|
1486
|
+
elsif has_text # has_text only relevant for dlist, which is more greedy until it has text for an item; has_text is always true for all other lists
|
1487
|
+
# in this block, we have to see whether we stay in the list
|
1488
|
+
# TODO any way to combine this with the check after skipping blank lines?
|
1489
|
+
if is_sibling_list_item?(this_line, list_type, sibling_trait)
|
1490
|
+
break
|
1491
|
+
elsif (nested_list_type = NESTABLE_LIST_CONTEXTS.find {|ctx| ListRxMap[ctx] =~ this_line })
|
1487
1492
|
buffer << this_line
|
1488
|
-
else
|
1489
|
-
# has_text is only relevant for dlist, which is more greedy until it has text for an item
|
1490
|
-
# for all other lists, has_text is always true
|
1491
|
-
# in this block, we have to see whether we stay in the list
|
1492
|
-
if has_text
|
1493
|
-
# TODO any way to combine this with the check after skipping blank lines?
|
1494
|
-
if is_sibling_list_item?(this_line, list_type, sibling_trait)
|
1495
|
-
break
|
1496
|
-
elsif nested_list_type = NESTABLE_LIST_CONTEXTS.find {|ctx| ListRxMap[ctx] =~ this_line }
|
1497
|
-
buffer << this_line
|
1498
|
-
within_nested_list = true
|
1499
|
-
if nested_list_type == :dlist && $3.nil_or_empty?
|
1500
|
-
# get greedy again
|
1501
|
-
has_text = false
|
1502
|
-
end
|
1503
|
-
# slurp up any literal paragraph offset by blank lines
|
1504
|
-
# NOTE we have to check for indented list items first
|
1505
|
-
elsif LiteralParagraphRx.match? this_line
|
1506
|
-
reader.unshift_line this_line
|
1507
|
-
if dlist
|
1508
|
-
# we may be in an indented list disguised as a literal paragraph
|
1509
|
-
# so we need to make sure we don't slurp up a legitimate sibling
|
1510
|
-
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true) {|line| is_sibling_list_item? line, list_type, sibling_trait }
|
1511
|
-
else
|
1512
|
-
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1513
|
-
end
|
1514
|
-
else
|
1515
|
-
break
|
1516
|
-
end
|
1517
|
-
else # only dlist in need of item text, so slurp it up!
|
1518
|
-
# pop the blank line so it's not interpretted as a list continuation
|
1519
|
-
buffer.pop unless within_nested_list
|
1520
|
-
buffer << this_line
|
1521
|
-
has_text = true
|
1522
|
-
end
|
1523
|
-
end
|
1524
|
-
else
|
1525
|
-
has_text = true unless this_line.empty?
|
1526
|
-
if nested_list_type = (within_nested_list ? [:dlist] : NESTABLE_LIST_CONTEXTS).find {|ctx| ListRxMap[ctx] =~ this_line }
|
1527
1493
|
within_nested_list = true
|
1528
1494
|
if nested_list_type == :dlist && $3.nil_or_empty?
|
1529
1495
|
# get greedy again
|
1530
1496
|
has_text = false
|
1531
1497
|
end
|
1498
|
+
# slurp up any literal paragraph offset by blank lines
|
1499
|
+
# NOTE we have to check for indented list items first
|
1500
|
+
elsif LiteralParagraphRx.match? this_line
|
1501
|
+
reader.unshift_line this_line
|
1502
|
+
if dlist
|
1503
|
+
# we may be in an indented list disguised as a literal paragraph
|
1504
|
+
# so we need to make sure we don't slurp up a legitimate sibling
|
1505
|
+
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true) {|line| is_sibling_list_item? line, list_type, sibling_trait }
|
1506
|
+
else
|
1507
|
+
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1508
|
+
end
|
1509
|
+
else
|
1510
|
+
break
|
1532
1511
|
end
|
1512
|
+
else # only dlist in need of item text, so slurp it up!
|
1513
|
+
# pop the blank line so it's not interpretted as a list continuation
|
1514
|
+
buffer.pop unless within_nested_list
|
1533
1515
|
buffer << this_line
|
1516
|
+
has_text = true
|
1517
|
+
end
|
1518
|
+
else
|
1519
|
+
has_text = true unless this_line.empty?
|
1520
|
+
if (nested_list_type = (within_nested_list ? [:dlist] : NESTABLE_LIST_CONTEXTS).find {|ctx| ListRxMap[ctx] =~ this_line })
|
1521
|
+
within_nested_list = true
|
1522
|
+
if nested_list_type == :dlist && $3.nil_or_empty?
|
1523
|
+
# get greedy again
|
1524
|
+
has_text = false
|
1525
|
+
end
|
1534
1526
|
end
|
1527
|
+
buffer << this_line
|
1535
1528
|
end
|
1536
1529
|
this_line = nil
|
1537
1530
|
end
|
@@ -1572,12 +1565,6 @@ class Parser
|
|
1572
1565
|
sect_style = attributes[1]
|
1573
1566
|
sect_id, sect_reftext, sect_title, sect_level, sect_atx = parse_section_title reader, document, attributes['id']
|
1574
1567
|
|
1575
|
-
if sect_reftext
|
1576
|
-
attributes['reftext'] = sect_reftext
|
1577
|
-
else
|
1578
|
-
sect_reftext = attributes['reftext']
|
1579
|
-
end
|
1580
|
-
|
1581
1568
|
if sect_style
|
1582
1569
|
if book && sect_style == 'abstract'
|
1583
1570
|
sect_name, sect_level = 'chapter', 1
|
@@ -1596,6 +1583,7 @@ class Parser
|
|
1596
1583
|
sect_name = 'section'
|
1597
1584
|
end
|
1598
1585
|
|
1586
|
+
attributes['reftext'] = sect_reftext if sect_reftext
|
1599
1587
|
section = Section.new parent, sect_level
|
1600
1588
|
section.id, section.title, section.sectname, section.source_location = sect_id, sect_title, sect_name, source_location
|
1601
1589
|
if sect_special
|
@@ -1603,7 +1591,7 @@ class Parser
|
|
1603
1591
|
if sect_numbered
|
1604
1592
|
section.numbered = true
|
1605
1593
|
elsif document.attributes['sectnums'] == 'all'
|
1606
|
-
section.numbered = book && sect_level == 1 ? :chapter : true
|
1594
|
+
section.numbered = (book && sect_level == 1 ? :chapter : true)
|
1607
1595
|
end
|
1608
1596
|
elsif document.attributes['sectnums'] && sect_level > 0
|
1609
1597
|
# NOTE a special section here is guaranteed to be nested in another section
|
@@ -1615,7 +1603,7 @@ class Parser
|
|
1615
1603
|
# generate an ID if one was not embedded or specified as anchor above section title
|
1616
1604
|
if (id = section.id || (section.id = (document.attributes.key? 'sectids') ? (generated_id = Section.generate_id section.title, document) : nil))
|
1617
1605
|
# convert title to resolve attributes while in scope
|
1618
|
-
section.title
|
1606
|
+
section.title unless generated_id || !(sect_title.include? ATTR_REF_HEAD)
|
1619
1607
|
unless document.register :refs, [id, section]
|
1620
1608
|
logger.warn message_with_context %(id assigned to section already in use: #{id}), source_location: (reader.cursor_at_line reader.lineno - (sect_atx ? 1 : 2))
|
1621
1609
|
end
|
@@ -1634,9 +1622,8 @@ class Parser
|
|
1634
1622
|
#
|
1635
1623
|
# Returns the Integer section level if the Reader is positioned at a section title or nil otherwise
|
1636
1624
|
def self.is_next_line_section?(reader, attributes)
|
1637
|
-
if (style = attributes[1]) && (style == 'discrete' || style == 'float')
|
1638
|
-
|
1639
|
-
elsif Compliance.underline_style_section_titles
|
1625
|
+
return if (style = attributes[1]) && (style == 'discrete' || style == 'float')
|
1626
|
+
if Compliance.underline_style_section_titles
|
1640
1627
|
next_lines = reader.peek_lines 2, style && style == 'comment'
|
1641
1628
|
is_section_title?(next_lines[0] || '', next_lines[1])
|
1642
1629
|
else
|
@@ -1877,13 +1864,12 @@ class Parser
|
|
1877
1864
|
if explicit
|
1878
1865
|
# rebuild implicit author names to reparse
|
1879
1866
|
authors.each_with_index do |author, idx|
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
end
|
1867
|
+
next if author
|
1868
|
+
authors[idx] = [
|
1869
|
+
author_metadata[%(firstname_#{name_idx = idx + 1})],
|
1870
|
+
author_metadata[%(middlename_#{name_idx})],
|
1871
|
+
author_metadata[%(lastname_#{name_idx})]
|
1872
|
+
].compact.map {|it| it.tr ' ', '_' }.join ' '
|
1887
1873
|
end if sparse
|
1888
1874
|
# process as names only
|
1889
1875
|
author_metadata = process_authors authors, true, false
|
@@ -2066,6 +2052,7 @@ class Parser
|
|
2066
2052
|
return true
|
2067
2053
|
end
|
2068
2054
|
end
|
2055
|
+
nil
|
2069
2056
|
end
|
2070
2057
|
|
2071
2058
|
# Process consecutive attribute entry lines, ignoring adjacent line comments and comment blocks.
|
@@ -2170,9 +2157,10 @@ class Parser
|
|
2170
2157
|
#
|
2171
2158
|
# Returns the String 0-index marker for this list item
|
2172
2159
|
def self.resolve_list_marker(list_type, marker, ordinal = 0, validate = false, reader = nil)
|
2173
|
-
|
2160
|
+
case list_type
|
2161
|
+
when :ulist
|
2174
2162
|
marker
|
2175
|
-
|
2163
|
+
when :olist
|
2176
2164
|
resolve_ordered_list_marker(marker, ordinal, validate, reader)[0]
|
2177
2165
|
else # :colist
|
2178
2166
|
'<1>'
|
@@ -2475,7 +2463,7 @@ class Parser
|
|
2475
2463
|
|
2476
2464
|
if pos == :start
|
2477
2465
|
if line.include? delimiter
|
2478
|
-
spec_part,
|
2466
|
+
spec_part, _, rest = line.partition delimiter
|
2479
2467
|
if (m = CellSpecStartRx.match spec_part)
|
2480
2468
|
return [{}, rest] if m[0].empty?
|
2481
2469
|
else
|
@@ -2484,14 +2472,12 @@ class Parser
|
|
2484
2472
|
else
|
2485
2473
|
return [nil, line]
|
2486
2474
|
end
|
2487
|
-
|
2488
|
-
if
|
2489
|
-
|
2490
|
-
|
2491
|
-
|
2492
|
-
|
2493
|
-
return [{}, line]
|
2494
|
-
end
|
2475
|
+
elsif (m = CellSpecEndRx.match line) # when pos == :end
|
2476
|
+
# NOTE return the line stripped of trailing whitespace if no cellspec is found in this case
|
2477
|
+
return [{}, line.rstrip] if m[0].lstrip.empty?
|
2478
|
+
rest = m.pre_match
|
2479
|
+
else
|
2480
|
+
return [{}, line]
|
2495
2481
|
end
|
2496
2482
|
|
2497
2483
|
spec = {}
|
@@ -2499,10 +2485,11 @@ class Parser
|
|
2499
2485
|
colspec, rowspec = m[1].split '.'
|
2500
2486
|
colspec = colspec.nil_or_empty? ? 1 : colspec.to_i
|
2501
2487
|
rowspec = rowspec.nil_or_empty? ? 1 : rowspec.to_i
|
2502
|
-
|
2488
|
+
case m[2]
|
2489
|
+
when '+'
|
2503
2490
|
spec['colspan'] = colspec unless colspec == 1
|
2504
2491
|
spec['rowspan'] = rowspec unless rowspec == 1
|
2505
|
-
|
2492
|
+
when '*'
|
2506
2493
|
spec['repeatcol'] = colspec unless colspec == 1
|
2507
2494
|
end
|
2508
2495
|
end
|
@@ -2527,7 +2514,7 @@ class Parser
|
|
2527
2514
|
# Public: Parse the first positional attribute and assign named attributes
|
2528
2515
|
#
|
2529
2516
|
# Parse the first positional attribute to extract the style, role and id
|
2530
|
-
# parts, assign the values to their
|
2517
|
+
# parts, assign the values to their corresponding attribute keys and return
|
2531
2518
|
# the parsed style from the first positional attribute.
|
2532
2519
|
#
|
2533
2520
|
# attributes - The Hash of attributes to process and update
|
@@ -2567,7 +2554,7 @@ class Parser
|
|
2567
2554
|
accum = ''
|
2568
2555
|
name = :option
|
2569
2556
|
else
|
2570
|
-
accum
|
2557
|
+
accum += c
|
2571
2558
|
end
|
2572
2559
|
end
|
2573
2560
|
|
@@ -2658,9 +2645,9 @@ class Parser
|
|
2658
2645
|
if tab_size > 0 && lines.any? {|line| line.include? TAB }
|
2659
2646
|
full_tab_space = ' ' * tab_size
|
2660
2647
|
lines.map! do |line|
|
2661
|
-
if line.empty?
|
2648
|
+
if line.empty? || (tab_idx = line.index TAB).nil?
|
2662
2649
|
line
|
2663
|
-
|
2650
|
+
else
|
2664
2651
|
if tab_idx == 0
|
2665
2652
|
leading_tabs = 0
|
2666
2653
|
line.each_byte do |b|
|
@@ -2678,22 +2665,20 @@ class Parser
|
|
2678
2665
|
if c == TAB
|
2679
2666
|
# calculate how many spaces this tab represents, then replace tab with spaces
|
2680
2667
|
if (offset = idx + spaces_added) % tab_size == 0
|
2681
|
-
spaces_added +=
|
2682
|
-
result
|
2668
|
+
spaces_added += tab_size - 1
|
2669
|
+
result += full_tab_space
|
2683
2670
|
else
|
2684
2671
|
unless (spaces = tab_size - offset % tab_size) == 1
|
2685
|
-
spaces_added +=
|
2672
|
+
spaces_added += spaces - 1
|
2686
2673
|
end
|
2687
|
-
result
|
2674
|
+
result += ' ' * spaces
|
2688
2675
|
end
|
2689
2676
|
else
|
2690
|
-
result
|
2677
|
+
result += c
|
2691
2678
|
end
|
2692
2679
|
idx += 1
|
2693
2680
|
end
|
2694
2681
|
result
|
2695
|
-
else
|
2696
|
-
line
|
2697
2682
|
end
|
2698
2683
|
end
|
2699
2684
|
end
|