asciidoctor 2.0.10 → 2.0.17
Sign up to get free protection for your applications and to get access to all the features.
- 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
|