asciidoctor 1.5.6 → 1.5.6.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +52 -1
- data/README-fr.adoc +21 -15
- data/README-jp.adoc +20 -8
- data/README-zh_CN.adoc +21 -9
- data/README.adoc +10 -7
- data/Rakefile +11 -4
- data/lib/asciidoctor.rb +30 -27
- data/lib/asciidoctor/abstract_node.rb +0 -5
- data/lib/asciidoctor/converter/html5.rb +1 -1
- data/lib/asciidoctor/extensions.rb +4 -5
- data/lib/asciidoctor/parser.rb +96 -105
- data/lib/asciidoctor/path_resolver.rb +7 -7
- data/lib/asciidoctor/reader.rb +88 -84
- data/lib/asciidoctor/substitutors.rb +19 -14
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +5 -5
- data/man/asciidoctor.adoc +1 -1
- data/test/attributes_test.rb +20 -20
- data/test/blocks_test.rb +51 -16
- data/test/converter_test.rb +10 -10
- data/test/document_test.rb +84 -69
- data/test/extensions_test.rb +48 -5
- data/test/invoker_test.rb +21 -21
- data/test/links_test.rb +4 -4
- data/test/lists_test.rb +10 -10
- data/test/paragraphs_test.rb +1 -1
- data/test/parser_test.rb +4 -4
- data/test/paths_test.rb +7 -0
- data/test/reader_test.rb +46 -12
- data/test/sections_test.rb +64 -42
- data/test/substitutions_test.rb +34 -14
- data/test/tables_test.rb +2 -2
- data/test/text_test.rb +12 -10
- metadata +3 -3
@@ -561,11 +561,6 @@ class AbstractNode
|
|
561
561
|
:target_name => asset_name, :recover => autocorrect)
|
562
562
|
end
|
563
563
|
|
564
|
-
# Public: Calculate the relative path to this absolute filename from the Document#base_dir
|
565
|
-
#def relative_path(filename)
|
566
|
-
# (@path_resolver ||= PathResolver.new).relative_path filename, @document.base_dir
|
567
|
-
#end
|
568
|
-
|
569
564
|
# Public: Check whether the specified String is a URI by
|
570
565
|
# matching it against the Asciidoctor::UriSniffRx regex.
|
571
566
|
#
|
@@ -1025,7 +1025,7 @@ Your browser does not support the video tag.
|
|
1025
1025
|
def inline_anchor node
|
1026
1026
|
case node.type
|
1027
1027
|
when :xref
|
1028
|
-
unless (text = node.text)
|
1028
|
+
unless (text = node.text) || (text = node.attributes['path'])
|
1029
1029
|
if AbstractNode === (ref = node.document.catalog[:refs][refid = node.attributes['refid']])
|
1030
1030
|
text = ref.xreftext((@xrefstyle ||= node.document.attributes['xrefstyle'])) || %([#{refid}])
|
1031
1031
|
else
|
@@ -162,9 +162,8 @@ module Extensions
|
|
162
162
|
# QUESTION is parse_content the right method name? should we wrap in open block automatically?
|
163
163
|
def parse_content parent, content, attributes = nil
|
164
164
|
reader = Reader === content ? content : (Reader.new content)
|
165
|
-
while reader.
|
166
|
-
|
167
|
-
parent << block if block
|
165
|
+
while ((block = Parser.next_block reader, parent, (attributes ? attributes.dup : {})) && parent << block) ||
|
166
|
+
reader.has_more_lines?
|
168
167
|
end
|
169
168
|
parent
|
170
169
|
end
|
@@ -486,7 +485,7 @@ module Extensions
|
|
486
485
|
include SyntaxDsl
|
487
486
|
|
488
487
|
def contexts *value
|
489
|
-
option :contexts, value.flatten
|
488
|
+
option :contexts, value.flatten.to_set
|
490
489
|
end
|
491
490
|
alias on_contexts contexts
|
492
491
|
alias on_context contexts
|
@@ -1426,7 +1425,7 @@ module Extensions
|
|
1426
1425
|
unless args.empty?
|
1427
1426
|
raise ::ArgumentError, %(Wrong number of arguments (#{argc} for 1..2))
|
1428
1427
|
end
|
1429
|
-
groups[name] = resolved_group
|
1428
|
+
groups[name.to_sym] = resolved_group
|
1430
1429
|
end
|
1431
1430
|
|
1432
1431
|
# Public: Unregister all statically-registered extension groups.
|
data/lib/asciidoctor/parser.rb
CHANGED
@@ -133,12 +133,12 @@ class Parser
|
|
133
133
|
# if the first line is the document title, add a header to the document and parse the header metadata
|
134
134
|
if implicit_doctitle
|
135
135
|
source_location = reader.cursor if document.sourcemap
|
136
|
-
document.id, _, doctitle, _,
|
136
|
+
document.id, _, doctitle, _, atx = parse_section_title reader, document
|
137
137
|
unless assigned_doctitle
|
138
138
|
document.title = assigned_doctitle = doctitle
|
139
139
|
end
|
140
140
|
# default to compat-mode if document uses atx-style doctitle
|
141
|
-
document.set_attr 'compat-mode' unless
|
141
|
+
document.set_attr 'compat-mode' unless atx || (document.attribute_locked? 'compat-mode')
|
142
142
|
if (separator = block_attributes.delete 'separator')
|
143
143
|
document.set_attr 'title-separator', separator unless document.attribute_locked? 'title-separator'
|
144
144
|
end
|
@@ -371,7 +371,7 @@ class Parser
|
|
371
371
|
end
|
372
372
|
end
|
373
373
|
|
374
|
-
reader.skip_blank_lines
|
374
|
+
reader.skip_blank_lines || break
|
375
375
|
end
|
376
376
|
|
377
377
|
if part
|
@@ -425,11 +425,8 @@ class Parser
|
|
425
425
|
# Returns a Block object built from the parsed content of the processed
|
426
426
|
# lines, or nothing if no block is found.
|
427
427
|
def self.next_block(reader, parent, attributes = {}, options = {})
|
428
|
-
#
|
429
|
-
skipped = reader.skip_blank_lines
|
430
|
-
|
431
|
-
# bail if we've reached the end of the parent block or document
|
432
|
-
return unless reader.has_more_lines?
|
428
|
+
# skip ahead to the block content; bail if we've reached the end of the reader
|
429
|
+
return unless (skipped = reader.skip_blank_lines)
|
433
430
|
|
434
431
|
# check for option to find list item text only
|
435
432
|
# if skipped a line, assume a list continuation was
|
@@ -444,12 +441,10 @@ class Parser
|
|
444
441
|
if options.fetch :parse_metadata, true
|
445
442
|
# read lines until there are no more metadata lines to read
|
446
443
|
while parse_block_metadata_line reader, document, attributes, options
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
attributes.clear
|
452
|
-
return
|
444
|
+
# discard the line just processed
|
445
|
+
reader.shift
|
446
|
+
# QUESTION should we clear the attributes? no known cases when it's necessary
|
447
|
+
reader.skip_blank_lines || return
|
453
448
|
end
|
454
449
|
end
|
455
450
|
|
@@ -515,7 +510,7 @@ class Parser
|
|
515
510
|
else
|
516
511
|
indented, ch0 = false, this_line.chr
|
517
512
|
layout_break_chars = md_syntax ? HYBRID_LAYOUT_BREAK_CHARS : LAYOUT_BREAK_CHARS
|
518
|
-
if (layout_break_chars.key? ch0) && (md_syntax ? (
|
513
|
+
if (layout_break_chars.key? ch0) && (md_syntax ? (ExtLayoutBreakRx.match? this_line) :
|
519
514
|
(this_line == ch0 * (ll = this_line.length) && ll > 2))
|
520
515
|
# NOTE we're letting break lines (horizontal rule, page_break, etc) have attributes
|
521
516
|
block = Block.new(parent, layout_break_chars[ch0], :content_model => :empty)
|
@@ -537,7 +532,7 @@ class Parser
|
|
537
532
|
block.parse_attributes(match[3], posattrs, :sub_input => true, :sub_result => false, :into => attributes)
|
538
533
|
# style doesn't have special meaning for media macros
|
539
534
|
attributes.delete 'style' if attributes.key? 'style'
|
540
|
-
if (target.include?
|
535
|
+
if (target.include? ATTR_REF_HEAD) && (target = block.sub_attributes target, :attribute_missing => 'drop-line').empty?
|
541
536
|
# retain as unparsed if attribute-missing is skip
|
542
537
|
if document.attributes.fetch('attribute-missing', Compliance.attribute_missing) == 'skip'
|
543
538
|
return Block.new(parent, :paragraph, :content_model => :simple, :source => [this_line])
|
@@ -594,7 +589,7 @@ class Parser
|
|
594
589
|
end
|
595
590
|
|
596
591
|
# haven't found anything yet, continue
|
597
|
-
if !indented &&
|
592
|
+
if !indented && CALLOUT_LIST_HEADS.include?(ch0 ||= this_line.chr) &&
|
598
593
|
(CalloutListSniffRx.match? this_line) && (match = CalloutListRx.match this_line)
|
599
594
|
block = List.new(parent, :colist)
|
600
595
|
attributes['style'] = 'arabic'
|
@@ -654,7 +649,7 @@ class Parser
|
|
654
649
|
break
|
655
650
|
|
656
651
|
elsif (style == 'float' || style == 'discrete') && (Compliance.underline_style_section_titles ?
|
657
|
-
(is_section_title? this_line,
|
652
|
+
(is_section_title? this_line, reader.peek_line) : !indented && (atx_section_title? this_line))
|
658
653
|
reader.unshift_line this_line
|
659
654
|
float_id, float_reftext, float_title, float_level, _ = parse_section_title(reader, document)
|
660
655
|
attributes['reftext'] = float_reftext if float_reftext
|
@@ -711,17 +706,9 @@ class Parser
|
|
711
706
|
|
712
707
|
# a normal paragraph: contiguous non-blank/non-continuation lines (left-indented or normal style)
|
713
708
|
else
|
709
|
+
# NOTE we only get here if there's at least one line that's not a line comment
|
714
710
|
lines = read_paragraph_lines reader, break_at_list, :skip_line_comments => true
|
715
711
|
|
716
|
-
# NOTE we need this logic because we've asked the reader to skip
|
717
|
-
# line comments, which may leave us w/ an empty buffer if those
|
718
|
-
# were the only lines found
|
719
|
-
if in_list && lines.empty?
|
720
|
-
# call advance since the reader preserved the last line
|
721
|
-
reader.advance
|
722
|
-
return
|
723
|
-
end
|
724
|
-
|
725
712
|
# NOTE don't check indented here since it's extremely rare
|
726
713
|
#if text_only || indented
|
727
714
|
if text_only
|
@@ -729,7 +716,7 @@ class Parser
|
|
729
716
|
# QUESTION do we even need to shift since whitespace is normalized by XML in this case?
|
730
717
|
adjust_indentation! lines if indented && style == 'normal'
|
731
718
|
block = Block.new(parent, :paragraph, :content_model => :simple, :source => lines, :attributes => attributes)
|
732
|
-
elsif (
|
719
|
+
elsif (ADMONITION_STYLE_HEADS.include? ch0) && (this_line.include? ':') && (AdmonitionParagraphRx =~ this_line)
|
733
720
|
lines[0] = $' # string after match
|
734
721
|
attributes['name'] = admonition_name = (attributes['style'] = $1).downcase
|
735
722
|
attributes['textlabel'] = (attributes.delete 'caption') || document.attributes[%(#{admonition_name}-caption)]
|
@@ -743,8 +730,8 @@ class Parser
|
|
743
730
|
lines.pop while lines[-1].empty?
|
744
731
|
end
|
745
732
|
attributes['style'] = 'quote'
|
746
|
-
# NOTE will only detect
|
747
|
-
# TODO could assume a
|
733
|
+
# NOTE will only detect discrete (aka free-floating) headings
|
734
|
+
# TODO could assume a discrete heading when inside a block context
|
748
735
|
# FIXME Reader needs to be created w/ line info
|
749
736
|
block = build_block(:quote, :compound, false, parent, Reader.new(lines), attributes)
|
750
737
|
elsif ch0 == '"' && lines.size > 1 && (lines[-1].start_with? '-- ') && (lines[-2].end_with? '"')
|
@@ -783,6 +770,7 @@ class Parser
|
|
783
770
|
|
784
771
|
when :comment
|
785
772
|
build_block(block_context, :skip, terminator, parent, reader, attributes)
|
773
|
+
attributes.clear
|
786
774
|
return
|
787
775
|
|
788
776
|
when :example
|
@@ -879,7 +867,7 @@ class Parser
|
|
879
867
|
attributes['cloaked-context'] = cloaked_context
|
880
868
|
end
|
881
869
|
block = build_block block_context, content_model, terminator, parent, reader, attributes, :extension => extension
|
882
|
-
unless block
|
870
|
+
unless block
|
883
871
|
attributes.clear
|
884
872
|
return
|
885
873
|
end
|
@@ -938,7 +926,7 @@ class Parser
|
|
938
926
|
# returns the match data if this line is the first line of a delimited block or nil if not
|
939
927
|
def self.is_delimited_block? line, return_match_data = false
|
940
928
|
# highly optimized for best performance
|
941
|
-
return unless (line_len = line.length) > 1 &&
|
929
|
+
return unless (line_len = line.length) > 1 && DELIMITED_BLOCK_HEADS.include?(line.slice 0, 2)
|
942
930
|
# catches open block
|
943
931
|
if line_len == 2
|
944
932
|
tip = line
|
@@ -1009,14 +997,11 @@ class Parser
|
|
1009
997
|
# NOTE could invoke filter in here, before and after parsing
|
1010
998
|
def self.build_block(block_context, content_model, terminator, parent, reader, attributes, options = {})
|
1011
999
|
if content_model == :skip
|
1012
|
-
skip_processing = true
|
1013
|
-
parse_as_content_model = :simple
|
1000
|
+
skip_processing, parse_as_content_model = true, :simple
|
1014
1001
|
elsif content_model == :raw
|
1015
|
-
skip_processing = false
|
1016
|
-
parse_as_content_model = :simple
|
1002
|
+
skip_processing, parse_as_content_model = false, :simple
|
1017
1003
|
else
|
1018
|
-
skip_processing = false
|
1019
|
-
parse_as_content_model = content_model
|
1004
|
+
skip_processing, parse_as_content_model = false, content_model
|
1020
1005
|
end
|
1021
1006
|
|
1022
1007
|
if terminator.nil?
|
@@ -1041,18 +1026,15 @@ class Parser
|
|
1041
1026
|
block_reader = Reader.new reader.read_lines_until(:terminator => terminator, :skip_processing => skip_processing), reader.cursor
|
1042
1027
|
end
|
1043
1028
|
|
1044
|
-
if content_model == :skip
|
1045
|
-
attributes.clear
|
1046
|
-
# FIXME we shouldn't be mixing return types
|
1047
|
-
return lines
|
1048
|
-
end
|
1049
|
-
|
1050
1029
|
if content_model == :verbatim
|
1051
1030
|
if (indent = attributes['indent'])
|
1052
1031
|
adjust_indentation! lines, indent, (attributes['tabsize'] || parent.document.attributes['tabsize'])
|
1053
1032
|
elsif (tab_size = (attributes['tabsize'] || parent.document.attributes['tabsize']).to_i) > 0
|
1054
1033
|
adjust_indentation! lines, nil, tab_size
|
1055
1034
|
end
|
1035
|
+
elsif content_model == :skip
|
1036
|
+
# QUESTION should we still invoke process method if extension is specified?
|
1037
|
+
return
|
1056
1038
|
end
|
1057
1039
|
|
1058
1040
|
if (extension = options[:extension])
|
@@ -1068,7 +1050,6 @@ class Parser
|
|
1068
1050
|
block_reader = Reader.new lines
|
1069
1051
|
end
|
1070
1052
|
else
|
1071
|
-
# FIXME need a test to verify this returns nil at the right time
|
1072
1053
|
return
|
1073
1054
|
end
|
1074
1055
|
else
|
@@ -1100,8 +1081,7 @@ class Parser
|
|
1100
1081
|
#
|
1101
1082
|
# Returns nothing.
|
1102
1083
|
def self.parse_blocks(reader, parent)
|
1103
|
-
while (block = next_block reader, parent)
|
1104
|
-
parent << block
|
1084
|
+
while ((block = next_block reader, parent) && parent << block) || reader.has_more_lines?
|
1105
1085
|
end
|
1106
1086
|
end
|
1107
1087
|
|
@@ -1155,7 +1135,7 @@ class Parser
|
|
1155
1135
|
list_block << list_item if list_item
|
1156
1136
|
list_item = nil
|
1157
1137
|
|
1158
|
-
reader.skip_blank_lines
|
1138
|
+
reader.skip_blank_lines || break
|
1159
1139
|
end
|
1160
1140
|
|
1161
1141
|
list_block
|
@@ -1190,13 +1170,13 @@ class Parser
|
|
1190
1170
|
text.scan(InlineAnchorScanRx) do
|
1191
1171
|
if (id = $1)
|
1192
1172
|
if (reftext = $2)
|
1193
|
-
next if (reftext.include?
|
1173
|
+
next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1194
1174
|
end
|
1195
1175
|
else
|
1196
1176
|
id = $3
|
1197
1177
|
if (reftext = $4)
|
1198
1178
|
reftext = reftext.gsub '\]', ']' if reftext.include? ']'
|
1199
|
-
next if (reftext.include?
|
1179
|
+
next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1200
1180
|
end
|
1201
1181
|
end
|
1202
1182
|
unless document.register :refs, [id, (Inline.new block, :anchor, reftext, :type => :ref, :id => id), reftext]
|
@@ -1306,7 +1286,7 @@ class Parser
|
|
1306
1286
|
end
|
1307
1287
|
|
1308
1288
|
# first skip the line with the marker / term
|
1309
|
-
reader.
|
1289
|
+
reader.shift
|
1310
1290
|
list_item_reader = Reader.new read_lines_for_list_item(reader, list_type, sibling_trait, has_text), reader.cursor
|
1311
1291
|
if list_item_reader.has_more_lines?
|
1312
1292
|
# NOTE peek on the other side of any comment lines
|
@@ -1329,13 +1309,10 @@ class Parser
|
|
1329
1309
|
# only relevant for :dlist
|
1330
1310
|
options = {:text => !has_text}
|
1331
1311
|
|
1332
|
-
# we can look for blocks until
|
1333
|
-
#
|
1334
|
-
|
1335
|
-
|
1336
|
-
if (new_block = next_block(list_item_reader, list_item, {}, options))
|
1337
|
-
list_item << new_block
|
1338
|
-
end
|
1312
|
+
# we can look for blocks until lines are exhausted without worrying about
|
1313
|
+
# sections since reader is confined to boundaries of list
|
1314
|
+
while ((block = next_block list_item_reader, list_item, {}, options) && list_item << block) ||
|
1315
|
+
list_item_reader.has_more_lines?
|
1339
1316
|
end
|
1340
1317
|
|
1341
1318
|
list_item.fold_first(continuation_connects_first_block, content_adjacent)
|
@@ -1463,10 +1440,10 @@ class Parser
|
|
1463
1440
|
elsif prev_line && prev_line.empty?
|
1464
1441
|
# advance to the next line of content
|
1465
1442
|
if this_line.empty?
|
1466
|
-
|
1467
|
-
this_line = reader.read_line
|
1468
|
-
# stop reading if we hit
|
1469
|
-
break
|
1443
|
+
# stop reading if we reach eof
|
1444
|
+
break unless (this_line = reader.skip_blank_lines && reader.read_line)
|
1445
|
+
# stop reading if we hit a sibling list item
|
1446
|
+
break if is_sibling_list_item? this_line, list_type, sibling_trait
|
1470
1447
|
end
|
1471
1448
|
|
1472
1449
|
if this_line == LIST_CONTINUATION
|
@@ -1554,7 +1531,7 @@ class Parser
|
|
1554
1531
|
def self.initialize_section reader, parent, attributes = {}
|
1555
1532
|
document = parent.document
|
1556
1533
|
source_location = reader.cursor if document.sourcemap
|
1557
|
-
sect_id, sect_reftext, sect_title, sect_level,
|
1534
|
+
sect_id, sect_reftext, sect_title, sect_level, atx = parse_section_title reader, document
|
1558
1535
|
if sect_reftext
|
1559
1536
|
attributes['reftext'] = sect_reftext
|
1560
1537
|
elsif attributes.key? 'reftext'
|
@@ -1602,7 +1579,7 @@ class Parser
|
|
1602
1579
|
if (id = section.id ||= (attributes['id'] ||
|
1603
1580
|
((document.attributes.key? 'sectids') ? (Section.generate_id section.title, document) : nil)))
|
1604
1581
|
unless document.register :refs, [id, section, sect_reftext || section.title]
|
1605
|
-
warn %(asciidoctor: WARNING: #{reader.path}: line #{reader.lineno - (
|
1582
|
+
warn %(asciidoctor: WARNING: #{reader.path}: line #{reader.lineno - (atx ? 1 : 2)}: id assigned to section already in use: #{id})
|
1606
1583
|
end
|
1607
1584
|
end
|
1608
1585
|
|
@@ -1619,10 +1596,12 @@ class Parser
|
|
1619
1596
|
#
|
1620
1597
|
# Returns the Integer section level if the Reader is positioned at a section title or nil otherwise
|
1621
1598
|
def self.is_next_line_section?(reader, attributes)
|
1622
|
-
if
|
1599
|
+
if (style = attributes[1]) && (style.start_with? 'discrete', 'float') && (DiscreteHeadingStyleRx.match? style)
|
1623
1600
|
return
|
1624
1601
|
elsif reader.has_more_lines?
|
1625
|
-
Compliance.underline_style_section_titles ?
|
1602
|
+
Compliance.underline_style_section_titles ?
|
1603
|
+
is_section_title?(*reader.peek_lines(2, style && style == 'comment')) :
|
1604
|
+
atx_section_title?(reader.peek_line)
|
1626
1605
|
end
|
1627
1606
|
end
|
1628
1607
|
|
@@ -1641,25 +1620,37 @@ class Parser
|
|
1641
1620
|
end
|
1642
1621
|
end
|
1643
1622
|
|
1644
|
-
# Public: Checks
|
1623
|
+
# Public: Checks whether the lines given are an atx or setext section title.
|
1645
1624
|
#
|
1646
|
-
# line1 -
|
1647
|
-
# line2 -
|
1625
|
+
# line1 - [String] candidate title.
|
1626
|
+
# line2 - [String] candidate underline (default: nil).
|
1648
1627
|
#
|
1649
|
-
# Returns the Integer section level if these lines are a section title
|
1628
|
+
# Returns the [Integer] section level if these lines are a section title, otherwise nothing.
|
1650
1629
|
def self.is_section_title?(line1, line2 = nil)
|
1651
|
-
|
1630
|
+
atx_section_title?(line1) || (line2.nil_or_empty? ? nil : setext_section_title?(line1, line2))
|
1652
1631
|
end
|
1653
1632
|
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1633
|
+
# Checks whether the line given is an atx section title.
|
1634
|
+
#
|
1635
|
+
# The level returned is 1 less than number of leading markers.
|
1636
|
+
#
|
1637
|
+
# line - [String] candidate title with leading atx marker.
|
1638
|
+
#
|
1639
|
+
# Returns the [Integer] section level if this line is an atx section title, otherwise nothing.
|
1640
|
+
def self.atx_section_title? line
|
1641
|
+
if Compliance.markdown_syntax ? ((line.start_with? '=', '#') && ExtAtxSectionTitleRx =~ line) :
|
1642
|
+
((line.start_with? '=') && AtxSectionTitleRx =~ line)
|
1658
1643
|
$1.length - 1
|
1659
1644
|
end
|
1660
1645
|
end
|
1661
1646
|
|
1662
|
-
|
1647
|
+
# Checks whether the lines given are an setext section title.
|
1648
|
+
#
|
1649
|
+
# line1 - [String] candidate title
|
1650
|
+
# line2 - [String] candidate underline
|
1651
|
+
#
|
1652
|
+
# Returns the [Integer] section level if these lines are an setext section title, otherwise nothing.
|
1653
|
+
def self.setext_section_title? line1, line2
|
1663
1654
|
if (level = SETEXT_SECTION_LEVELS[line2_ch1 = line2.chr]) &&
|
1664
1655
|
line2_ch1 * (line2_len = line2.length) == line2 && SetextSectionTitleRx.match?(line1) &&
|
1665
1656
|
(line_length(line1) - line2_len).abs < 2
|
@@ -1669,18 +1660,20 @@ class Parser
|
|
1669
1660
|
|
1670
1661
|
# Internal: Parse the section title from the current position of the reader
|
1671
1662
|
#
|
1672
|
-
# Parse
|
1663
|
+
# Parse an atx (single-line) or setext (underlined) section title. After this method is called,
|
1673
1664
|
# the Reader will be positioned at the line after the section title.
|
1674
1665
|
#
|
1675
|
-
#
|
1676
|
-
#
|
1666
|
+
# For efficiency, we don't reuse methods internally that check for a section title.
|
1667
|
+
#
|
1668
|
+
# reader - the source [Reader], positioned at a section title.
|
1669
|
+
# document - the current [Document].
|
1677
1670
|
#
|
1678
1671
|
# Examples
|
1679
1672
|
#
|
1680
1673
|
# reader.lines
|
1681
1674
|
# # => ["Foo", "~~~"]
|
1682
1675
|
#
|
1683
|
-
# id, reftext, title, level,
|
1676
|
+
# id, reftext, title, level, atx = parse_section_title(reader, document)
|
1684
1677
|
#
|
1685
1678
|
# title
|
1686
1679
|
# # => "Foo"
|
@@ -1688,13 +1681,13 @@ class Parser
|
|
1688
1681
|
# # => 2
|
1689
1682
|
# id
|
1690
1683
|
# # => nil
|
1691
|
-
#
|
1684
|
+
# atx
|
1692
1685
|
# # => false
|
1693
1686
|
#
|
1694
1687
|
# line1
|
1695
1688
|
# # => "==== Foo"
|
1696
1689
|
#
|
1697
|
-
# id, reftext, title, level,
|
1690
|
+
# id, reftext, title, level, atx = parse_section_title(reader, document)
|
1698
1691
|
#
|
1699
1692
|
# title
|
1700
1693
|
# # => "Foo"
|
@@ -1702,22 +1695,20 @@ class Parser
|
|
1702
1695
|
# # => 3
|
1703
1696
|
# id
|
1704
1697
|
# # => nil
|
1705
|
-
#
|
1698
|
+
# atx
|
1706
1699
|
# # => true
|
1707
1700
|
#
|
1708
|
-
#
|
1709
|
-
#
|
1710
|
-
#
|
1711
|
-
#--
|
1712
|
-
# NOTE for efficiency, we don't reuse methods that check for a section title
|
1701
|
+
# Returns an 5-element [Array] containing the id (String), reftext (String),
|
1702
|
+
# title (String), level (Integer), and flag (Boolean) indicating whether an
|
1703
|
+
# atx section title was matched, or nothing.
|
1713
1704
|
def self.parse_section_title(reader, document)
|
1714
1705
|
sect_id = sect_reftext = nil
|
1715
1706
|
line1 = reader.read_line
|
1716
1707
|
|
1717
|
-
|
1718
|
-
|
1708
|
+
if Compliance.markdown_syntax ? ((line1.start_with? '=', '#') && ExtAtxSectionTitleRx =~ line1) :
|
1709
|
+
((line1.start_with? '=') && AtxSectionTitleRx =~ line1)
|
1719
1710
|
# NOTE level is 1 less than number of line markers
|
1720
|
-
sect_level, sect_title,
|
1711
|
+
sect_level, sect_title, atx = $1.length - 1, $2, true
|
1721
1712
|
if sect_title.end_with?(']]') && InlineSectionAnchorRx =~ sect_title && !$1 # escaped
|
1722
1713
|
sect_title, sect_id, sect_reftext = (sect_title.slice 0, sect_title.length - $&.length), $2, $3
|
1723
1714
|
end
|
@@ -1725,16 +1716,16 @@ class Parser
|
|
1725
1716
|
(sect_level = SETEXT_SECTION_LEVELS[line2_ch1 = line2.chr]) &&
|
1726
1717
|
line2_ch1 * (line2_len = line2.length) == line2 && (sect_title = SetextSectionTitleRx =~ line1 && $1) &&
|
1727
1718
|
(line_length(line1) - line2_len).abs < 2
|
1728
|
-
|
1719
|
+
atx = false
|
1729
1720
|
if sect_title.end_with?(']]') && InlineSectionAnchorRx =~ sect_title && !$1 # escaped
|
1730
1721
|
sect_title, sect_id, sect_reftext = (sect_title.slice 0, sect_title.length - $&.length), $2, $3
|
1731
1722
|
end
|
1732
|
-
reader.
|
1723
|
+
reader.shift
|
1733
1724
|
else
|
1734
1725
|
raise %(Unrecognized section at #{reader.prev_line_info})
|
1735
1726
|
end
|
1736
1727
|
sect_level += document.attr('leveloffset').to_i if document.attr?('leveloffset')
|
1737
|
-
[sect_id, sect_reftext, sect_title, sect_level,
|
1728
|
+
[sect_id, sect_reftext, sect_title, sect_level, atx]
|
1738
1729
|
end
|
1739
1730
|
|
1740
1731
|
# Public: Calculate the number of unicode characters in the line, excluding the endline
|
@@ -1981,8 +1972,8 @@ class Parser
|
|
1981
1972
|
def self.parse_block_metadata_lines reader, document, attributes = {}, options = {}
|
1982
1973
|
while parse_block_metadata_line reader, document, attributes, options
|
1983
1974
|
# discard the line just processed
|
1984
|
-
reader.
|
1985
|
-
reader.skip_blank_lines
|
1975
|
+
reader.shift
|
1976
|
+
reader.skip_blank_lines || break
|
1986
1977
|
end
|
1987
1978
|
attributes
|
1988
1979
|
end
|
@@ -2016,7 +2007,7 @@ class Parser
|
|
2016
2007
|
# NOTE registration of id and reftext is deferred until block is processed
|
2017
2008
|
attributes['id'] = $1
|
2018
2009
|
if (reftext = $2)
|
2019
|
-
attributes['reftext'] = (reftext.include?
|
2010
|
+
attributes['reftext'] = (reftext.include? ATTR_REF_HEAD) ? (document.sub_attributes reftext) : reftext
|
2020
2011
|
end
|
2021
2012
|
return true
|
2022
2013
|
end
|
@@ -2054,7 +2045,7 @@ class Parser
|
|
2054
2045
|
reader.skip_comment_lines
|
2055
2046
|
while process_attribute_entry reader, document, attributes
|
2056
2047
|
# discard line just processed
|
2057
|
-
reader.
|
2048
|
+
reader.shift
|
2058
2049
|
reader.skip_comment_lines
|
2059
2050
|
end
|
2060
2051
|
end
|
@@ -2266,11 +2257,11 @@ class Parser
|
|
2266
2257
|
explicit_colspecs = true
|
2267
2258
|
end
|
2268
2259
|
|
2269
|
-
skipped = table_reader.skip_blank_lines
|
2270
|
-
|
2260
|
+
skipped = table_reader.skip_blank_lines || 0
|
2271
2261
|
parser_ctx = Table::ParserContext.new table_reader, table, attributes
|
2272
2262
|
format, loop_idx, implicit_header_boundary = parser_ctx.format, -1, nil
|
2273
2263
|
implicit_header = true unless skipped > 0 || (attributes.key? 'header-option') || (attributes.key? 'noheader-option')
|
2264
|
+
|
2274
2265
|
while (line = table_reader.read_line)
|
2275
2266
|
if (loop_idx += 1) > 0 && line.empty?
|
2276
2267
|
line = nil
|
@@ -2361,11 +2352,11 @@ class Parser
|
|
2361
2352
|
end
|
2362
2353
|
end
|
2363
2354
|
|
2364
|
-
|
2365
|
-
|
2366
|
-
|
2367
|
-
|
2368
|
-
|
2355
|
+
# NOTE cell may already be closed if table format is csv or dsv
|
2356
|
+
if parser_ctx.cell_open?
|
2357
|
+
parser_ctx.close_cell true unless table_reader.has_more_lines?
|
2358
|
+
else
|
2359
|
+
table_reader.skip_blank_lines || break
|
2369
2360
|
end
|
2370
2361
|
end
|
2371
2362
|
|