asciidoctor 2.0.10 → 2.0.17
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 +294 -30
- data/LICENSE +1 -1
- data/README-de.adoc +16 -20
- data/README-fr.adoc +15 -22
- data/README-jp.adoc +15 -26
- data/README-zh_CN.adoc +21 -25
- data/README.adoc +161 -138
- data/asciidoctor.gemspec +6 -13
- data/data/locale/attributes-ar.adoc +4 -3
- data/data/locale/attributes-be.adoc +23 -0
- data/data/locale/attributes-bg.adoc +4 -3
- data/data/locale/attributes-ca.adoc +6 -5
- data/data/locale/attributes-cs.adoc +4 -3
- data/data/locale/attributes-da.adoc +6 -5
- data/data/locale/attributes-de.adoc +4 -4
- data/data/locale/attributes-en.adoc +4 -4
- data/data/locale/attributes-es.adoc +6 -5
- data/data/locale/attributes-fa.adoc +4 -3
- data/data/locale/attributes-fi.adoc +4 -3
- data/data/locale/attributes-fr.adoc +8 -7
- data/data/locale/attributes-hu.adoc +4 -3
- data/data/locale/attributes-id.adoc +4 -3
- data/data/locale/attributes-it.adoc +6 -5
- data/data/locale/attributes-ja.adoc +4 -3
- data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
- data/data/locale/attributes-nb.adoc +4 -3
- data/data/locale/attributes-nl.adoc +6 -5
- data/data/locale/attributes-nn.adoc +4 -3
- data/data/locale/attributes-pl.adoc +8 -7
- data/data/locale/attributes-pt.adoc +6 -5
- data/data/locale/attributes-pt_BR.adoc +6 -5
- data/data/locale/attributes-ro.adoc +4 -3
- data/data/locale/attributes-ru.adoc +6 -5
- data/data/locale/attributes-sr.adoc +4 -4
- data/data/locale/attributes-sr_Latn.adoc +4 -4
- data/data/locale/attributes-sv.adoc +4 -4
- data/data/locale/attributes-th.adoc +23 -0
- data/data/locale/attributes-tr.adoc +4 -3
- data/data/locale/attributes-uk.adoc +6 -5
- data/data/locale/attributes-vi.adoc +23 -0
- data/data/locale/attributes-zh_CN.adoc +4 -3
- data/data/locale/attributes-zh_TW.adoc +4 -3
- data/data/reference/syntax.adoc +14 -7
- data/data/stylesheets/asciidoctor-default.css +76 -76
- data/data/stylesheets/coderay-asciidoctor.css +9 -9
- data/lib/asciidoctor/abstract_block.rb +20 -13
- data/lib/asciidoctor/abstract_node.rb +23 -12
- data/lib/asciidoctor/attribute_list.rb +64 -72
- data/lib/asciidoctor/block.rb +6 -6
- data/lib/asciidoctor/cli/invoker.rb +3 -2
- data/lib/asciidoctor/cli/options.rb +32 -31
- data/lib/asciidoctor/convert.rb +168 -162
- data/lib/asciidoctor/converter/docbook5.rb +49 -34
- data/lib/asciidoctor/converter/html5.rb +180 -139
- data/lib/asciidoctor/converter/manpage.rb +118 -90
- data/lib/asciidoctor/converter/template.rb +15 -13
- data/lib/asciidoctor/converter.rb +19 -16
- data/lib/asciidoctor/core_ext/hash/merge.rb +1 -1
- data/lib/asciidoctor/document.rb +77 -86
- data/lib/asciidoctor/extensions.rb +22 -16
- data/lib/asciidoctor/helpers.rb +20 -15
- data/lib/asciidoctor/list.rb +2 -6
- data/lib/asciidoctor/load.rb +103 -101
- data/lib/asciidoctor/logging.rb +10 -8
- data/lib/asciidoctor/parser.rb +211 -220
- data/lib/asciidoctor/path_resolver.rb +17 -15
- data/lib/asciidoctor/reader.rb +87 -79
- data/lib/asciidoctor/rx.rb +9 -7
- data/lib/asciidoctor/section.rb +7 -0
- data/lib/asciidoctor/substitutors.rb +167 -148
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +3 -2
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +13 -5
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +19 -11
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +35 -20
- data/lib/asciidoctor/syntax_highlighter.rb +16 -16
- data/lib/asciidoctor/table.rb +70 -43
- data/lib/asciidoctor/timings.rb +3 -3
- data/lib/asciidoctor/version.rb +1 -1
- data/lib/asciidoctor.rb +45 -19
- data/man/asciidoctor.1 +29 -31
- data/man/asciidoctor.adoc +35 -29
- metadata +17 -70
data/lib/asciidoctor/parser.rb
CHANGED
@@ -89,10 +89,10 @@ class Parser
|
|
89
89
|
#
|
90
90
|
# returns the Document object
|
91
91
|
def self.parse(reader, document, options = {})
|
92
|
-
block_attributes = parse_document_header(reader, document)
|
92
|
+
block_attributes = parse_document_header(reader, document, (header_only = options[:header_only]))
|
93
93
|
|
94
94
|
# NOTE don't use a postfix conditional here as it's known to confuse JRuby in certain circumstances
|
95
|
-
unless
|
95
|
+
unless header_only
|
96
96
|
while reader.has_more_lines?
|
97
97
|
new_section, block_attributes = next_section(reader, document, block_attributes)
|
98
98
|
if new_section
|
@@ -117,14 +117,15 @@ class Parser
|
|
117
117
|
# which are automatically removed by the reader.
|
118
118
|
#
|
119
119
|
# returns the Hash of orphan block attributes captured above the header
|
120
|
-
def self.parse_document_header(reader, document)
|
120
|
+
def self.parse_document_header(reader, document, header_only = false)
|
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,
|
126
126
|
# carry attributes over to the document body
|
127
127
|
if (implicit_doctitle = is_next_line_doctitle? reader, block_attrs, doc_attrs['leveloffset']) && block_attrs['title']
|
128
|
+
doc_attrs['authorcount'] = 0
|
128
129
|
return document.finalize_header block_attrs, false
|
129
130
|
end
|
130
131
|
|
@@ -144,7 +145,10 @@ class Parser
|
|
144
145
|
l0_section_title = nil
|
145
146
|
else
|
146
147
|
document.title = l0_section_title
|
147
|
-
doc_attrs['doctitle'] = doctitle_attr_val = document.
|
148
|
+
if (doc_attrs['doctitle'] = doctitle_attr_val = document.sub_specialchars l0_section_title).include? ATTR_REF_HEAD
|
149
|
+
# QUESTION should we defer substituting attributes until the end of the header? or should we substitute again if necessary?
|
150
|
+
doc_attrs['doctitle'] = doctitle_attr_val = document.sub_attributes doctitle_attr_val, attribute_missing: 'skip'
|
151
|
+
end
|
148
152
|
end
|
149
153
|
document.header.source_location = source_location if source_location
|
150
154
|
# default to compat-mode if document has setext doctitle
|
@@ -165,7 +169,7 @@ class Parser
|
|
165
169
|
end
|
166
170
|
block_attrs.clear
|
167
171
|
(modified_attrs = document.instance_variable_get :@attributes_modified).delete 'doctitle'
|
168
|
-
parse_header_metadata reader, document
|
172
|
+
parse_header_metadata reader, document, nil
|
169
173
|
if modified_attrs.include? 'doctitle'
|
170
174
|
if (val = doc_attrs['doctitle']).nil_or_empty? || val == doctitle_attr_val
|
171
175
|
doc_attrs['doctitle'] = doctitle_attr_val
|
@@ -176,10 +180,19 @@ class Parser
|
|
176
180
|
modified_attrs << 'doctitle'
|
177
181
|
end
|
178
182
|
document.register :refs, [doc_id, document] if doc_id
|
183
|
+
elsif (author = doc_attrs['author'])
|
184
|
+
author_metadata = process_authors author, true, false
|
185
|
+
author_metadata.delete 'authorinitials' if doc_attrs['authorinitials']
|
186
|
+
doc_attrs.update author_metadata
|
187
|
+
elsif (author = doc_attrs['authors'])
|
188
|
+
author_metadata = process_authors author, true
|
189
|
+
doc_attrs.update author_metadata
|
190
|
+
else
|
191
|
+
doc_attrs['authorcount'] = 0
|
179
192
|
end
|
180
193
|
|
181
194
|
# parse title and consume name section of manpage document
|
182
|
-
parse_manpage_header reader, document, block_attrs if document.doctype == 'manpage'
|
195
|
+
parse_manpage_header reader, document, block_attrs, header_only if document.doctype == 'manpage'
|
183
196
|
|
184
197
|
# NOTE block_attrs are the block-level attributes (not document attributes) that
|
185
198
|
# precede the first line of content (document title, first section or first block)
|
@@ -189,7 +202,7 @@ class Parser
|
|
189
202
|
# Public: Parses the manpage header of the AsciiDoc source read from the Reader
|
190
203
|
#
|
191
204
|
# returns Nothing
|
192
|
-
def self.parse_manpage_header(reader, document, block_attributes)
|
205
|
+
def self.parse_manpage_header(reader, document, block_attributes, header_only = false)
|
193
206
|
if ManpageTitleVolnumRx =~ (doc_attrs = document.attributes)['doctitle']
|
194
207
|
doc_attrs['manvolnum'] = manvolnum = $2
|
195
208
|
doc_attrs['mantitle'] = (((mantitle = $1).include? ATTR_REF_HEAD) ? (document.sub_attributes mantitle) : mantitle).downcase
|
@@ -206,6 +219,8 @@ class Parser
|
|
206
219
|
doc_attrs['docname'] = manname
|
207
220
|
doc_attrs['outfilesuffix'] = %(.#{manvolnum})
|
208
221
|
end
|
222
|
+
elsif header_only
|
223
|
+
# done
|
209
224
|
else
|
210
225
|
reader.skip_blank_lines
|
211
226
|
reader.save
|
@@ -215,9 +230,6 @@ class Parser
|
|
215
230
|
name_section = initialize_section reader, document, {}
|
216
231
|
name_section_buffer = (reader.read_lines_until break_on_blank_lines: true, skip_line_comments: true).map {|l| l.lstrip }.join ' '
|
217
232
|
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
233
|
if (manname = $1).include? ATTR_REF_HEAD
|
222
234
|
manname = document.sub_attributes manname
|
223
235
|
end
|
@@ -226,8 +238,14 @@ class Parser
|
|
226
238
|
else
|
227
239
|
mannames = [manname]
|
228
240
|
end
|
241
|
+
if (manpurpose = $2).include? ATTR_REF_HEAD
|
242
|
+
manpurpose = document.sub_attributes manpurpose
|
243
|
+
end
|
244
|
+
doc_attrs['manname-title'] ||= name_section.title
|
245
|
+
doc_attrs['manname-id'] = name_section.id if name_section.id
|
229
246
|
doc_attrs['manname'] = manname
|
230
247
|
doc_attrs['mannames'] = mannames
|
248
|
+
doc_attrs['manpurpose'] = manpurpose
|
231
249
|
if document.backend == 'manpage'
|
232
250
|
doc_attrs['docname'] = manname
|
233
251
|
doc_attrs['outfilesuffix'] = %(.#{manvolnum})
|
@@ -327,7 +345,7 @@ class Parser
|
|
327
345
|
if current_level == 0
|
328
346
|
part = book
|
329
347
|
elsif current_level == 1 && section.special
|
330
|
-
# NOTE technically preface
|
348
|
+
# NOTE technically preface sections are only permitted in the book doctype
|
331
349
|
unless (sectname = section.sectname) == 'appendix' || sectname == 'preface' || sectname == 'abstract'
|
332
350
|
expected_next_level = nil
|
333
351
|
end
|
@@ -415,9 +433,6 @@ class Parser
|
|
415
433
|
|
416
434
|
(intro || section).blocks << new_block
|
417
435
|
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
436
|
end
|
422
437
|
end
|
423
438
|
|
@@ -433,8 +448,10 @@ class Parser
|
|
433
448
|
# is treated like an untitled section
|
434
449
|
elsif preamble # implies parent == document
|
435
450
|
if preamble.blocks?
|
451
|
+
if book || document.blocks[1] || !Compliance.unwrap_standalone_preamble
|
452
|
+
preamble.source_location = preamble.blocks[0].source_location if document.sourcemap
|
436
453
|
# unwrap standalone preamble (i.e., document has no sections) except for books, if permissible
|
437
|
-
|
454
|
+
else
|
438
455
|
document.blocks.shift
|
439
456
|
while (child_block = preamble.blocks.shift)
|
440
457
|
document << child_block
|
@@ -447,10 +464,10 @@ class Parser
|
|
447
464
|
end
|
448
465
|
|
449
466
|
# The attributes returned here are orphaned attributes that fall at the end
|
450
|
-
# of a section that need to get
|
467
|
+
# of a section that need to get transferred to the next section
|
451
468
|
# see "trailing block attributes transfer to the following section" in
|
452
469
|
# test/attributes_test.rb for an example
|
453
|
-
[section
|
470
|
+
[section == parent ? nil : section, attributes.merge]
|
454
471
|
end
|
455
472
|
|
456
473
|
# Public: Parse and return the next Block at the Reader's current location
|
@@ -642,7 +659,7 @@ class Parser
|
|
642
659
|
if (default_attrs = ext_config[:default_attrs])
|
643
660
|
attributes.update(default_attrs) {|_, old_v| old_v }
|
644
661
|
end
|
645
|
-
if (block = extension.process_method[parent, target, attributes])
|
662
|
+
if (block = extension.process_method[parent, target, attributes]) && block != parent
|
646
663
|
attributes.replace block.attributes
|
647
664
|
break
|
648
665
|
else
|
@@ -818,8 +835,8 @@ class Parser
|
|
818
835
|
if comma_idx > 0
|
819
836
|
language = (language.slice 0, comma_idx).strip
|
820
837
|
attributes['linenums'] = '' if comma_idx < ll - 4
|
821
|
-
|
822
|
-
attributes['linenums'] = ''
|
838
|
+
elsif ll > 4
|
839
|
+
attributes['linenums'] = ''
|
823
840
|
end
|
824
841
|
else
|
825
842
|
language = language.lstrip
|
@@ -858,6 +875,7 @@ class Parser
|
|
858
875
|
when :literal
|
859
876
|
block = build_block(block_context, :verbatim, terminator, parent, reader, attributes)
|
860
877
|
when :example
|
878
|
+
attributes['caption'] = '' if attributes['collapsible-option']
|
861
879
|
block = build_block(block_context, :compound, terminator, parent, reader, attributes)
|
862
880
|
when :quote, :verse
|
863
881
|
AttributeList.rekey(attributes, [nil, 'attribution', 'citetitle'])
|
@@ -899,9 +917,7 @@ class Parser
|
|
899
917
|
# FIXME title and caption should be assigned when block is constructed (though we need to handle all cases)
|
900
918
|
if attributes['title']
|
901
919
|
block.title = block_title = attributes.delete 'title'
|
902
|
-
|
903
|
-
block.assign_caption (attributes.delete 'caption')
|
904
|
-
end
|
920
|
+
block.assign_caption attributes.delete 'caption' if CAPTION_ATTRIBUTE_NAMES[block.context]
|
905
921
|
end
|
906
922
|
# TODO eventually remove the style attribute from the attributes hash
|
907
923
|
#block.style = attributes.delete 'style'
|
@@ -963,17 +979,12 @@ class Parser
|
|
963
979
|
# special case for fenced code blocks
|
964
980
|
if Compliance.markdown_syntax && (tip.start_with? '`')
|
965
981
|
if tip_len == 4
|
966
|
-
if tip == '````'
|
967
|
-
return
|
968
|
-
elsif (tip = tip.chop) == '```'
|
969
|
-
line = tip
|
970
|
-
line_len = tip_len = 3
|
971
|
-
else
|
982
|
+
if tip == '````' || (tip = tip.chop) != '```'
|
972
983
|
return
|
973
984
|
end
|
974
|
-
|
975
|
-
|
976
|
-
|
985
|
+
line = tip
|
986
|
+
line_len = tip_len = 3
|
987
|
+
elsif tip != '```'
|
977
988
|
return
|
978
989
|
end
|
979
990
|
elsif tip_len == 3
|
@@ -991,9 +1002,10 @@ class Parser
|
|
991
1002
|
# if terminator is false, that means the all the lines in the reader should be parsed
|
992
1003
|
# NOTE could invoke filter in here, before and after parsing
|
993
1004
|
def self.build_block(block_context, content_model, terminator, parent, reader, attributes, options = {})
|
994
|
-
|
1005
|
+
case content_model
|
1006
|
+
when :skip
|
995
1007
|
skip_processing, parse_as_content_model = true, :simple
|
996
|
-
|
1008
|
+
when :raw
|
997
1009
|
skip_processing, parse_as_content_model = false, :simple
|
998
1010
|
else
|
999
1011
|
skip_processing, parse_as_content_model = false, content_model
|
@@ -1022,14 +1034,15 @@ class Parser
|
|
1022
1034
|
block_reader = Reader.new reader.read_lines_until(terminator: terminator, skip_processing: skip_processing, context: block_context, cursor: :at_mark), block_cursor
|
1023
1035
|
end
|
1024
1036
|
|
1025
|
-
|
1037
|
+
case content_model
|
1038
|
+
when :verbatim
|
1026
1039
|
tab_size = (attributes['tabsize'] || parent.document.attributes['tabsize']).to_i
|
1027
1040
|
if (indent = attributes['indent'])
|
1028
1041
|
adjust_indentation! lines, indent.to_i, tab_size
|
1029
1042
|
elsif tab_size > 0
|
1030
1043
|
adjust_indentation! lines, -1, tab_size
|
1031
1044
|
end
|
1032
|
-
|
1045
|
+
when :skip
|
1033
1046
|
# QUESTION should we still invoke process method if extension is specified?
|
1034
1047
|
return
|
1035
1048
|
end
|
@@ -1037,12 +1050,12 @@ class Parser
|
|
1037
1050
|
if (extension = options[:extension])
|
1038
1051
|
# QUESTION do we want to delete the style?
|
1039
1052
|
attributes.delete('style')
|
1040
|
-
if (block = extension.process_method[parent, block_reader || (Reader.new lines), attributes.merge])
|
1053
|
+
if (block = extension.process_method[parent, block_reader || (Reader.new lines), attributes.merge]) && block != parent
|
1041
1054
|
attributes.replace block.attributes
|
1042
1055
|
# FIXME if the content model is set to compound, but we only have simple in this context, then
|
1043
1056
|
# forcefully set the content_model to simple to prevent parsing blocks from children
|
1044
1057
|
# TODO document this behavior!!
|
1045
|
-
if block.content_model == :compound && !(lines = block.lines).empty?
|
1058
|
+
if block.content_model == :compound && Block === block && !(lines = block.lines).empty?
|
1046
1059
|
content_model = :compound
|
1047
1060
|
block_reader = Reader.new lines
|
1048
1061
|
end
|
@@ -1148,14 +1161,16 @@ class Parser
|
|
1148
1161
|
def self.catalog_inline_anchors text, block, document, reader
|
1149
1162
|
text.scan InlineAnchorScanRx do
|
1150
1163
|
if (id = $1)
|
1151
|
-
if (reftext = $2)
|
1152
|
-
next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1153
|
-
end
|
1164
|
+
next if (reftext = $2) && (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1154
1165
|
else
|
1155
1166
|
id = $3
|
1156
1167
|
if (reftext = $4)
|
1157
|
-
|
1158
|
-
|
1168
|
+
if reftext.include? ']'
|
1169
|
+
reftext = reftext.gsub '\]', ']'
|
1170
|
+
reftext = document.sub_attributes reftext if reftext.include? ATTR_REF_HEAD
|
1171
|
+
elsif (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1172
|
+
next
|
1173
|
+
end
|
1159
1174
|
end
|
1160
1175
|
end
|
1161
1176
|
unless document.register :refs, [id, (Inline.new block, :anchor, reftext, type: :ref, id: id)]
|
@@ -1288,7 +1303,8 @@ class Parser
|
|
1288
1303
|
has_text = true
|
1289
1304
|
list_item = ListItem.new(list_block, (item_text = match[2]))
|
1290
1305
|
list_item.source_location = reader.cursor if list_block.document.sourcemap
|
1291
|
-
|
1306
|
+
case list_type
|
1307
|
+
when :ulist
|
1292
1308
|
list_item.marker = sibling_trait
|
1293
1309
|
if item_text.start_with?('[')
|
1294
1310
|
if style && style == 'bibliography'
|
@@ -1306,13 +1322,13 @@ class Parser
|
|
1306
1322
|
list_item.text = item_text.slice(4, item_text.length)
|
1307
1323
|
end
|
1308
1324
|
end
|
1309
|
-
|
1325
|
+
when :olist
|
1310
1326
|
sibling_trait, implicit_style = resolve_ordered_list_marker(sibling_trait, (ordinal = list_block.items.size), true, reader)
|
1311
1327
|
list_item.marker = sibling_trait
|
1312
1328
|
if ordinal == 0 && !style
|
1313
1329
|
# using list level makes more sense, but we don't track it
|
1314
|
-
# basing style on marker level is compliant with AsciiDoc
|
1315
|
-
list_block.style = implicit_style || (
|
1330
|
+
# basing style on marker level is compliant with AsciiDoc.py
|
1331
|
+
list_block.style = implicit_style || (ORDERED_LIST_STYLES[sibling_trait.length - 1] || 'arabic').to_s
|
1316
1332
|
end
|
1317
1333
|
if item_text.start_with?('[[') && LeadingInlineAnchorRx =~ item_text
|
1318
1334
|
catalog_inline_anchor $1, $2, list_item, reader
|
@@ -1438,95 +1454,89 @@ class Parser
|
|
1438
1454
|
# FIXME to be AsciiDoc compliant, we shouldn't break if style in attribute line is "literal" (i.e., [literal])
|
1439
1455
|
elsif dlist && continuation != :active && (BlockAttributeLineRx.match? this_line)
|
1440
1456
|
break
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
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 }
|
1453
|
-
else
|
1454
|
-
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1455
|
-
end
|
1456
|
-
continuation = :inactive
|
1457
|
-
# let block metadata play out until we find the block
|
1458
|
-
elsif (BlockTitleRx.match? this_line) || (BlockAttributeLineRx.match? this_line) || (AttributeEntryRx.match? this_line)
|
1459
|
-
buffer << this_line
|
1457
|
+
elsif continuation == :active && !this_line.empty?
|
1458
|
+
# literal paragraphs have special considerations (and this is one of
|
1459
|
+
# two entry points into one)
|
1460
|
+
# if we don't process it as a whole, then a line in it that looks like a
|
1461
|
+
# list item will throw off the exit from it
|
1462
|
+
if LiteralParagraphRx.match? this_line
|
1463
|
+
reader.unshift_line this_line
|
1464
|
+
if dlist
|
1465
|
+
# we may be in an indented list disguised as a literal paragraph
|
1466
|
+
# so we need to make sure we don't slurp up a legitimate sibling
|
1467
|
+
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 }
|
1460
1468
|
else
|
1461
|
-
|
1462
|
-
within_nested_list = true
|
1463
|
-
if nested_list_type == :dlist && $3.nil_or_empty?
|
1464
|
-
# get greedy again
|
1465
|
-
has_text = false
|
1466
|
-
end
|
1467
|
-
end
|
1468
|
-
buffer << this_line
|
1469
|
-
continuation = :inactive
|
1469
|
+
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1470
1470
|
end
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1471
|
+
continuation = :inactive
|
1472
|
+
# let block metadata play out until we find the block
|
1473
|
+
elsif (BlockTitleRx.match? this_line) || (BlockAttributeLineRx.match? this_line) || (AttributeEntryRx.match? this_line)
|
1474
|
+
buffer << this_line
|
1475
|
+
else
|
1476
|
+
if (nested_list_type = (within_nested_list ? [:dlist] : NESTABLE_LIST_CONTEXTS).find {|ctx| ListRxMap[ctx].match? this_line })
|
1477
|
+
within_nested_list = true
|
1478
|
+
if nested_list_type == :dlist && $3.nil_or_empty?
|
1479
|
+
# get greedy again
|
1480
|
+
has_text = false
|
1481
|
+
end
|
1478
1482
|
end
|
1483
|
+
buffer << this_line
|
1484
|
+
continuation = :inactive
|
1485
|
+
end
|
1486
|
+
elsif prev_line && prev_line.empty?
|
1487
|
+
# advance to the next line of content
|
1488
|
+
if this_line.empty?
|
1489
|
+
# stop reading if we reach eof
|
1490
|
+
break unless (this_line = reader.skip_blank_lines && reader.read_line)
|
1491
|
+
# stop reading if we hit a sibling list item
|
1492
|
+
break if is_sibling_list_item? this_line, list_type, sibling_trait
|
1493
|
+
end
|
1479
1494
|
|
1480
|
-
|
1481
|
-
|
1495
|
+
if this_line == LIST_CONTINUATION
|
1496
|
+
detached_continuation = buffer.size
|
1497
|
+
buffer << this_line
|
1498
|
+
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
|
1499
|
+
# in this block, we have to see whether we stay in the list
|
1500
|
+
# TODO any way to combine this with the check after skipping blank lines?
|
1501
|
+
if is_sibling_list_item?(this_line, list_type, sibling_trait)
|
1502
|
+
break
|
1503
|
+
elsif (nested_list_type = NESTABLE_LIST_CONTEXTS.find {|ctx| ListRxMap[ctx] =~ this_line })
|
1482
1504
|
buffer << this_line
|
1483
|
-
else
|
1484
|
-
# has_text is only relevant for dlist, which is more greedy until it has text for an item
|
1485
|
-
# for all other lists, has_text is always true
|
1486
|
-
# in this block, we have to see whether we stay in the list
|
1487
|
-
if has_text
|
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 }
|
1492
|
-
buffer << this_line
|
1493
|
-
within_nested_list = true
|
1494
|
-
if nested_list_type == :dlist && $3.nil_or_empty?
|
1495
|
-
# get greedy again
|
1496
|
-
has_text = false
|
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
|
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
|
1515
|
-
buffer << this_line
|
1516
|
-
has_text = true
|
1517
|
-
end
|
1518
|
-
end
|
1519
|
-
else
|
1520
|
-
has_text = true unless this_line.empty?
|
1521
|
-
if nested_list_type = (within_nested_list ? [:dlist] : NESTABLE_LIST_CONTEXTS).find {|ctx| ListRxMap[ctx] =~ this_line }
|
1522
1505
|
within_nested_list = true
|
1523
1506
|
if nested_list_type == :dlist && $3.nil_or_empty?
|
1524
1507
|
# get greedy again
|
1525
1508
|
has_text = false
|
1526
1509
|
end
|
1510
|
+
# slurp up any literal paragraph offset by blank lines
|
1511
|
+
# NOTE we have to check for indented list items first
|
1512
|
+
elsif LiteralParagraphRx.match? this_line
|
1513
|
+
reader.unshift_line this_line
|
1514
|
+
if dlist
|
1515
|
+
# we may be in an indented list disguised as a literal paragraph
|
1516
|
+
# so we need to make sure we don't slurp up a legitimate sibling
|
1517
|
+
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 }
|
1518
|
+
else
|
1519
|
+
buffer.concat reader.read_lines_until(preserve_last_line: true, break_on_blank_lines: true, break_on_list_continuation: true)
|
1520
|
+
end
|
1521
|
+
else
|
1522
|
+
break
|
1527
1523
|
end
|
1524
|
+
else # only dlist in need of item text, so slurp it up!
|
1525
|
+
# pop the blank line so it's not interpreted as a list continuation
|
1526
|
+
buffer.pop unless within_nested_list
|
1528
1527
|
buffer << this_line
|
1528
|
+
has_text = true
|
1529
|
+
end
|
1530
|
+
else
|
1531
|
+
has_text = true unless this_line.empty?
|
1532
|
+
if (nested_list_type = (within_nested_list ? [:dlist] : NESTABLE_LIST_CONTEXTS).find {|ctx| ListRxMap[ctx] =~ this_line })
|
1533
|
+
within_nested_list = true
|
1534
|
+
if nested_list_type == :dlist && $3.nil_or_empty?
|
1535
|
+
# get greedy again
|
1536
|
+
has_text = false
|
1537
|
+
end
|
1529
1538
|
end
|
1539
|
+
buffer << this_line
|
1530
1540
|
end
|
1531
1541
|
this_line = nil
|
1532
1542
|
end
|
@@ -1567,12 +1577,6 @@ class Parser
|
|
1567
1577
|
sect_style = attributes[1]
|
1568
1578
|
sect_id, sect_reftext, sect_title, sect_level, sect_atx = parse_section_title reader, document, attributes['id']
|
1569
1579
|
|
1570
|
-
if sect_reftext
|
1571
|
-
attributes['reftext'] = sect_reftext
|
1572
|
-
else
|
1573
|
-
sect_reftext = attributes['reftext']
|
1574
|
-
end
|
1575
|
-
|
1576
1580
|
if sect_style
|
1577
1581
|
if book && sect_style == 'abstract'
|
1578
1582
|
sect_name, sect_level = 'chapter', 1
|
@@ -1591,6 +1595,7 @@ class Parser
|
|
1591
1595
|
sect_name = 'section'
|
1592
1596
|
end
|
1593
1597
|
|
1598
|
+
attributes['reftext'] = sect_reftext if sect_reftext
|
1594
1599
|
section = Section.new parent, sect_level
|
1595
1600
|
section.id, section.title, section.sectname, section.source_location = sect_id, sect_title, sect_name, source_location
|
1596
1601
|
if sect_special
|
@@ -1598,7 +1603,7 @@ class Parser
|
|
1598
1603
|
if sect_numbered
|
1599
1604
|
section.numbered = true
|
1600
1605
|
elsif document.attributes['sectnums'] == 'all'
|
1601
|
-
section.numbered = book && sect_level == 1 ? :chapter : true
|
1606
|
+
section.numbered = (book && sect_level == 1 ? :chapter : true)
|
1602
1607
|
end
|
1603
1608
|
elsif document.attributes['sectnums'] && sect_level > 0
|
1604
1609
|
# NOTE a special section here is guaranteed to be nested in another section
|
@@ -1610,7 +1615,7 @@ class Parser
|
|
1610
1615
|
# generate an ID if one was not embedded or specified as anchor above section title
|
1611
1616
|
if (id = section.id || (section.id = (document.attributes.key? 'sectids') ? (generated_id = Section.generate_id section.title, document) : nil))
|
1612
1617
|
# convert title to resolve attributes while in scope
|
1613
|
-
section.title
|
1618
|
+
section.title unless generated_id || !(sect_title.include? ATTR_REF_HEAD)
|
1614
1619
|
unless document.register :refs, [id, section]
|
1615
1620
|
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))
|
1616
1621
|
end
|
@@ -1629,9 +1634,8 @@ class Parser
|
|
1629
1634
|
#
|
1630
1635
|
# Returns the Integer section level if the Reader is positioned at a section title or nil otherwise
|
1631
1636
|
def self.is_next_line_section?(reader, attributes)
|
1632
|
-
if (style = attributes[1]) && (style == 'discrete' || style == 'float')
|
1633
|
-
|
1634
|
-
elsif Compliance.underline_style_section_titles
|
1637
|
+
return if (style = attributes[1]) && (style == 'discrete' || style == 'float')
|
1638
|
+
if Compliance.underline_style_section_titles
|
1635
1639
|
next_lines = reader.peek_lines 2, style && style == 'comment'
|
1636
1640
|
is_section_title?(next_lines[0] || '', next_lines[1])
|
1637
1641
|
else
|
@@ -1777,38 +1781,31 @@ class Parser
|
|
1777
1781
|
# parse_header_metadata(Reader.new data, nil, normalize: true)
|
1778
1782
|
# # => { 'author' => 'Author Name', 'firstname' => 'Author', 'lastname' => 'Name', 'email' => 'author@example.org',
|
1779
1783
|
# # 'revnumber' => '1.0', 'revdate' => '2012-12-21', 'revremark' => 'Coincide w/ end of world.' }
|
1780
|
-
def self.parse_header_metadata
|
1784
|
+
def self.parse_header_metadata reader, document = nil, retrieve = true
|
1781
1785
|
doc_attrs = document && document.attributes
|
1782
1786
|
# NOTE this will discard any comment lines, but not skip blank lines
|
1783
1787
|
process_attribute_entries reader, document
|
1784
1788
|
|
1785
|
-
metadata, implicit_author, implicit_authorinitials = implicit_authors = {}, nil, nil
|
1786
|
-
|
1787
1789
|
if reader.has_more_lines? && !reader.next_line_empty?
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
doc_attrs[key] = ::String === val ? (document.apply_header_subs val) : val unless doc_attrs.key? key
|
1794
|
-
end
|
1795
|
-
|
1796
|
-
implicit_author = doc_attrs['author']
|
1797
|
-
implicit_authorinitials = doc_attrs['authorinitials']
|
1798
|
-
implicit_authors = doc_attrs['authors']
|
1790
|
+
authorcount = (implicit_author_metadata = process_authors reader.read_line).delete 'authorcount'
|
1791
|
+
if document && (doc_attrs['authorcount'] = authorcount) > 0
|
1792
|
+
implicit_author_metadata.each do |key, val|
|
1793
|
+
# apply header subs and assign to document; attributes substitution only relevant for email
|
1794
|
+
doc_attrs[key] = document.apply_header_subs val unless doc_attrs.key? key
|
1799
1795
|
end
|
1800
|
-
|
1801
|
-
|
1796
|
+
implicit_author = doc_attrs['author']
|
1797
|
+
implicit_authorinitials = doc_attrs['authorinitials']
|
1798
|
+
implicit_authors = doc_attrs['authors']
|
1802
1799
|
end
|
1800
|
+
implicit_author_metadata['authorcount'] = authorcount
|
1803
1801
|
|
1804
1802
|
# NOTE this will discard any comment lines, but not skip blank lines
|
1805
1803
|
process_attribute_entries reader, document
|
1806
1804
|
|
1807
|
-
rev_metadata = {}
|
1808
|
-
|
1809
1805
|
if reader.has_more_lines? && !reader.next_line_empty?
|
1810
1806
|
rev_line = reader.read_line
|
1811
|
-
if (match = RevisionInfoLineRx.match
|
1807
|
+
if (match = RevisionInfoLineRx.match rev_line)
|
1808
|
+
rev_metadata = {}
|
1812
1809
|
rev_metadata['revnumber'] = match[1].rstrip if match[1]
|
1813
1810
|
unless (component = match[2].strip).empty?
|
1814
1811
|
# version must begin with 'v' if date is absent
|
@@ -1819,31 +1816,24 @@ class Parser
|
|
1819
1816
|
end
|
1820
1817
|
end
|
1821
1818
|
rev_metadata['revremark'] = match[3].rstrip if match[3]
|
1819
|
+
if document && !rev_metadata.empty?
|
1820
|
+
# apply header subs and assign to document
|
1821
|
+
rev_metadata.each do |key, val|
|
1822
|
+
doc_attrs[key] = document.apply_header_subs val unless doc_attrs.key? key
|
1823
|
+
end
|
1824
|
+
end
|
1822
1825
|
else
|
1823
1826
|
# throw it back
|
1824
1827
|
reader.unshift_line rev_line
|
1825
1828
|
end
|
1826
1829
|
end
|
1827
1830
|
|
1828
|
-
unless rev_metadata.empty?
|
1829
|
-
if document
|
1830
|
-
# apply header subs and assign to document
|
1831
|
-
rev_metadata.each do |key, val|
|
1832
|
-
unless doc_attrs.key? key
|
1833
|
-
doc_attrs[key] = document.apply_header_subs val
|
1834
|
-
end
|
1835
|
-
end
|
1836
|
-
end
|
1837
|
-
|
1838
|
-
metadata.update rev_metadata
|
1839
|
-
end
|
1840
|
-
|
1841
1831
|
# NOTE this will discard any comment lines, but not skip blank lines
|
1842
1832
|
process_attribute_entries reader, document
|
1843
1833
|
|
1844
1834
|
reader.skip_blank_lines
|
1845
1835
|
else
|
1846
|
-
|
1836
|
+
implicit_author_metadata = {}
|
1847
1837
|
end
|
1848
1838
|
|
1849
1839
|
# process author attribute entries that override (or stand in for) the implicit author line
|
@@ -1860,7 +1850,7 @@ class Parser
|
|
1860
1850
|
while doc_attrs.key? author_key
|
1861
1851
|
# only use indexed author attribute if value is different
|
1862
1852
|
# leaves corner case if line matches with underscores converted to spaces; use double space to force
|
1863
|
-
if (author_override = doc_attrs[author_key]) ==
|
1853
|
+
if (author_override = doc_attrs[author_key]) == implicit_author_metadata[author_key]
|
1864
1854
|
authors << nil
|
1865
1855
|
sparse = true
|
1866
1856
|
else
|
@@ -1872,23 +1862,26 @@ class Parser
|
|
1872
1862
|
if explicit
|
1873
1863
|
# rebuild implicit author names to reparse
|
1874
1864
|
authors.each_with_index do |author, idx|
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
1880
|
-
|
1881
|
-
end
|
1865
|
+
next if author
|
1866
|
+
authors[idx] = [
|
1867
|
+
implicit_author_metadata[%(firstname_#{name_idx = idx + 1})],
|
1868
|
+
implicit_author_metadata[%(middlename_#{name_idx})],
|
1869
|
+
implicit_author_metadata[%(lastname_#{name_idx})]
|
1870
|
+
].compact.map {|it| it.tr ' ', '_' }.join ' '
|
1882
1871
|
end if sparse
|
1883
1872
|
# process as names only
|
1884
1873
|
author_metadata = process_authors authors, true, false
|
1885
1874
|
else
|
1886
|
-
author_metadata = {}
|
1875
|
+
author_metadata = { 'authorcount' => 0 }
|
1887
1876
|
end
|
1888
1877
|
end
|
1889
1878
|
|
1890
|
-
if author_metadata
|
1891
|
-
|
1879
|
+
if author_metadata['authorcount'] == 0
|
1880
|
+
if authorcount
|
1881
|
+
author_metadata = nil
|
1882
|
+
else
|
1883
|
+
doc_attrs['authorcount'] = 0
|
1884
|
+
end
|
1892
1885
|
else
|
1893
1886
|
doc_attrs.update author_metadata
|
1894
1887
|
|
@@ -1899,7 +1892,7 @@ class Parser
|
|
1899
1892
|
end
|
1900
1893
|
end
|
1901
1894
|
|
1902
|
-
|
1895
|
+
implicit_author_metadata.merge rev_metadata.to_h, author_metadata.to_h if retrieve
|
1903
1896
|
end
|
1904
1897
|
|
1905
1898
|
# Internal: Parse the author line into a Hash of author metadata
|
@@ -2061,6 +2054,7 @@ class Parser
|
|
2061
2054
|
return true
|
2062
2055
|
end
|
2063
2056
|
end
|
2057
|
+
nil
|
2064
2058
|
end
|
2065
2059
|
|
2066
2060
|
# Process consecutive attribute entry lines, ignoring adjacent line comments and comment blocks.
|
@@ -2119,6 +2113,8 @@ class Parser
|
|
2119
2113
|
name = 'sectnums'
|
2120
2114
|
elsif name == 'hardbreaks'
|
2121
2115
|
name = 'hardbreaks-option'
|
2116
|
+
elsif name == 'showtitle'
|
2117
|
+
store_attribute 'notitle', (value ? nil : ''), doc, attrs
|
2122
2118
|
end
|
2123
2119
|
|
2124
2120
|
if doc
|
@@ -2163,9 +2159,10 @@ class Parser
|
|
2163
2159
|
#
|
2164
2160
|
# Returns the String 0-index marker for this list item
|
2165
2161
|
def self.resolve_list_marker(list_type, marker, ordinal = 0, validate = false, reader = nil)
|
2166
|
-
|
2162
|
+
case list_type
|
2163
|
+
when :ulist
|
2167
2164
|
marker
|
2168
|
-
|
2165
|
+
when :olist
|
2169
2166
|
resolve_ordered_list_marker(marker, ordinal, validate, reader)[0]
|
2170
2167
|
else # :colist
|
2171
2168
|
'<1>'
|
@@ -2273,9 +2270,15 @@ class Parser
|
|
2273
2270
|
end
|
2274
2271
|
|
2275
2272
|
skipped = table_reader.skip_blank_lines || 0
|
2273
|
+
if attributes['header-option']
|
2274
|
+
table.has_header_option = true
|
2275
|
+
elsif skipped == 0 && !attributes['noheader-option']
|
2276
|
+
# NOTE: assume table has header until we know otherwise; if it doesn't (nil), cells in first row get reprocessed
|
2277
|
+
table.has_header_option = :implicit
|
2278
|
+
implicit_header = true
|
2279
|
+
end
|
2276
2280
|
parser_ctx = Table::ParserContext.new table_reader, table, attributes
|
2277
2281
|
format, loop_idx, implicit_header_boundary = parser_ctx.format, -1, nil
|
2278
|
-
implicit_header = true unless skipped > 0 || attributes['header-option'] || attributes['noheader-option']
|
2279
2282
|
|
2280
2283
|
while (line = table_reader.read_line)
|
2281
2284
|
if (beyond_first = (loop_idx += 1) > 0) && line.empty?
|
@@ -2295,7 +2298,7 @@ class Parser
|
|
2295
2298
|
implicit_header_boundary = nil if implicit_header_boundary
|
2296
2299
|
# otherwise, the cell continues from previous line
|
2297
2300
|
elsif implicit_header_boundary && implicit_header_boundary == loop_idx
|
2298
|
-
implicit_header
|
2301
|
+
table.has_header_option = implicit_header = implicit_header_boundary = nil
|
2299
2302
|
end
|
2300
2303
|
end
|
2301
2304
|
end
|
@@ -2307,7 +2310,7 @@ class Parser
|
|
2307
2310
|
if table_reader.has_more_lines? && table_reader.peek_line.empty?
|
2308
2311
|
implicit_header_boundary = 1
|
2309
2312
|
else
|
2310
|
-
implicit_header =
|
2313
|
+
table.has_header_option = implicit_header = nil
|
2311
2314
|
end
|
2312
2315
|
end
|
2313
2316
|
end
|
@@ -2358,7 +2361,7 @@ class Parser
|
|
2358
2361
|
case format
|
2359
2362
|
when 'csv'
|
2360
2363
|
if parser_ctx.buffer_has_unclosed_quotes?
|
2361
|
-
implicit_header
|
2364
|
+
table.has_header_option = implicit_header = implicit_header_boundary = nil if implicit_header_boundary && loop_idx == 0
|
2362
2365
|
parser_ctx.keep_cell_open
|
2363
2366
|
else
|
2364
2367
|
parser_ctx.close_cell true
|
@@ -2380,15 +2383,8 @@ class Parser
|
|
2380
2383
|
end
|
2381
2384
|
end
|
2382
2385
|
|
2383
|
-
unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
|
2384
|
-
|
2385
|
-
end
|
2386
|
-
|
2387
|
-
if implicit_header
|
2388
|
-
table.has_header_option = true
|
2389
|
-
attributes['header-option'] = ''
|
2390
|
-
end
|
2391
|
-
|
2386
|
+
table.assign_column_widths unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
|
2387
|
+
table.has_header_option = true if implicit_header
|
2392
2388
|
table.partition_header_footer attributes
|
2393
2389
|
|
2394
2390
|
table
|
@@ -2469,7 +2465,7 @@ class Parser
|
|
2469
2465
|
|
2470
2466
|
if pos == :start
|
2471
2467
|
if line.include? delimiter
|
2472
|
-
spec_part,
|
2468
|
+
spec_part, _, rest = line.partition delimiter
|
2473
2469
|
if (m = CellSpecStartRx.match spec_part)
|
2474
2470
|
return [{}, rest] if m[0].empty?
|
2475
2471
|
else
|
@@ -2478,14 +2474,12 @@ class Parser
|
|
2478
2474
|
else
|
2479
2475
|
return [nil, line]
|
2480
2476
|
end
|
2481
|
-
|
2482
|
-
if
|
2483
|
-
|
2484
|
-
|
2485
|
-
|
2486
|
-
|
2487
|
-
return [{}, line]
|
2488
|
-
end
|
2477
|
+
elsif (m = CellSpecEndRx.match line) # when pos == :end
|
2478
|
+
# NOTE return the line stripped of trailing whitespace if no cellspec is found in this case
|
2479
|
+
return [{}, line.rstrip] if m[0].lstrip.empty?
|
2480
|
+
rest = m.pre_match
|
2481
|
+
else
|
2482
|
+
return [{}, line]
|
2489
2483
|
end
|
2490
2484
|
|
2491
2485
|
spec = {}
|
@@ -2493,10 +2487,11 @@ class Parser
|
|
2493
2487
|
colspec, rowspec = m[1].split '.'
|
2494
2488
|
colspec = colspec.nil_or_empty? ? 1 : colspec.to_i
|
2495
2489
|
rowspec = rowspec.nil_or_empty? ? 1 : rowspec.to_i
|
2496
|
-
|
2490
|
+
case m[2]
|
2491
|
+
when '+'
|
2497
2492
|
spec['colspan'] = colspec unless colspec == 1
|
2498
2493
|
spec['rowspan'] = rowspec unless rowspec == 1
|
2499
|
-
|
2494
|
+
when '*'
|
2500
2495
|
spec['repeatcol'] = colspec unless colspec == 1
|
2501
2496
|
end
|
2502
2497
|
end
|
@@ -2521,7 +2516,7 @@ class Parser
|
|
2521
2516
|
# Public: Parse the first positional attribute and assign named attributes
|
2522
2517
|
#
|
2523
2518
|
# Parse the first positional attribute to extract the style, role and id
|
2524
|
-
# parts, assign the values to their
|
2519
|
+
# parts, assign the values to their corresponding attribute keys and return
|
2525
2520
|
# the parsed style from the first positional attribute.
|
2526
2521
|
#
|
2527
2522
|
# attributes - The Hash of attributes to process and update
|
@@ -2561,7 +2556,7 @@ class Parser
|
|
2561
2556
|
accum = ''
|
2562
2557
|
name = :option
|
2563
2558
|
else
|
2564
|
-
accum
|
2559
|
+
accum += c
|
2565
2560
|
end
|
2566
2561
|
end
|
2567
2562
|
|
@@ -2579,9 +2574,7 @@ class Parser
|
|
2579
2574
|
attributes['role'] = (existing_role = attributes['role']).nil_or_empty? ? (parsed_attrs[:role].join ' ') : %(#{existing_role} #{parsed_attrs[:role].join ' '})
|
2580
2575
|
end
|
2581
2576
|
|
2582
|
-
if parsed_attrs.key? :option
|
2583
|
-
(opts = parsed_attrs[:option]).each {|opt| attributes[%(#{opt}-option)] = '' }
|
2584
|
-
end
|
2577
|
+
parsed_attrs[:option].each {|opt| attributes[%(#{opt}-option)] = '' } if parsed_attrs.key? :option
|
2585
2578
|
|
2586
2579
|
parsed_style
|
2587
2580
|
else
|
@@ -2654,9 +2647,9 @@ class Parser
|
|
2654
2647
|
if tab_size > 0 && lines.any? {|line| line.include? TAB }
|
2655
2648
|
full_tab_space = ' ' * tab_size
|
2656
2649
|
lines.map! do |line|
|
2657
|
-
if line.empty?
|
2650
|
+
if line.empty? || (tab_idx = line.index TAB).nil?
|
2658
2651
|
line
|
2659
|
-
|
2652
|
+
else
|
2660
2653
|
if tab_idx == 0
|
2661
2654
|
leading_tabs = 0
|
2662
2655
|
line.each_byte do |b|
|
@@ -2674,22 +2667,20 @@ class Parser
|
|
2674
2667
|
if c == TAB
|
2675
2668
|
# calculate how many spaces this tab represents, then replace tab with spaces
|
2676
2669
|
if (offset = idx + spaces_added) % tab_size == 0
|
2677
|
-
spaces_added +=
|
2678
|
-
result
|
2670
|
+
spaces_added += tab_size - 1
|
2671
|
+
result += full_tab_space
|
2679
2672
|
else
|
2680
2673
|
unless (spaces = tab_size - offset % tab_size) == 1
|
2681
|
-
spaces_added +=
|
2674
|
+
spaces_added += spaces - 1
|
2682
2675
|
end
|
2683
|
-
result
|
2676
|
+
result += ' ' * spaces
|
2684
2677
|
end
|
2685
2678
|
else
|
2686
|
-
result
|
2679
|
+
result += c
|
2687
2680
|
end
|
2688
2681
|
idx += 1
|
2689
2682
|
end
|
2690
2683
|
result
|
2691
|
-
else
|
2692
|
-
line
|
2693
2684
|
end
|
2694
2685
|
end
|
2695
2686
|
end
|