asciidoctor 2.0.15 → 2.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +38 -2
- data/LICENSE +1 -1
- data/README-de.adoc +3 -3
- data/README-fr.adoc +3 -3
- data/README-jp.adoc +3 -3
- data/README-zh_CN.adoc +3 -3
- data/README.adoc +2 -2
- data/asciidoctor.gemspec +1 -8
- data/data/locale/attributes-th.adoc +23 -0
- data/data/locale/attributes-vi.adoc +23 -0
- data/data/stylesheets/asciidoctor-default.css +50 -48
- data/lib/asciidoctor.rb +7 -7
- data/lib/asciidoctor/abstract_block.rb +4 -4
- data/lib/asciidoctor/abstract_node.rb +9 -8
- data/lib/asciidoctor/block.rb +6 -6
- data/lib/asciidoctor/cli/invoker.rb +0 -1
- data/lib/asciidoctor/cli/options.rb +22 -22
- data/lib/asciidoctor/convert.rb +1 -0
- data/lib/asciidoctor/converter.rb +5 -3
- data/lib/asciidoctor/converter/docbook5.rb +20 -22
- data/lib/asciidoctor/converter/html5.rb +70 -60
- data/lib/asciidoctor/converter/manpage.rb +61 -52
- data/lib/asciidoctor/converter/template.rb +11 -12
- data/lib/asciidoctor/document.rb +22 -37
- data/lib/asciidoctor/extensions.rb +10 -10
- data/lib/asciidoctor/list.rb +2 -6
- data/lib/asciidoctor/load.rb +10 -9
- data/lib/asciidoctor/logging.rb +10 -8
- data/lib/asciidoctor/parser.rb +122 -141
- data/lib/asciidoctor/path_resolver.rb +3 -3
- data/lib/asciidoctor/reader.rb +67 -68
- data/lib/asciidoctor/rx.rb +2 -1
- data/lib/asciidoctor/substitutors.rb +97 -99
- 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 +2 -1
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +2 -1
- 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 +8 -9
- data/man/asciidoctor.adoc +7 -6
- metadata +7 -61
data/lib/asciidoctor/load.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Asciidoctor
|
2
3
|
class << self
|
3
4
|
# Public: Parse the AsciiDoc source input into a {Document}
|
@@ -53,7 +54,7 @@ module Asciidoctor
|
|
53
54
|
end
|
54
55
|
|
55
56
|
if ::File === input
|
56
|
-
# File#mtime on JRuby for Windows doesn't honor TZ environment variable; see https://github.com/jruby/jruby/issues/6659
|
57
|
+
# File#mtime on JRuby 9.1 for Windows doesn't honor TZ environment variable; see https://github.com/jruby/jruby/issues/6659
|
57
58
|
options[:input_mtime] = RUBY_ENGINE == 'jruby' ? (::Time.at input.mtime.to_i) : input.mtime
|
58
59
|
# NOTE defer setting infile and indir until we get a better sense of their purpose
|
59
60
|
# TODO cli checks if input path can be read and is file, but might want to add check to API too
|
@@ -84,23 +85,23 @@ module Asciidoctor
|
|
84
85
|
|
85
86
|
timings.record :parse if timings
|
86
87
|
doc
|
87
|
-
rescue =>
|
88
|
+
rescue => e
|
88
89
|
begin
|
89
90
|
context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
|
90
|
-
if
|
91
|
+
if e.respond_to? :exception
|
91
92
|
# The original message must be explicitly preserved when wrapping a Ruby exception
|
92
|
-
|
93
|
+
wrapped_e = e.exception %(#{context} - #{e.message})
|
93
94
|
# JRuby automatically sets backtrace; MRI did not until 2.6
|
94
|
-
|
95
|
+
wrapped_e.set_backtrace e.backtrace
|
95
96
|
else
|
96
97
|
# Likely a Java exception class
|
97
|
-
|
98
|
-
|
98
|
+
wrapped_e = e.class.new context, e
|
99
|
+
wrapped_e.stack_trace = e.stack_trace
|
99
100
|
end
|
100
101
|
rescue
|
101
|
-
|
102
|
+
wrapped_e = e
|
102
103
|
end
|
103
|
-
raise
|
104
|
+
raise wrapped_e
|
104
105
|
end
|
105
106
|
|
106
107
|
# Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
|
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
@@ -421,9 +421,6 @@ class Parser
|
|
421
421
|
|
422
422
|
(intro || section).blocks << new_block
|
423
423
|
attributes.clear
|
424
|
-
#else
|
425
|
-
# # don't clear attributes if we don't find a block because they may
|
426
|
-
# # be trailing attributes that didn't get associated with a block
|
427
424
|
end
|
428
425
|
end
|
429
426
|
|
@@ -458,7 +455,7 @@ class Parser
|
|
458
455
|
# of a section that need to get transfered to the next section
|
459
456
|
# see "trailing block attributes transfer to the following section" in
|
460
457
|
# test/attributes_test.rb for an example
|
461
|
-
[section
|
458
|
+
[section == parent ? nil : section, attributes.merge]
|
462
459
|
end
|
463
460
|
|
464
461
|
# Public: Parse and return the next Block at the Reader's current location
|
@@ -826,8 +823,8 @@ class Parser
|
|
826
823
|
if comma_idx > 0
|
827
824
|
language = (language.slice 0, comma_idx).strip
|
828
825
|
attributes['linenums'] = '' if comma_idx < ll - 4
|
829
|
-
|
830
|
-
attributes['linenums'] = ''
|
826
|
+
elsif ll > 4
|
827
|
+
attributes['linenums'] = ''
|
831
828
|
end
|
832
829
|
else
|
833
830
|
language = language.lstrip
|
@@ -908,7 +905,7 @@ class Parser
|
|
908
905
|
# FIXME title and caption should be assigned when block is constructed (though we need to handle all cases)
|
909
906
|
if attributes['title']
|
910
907
|
block.title = block_title = attributes.delete 'title'
|
911
|
-
block.assign_caption
|
908
|
+
block.assign_caption attributes.delete 'caption' if CAPTION_ATTRIBUTE_NAMES[block.context]
|
912
909
|
end
|
913
910
|
# TODO eventually remove the style attribute from the attributes hash
|
914
911
|
#block.style = attributes.delete 'style'
|
@@ -970,17 +967,12 @@ class Parser
|
|
970
967
|
# special case for fenced code blocks
|
971
968
|
if Compliance.markdown_syntax && (tip.start_with? '`')
|
972
969
|
if tip_len == 4
|
973
|
-
if tip == '````'
|
974
|
-
return
|
975
|
-
elsif (tip = tip.chop) == '```'
|
976
|
-
line = tip
|
977
|
-
line_len = tip_len = 3
|
978
|
-
else
|
970
|
+
if tip == '````' || (tip = tip.chop) != '```'
|
979
971
|
return
|
980
972
|
end
|
981
|
-
|
982
|
-
|
983
|
-
|
973
|
+
line = tip
|
974
|
+
line_len = tip_len = 3
|
975
|
+
elsif tip != '```'
|
984
976
|
return
|
985
977
|
end
|
986
978
|
elsif tip_len == 3
|
@@ -998,9 +990,10 @@ class Parser
|
|
998
990
|
# if terminator is false, that means the all the lines in the reader should be parsed
|
999
991
|
# NOTE could invoke filter in here, before and after parsing
|
1000
992
|
def self.build_block(block_context, content_model, terminator, parent, reader, attributes, options = {})
|
1001
|
-
|
993
|
+
case content_model
|
994
|
+
when :skip
|
1002
995
|
skip_processing, parse_as_content_model = true, :simple
|
1003
|
-
|
996
|
+
when :raw
|
1004
997
|
skip_processing, parse_as_content_model = false, :simple
|
1005
998
|
else
|
1006
999
|
skip_processing, parse_as_content_model = false, content_model
|
@@ -1029,14 +1022,15 @@ class Parser
|
|
1029
1022
|
block_reader = Reader.new reader.read_lines_until(terminator: terminator, skip_processing: skip_processing, context: block_context, cursor: :at_mark), block_cursor
|
1030
1023
|
end
|
1031
1024
|
|
1032
|
-
|
1025
|
+
case content_model
|
1026
|
+
when :verbatim
|
1033
1027
|
tab_size = (attributes['tabsize'] || parent.document.attributes['tabsize']).to_i
|
1034
1028
|
if (indent = attributes['indent'])
|
1035
1029
|
adjust_indentation! lines, indent.to_i, tab_size
|
1036
1030
|
elsif tab_size > 0
|
1037
1031
|
adjust_indentation! lines, -1, tab_size
|
1038
1032
|
end
|
1039
|
-
|
1033
|
+
when :skip
|
1040
1034
|
# QUESTION should we still invoke process method if extension is specified?
|
1041
1035
|
return
|
1042
1036
|
end
|
@@ -1297,7 +1291,8 @@ class Parser
|
|
1297
1291
|
has_text = true
|
1298
1292
|
list_item = ListItem.new(list_block, (item_text = match[2]))
|
1299
1293
|
list_item.source_location = reader.cursor if list_block.document.sourcemap
|
1300
|
-
|
1294
|
+
case list_type
|
1295
|
+
when :ulist
|
1301
1296
|
list_item.marker = sibling_trait
|
1302
1297
|
if item_text.start_with?('[')
|
1303
1298
|
if style && style == 'bibliography'
|
@@ -1315,13 +1310,13 @@ class Parser
|
|
1315
1310
|
list_item.text = item_text.slice(4, item_text.length)
|
1316
1311
|
end
|
1317
1312
|
end
|
1318
|
-
|
1313
|
+
when :olist
|
1319
1314
|
sibling_trait, implicit_style = resolve_ordered_list_marker(sibling_trait, (ordinal = list_block.items.size), true, reader)
|
1320
1315
|
list_item.marker = sibling_trait
|
1321
1316
|
if ordinal == 0 && !style
|
1322
1317
|
# using list level makes more sense, but we don't track it
|
1323
1318
|
# basing style on marker level is compliant with AsciiDoc.py
|
1324
|
-
list_block.style = implicit_style || (
|
1319
|
+
list_block.style = implicit_style || (ORDERED_LIST_STYLES[sibling_trait.length - 1] || 'arabic').to_s
|
1325
1320
|
end
|
1326
1321
|
if item_text.start_with?('[[') && LeadingInlineAnchorRx =~ item_text
|
1327
1322
|
catalog_inline_anchor $1, $2, list_item, reader
|
@@ -1447,95 +1442,89 @@ class Parser
|
|
1447
1442
|
# FIXME to be AsciiDoc compliant, we shouldn't break if style in attribute line is "literal" (i.e., [literal])
|
1448
1443
|
elsif dlist && continuation != :active && (BlockAttributeLineRx.match? this_line)
|
1449
1444
|
break
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
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 }
|
1462
|
-
else
|
1463
|
-
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1464
|
-
end
|
1465
|
-
continuation = :inactive
|
1466
|
-
# let block metadata play out until we find the block
|
1467
|
-
elsif (BlockTitleRx.match? this_line) || (BlockAttributeLineRx.match? this_line) || (AttributeEntryRx.match? this_line)
|
1468
|
-
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 }
|
1469
1456
|
else
|
1470
|
-
|
1471
|
-
within_nested_list = true
|
1472
|
-
if nested_list_type == :dlist && $3.nil_or_empty?
|
1473
|
-
# get greedy again
|
1474
|
-
has_text = false
|
1475
|
-
end
|
1476
|
-
end
|
1477
|
-
buffer << this_line
|
1478
|
-
continuation = :inactive
|
1457
|
+
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1479
1458
|
end
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
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
|
1487
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
|
1488
1482
|
|
1489
|
-
|
1490
|
-
|
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 })
|
1491
1492
|
buffer << this_line
|
1492
|
-
else
|
1493
|
-
# has_text is only relevant for dlist, which is more greedy until it has text for an item
|
1494
|
-
# for all other lists, has_text is always true
|
1495
|
-
# in this block, we have to see whether we stay in the list
|
1496
|
-
if has_text
|
1497
|
-
# TODO any way to combine this with the check after skipping blank lines?
|
1498
|
-
if is_sibling_list_item?(this_line, list_type, sibling_trait)
|
1499
|
-
break
|
1500
|
-
elsif nested_list_type = NESTABLE_LIST_CONTEXTS.find {|ctx| ListRxMap[ctx] =~ this_line }
|
1501
|
-
buffer << this_line
|
1502
|
-
within_nested_list = true
|
1503
|
-
if nested_list_type == :dlist && $3.nil_or_empty?
|
1504
|
-
# get greedy again
|
1505
|
-
has_text = false
|
1506
|
-
end
|
1507
|
-
# slurp up any literal paragraph offset by blank lines
|
1508
|
-
# NOTE we have to check for indented list items first
|
1509
|
-
elsif LiteralParagraphRx.match? this_line
|
1510
|
-
reader.unshift_line this_line
|
1511
|
-
if dlist
|
1512
|
-
# we may be in an indented list disguised as a literal paragraph
|
1513
|
-
# so we need to make sure we don't slurp up a legitimate sibling
|
1514
|
-
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 }
|
1515
|
-
else
|
1516
|
-
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1517
|
-
end
|
1518
|
-
else
|
1519
|
-
break
|
1520
|
-
end
|
1521
|
-
else # only dlist in need of item text, so slurp it up!
|
1522
|
-
# pop the blank line so it's not interpretted as a list continuation
|
1523
|
-
buffer.pop unless within_nested_list
|
1524
|
-
buffer << this_line
|
1525
|
-
has_text = true
|
1526
|
-
end
|
1527
|
-
end
|
1528
|
-
else
|
1529
|
-
has_text = true unless this_line.empty?
|
1530
|
-
if nested_list_type = (within_nested_list ? [:dlist] : NESTABLE_LIST_CONTEXTS).find {|ctx| ListRxMap[ctx] =~ this_line }
|
1531
1493
|
within_nested_list = true
|
1532
1494
|
if nested_list_type == :dlist && $3.nil_or_empty?
|
1533
1495
|
# get greedy again
|
1534
1496
|
has_text = false
|
1535
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
|
1536
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
|
1537
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
|
1538
1526
|
end
|
1527
|
+
buffer << this_line
|
1539
1528
|
end
|
1540
1529
|
this_line = nil
|
1541
1530
|
end
|
@@ -1576,12 +1565,6 @@ class Parser
|
|
1576
1565
|
sect_style = attributes[1]
|
1577
1566
|
sect_id, sect_reftext, sect_title, sect_level, sect_atx = parse_section_title reader, document, attributes['id']
|
1578
1567
|
|
1579
|
-
if sect_reftext
|
1580
|
-
attributes['reftext'] = sect_reftext
|
1581
|
-
else
|
1582
|
-
sect_reftext = attributes['reftext']
|
1583
|
-
end
|
1584
|
-
|
1585
1568
|
if sect_style
|
1586
1569
|
if book && sect_style == 'abstract'
|
1587
1570
|
sect_name, sect_level = 'chapter', 1
|
@@ -1600,6 +1583,7 @@ class Parser
|
|
1600
1583
|
sect_name = 'section'
|
1601
1584
|
end
|
1602
1585
|
|
1586
|
+
attributes['reftext'] = sect_reftext if sect_reftext
|
1603
1587
|
section = Section.new parent, sect_level
|
1604
1588
|
section.id, section.title, section.sectname, section.source_location = sect_id, sect_title, sect_name, source_location
|
1605
1589
|
if sect_special
|
@@ -1607,7 +1591,7 @@ class Parser
|
|
1607
1591
|
if sect_numbered
|
1608
1592
|
section.numbered = true
|
1609
1593
|
elsif document.attributes['sectnums'] == 'all'
|
1610
|
-
section.numbered = book && sect_level == 1 ? :chapter : true
|
1594
|
+
section.numbered = (book && sect_level == 1 ? :chapter : true)
|
1611
1595
|
end
|
1612
1596
|
elsif document.attributes['sectnums'] && sect_level > 0
|
1613
1597
|
# NOTE a special section here is guaranteed to be nested in another section
|
@@ -1619,7 +1603,7 @@ class Parser
|
|
1619
1603
|
# generate an ID if one was not embedded or specified as anchor above section title
|
1620
1604
|
if (id = section.id || (section.id = (document.attributes.key? 'sectids') ? (generated_id = Section.generate_id section.title, document) : nil))
|
1621
1605
|
# convert title to resolve attributes while in scope
|
1622
|
-
section.title
|
1606
|
+
section.title unless generated_id || !(sect_title.include? ATTR_REF_HEAD)
|
1623
1607
|
unless document.register :refs, [id, section]
|
1624
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))
|
1625
1609
|
end
|
@@ -1638,9 +1622,8 @@ class Parser
|
|
1638
1622
|
#
|
1639
1623
|
# Returns the Integer section level if the Reader is positioned at a section title or nil otherwise
|
1640
1624
|
def self.is_next_line_section?(reader, attributes)
|
1641
|
-
if (style = attributes[1]) && (style == 'discrete' || style == 'float')
|
1642
|
-
|
1643
|
-
elsif Compliance.underline_style_section_titles
|
1625
|
+
return if (style = attributes[1]) && (style == 'discrete' || style == 'float')
|
1626
|
+
if Compliance.underline_style_section_titles
|
1644
1627
|
next_lines = reader.peek_lines 2, style && style == 'comment'
|
1645
1628
|
is_section_title?(next_lines[0] || '', next_lines[1])
|
1646
1629
|
else
|
@@ -1881,13 +1864,12 @@ class Parser
|
|
1881
1864
|
if explicit
|
1882
1865
|
# rebuild implicit author names to reparse
|
1883
1866
|
authors.each_with_index do |author, idx|
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
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 ' '
|
1891
1873
|
end if sparse
|
1892
1874
|
# process as names only
|
1893
1875
|
author_metadata = process_authors authors, true, false
|
@@ -2070,6 +2052,7 @@ class Parser
|
|
2070
2052
|
return true
|
2071
2053
|
end
|
2072
2054
|
end
|
2055
|
+
nil
|
2073
2056
|
end
|
2074
2057
|
|
2075
2058
|
# Process consecutive attribute entry lines, ignoring adjacent line comments and comment blocks.
|
@@ -2174,9 +2157,10 @@ class Parser
|
|
2174
2157
|
#
|
2175
2158
|
# Returns the String 0-index marker for this list item
|
2176
2159
|
def self.resolve_list_marker(list_type, marker, ordinal = 0, validate = false, reader = nil)
|
2177
|
-
|
2160
|
+
case list_type
|
2161
|
+
when :ulist
|
2178
2162
|
marker
|
2179
|
-
|
2163
|
+
when :olist
|
2180
2164
|
resolve_ordered_list_marker(marker, ordinal, validate, reader)[0]
|
2181
2165
|
else # :colist
|
2182
2166
|
'<1>'
|
@@ -2479,7 +2463,7 @@ class Parser
|
|
2479
2463
|
|
2480
2464
|
if pos == :start
|
2481
2465
|
if line.include? delimiter
|
2482
|
-
spec_part,
|
2466
|
+
spec_part, _, rest = line.partition delimiter
|
2483
2467
|
if (m = CellSpecStartRx.match spec_part)
|
2484
2468
|
return [{}, rest] if m[0].empty?
|
2485
2469
|
else
|
@@ -2488,14 +2472,12 @@ class Parser
|
|
2488
2472
|
else
|
2489
2473
|
return [nil, line]
|
2490
2474
|
end
|
2491
|
-
|
2492
|
-
if
|
2493
|
-
|
2494
|
-
|
2495
|
-
|
2496
|
-
|
2497
|
-
return [{}, line]
|
2498
|
-
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]
|
2499
2481
|
end
|
2500
2482
|
|
2501
2483
|
spec = {}
|
@@ -2503,10 +2485,11 @@ class Parser
|
|
2503
2485
|
colspec, rowspec = m[1].split '.'
|
2504
2486
|
colspec = colspec.nil_or_empty? ? 1 : colspec.to_i
|
2505
2487
|
rowspec = rowspec.nil_or_empty? ? 1 : rowspec.to_i
|
2506
|
-
|
2488
|
+
case m[2]
|
2489
|
+
when '+'
|
2507
2490
|
spec['colspan'] = colspec unless colspec == 1
|
2508
2491
|
spec['rowspan'] = rowspec unless rowspec == 1
|
2509
|
-
|
2492
|
+
when '*'
|
2510
2493
|
spec['repeatcol'] = colspec unless colspec == 1
|
2511
2494
|
end
|
2512
2495
|
end
|
@@ -2531,7 +2514,7 @@ class Parser
|
|
2531
2514
|
# Public: Parse the first positional attribute and assign named attributes
|
2532
2515
|
#
|
2533
2516
|
# Parse the first positional attribute to extract the style, role and id
|
2534
|
-
# parts, assign the values to their
|
2517
|
+
# parts, assign the values to their corresponding attribute keys and return
|
2535
2518
|
# the parsed style from the first positional attribute.
|
2536
2519
|
#
|
2537
2520
|
# attributes - The Hash of attributes to process and update
|
@@ -2571,7 +2554,7 @@ class Parser
|
|
2571
2554
|
accum = ''
|
2572
2555
|
name = :option
|
2573
2556
|
else
|
2574
|
-
accum
|
2557
|
+
accum += c
|
2575
2558
|
end
|
2576
2559
|
end
|
2577
2560
|
|
@@ -2662,9 +2645,9 @@ class Parser
|
|
2662
2645
|
if tab_size > 0 && lines.any? {|line| line.include? TAB }
|
2663
2646
|
full_tab_space = ' ' * tab_size
|
2664
2647
|
lines.map! do |line|
|
2665
|
-
if line.empty?
|
2648
|
+
if line.empty? || (tab_idx = line.index TAB).nil?
|
2666
2649
|
line
|
2667
|
-
|
2650
|
+
else
|
2668
2651
|
if tab_idx == 0
|
2669
2652
|
leading_tabs = 0
|
2670
2653
|
line.each_byte do |b|
|
@@ -2682,22 +2665,20 @@ class Parser
|
|
2682
2665
|
if c == TAB
|
2683
2666
|
# calculate how many spaces this tab represents, then replace tab with spaces
|
2684
2667
|
if (offset = idx + spaces_added) % tab_size == 0
|
2685
|
-
spaces_added +=
|
2686
|
-
result
|
2668
|
+
spaces_added += tab_size - 1
|
2669
|
+
result += full_tab_space
|
2687
2670
|
else
|
2688
2671
|
unless (spaces = tab_size - offset % tab_size) == 1
|
2689
|
-
spaces_added +=
|
2672
|
+
spaces_added += spaces - 1
|
2690
2673
|
end
|
2691
|
-
result
|
2674
|
+
result += ' ' * spaces
|
2692
2675
|
end
|
2693
2676
|
else
|
2694
|
-
result
|
2677
|
+
result += c
|
2695
2678
|
end
|
2696
2679
|
idx += 1
|
2697
2680
|
end
|
2698
2681
|
result
|
2699
|
-
else
|
2700
|
-
line
|
2701
2682
|
end
|
2702
2683
|
end
|
2703
2684
|
end
|