asciidoctor 2.0.9 → 2.0.14
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 +193 -16
- data/LICENSE +1 -1
- data/README-de.adoc +12 -13
- data/README-fr.adoc +11 -15
- data/README-jp.adoc +242 -185
- data/README-zh_CN.adoc +17 -18
- data/README.adoc +133 -131
- data/asciidoctor.gemspec +6 -6
- 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 +6 -5
- 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-tr.adoc +4 -3
- data/data/locale/attributes-uk.adoc +6 -5
- 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 +30 -30
- data/lib/asciidoctor.rb +40 -14
- data/lib/asciidoctor/abstract_block.rb +9 -4
- data/lib/asciidoctor/abstract_node.rb +16 -6
- data/lib/asciidoctor/attribute_list.rb +63 -71
- data/lib/asciidoctor/cli/invoker.rb +2 -0
- data/lib/asciidoctor/cli/options.rb +10 -9
- data/lib/asciidoctor/convert.rb +167 -162
- data/lib/asciidoctor/converter.rb +13 -12
- data/lib/asciidoctor/converter/docbook5.rb +5 -9
- data/lib/asciidoctor/converter/html5.rb +58 -45
- data/lib/asciidoctor/converter/manpage.rb +61 -38
- data/lib/asciidoctor/converter/template.rb +3 -0
- data/lib/asciidoctor/document.rb +44 -51
- data/lib/asciidoctor/extensions.rb +2 -4
- data/lib/asciidoctor/helpers.rb +20 -15
- data/lib/asciidoctor/load.rb +102 -101
- data/lib/asciidoctor/parser.rb +40 -32
- data/lib/asciidoctor/path_resolver.rb +14 -12
- data/lib/asciidoctor/reader.rb +20 -13
- data/lib/asciidoctor/rx.rb +7 -6
- data/lib/asciidoctor/substitutors.rb +69 -50
- data/lib/asciidoctor/syntax_highlighter.rb +15 -7
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +1 -1
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +12 -4
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +6 -7
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +33 -19
- data/lib/asciidoctor/table.rb +52 -23
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +8 -8
- data/man/asciidoctor.adoc +4 -4
- metadata +16 -15
data/lib/asciidoctor/parser.rb
CHANGED
@@ -119,7 +119,7 @@ class Parser
|
|
119
119
|
# returns the Hash of orphan block attributes captured above the header
|
120
120
|
def self.parse_document_header(reader, document)
|
121
121
|
# capture lines of block-level metadata and plow away comment lines that precede first block
|
122
|
-
block_attrs = parse_block_metadata_lines reader, document
|
122
|
+
block_attrs = reader.skip_blank_lines ? (parse_block_metadata_lines reader, document) : {}
|
123
123
|
doc_attrs = document.attributes
|
124
124
|
|
125
125
|
# special case, block title is not allowed above document title,
|
@@ -144,7 +144,10 @@ class Parser
|
|
144
144
|
l0_section_title = nil
|
145
145
|
else
|
146
146
|
document.title = l0_section_title
|
147
|
-
doc_attrs['doctitle'] = doctitle_attr_val = document.
|
147
|
+
if (doc_attrs['doctitle'] = doctitle_attr_val = document.sub_specialchars l0_section_title).include? ATTR_REF_HEAD
|
148
|
+
# QUESTION should we defer substituting attributes until the end of the header? or should we substitute again if necessary?
|
149
|
+
doc_attrs['doctitle'] = doctitle_attr_val = document.sub_attributes doctitle_attr_val, attribute_missing: 'skip'
|
150
|
+
end
|
148
151
|
end
|
149
152
|
document.header.source_location = source_location if source_location
|
150
153
|
# default to compat-mode if document has setext doctitle
|
@@ -215,9 +218,6 @@ class Parser
|
|
215
218
|
name_section = initialize_section reader, document, {}
|
216
219
|
name_section_buffer = (reader.read_lines_until break_on_blank_lines: true, skip_line_comments: true).map {|l| l.lstrip }.join ' '
|
217
220
|
if ManpageNamePurposeRx =~ name_section_buffer
|
218
|
-
doc_attrs['manname-title'] ||= name_section.title
|
219
|
-
doc_attrs['manname-id'] = name_section.id if name_section.id
|
220
|
-
doc_attrs['manpurpose'] = $2
|
221
221
|
if (manname = $1).include? ATTR_REF_HEAD
|
222
222
|
manname = document.sub_attributes manname
|
223
223
|
end
|
@@ -226,8 +226,14 @@ class Parser
|
|
226
226
|
else
|
227
227
|
mannames = [manname]
|
228
228
|
end
|
229
|
+
if (manpurpose = $2).include? ATTR_REF_HEAD
|
230
|
+
manpurpose = document.sub_attributes manpurpose
|
231
|
+
end
|
232
|
+
doc_attrs['manname-title'] ||= name_section.title
|
233
|
+
doc_attrs['manname-id'] = name_section.id if name_section.id
|
229
234
|
doc_attrs['manname'] = manname
|
230
235
|
doc_attrs['mannames'] = mannames
|
236
|
+
doc_attrs['manpurpose'] = manpurpose
|
231
237
|
if document.backend == 'manpage'
|
232
238
|
doc_attrs['docname'] = manname
|
233
239
|
doc_attrs['outfilesuffix'] = %(.#{manvolnum})
|
@@ -327,7 +333,7 @@ class Parser
|
|
327
333
|
if current_level == 0
|
328
334
|
part = book
|
329
335
|
elsif current_level == 1 && section.special
|
330
|
-
# NOTE technically preface
|
336
|
+
# NOTE technically preface sections are only permitted in the book doctype
|
331
337
|
unless (sectname = section.sectname) == 'appendix' || sectname == 'preface' || sectname == 'abstract'
|
332
338
|
expected_next_level = nil
|
333
339
|
end
|
@@ -433,8 +439,10 @@ class Parser
|
|
433
439
|
# is treated like an untitled section
|
434
440
|
elsif preamble # implies parent == document
|
435
441
|
if preamble.blocks?
|
442
|
+
if book || document.blocks[1] || !Compliance.unwrap_standalone_preamble
|
443
|
+
preamble.source_location = preamble.blocks[0].source_location if document.sourcemap
|
436
444
|
# unwrap standalone preamble (i.e., document has no sections) except for books, if permissible
|
437
|
-
|
445
|
+
else
|
438
446
|
document.blocks.shift
|
439
447
|
while (child_block = preamble.blocks.shift)
|
440
448
|
document << child_block
|
@@ -858,6 +866,7 @@ class Parser
|
|
858
866
|
when :literal
|
859
867
|
block = build_block(block_context, :verbatim, terminator, parent, reader, attributes)
|
860
868
|
when :example
|
869
|
+
attributes['caption'] = '' if attributes['collapsible-option']
|
861
870
|
block = build_block(block_context, :compound, terminator, parent, reader, attributes)
|
862
871
|
when :quote, :verse
|
863
872
|
AttributeList.rekey(attributes, [nil, 'attribution', 'citetitle'])
|
@@ -899,9 +908,7 @@ class Parser
|
|
899
908
|
# FIXME title and caption should be assigned when block is constructed (though we need to handle all cases)
|
900
909
|
if attributes['title']
|
901
910
|
block.title = block_title = attributes.delete 'title'
|
902
|
-
|
903
|
-
block.assign_caption (attributes.delete 'caption')
|
904
|
-
end
|
911
|
+
block.assign_caption (attributes.delete 'caption') if CAPTION_ATTRIBUTE_NAMES[block.context]
|
905
912
|
end
|
906
913
|
# TODO eventually remove the style attribute from the attributes hash
|
907
914
|
#block.style = attributes.delete 'style'
|
@@ -1148,14 +1155,16 @@ class Parser
|
|
1148
1155
|
def self.catalog_inline_anchors text, block, document, reader
|
1149
1156
|
text.scan InlineAnchorScanRx do
|
1150
1157
|
if (id = $1)
|
1151
|
-
if (reftext = $2)
|
1152
|
-
next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1153
|
-
end
|
1158
|
+
next if (reftext = $2) && (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1154
1159
|
else
|
1155
1160
|
id = $3
|
1156
1161
|
if (reftext = $4)
|
1157
|
-
|
1158
|
-
|
1162
|
+
if reftext.include? ']'
|
1163
|
+
reftext = reftext.gsub '\]', ']'
|
1164
|
+
reftext = document.sub_attributes reftext if reftext.include? ATTR_REF_HEAD
|
1165
|
+
elsif (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1166
|
+
next
|
1167
|
+
end
|
1159
1168
|
end
|
1160
1169
|
end
|
1161
1170
|
unless document.register :refs, [id, (Inline.new block, :anchor, reftext, type: :ref, id: id)]
|
@@ -1311,7 +1320,7 @@ class Parser
|
|
1311
1320
|
list_item.marker = sibling_trait
|
1312
1321
|
if ordinal == 0 && !style
|
1313
1322
|
# using list level makes more sense, but we don't track it
|
1314
|
-
# basing style on marker level is compliant with AsciiDoc
|
1323
|
+
# basing style on marker level is compliant with AsciiDoc.py
|
1315
1324
|
list_block.style = implicit_style || ((ORDERED_LIST_STYLES[sibling_trait.length - 1] || 'arabic').to_s)
|
1316
1325
|
end
|
1317
1326
|
if item_text.start_with?('[[') && LeadingInlineAnchorRx =~ item_text
|
@@ -2119,6 +2128,8 @@ class Parser
|
|
2119
2128
|
name = 'sectnums'
|
2120
2129
|
elsif name == 'hardbreaks'
|
2121
2130
|
name = 'hardbreaks-option'
|
2131
|
+
elsif name == 'showtitle'
|
2132
|
+
store_attribute 'notitle', (value ? nil : ''), doc, attrs
|
2122
2133
|
end
|
2123
2134
|
|
2124
2135
|
if doc
|
@@ -2273,9 +2284,15 @@ class Parser
|
|
2273
2284
|
end
|
2274
2285
|
|
2275
2286
|
skipped = table_reader.skip_blank_lines || 0
|
2287
|
+
if attributes['header-option']
|
2288
|
+
table.has_header_option = true
|
2289
|
+
elsif skipped == 0 && !attributes['noheader-option']
|
2290
|
+
# NOTE: assume table has header until we know otherwise; if it doesn't (nil), cells in first row get reprocessed
|
2291
|
+
table.has_header_option = :implicit
|
2292
|
+
implicit_header = true
|
2293
|
+
end
|
2276
2294
|
parser_ctx = Table::ParserContext.new table_reader, table, attributes
|
2277
2295
|
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
2296
|
|
2280
2297
|
while (line = table_reader.read_line)
|
2281
2298
|
if (beyond_first = (loop_idx += 1) > 0) && line.empty?
|
@@ -2295,7 +2312,7 @@ class Parser
|
|
2295
2312
|
implicit_header_boundary = nil if implicit_header_boundary
|
2296
2313
|
# otherwise, the cell continues from previous line
|
2297
2314
|
elsif implicit_header_boundary && implicit_header_boundary == loop_idx
|
2298
|
-
implicit_header
|
2315
|
+
table.has_header_option = implicit_header = implicit_header_boundary = nil
|
2299
2316
|
end
|
2300
2317
|
end
|
2301
2318
|
end
|
@@ -2307,7 +2324,7 @@ class Parser
|
|
2307
2324
|
if table_reader.has_more_lines? && table_reader.peek_line.empty?
|
2308
2325
|
implicit_header_boundary = 1
|
2309
2326
|
else
|
2310
|
-
implicit_header =
|
2327
|
+
table.has_header_option = implicit_header = nil
|
2311
2328
|
end
|
2312
2329
|
end
|
2313
2330
|
end
|
@@ -2358,7 +2375,7 @@ class Parser
|
|
2358
2375
|
case format
|
2359
2376
|
when 'csv'
|
2360
2377
|
if parser_ctx.buffer_has_unclosed_quotes?
|
2361
|
-
implicit_header
|
2378
|
+
table.has_header_option = implicit_header = implicit_header_boundary = nil if implicit_header_boundary && loop_idx == 0
|
2362
2379
|
parser_ctx.keep_cell_open
|
2363
2380
|
else
|
2364
2381
|
parser_ctx.close_cell true
|
@@ -2380,15 +2397,8 @@ class Parser
|
|
2380
2397
|
end
|
2381
2398
|
end
|
2382
2399
|
|
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
|
-
|
2400
|
+
table.assign_column_widths unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
|
2401
|
+
table.has_header_option = true if implicit_header
|
2392
2402
|
table.partition_header_footer attributes
|
2393
2403
|
|
2394
2404
|
table
|
@@ -2579,9 +2589,7 @@ class Parser
|
|
2579
2589
|
attributes['role'] = (existing_role = attributes['role']).nil_or_empty? ? (parsed_attrs[:role].join ' ') : %(#{existing_role} #{parsed_attrs[:role].join ' '})
|
2580
2590
|
end
|
2581
2591
|
|
2582
|
-
if parsed_attrs.key? :option
|
2583
|
-
(opts = parsed_attrs[:option]).each {|opt| attributes[%(#{opt}-option)] = '' }
|
2584
|
-
end
|
2592
|
+
parsed_attrs[:option].each {|opt| attributes[%(#{opt}-option)] = '' } if parsed_attrs.key? :option
|
2585
2593
|
|
2586
2594
|
parsed_style
|
2587
2595
|
else
|
@@ -331,11 +331,12 @@ class PathResolver
|
|
331
331
|
|
332
332
|
# Public: Securely resolve a system path
|
333
333
|
#
|
334
|
-
#
|
335
|
-
#
|
336
|
-
# that
|
337
|
-
# path
|
338
|
-
#
|
334
|
+
# Resolves the target to an absolute path on the current filesystem. The target is assumed to be
|
335
|
+
# relative to the start path, jail path, or working directory (specified in the constructor), in
|
336
|
+
# that order. If a jail path is specified, the resolved path is forced to descend from the jail
|
337
|
+
# path. If a jail path is not provided, the resolved path may be any location on the system. If
|
338
|
+
# the target is an absolute path, use it as is (unless it breaches the jail path). Expands all
|
339
|
+
# parent and self references in the resolved path.
|
339
340
|
#
|
340
341
|
# target - the String target path
|
341
342
|
# start - the String start path from which to resolve a relative target; falls back to jail, if
|
@@ -347,8 +348,9 @@ class PathResolver
|
|
347
348
|
# automatically recover when an illegal path is encountered
|
348
349
|
# * :target_name is used in messages to refer to the path being resolved
|
349
350
|
#
|
350
|
-
#
|
351
|
-
# if specified. The path is posixified and all parent and self references in the path
|
351
|
+
# Returns an absolute String path relative to the start path, if specified, and confined to the
|
352
|
+
# jail path, if specified. The path is posixified and all parent and self references in the path
|
353
|
+
# are expanded.
|
352
354
|
def system_path target, start = nil, jail = nil, opts = {}
|
353
355
|
if jail
|
354
356
|
raise ::SecurityError, %(Jail is not an absolute path: #{jail}) unless root? jail
|
@@ -362,7 +364,7 @@ class PathResolver
|
|
362
364
|
if jail && !(descends_from? target_path, jail)
|
363
365
|
if opts.fetch :recover, true
|
364
366
|
logger.warn %(#{opts[:target_name] || 'path'} is outside of jail; recovering automatically)
|
365
|
-
target_segments,
|
367
|
+
target_segments, = partition_path target_path
|
366
368
|
jail_segments, jail_root = partition_path jail
|
367
369
|
return join_path jail_segments + target_segments, jail_root
|
368
370
|
else
|
@@ -371,7 +373,7 @@ class PathResolver
|
|
371
373
|
end
|
372
374
|
return target_path
|
373
375
|
else
|
374
|
-
target_segments,
|
376
|
+
target_segments, = partition_path target
|
375
377
|
end
|
376
378
|
else
|
377
379
|
target_segments = []
|
@@ -387,7 +389,7 @@ class PathResolver
|
|
387
389
|
return expand_path start
|
388
390
|
end
|
389
391
|
else
|
390
|
-
target_segments,
|
392
|
+
target_segments, = partition_path start
|
391
393
|
start = jail || @working_dir
|
392
394
|
end
|
393
395
|
elsif start.nil_or_empty?
|
@@ -419,7 +421,7 @@ class PathResolver
|
|
419
421
|
if (resolved_segments = start_segments + target_segments).include? DOT_DOT
|
420
422
|
unresolved_segments, resolved_segments = resolved_segments, []
|
421
423
|
if jail
|
422
|
-
jail_segments,
|
424
|
+
jail_segments, = partition_path jail unless jail_segments
|
423
425
|
warned = false
|
424
426
|
unresolved_segments.each do |segment|
|
425
427
|
if segment == DOT_DOT
|
@@ -450,7 +452,7 @@ class PathResolver
|
|
450
452
|
target_path
|
451
453
|
elsif opts.fetch :recover, true
|
452
454
|
logger.warn %(#{opts[:target_name] || 'path'} is outside of jail; recovering automatically)
|
453
|
-
jail_segments,
|
455
|
+
jail_segments, = partition_path jail unless jail_segments
|
454
456
|
join_path jail_segments + target_segments, jail_root
|
455
457
|
else
|
456
458
|
raise ::SecurityError, %(#{opts[:target_name] || 'path'} #{target} is outside of jail: #{jail} (disallowed in safe mode))
|
data/lib/asciidoctor/reader.rb
CHANGED
@@ -44,11 +44,11 @@ class Reader
|
|
44
44
|
@file = nil
|
45
45
|
@dir = '.'
|
46
46
|
@path = '<stdin>'
|
47
|
-
@lineno = 1
|
47
|
+
@lineno = 1
|
48
48
|
elsif ::String === cursor
|
49
49
|
@file = cursor
|
50
50
|
@dir, @path = ::File.split @file
|
51
|
-
@lineno = 1
|
51
|
+
@lineno = 1
|
52
52
|
else
|
53
53
|
if (@file = cursor.file)
|
54
54
|
@dir = cursor.dir || (::File.dirname @file)
|
@@ -57,7 +57,7 @@ class Reader
|
|
57
57
|
@dir = cursor.dir || '.'
|
58
58
|
@path = cursor.path || '<stdin>'
|
59
59
|
end
|
60
|
-
@lineno = cursor.lineno || 1
|
60
|
+
@lineno = cursor.lineno || 1
|
61
61
|
end
|
62
62
|
@lines = prepare_lines data, opts
|
63
63
|
@source_lines = @lines.drop 0
|
@@ -570,17 +570,18 @@ class Reader
|
|
570
570
|
#
|
571
571
|
# data - A String Array or String of source data to be normalized.
|
572
572
|
# opts - A Hash of options to control how lines are prepared.
|
573
|
-
# :normalize - Enables line normalization, which coerces the encoding to UTF-8 and removes trailing whitespace
|
574
|
-
# (optional,
|
573
|
+
# :normalize - Enables line normalization, which coerces the encoding to UTF-8 and removes trailing whitespace;
|
574
|
+
# :rstrip removes all trailing whitespace; :chomp removes trailing newline only (optional, not set).
|
575
575
|
#
|
576
576
|
# Returns A String Array of source lines. If the source data is an Array, this method returns a copy.
|
577
577
|
def prepare_lines data, opts = {}
|
578
|
-
if opts[:normalize]
|
579
|
-
|
578
|
+
if (normalize = opts[:normalize])
|
579
|
+
trim_end = normalize == :chomp ? false : true
|
580
|
+
::Array === data ? (Helpers.prepare_source_array data, trim_end) : (Helpers.prepare_source_string data, trim_end)
|
580
581
|
elsif ::Array === data
|
581
582
|
data.drop 0
|
582
583
|
elsif data
|
583
|
-
data.split LF, -1
|
584
|
+
data.chomp.split LF, -1
|
584
585
|
else
|
585
586
|
[]
|
586
587
|
end
|
@@ -717,7 +718,7 @@ class PreprocessorReader < Reader
|
|
717
718
|
end
|
718
719
|
|
719
720
|
# effectively fill the buffer
|
720
|
-
if (@lines = prepare_lines data, normalize:
|
721
|
+
if (@lines = prepare_lines data, normalize: @process_lines || :chomp, condense: false, indent: attributes['indent']).empty?
|
721
722
|
pop_include
|
722
723
|
else
|
723
724
|
# FIXME we eventually want to handle leveloffset without affecting the lines
|
@@ -808,7 +809,6 @@ class PreprocessorReader < Reader
|
|
808
809
|
end
|
809
810
|
|
810
811
|
if opts.fetch :condense, true
|
811
|
-
result.shift && @lineno += 1 while (first = result[0]) && first.empty?
|
812
812
|
result.pop while (last = result[-1]) && last.empty?
|
813
813
|
end
|
814
814
|
|
@@ -1060,7 +1060,13 @@ class PreprocessorReader < Reader
|
|
1060
1060
|
return inc_path
|
1061
1061
|
end
|
1062
1062
|
|
1063
|
+
if (enc = parsed_attrs['encoding']) && (::Encoding.find enc rescue nil)
|
1064
|
+
(read_mode_params = read_mode.split ':')[1] = enc
|
1065
|
+
read_mode = read_mode_params.join ':'
|
1066
|
+
end unless RUBY_ENGINE_OPAL
|
1067
|
+
|
1063
1068
|
inc_linenos = inc_tags = nil
|
1069
|
+
# NOTE attrlist is nil if missing from include directive
|
1064
1070
|
if attrlist
|
1065
1071
|
if parsed_attrs.key? 'lines'
|
1066
1072
|
inc_linenos = []
|
@@ -1131,9 +1137,10 @@ class PreprocessorReader < Reader
|
|
1131
1137
|
else
|
1132
1138
|
select = base_select = wildcard = inc_tags.delete '**'
|
1133
1139
|
end
|
1140
|
+
elsif inc_tags.key? '*'
|
1141
|
+
select = base_select = !(wildcard = inc_tags.delete '*')
|
1134
1142
|
else
|
1135
|
-
select = base_select =
|
1136
|
-
wildcard = inc_tags.delete '*'
|
1143
|
+
select = base_select = false
|
1137
1144
|
end
|
1138
1145
|
begin
|
1139
1146
|
reader.call inc_path, read_mode do |f|
|
@@ -1148,7 +1155,7 @@ class PreprocessorReader < Reader
|
|
1148
1155
|
active_tag, select = tag_stack.empty? ? [nil, base_select] : tag_stack[-1]
|
1149
1156
|
elsif inc_tags.key? this_tag
|
1150
1157
|
include_cursor = create_include_cursor inc_path, expanded_target, inc_lineno
|
1151
|
-
if (idx = tag_stack.rindex {|key
|
1158
|
+
if (idx = tag_stack.rindex {|key,| key == this_tag })
|
1152
1159
|
idx == 0 ? tag_stack.shift : (tag_stack.delete_at idx)
|
1153
1160
|
logger.warn message_with_context %(mismatched end tag (expected '#{active_tag}' but found '#{this_tag}') at line #{inc_lineno} of include #{target_type}: #{inc_path}), source_location: cursor, include_location: include_cursor
|
1154
1161
|
else
|
data/lib/asciidoctor/rx.rb
CHANGED
@@ -269,7 +269,7 @@ module Asciidoctor
|
|
269
269
|
#
|
270
270
|
# NOTE we only have to check as far as the blank character because we know it means non-whitespace follows.
|
271
271
|
# IMPORTANT if this regexp does not agree with the regexp for each list type, the parser will hang.
|
272
|
-
AnyListRx = %r(^(?:[ \t]*(?:-|\*\**|\.\.*|\u2022|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|(?!//[^/])[ \t]*[^ \t]#{CC_ANY}*?(?::::{0,2}|;;)(?:$|[ \t])
|
272
|
+
AnyListRx = %r(^(?:[ \t]*(?:-|\*\**|\.\.*|\u2022|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|(?!//[^/])[ \t]*[^ \t]#{CC_ANY}*?(?::::{0,2}|;;)(?:$|[ \t])|<(?:\d+|\.)>[ \t]))
|
273
273
|
|
274
274
|
# Matches an unordered list item (one level for hyphens, up to 5 levels for asterisks).
|
275
275
|
#
|
@@ -469,7 +469,7 @@ module Asciidoctor
|
|
469
469
|
# footnoteref:[id,text] (legacy)
|
470
470
|
# footnoteref:[id] (legacy)
|
471
471
|
#
|
472
|
-
InlineFootnoteMacroRx = /\\?footnote(?:(ref):|:([#{CC_WORD}-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\]/m
|
472
|
+
InlineFootnoteMacroRx = /\\?footnote(?:(ref):|:([#{CC_WORD}-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\](?!<\/a>)/m
|
473
473
|
|
474
474
|
# Matches an image or icon inline macro.
|
475
475
|
#
|
@@ -514,9 +514,10 @@ module Asciidoctor
|
|
514
514
|
# https://github.com[GitHub]
|
515
515
|
# <https://github.com>
|
516
516
|
# link:https://github.com[]
|
517
|
+
# "https://github.com[]"
|
517
518
|
#
|
518
519
|
# FIXME revisit! the main issue is we need different rules for implicit vs explicit
|
519
|
-
InlineLinkRx = %r((^|link:|#{CG_BLANK}|<|[>\(\)\[\];])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*([^\s.,\[\]<]))(?:\[(|#{CC_ALL}*?[^\\])\])?)m
|
520
|
+
InlineLinkRx = %r((^|link:|#{CG_BLANK}|<|[>\(\)\[\];"'])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*([^\s.,\[\]<]))(?:\[(|#{CC_ALL}*?[^\\])\])?)m
|
520
521
|
|
521
522
|
# Match a link or e-mail inline macro.
|
522
523
|
#
|
@@ -551,7 +552,7 @@ module Asciidoctor
|
|
551
552
|
# menu:View[Page Style > No Style]
|
552
553
|
# menu:View[Page Style, No Style]
|
553
554
|
#
|
554
|
-
InlineMenuMacroRx = /\\?menu:(#{CG_WORD}|[#{CC_WORD}&][^\n\[]*[^\s\[])\[ *(?:|(#{CC_ALL}*?[^\\]))
|
555
|
+
InlineMenuMacroRx = /\\?menu:(#{CG_WORD}|[#{CC_WORD}&][^\n\[]*[^\s\[])\[ *(?:|(#{CC_ALL}*?[^\\]))\]/m
|
555
556
|
|
556
557
|
# Matches an implicit menu inline macro.
|
557
558
|
#
|
@@ -591,7 +592,7 @@ module Asciidoctor
|
|
591
592
|
# $$text$$
|
592
593
|
# pass:quotes[text]
|
593
594
|
#
|
594
|
-
# NOTE we have to support an empty pass:[] for compatibility with AsciiDoc
|
595
|
+
# NOTE we have to support an empty pass:[] for compatibility with AsciiDoc.py
|
595
596
|
InlinePassMacroRx = /(?:(?:(\\?)\[([^\]]+)\])?(\\{0,2})(\+\+\+?|\$\$)(#{CC_ALL}*?)\4|(\\?)pass:([a-z]+(?:,[a-z-]+)*)?\[(|#{CC_ALL}*?[^\\])\])/m
|
596
597
|
|
597
598
|
# Matches an xref (i.e., cross-reference) inline macro, which may span multiple lines.
|
@@ -610,7 +611,7 @@ module Asciidoctor
|
|
610
611
|
# Matches a trailing + preceded by at least one space character,
|
611
612
|
# which forces a hard line break (<br> tag in HTML output).
|
612
613
|
#
|
613
|
-
# NOTE AsciiDoc
|
614
|
+
# NOTE AsciiDoc.py allows + to be preceded by TAB; Asciidoctor does not
|
614
615
|
#
|
615
616
|
# Examples
|
616
617
|
#
|
@@ -331,8 +331,8 @@ module Substitutors
|
|
331
331
|
target ||= ext_config[:format] == :short ? content : target
|
332
332
|
end
|
333
333
|
if (Inline === (replacement = extension.process_method[self, target, attributes]))
|
334
|
-
if (inline_subs = replacement.attributes.delete 'subs')
|
335
|
-
replacement.text = apply_subs replacement.text,
|
334
|
+
if (inline_subs = replacement.attributes.delete 'subs') && (inline_subs = expand_subs inline_subs, 'custom inline macro')
|
335
|
+
replacement.text = apply_subs replacement.text, inline_subs
|
336
336
|
end
|
337
337
|
replacement.convert
|
338
338
|
elsif replacement
|
@@ -542,14 +542,17 @@ module Substitutors
|
|
542
542
|
end
|
543
543
|
|
544
544
|
prefix, suffix = $1, ''
|
545
|
-
# NOTE if $4 is set,
|
545
|
+
# NOTE if $4 is set, we're looking at a formal macro (e.g., https://example.org[])
|
546
546
|
if $4
|
547
547
|
prefix = '' if prefix == 'link:'
|
548
548
|
text = $4
|
549
549
|
else
|
550
|
-
# invalid macro syntax (link: prefix w/o trailing square brackets)
|
551
|
-
# FIXME we probably shouldn't even get here
|
552
|
-
|
550
|
+
# invalid macro syntax (link: prefix w/o trailing square brackets or enclosed in double quotes)
|
551
|
+
# FIXME we probably shouldn't even get here when the link: prefix is present; the regex is doing too much
|
552
|
+
case prefix
|
553
|
+
when 'link:', ?", ?'
|
554
|
+
next $&
|
555
|
+
end
|
553
556
|
text = ''
|
554
557
|
case $3
|
555
558
|
when ')'
|
@@ -591,7 +594,8 @@ module Substitutors
|
|
591
594
|
unless text.empty?
|
592
595
|
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
|
593
596
|
if !doc.compat_mode && (text.include? '=')
|
594
|
-
|
597
|
+
# NOTE if an equals sign (=) is present, extract attributes from text
|
598
|
+
text, attrs = extract_attributes_from_text text, ''
|
595
599
|
link_opts[:id] = attrs['id']
|
596
600
|
end
|
597
601
|
|
@@ -637,7 +641,8 @@ module Substitutors
|
|
637
641
|
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
|
638
642
|
if mailto
|
639
643
|
if !doc.compat_mode && (text.include? ',')
|
640
|
-
|
644
|
+
# NOTE if a comma (,) is present, extract attributes from text
|
645
|
+
text, attrs = extract_attributes_from_text text, ''
|
641
646
|
link_opts[:id] = attrs['id']
|
642
647
|
if attrs.key? 2
|
643
648
|
if attrs.key? 3
|
@@ -648,7 +653,8 @@ module Substitutors
|
|
648
653
|
end
|
649
654
|
end
|
650
655
|
elsif !doc.compat_mode && (text.include? '=')
|
651
|
-
|
656
|
+
# NOTE if an equals sign (=) is present, extract attributes from text
|
657
|
+
text, attrs = extract_attributes_from_text text, ''
|
652
658
|
link_opts[:id] = attrs['id']
|
653
659
|
end
|
654
660
|
|
@@ -739,8 +745,8 @@ module Substitutors
|
|
739
745
|
refid = $2
|
740
746
|
if (text = $3)
|
741
747
|
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
|
742
|
-
# NOTE if an
|
743
|
-
text =
|
748
|
+
# NOTE if an equals sign (=) is present, extract attributes from text
|
749
|
+
text, attrs = extract_attributes_from_text text if !doc.compat_mode && (text.include? '=')
|
744
750
|
end
|
745
751
|
end
|
746
752
|
|
@@ -804,7 +810,7 @@ module Substitutors
|
|
804
810
|
# handles: id (in compat mode or when natural xrefs are disabled)
|
805
811
|
elsif doc.compat_mode || !Compliance.natural_xrefs
|
806
812
|
refid, target = fragment, %(##{fragment})
|
807
|
-
logger.info %(possible invalid reference: #{refid}) if logger.info? && doc.catalog[:refs][refid]
|
813
|
+
logger.info %(possible invalid reference: #{refid}) if logger.info? && !doc.catalog[:refs][refid]
|
808
814
|
# handles: id
|
809
815
|
elsif doc.catalog[:refs][fragment]
|
810
816
|
refid, target = fragment, %(##{fragment})
|
@@ -843,19 +849,17 @@ module Substitutors
|
|
843
849
|
end
|
844
850
|
|
845
851
|
if id
|
846
|
-
if
|
852
|
+
if (footnote = doc.footnotes.find {|candidate| candidate.id == id })
|
853
|
+
index, text = footnote.index, footnote.text
|
854
|
+
type, target, id = :xref, id, nil
|
855
|
+
elsif text
|
847
856
|
text = restore_passthroughs(normalize_text text, true, true)
|
848
857
|
index = doc.counter('footnote-number')
|
849
858
|
doc.register(:footnotes, Document::Footnote.new(index, id, text))
|
850
859
|
type, target = :ref, nil
|
851
860
|
else
|
852
|
-
|
853
|
-
|
854
|
-
else
|
855
|
-
logger.warn %(invalid footnote reference: #{id})
|
856
|
-
index, text = nil, id
|
857
|
-
end
|
858
|
-
type, target, id = :xref, id, nil
|
861
|
+
logger.warn %(invalid footnote reference: #{id})
|
862
|
+
type, target, text, id = :xref, id, id, nil
|
859
863
|
end
|
860
864
|
elsif text
|
861
865
|
text = restore_passthroughs(normalize_text text, true, true)
|
@@ -917,7 +921,7 @@ module Substitutors
|
|
917
921
|
# use sub since it might be behind a line comment
|
918
922
|
$&.sub RS, ''
|
919
923
|
else
|
920
|
-
Inline.new(self, :callout, $4 == '.' ? (autonum += 1).to_s : $4, id: @document.callouts.read_next_id, attributes: { 'guard' => $1 }).convert
|
924
|
+
Inline.new(self, :callout, $4 == '.' ? (autonum += 1).to_s : $4, id: @document.callouts.read_next_id, attributes: { 'guard' => $1 || ($3 == '--' ? ['<!--', '-->'] : nil) }).convert
|
921
925
|
end
|
922
926
|
end
|
923
927
|
end
|
@@ -945,7 +949,7 @@ module Substitutors
|
|
945
949
|
if (linenums_mode = (attr? 'linenums') ? (doc_attrs[%(#{syntax_hl_name}-linenums-mode)] || :table).to_sym : nil)
|
946
950
|
start_line_number = 1 if (start_line_number = (attr 'start', 1).to_i) < 1
|
947
951
|
end
|
948
|
-
highlight_lines = resolve_lines_to_highlight source, (attr 'highlight') if attr? 'highlight'
|
952
|
+
highlight_lines = resolve_lines_to_highlight source, (attr 'highlight'), start_line_number if attr? 'highlight'
|
949
953
|
|
950
954
|
highlighted, source_offset = syntax_hl.highlight self, source, (attr 'language'),
|
951
955
|
callouts: callout_marks,
|
@@ -968,9 +972,10 @@ module Substitutors
|
|
968
972
|
#
|
969
973
|
# source - The String source.
|
970
974
|
# spec - The lines specifier (e.g., "1-5, !2, 10" or "1..5;!2;10")
|
975
|
+
# start - The line number of the first line (optional, default: false)
|
971
976
|
#
|
972
977
|
# Returns an [Array] of unique, sorted line numbers.
|
973
|
-
def resolve_lines_to_highlight source, spec
|
978
|
+
def resolve_lines_to_highlight source, spec, start = nil
|
974
979
|
lines = []
|
975
980
|
spec = spec.delete ' ' if spec.include? ' '
|
976
981
|
((spec.include? ',') ? (spec.split ',') : (spec.split ';')).map do |entry|
|
@@ -981,21 +986,22 @@ module Substitutors
|
|
981
986
|
if (delim = (entry.include? '..') ? '..' : ((entry.include? '-') ? '-' : nil))
|
982
987
|
from, delim, to = entry.partition delim
|
983
988
|
to = (source.count LF) + 1 if to.empty? || (to = to.to_i) < 0
|
984
|
-
line_nums = (from.to_i..to).to_a
|
985
|
-
if negate
|
986
|
-
lines -= line_nums
|
987
|
-
else
|
988
|
-
lines.concat line_nums
|
989
|
-
end
|
990
|
-
else
|
991
989
|
if negate
|
992
|
-
lines
|
990
|
+
lines -= (from.to_i..to).to_a
|
993
991
|
else
|
994
|
-
lines
|
992
|
+
lines |= (from.to_i..to).to_a
|
995
993
|
end
|
994
|
+
elsif negate
|
995
|
+
lines.delete entry.to_i
|
996
|
+
elsif !lines.include?(line = entry.to_i)
|
997
|
+
lines << line
|
996
998
|
end
|
997
999
|
end
|
998
|
-
lines.
|
1000
|
+
# If the start attribute is defined, then the lines to highlight specified by the provided spec should be relative to the start value.
|
1001
|
+
unless (shift = start ? start - 1 : 0) == 0
|
1002
|
+
lines = lines.map {|it| it - shift }
|
1003
|
+
end
|
1004
|
+
lines.sort
|
999
1005
|
end
|
1000
1006
|
|
1001
1007
|
# Public: Extract the passthrough text from the document for reinsertion after processing.
|
@@ -1112,7 +1118,7 @@ module Substitutors
|
|
1112
1118
|
end
|
1113
1119
|
subs = $2
|
1114
1120
|
content = normalize_text $3, nil, true
|
1115
|
-
# NOTE drop enclosing $ signs around latexmath for backwards compatibility with AsciiDoc
|
1121
|
+
# NOTE drop enclosing $ signs around latexmath for backwards compatibility with AsciiDoc.py
|
1116
1122
|
content = content.slice 1, content.length - 2 if type == :latexmath && (content.start_with? '$') && (content.end_with? '$')
|
1117
1123
|
subs = subs ? (resolve_pass_subs subs) : ((@document.basebackend? 'html') ? BASIC_SUBS : nil)
|
1118
1124
|
passthrus[passthru_key = passthrus.size] = { text: content, subs: subs, type: type }
|
@@ -1226,17 +1232,16 @@ module Substitutors
|
|
1226
1232
|
resolve_subs subs, :inline, nil, 'passthrough macro'
|
1227
1233
|
end
|
1228
1234
|
|
1229
|
-
# Public: Expand all groups in the subs list and return. If no subs are
|
1235
|
+
# Public: Expand all groups in the subs list and return. If no subs are resolved, return nil.
|
1230
1236
|
#
|
1231
|
-
# subs - The substitutions to expand; can be a Symbol, Symbol Array or
|
1237
|
+
# subs - The substitutions to expand; can be a Symbol, Symbol Array, or String
|
1238
|
+
# subject - The String to use in log messages to communicate the subject for which subs are being resolved (default: nil)
|
1232
1239
|
#
|
1233
1240
|
# Returns a Symbol Array of substitutions to pass to apply_subs or nil if no substitutions were resolved.
|
1234
|
-
def expand_subs subs
|
1241
|
+
def expand_subs subs, subject = nil
|
1235
1242
|
if ::Symbol === subs
|
1236
|
-
|
1237
|
-
|
1238
|
-
end
|
1239
|
-
else
|
1243
|
+
subs == :none ? nil : SUB_GROUPS[subs] || [subs]
|
1244
|
+
elsif ::Array === subs
|
1240
1245
|
expanded_subs = []
|
1241
1246
|
subs.each do |key|
|
1242
1247
|
unless key == :none
|
@@ -1247,8 +1252,9 @@ module Substitutors
|
|
1247
1252
|
end
|
1248
1253
|
end
|
1249
1254
|
end
|
1250
|
-
|
1251
1255
|
expanded_subs.empty? ? nil : expanded_subs
|
1256
|
+
else
|
1257
|
+
resolve_subs subs, :inline, nil, subject
|
1252
1258
|
end
|
1253
1259
|
end
|
1254
1260
|
|
@@ -1270,7 +1276,7 @@ module Substitutors
|
|
1270
1276
|
# NOTE :literal with listparagraph-option gets folded into text of list item later
|
1271
1277
|
default_subs = @context == :verse ? NORMAL_SUBS : VERBATIM_SUBS
|
1272
1278
|
when :raw
|
1273
|
-
# TODO make pass subs a compliance setting; AsciiDoc
|
1279
|
+
# TODO make pass subs a compliance setting; AsciiDoc.py performs :attributes and :macros on a pass block
|
1274
1280
|
default_subs = @context == :stem ? BASIC_SUBS : NO_SUBS
|
1275
1281
|
else
|
1276
1282
|
return @subs
|
@@ -1321,10 +1327,23 @@ module Substitutors
|
|
1321
1327
|
|
1322
1328
|
private
|
1323
1329
|
|
1330
|
+
# This method is used in cases when the attrlist can be mixed with the text of a macro.
|
1331
|
+
# If no attributes are detected aside from the first positional attribute, and the first positional
|
1332
|
+
# attribute matches the attrlist, then the original text is returned.
|
1333
|
+
def extract_attributes_from_text text, default_text = nil
|
1334
|
+
attrlist = (text.include? LF) ? (text.tr LF, ' ') : text
|
1335
|
+
if (resolved_text = (attrs = (AttributeList.new attrlist, self).parse)[1])
|
1336
|
+
# NOTE if resolved text remains unchanged, clear attributes and return unparsed text
|
1337
|
+
resolved_text == attrlist ? [text, attrs.clear] : [resolved_text, attrs]
|
1338
|
+
else
|
1339
|
+
[default_text, attrs]
|
1340
|
+
end
|
1341
|
+
end
|
1342
|
+
|
1324
1343
|
# Internal: Extract the callout numbers from the source to prepare it for syntax highlighting.
|
1325
1344
|
def extract_callouts source
|
1326
1345
|
callout_marks = {}
|
1327
|
-
lineno = 0
|
1346
|
+
autonum = lineno = 0
|
1328
1347
|
last_lineno = nil
|
1329
1348
|
callout_rx = (attr? 'line-comment') ? CalloutExtractRxMap[attr 'line-comment'] : CalloutExtractRx
|
1330
1349
|
# extract callout marks, indexed by line number
|
@@ -1336,7 +1355,7 @@ module Substitutors
|
|
1336
1355
|
# use sub since it might be behind a line comment
|
1337
1356
|
$&.sub RS, ''
|
1338
1357
|
else
|
1339
|
-
(callout_marks[lineno] ||= []) << [$1, $4]
|
1358
|
+
(callout_marks[lineno] ||= []) << [$1 || ($3 == '--' ? ['<!--', '-->'] : nil), $4 == '.' ? (autonum += 1).to_s : $4]
|
1340
1359
|
last_lineno = lineno
|
1341
1360
|
''
|
1342
1361
|
end
|
@@ -1358,15 +1377,15 @@ module Substitutors
|
|
1358
1377
|
else
|
1359
1378
|
preamble = ''
|
1360
1379
|
end
|
1361
|
-
|
1380
|
+
lineno = 0
|
1362
1381
|
preamble + ((source.split LF, -1).map do |line|
|
1363
1382
|
if (conums = callout_marks.delete lineno += 1)
|
1364
1383
|
if conums.size == 1
|
1365
|
-
guard,
|
1366
|
-
%(#{line}#{Inline.new(self, :callout,
|
1384
|
+
guard, numeral = conums[0]
|
1385
|
+
%(#{line}#{Inline.new(self, :callout, numeral, id: @document.callouts.read_next_id, attributes: { 'guard' => guard }).convert})
|
1367
1386
|
else
|
1368
|
-
%(#{line}#{conums.map do |guard_it,
|
1369
|
-
Inline.new(self, :callout,
|
1387
|
+
%(#{line}#{conums.map do |guard_it, numeral_it|
|
1388
|
+
Inline.new(self, :callout, numeral_it, id: @document.callouts.read_next_id, attributes: { 'guard' => guard_it }).convert
|
1370
1389
|
end.join ' '})
|
1371
1390
|
end
|
1372
1391
|
else
|