asciidoctor 2.0.12 → 2.0.16
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 +142 -22
- data/LICENSE +1 -1
- data/README-de.adoc +15 -6
- data/README-fr.adoc +14 -8
- data/README-jp.adoc +15 -6
- data/README-zh_CN.adoc +14 -5
- data/README.adoc +135 -125
- data/asciidoctor.gemspec +4 -11
- data/data/locale/attributes-be.adoc +23 -0
- data/data/locale/attributes-it.adoc +4 -4
- data/data/locale/attributes-nl.adoc +6 -6
- data/data/locale/attributes-th.adoc +23 -0
- data/data/locale/attributes-vi.adoc +23 -0
- data/data/reference/syntax.adoc +14 -7
- data/data/stylesheets/asciidoctor-default.css +51 -52
- data/lib/asciidoctor.rb +12 -12
- data/lib/asciidoctor/abstract_block.rb +4 -4
- data/lib/asciidoctor/abstract_node.rb +10 -9
- data/lib/asciidoctor/attribute_list.rb +6 -6
- data/lib/asciidoctor/block.rb +6 -6
- data/lib/asciidoctor/cli/invoker.rb +0 -1
- data/lib/asciidoctor/cli/options.rb +27 -26
- data/lib/asciidoctor/convert.rb +1 -0
- data/lib/asciidoctor/converter.rb +5 -3
- data/lib/asciidoctor/converter/docbook5.rb +45 -26
- data/lib/asciidoctor/converter/html5.rb +89 -69
- data/lib/asciidoctor/converter/manpage.rb +113 -86
- data/lib/asciidoctor/converter/template.rb +11 -12
- data/lib/asciidoctor/document.rb +44 -51
- data/lib/asciidoctor/extensions.rb +10 -12
- data/lib/asciidoctor/helpers.rb +3 -6
- data/lib/asciidoctor/list.rb +2 -6
- data/lib/asciidoctor/load.rb +13 -11
- data/lib/asciidoctor/logging.rb +10 -8
- data/lib/asciidoctor/parser.rb +135 -150
- data/lib/asciidoctor/path_resolver.rb +3 -3
- data/lib/asciidoctor/reader.rb +72 -71
- data/lib/asciidoctor/rx.rb +4 -3
- data/lib/asciidoctor/substitutors.rb +117 -117
- data/lib/asciidoctor/syntax_highlighter.rb +8 -11
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +2 -1
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +1 -1
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +6 -5
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +33 -26
- data/lib/asciidoctor/table.rb +17 -19
- data/lib/asciidoctor/timings.rb +3 -3
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +10 -11
- data/man/asciidoctor.adoc +8 -7
- metadata +14 -67
@@ -109,7 +109,7 @@ class PathResolver
|
|
109
109
|
SLASH = '/'
|
110
110
|
BACKSLASH = '\\'
|
111
111
|
DOUBLE_SLASH = '//'
|
112
|
-
WindowsRootRx =
|
112
|
+
WindowsRootRx = %r(^(?:[a-zA-Z]:)?[\\/])
|
113
113
|
|
114
114
|
attr_accessor :file_separator
|
115
115
|
attr_accessor :working_dir
|
@@ -290,8 +290,8 @@ class PathResolver
|
|
290
290
|
# ex. ./sample/path
|
291
291
|
elsif posix_path.start_with? DOT_SLASH
|
292
292
|
root = DOT_SLASH
|
293
|
-
# else ex. sample/path
|
294
293
|
end
|
294
|
+
# otherwise ex. sample/path
|
295
295
|
elsif root? posix_path
|
296
296
|
# ex. //sample/path
|
297
297
|
if unc? posix_path
|
@@ -306,8 +306,8 @@ class PathResolver
|
|
306
306
|
# ex. ./sample/path
|
307
307
|
elsif posix_path.start_with? DOT_SLASH
|
308
308
|
root = DOT_SLASH
|
309
|
-
# else ex. sample/path
|
310
309
|
end
|
310
|
+
# otherwise ex. sample/path
|
311
311
|
|
312
312
|
path_segments = (root ? (posix_path.slice root.length, posix_path.length) : posix_path).split SLASH
|
313
313
|
# strip out all dot entries
|
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,10 +57,9 @@ 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
|
-
@lines = prepare_lines data, opts
|
63
|
-
@source_lines = @lines.drop 0
|
62
|
+
@lines = (@source_lines = prepare_lines data, opts).reverse
|
64
63
|
@mark = nil
|
65
64
|
@look_ahead = 0
|
66
65
|
@process_lines = true
|
@@ -127,7 +126,7 @@ class Reader
|
|
127
126
|
# Returns nothing if there is no more data.
|
128
127
|
def peek_line direct = false
|
129
128
|
if direct || @look_ahead > 0
|
130
|
-
@unescape_next_line ? ((line = @lines[
|
129
|
+
@unescape_next_line ? ((line = @lines[-1]).slice 1, line.length) : @lines[-1]
|
131
130
|
elsif @lines.empty?
|
132
131
|
@look_ahead = 0
|
133
132
|
nil
|
@@ -135,7 +134,7 @@ class Reader
|
|
135
134
|
# FIXME the problem with this approach is that we aren't
|
136
135
|
# retaining the modified line (hence the @unescape_next_line tweak)
|
137
136
|
# perhaps we need a stack of proxied lines
|
138
|
-
(
|
137
|
+
(process_line @lines[-1]) || peek_line
|
139
138
|
end
|
140
139
|
end
|
141
140
|
|
@@ -192,9 +191,7 @@ class Reader
|
|
192
191
|
def read_lines
|
193
192
|
lines = []
|
194
193
|
# has_more_lines? triggers preprocessor
|
195
|
-
while has_more_lines?
|
196
|
-
lines << shift
|
197
|
-
end
|
194
|
+
lines << shift while has_more_lines?
|
198
195
|
lines
|
199
196
|
end
|
200
197
|
alias readlines read_lines
|
@@ -241,7 +238,6 @@ class Reader
|
|
241
238
|
# Returns nothing.
|
242
239
|
def unshift_lines lines_to_restore
|
243
240
|
unshift_all lines_to_restore
|
244
|
-
nil
|
245
241
|
end
|
246
242
|
alias restore_lines unshift_lines
|
247
243
|
|
@@ -334,7 +330,7 @@ class Reader
|
|
334
330
|
comment_lines = []
|
335
331
|
# optimized code for shortest execution path
|
336
332
|
while (next_line = peek_line) && !next_line.empty?
|
337
|
-
if
|
333
|
+
if next_line.start_with? '//'
|
338
334
|
comment_lines << shift
|
339
335
|
else
|
340
336
|
break
|
@@ -407,34 +403,22 @@ class Reader
|
|
407
403
|
break_on_list_continuation = options[:break_on_list_continuation]
|
408
404
|
end
|
409
405
|
skip_comments = options[:skip_line_comments]
|
410
|
-
|
406
|
+
line_read = line_restored = nil
|
411
407
|
shift if options[:skip_first_line]
|
412
|
-
while
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
if break_on_list_continuation && line_read && line == LIST_CONTINUATION
|
418
|
-
options[:preserve_last_line] = true
|
419
|
-
break true
|
420
|
-
end
|
421
|
-
break true if block_given? && (yield line)
|
422
|
-
break false
|
423
|
-
end
|
424
|
-
if complete
|
425
|
-
if options[:read_last_line]
|
426
|
-
result << line
|
427
|
-
line_read = true
|
428
|
-
end
|
408
|
+
while (line = read_line)
|
409
|
+
if terminator ? line == terminator : ((break_on_blank_lines && line.empty?) ||
|
410
|
+
(break_on_list_continuation && line_read && line == LIST_CONTINUATION && (options[:preserve_last_line] = true)) ||
|
411
|
+
(block_given? && (yield line)))
|
412
|
+
result << line if options[:read_last_line]
|
429
413
|
if options[:preserve_last_line]
|
430
414
|
unshift line
|
431
415
|
line_restored = true
|
432
416
|
end
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
417
|
+
break
|
418
|
+
end
|
419
|
+
unless skip_comments && (line.start_with? '//') && !(line.start_with? '///')
|
420
|
+
result << line
|
421
|
+
line_read = true
|
438
422
|
end
|
439
423
|
end
|
440
424
|
if restore_process_lines
|
@@ -459,21 +443,37 @@ class Reader
|
|
459
443
|
def shift
|
460
444
|
@lineno += 1
|
461
445
|
@look_ahead -= 1 unless @look_ahead == 0
|
462
|
-
@lines.
|
446
|
+
@lines.pop
|
463
447
|
end
|
464
448
|
|
465
449
|
# Internal: Restore the line to the stack and decrement the lineno
|
466
450
|
def unshift line
|
467
451
|
@lineno -= 1
|
468
452
|
@look_ahead += 1
|
469
|
-
@lines.
|
453
|
+
@lines.push line
|
454
|
+
nil
|
470
455
|
end
|
471
456
|
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
457
|
+
if ::RUBY_ENGINE == 'jruby'
|
458
|
+
# Internal: Restore the lines to the stack and decrement the lineno
|
459
|
+
def unshift_all lines_to_restore
|
460
|
+
@lineno -= lines_to_restore.size
|
461
|
+
@look_ahead += lines_to_restore.size
|
462
|
+
if lines_to_restore.respond_to? :reverse
|
463
|
+
@lines.push(*lines_to_restore.reverse)
|
464
|
+
else
|
465
|
+
lines_to_restore.reverse_each {|it| @lines.push it }
|
466
|
+
end
|
467
|
+
nil
|
468
|
+
end
|
469
|
+
else
|
470
|
+
# Internal: Restore the lines to the stack and decrement the lineno
|
471
|
+
def unshift_all lines_to_restore
|
472
|
+
@lineno -= lines_to_restore.size
|
473
|
+
@look_ahead += lines_to_restore.size
|
474
|
+
@lines.push(*lines_to_restore.reverse)
|
475
|
+
nil
|
476
|
+
end
|
477
477
|
end
|
478
478
|
|
479
479
|
def cursor
|
@@ -516,12 +516,12 @@ class Reader
|
|
516
516
|
#
|
517
517
|
# Returns A copy of the String Array of lines remaining in this Reader
|
518
518
|
def lines
|
519
|
-
@lines.
|
519
|
+
@lines.reverse
|
520
520
|
end
|
521
521
|
|
522
522
|
# Public: Get a copy of the remaining lines managed by this Reader joined as a String
|
523
523
|
def string
|
524
|
-
@lines.join LF
|
524
|
+
@lines.reverse.join LF
|
525
525
|
end
|
526
526
|
|
527
527
|
# Public: Get the source lines for this Reader joined as a String
|
@@ -576,8 +576,7 @@ class Reader
|
|
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
578
|
if (normalize = opts[:normalize])
|
579
|
-
|
580
|
-
::Array === data ? (Helpers.prepare_source_array data, trim_end) : (Helpers.prepare_source_string data, trim_end)
|
579
|
+
::Array === data ? (Helpers.prepare_source_array data, normalize != :chomp) : (Helpers.prepare_source_string data, normalize != :chomp)
|
581
580
|
elsif ::Array === data
|
582
581
|
data.drop 0
|
583
582
|
elsif data
|
@@ -690,6 +689,7 @@ class PreprocessorReader < Reader
|
|
690
689
|
@path = (path ||= ::File.basename file)
|
691
690
|
# only process lines in AsciiDoc files
|
692
691
|
if (@process_lines = file.end_with?(*ASCIIDOC_EXTENSIONS.keys))
|
692
|
+
# NOTE registering the include with a nil value tracks it while not making it visible to interdocument xrefs
|
693
693
|
@includes[path.slice 0, (path.rindex '.')] = attributes['partial-option'] ? nil : true
|
694
694
|
end
|
695
695
|
else
|
@@ -697,6 +697,7 @@ class PreprocessorReader < Reader
|
|
697
697
|
# we don't know what file type we have, so assume AsciiDoc
|
698
698
|
@process_lines = true
|
699
699
|
if (@path = path)
|
700
|
+
# NOTE registering the include with a nil value tracks it while not making it visible to interdocument xrefs
|
700
701
|
@includes[Helpers.rootname path] = attributes['partial-option'] ? nil : true
|
701
702
|
else
|
702
703
|
@path = '<stdin>'
|
@@ -718,21 +719,16 @@ class PreprocessorReader < Reader
|
|
718
719
|
end
|
719
720
|
|
720
721
|
# effectively fill the buffer
|
721
|
-
if (@lines = prepare_lines data, normalize: @process_lines || :chomp, condense:
|
722
|
+
if (@lines = prepare_lines data, normalize: @process_lines || :chomp, condense: false, indent: attributes['indent']).empty?
|
722
723
|
pop_include
|
723
724
|
else
|
724
725
|
# FIXME we eventually want to handle leveloffset without affecting the lines
|
725
726
|
if attributes.key? 'leveloffset'
|
726
|
-
@lines.
|
727
|
-
|
728
|
-
@lines << ''
|
729
|
-
if (old_leveloffset = @document.attr 'leveloffset')
|
730
|
-
@lines << %(:leveloffset: #{old_leveloffset})
|
731
|
-
else
|
732
|
-
@lines << ':leveloffset!:'
|
733
|
-
end
|
734
|
-
# compensate for these extra lines
|
727
|
+
@lines = [((leveloffset = @document.attr 'leveloffset') ? %(:leveloffset: #{leveloffset}) : ':leveloffset!:'), ''] + @lines.reverse + ['', %(:leveloffset: #{attributes['leveloffset']})]
|
728
|
+
# compensate for these extra lines at the top
|
735
729
|
@lineno -= 2
|
730
|
+
else
|
731
|
+
@lines.reverse!
|
736
732
|
end
|
737
733
|
|
738
734
|
# FIXME kind of a hack
|
@@ -802,14 +798,11 @@ class PreprocessorReader < Reader
|
|
802
798
|
result = super
|
803
799
|
|
804
800
|
# QUESTION should this work for AsciiDoc table cell content? Currently it does not.
|
805
|
-
if @document && @document.attributes['skip-front-matter']
|
806
|
-
|
807
|
-
@document.attributes['front-matter'] = front_matter.join LF
|
808
|
-
end
|
801
|
+
if @document && @document.attributes['skip-front-matter'] && (front_matter = skip_front_matter! result)
|
802
|
+
@document.attributes['front-matter'] = front_matter.join LF
|
809
803
|
end
|
810
804
|
|
811
805
|
if opts.fetch :condense, true
|
812
|
-
result.shift && @lineno += 1 while (first = result[0]) && first.empty?
|
813
806
|
result.pop while (last = result[-1]) && last.empty?
|
814
807
|
end
|
815
808
|
|
@@ -956,11 +949,12 @@ class PreprocessorReader < Reader
|
|
956
949
|
if no_target
|
957
950
|
# the text in brackets must match a conditional expression
|
958
951
|
if text && EvalExpressionRx =~ text.strip
|
952
|
+
# NOTE assignments must happen before call to resolve_expr_val for compatiblity with Opal
|
959
953
|
lhs = $1
|
954
|
+
# regex enforces a restricted set of math-related operations (==, !=, <=, >=, <, >)
|
960
955
|
op = $2
|
961
956
|
rhs = $3
|
962
|
-
|
963
|
-
skip = ((resolve_expr_val lhs).send op, (resolve_expr_val rhs)) ? false : true
|
957
|
+
skip = ((resolve_expr_val lhs).send op, (resolve_expr_val rhs)) ? false : true rescue true
|
964
958
|
else
|
965
959
|
logger.error message_with_context %(malformed preprocessor directive - #{text ? 'invalid expression' : 'missing expression'}: ifeval::[#{text}]), source_location: cursor
|
966
960
|
return true
|
@@ -1050,10 +1044,11 @@ class PreprocessorReader < Reader
|
|
1050
1044
|
|
1051
1045
|
parsed_attrs = doc.parse_attributes attrlist, [], sub_input: true
|
1052
1046
|
inc_path, target_type, relpath = resolve_include_path expanded_target, attrlist, parsed_attrs
|
1053
|
-
|
1047
|
+
case target_type
|
1048
|
+
when :file
|
1054
1049
|
reader = ::File.method :open
|
1055
1050
|
read_mode = FILE_READ_MODE
|
1056
|
-
|
1051
|
+
when :uri
|
1057
1052
|
reader = ::OpenURI.method :open_uri
|
1058
1053
|
read_mode = URI_READ_MODE
|
1059
1054
|
else
|
@@ -1074,7 +1069,7 @@ class PreprocessorReader < Reader
|
|
1074
1069
|
(split_delimited_value parsed_attrs['lines']).each do |linedef|
|
1075
1070
|
if linedef.include? '..'
|
1076
1071
|
from, _, to = linedef.partition '..'
|
1077
|
-
inc_linenos += (to.empty? || (to = to.to_i) < 0) ? [from.to_i,
|
1072
|
+
inc_linenos += (to.empty? || (to = to.to_i) < 0) ? [from.to_i, ::Float::INFINITY] : (from.to_i..to).to_a
|
1078
1073
|
else
|
1079
1074
|
inc_linenos << linedef.to_i
|
1080
1075
|
end
|
@@ -1132,15 +1127,21 @@ class PreprocessorReader < Reader
|
|
1132
1127
|
elsif inc_tags
|
1133
1128
|
inc_lines, inc_offset, inc_lineno, tag_stack, tags_used, active_tag = [], nil, 0, [], ::Set.new, nil
|
1134
1129
|
if inc_tags.key? '**'
|
1130
|
+
select = base_select = inc_tags.delete '**'
|
1135
1131
|
if inc_tags.key? '*'
|
1136
|
-
select = base_select = inc_tags.delete '**'
|
1137
1132
|
wildcard = inc_tags.delete '*'
|
1133
|
+
elsif !select && inc_tags.values.first == false
|
1134
|
+
wildcard = true
|
1135
|
+
end
|
1136
|
+
elsif inc_tags.key? '*'
|
1137
|
+
if inc_tags.keys.first == '*'
|
1138
|
+
select = base_select = !(wildcard = inc_tags.delete '*')
|
1138
1139
|
else
|
1139
|
-
select = base_select =
|
1140
|
+
select = base_select = false
|
1141
|
+
wildcard = inc_tags.delete '*'
|
1140
1142
|
end
|
1141
1143
|
else
|
1142
1144
|
select = base_select = !(inc_tags.value? true)
|
1143
|
-
wildcard = inc_tags.delete '*'
|
1144
1145
|
end
|
1145
1146
|
begin
|
1146
1147
|
reader.call inc_path, read_mode do |f|
|
@@ -1191,7 +1192,7 @@ class PreprocessorReader < Reader
|
|
1191
1192
|
end
|
1192
1193
|
shift
|
1193
1194
|
if inc_offset
|
1194
|
-
parsed_attrs['partial-option'] = '' unless base_select && wildcard && inc_tags.empty?
|
1195
|
+
parsed_attrs['partial-option'] = '' unless base_select && wildcard != false && inc_tags.empty?
|
1195
1196
|
# FIXME not accounting for skipped lines in reader line numbering
|
1196
1197
|
push_include inc_lines, inc_path, relpath, inc_offset, parsed_attrs
|
1197
1198
|
end
|
@@ -1262,7 +1263,7 @@ class PreprocessorReader < Reader
|
|
1262
1263
|
end
|
1263
1264
|
|
1264
1265
|
def pop_include
|
1265
|
-
|
1266
|
+
unless @include_stack.empty?
|
1266
1267
|
@lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines = @include_stack.pop
|
1267
1268
|
# FIXME kind of a hack
|
1268
1269
|
#Document::AttributeEntry.new('infile', @file).save_to_next_block @document
|
data/lib/asciidoctor/rx.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Asciidoctor
|
2
3
|
# A collection of regular expression constants used by the parser. (For speed, these are not defined in the Rx module,
|
3
4
|
# but rather directly in the Asciidoctor module).
|
@@ -469,7 +470,7 @@ module Asciidoctor
|
|
469
470
|
# footnoteref:[id,text] (legacy)
|
470
471
|
# footnoteref:[id] (legacy)
|
471
472
|
#
|
472
|
-
InlineFootnoteMacroRx =
|
473
|
+
InlineFootnoteMacroRx = %r(\\?footnote(?:(ref):|:([#{CC_WORD}-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\](?!</a>))m
|
473
474
|
|
474
475
|
# Matches an image or icon inline macro.
|
475
476
|
#
|
@@ -592,7 +593,7 @@ module Asciidoctor
|
|
592
593
|
# $$text$$
|
593
594
|
# pass:quotes[text]
|
594
595
|
#
|
595
|
-
# NOTE we have to support an empty pass:[] for compatibility with AsciiDoc
|
596
|
+
# NOTE we have to support an empty pass:[] for compatibility with AsciiDoc.py
|
596
597
|
InlinePassMacroRx = /(?:(?:(\\?)\[([^\]]+)\])?(\\{0,2})(\+\+\+?|\$\$)(#{CC_ALL}*?)\4|(\\?)pass:([a-z]+(?:,[a-z-]+)*)?\[(|#{CC_ALL}*?[^\\])\])/m
|
597
598
|
|
598
599
|
# Matches an xref (i.e., cross-reference) inline macro, which may span multiple lines.
|
@@ -611,7 +612,7 @@ module Asciidoctor
|
|
611
612
|
# Matches a trailing + preceded by at least one space character,
|
612
613
|
# which forces a hard line break (<br> tag in HTML output).
|
613
614
|
#
|
614
|
-
# NOTE AsciiDoc
|
615
|
+
# NOTE AsciiDoc.py allows + to be preceded by TAB; Asciidoctor does not
|
615
616
|
#
|
616
617
|
# Examples
|
617
618
|
#
|
@@ -330,13 +330,13 @@ module Substitutors
|
|
330
330
|
# NOTE for convenience, map content (unparsed attrlist) to target when format is short
|
331
331
|
target ||= ext_config[:format] == :short ? content : target
|
332
332
|
end
|
333
|
-
if
|
334
|
-
if (inline_subs = replacement.attributes.delete 'subs')
|
335
|
-
replacement.text = apply_subs replacement.text,
|
333
|
+
if Inline === (replacement = extension.process_method[self, target, attributes])
|
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
|
339
|
-
logger.info %(expected substitution value for custom inline macro to be of type Inline; got #{replacement.class}: #{match})
|
339
|
+
logger.info { %(expected substitution value for custom inline macro to be of type Inline; got #{replacement.class}: #{match}) }
|
340
340
|
replacement
|
341
341
|
else
|
342
342
|
''
|
@@ -445,23 +445,16 @@ module Substitutors
|
|
445
445
|
# indexterm:[Tigers,Big cats]
|
446
446
|
if (attrlist = normalize_text $2, true, true).include? '='
|
447
447
|
if (primary = (attrs = (AttributeList.new attrlist, self).parse)[1])
|
448
|
-
attrs['terms'] =
|
449
|
-
if (secondary = attrs[2])
|
450
|
-
terms << secondary
|
451
|
-
if (tertiary = attrs[3])
|
452
|
-
terms << tertiary
|
453
|
-
end
|
454
|
-
end
|
448
|
+
attrs['terms'] = [primary]
|
455
449
|
if (see_also = attrs['see-also'])
|
456
450
|
attrs['see-also'] = (see_also.include? ',') ? (see_also.split ',').map {|it| it.lstrip } : [see_also]
|
457
451
|
end
|
458
452
|
else
|
459
|
-
attrs = { 'terms' =>
|
453
|
+
attrs = { 'terms' => attrlist }
|
460
454
|
end
|
461
455
|
else
|
462
|
-
attrs = { 'terms' => (
|
456
|
+
attrs = { 'terms' => (split_simple_csv attrlist) }
|
463
457
|
end
|
464
|
-
#doc.register :indexterms, terms
|
465
458
|
(Inline.new self, :indexterm, nil, attributes: attrs).convert
|
466
459
|
when 'indexterm2'
|
467
460
|
# honor the escape
|
@@ -474,34 +467,33 @@ module Substitutors
|
|
474
467
|
attrs['see-also'] = (see_also.include? ',') ? (see_also.split ',').map {|it| it.lstrip } : [see_also]
|
475
468
|
end
|
476
469
|
end
|
477
|
-
#doc.register :indexterms, [term]
|
478
470
|
(Inline.new self, :indexterm, term, attributes: attrs, type: :visible).convert
|
479
471
|
else
|
480
|
-
|
472
|
+
encl_text = $3
|
481
473
|
# honor the escape
|
482
474
|
if $&.start_with? RS
|
483
475
|
# escape concealed index term, but process nested flow index term
|
484
|
-
if (
|
485
|
-
|
476
|
+
if (encl_text.start_with? '(') && (encl_text.end_with? ')')
|
477
|
+
encl_text = encl_text.slice 1, encl_text.length - 2
|
486
478
|
visible, before, after = true, '(', ')'
|
487
479
|
else
|
488
480
|
next $&.slice 1, $&.length
|
489
481
|
end
|
490
482
|
else
|
491
483
|
visible = true
|
492
|
-
if
|
493
|
-
if
|
494
|
-
|
484
|
+
if encl_text.start_with? '('
|
485
|
+
if encl_text.end_with? ')'
|
486
|
+
encl_text, visible = (encl_text.slice 1, encl_text.length - 2), false
|
495
487
|
else
|
496
|
-
|
488
|
+
encl_text, before, after = (encl_text.slice 1, encl_text.length), '(', ''
|
497
489
|
end
|
498
|
-
elsif
|
499
|
-
|
490
|
+
elsif encl_text.end_with? ')'
|
491
|
+
encl_text, before, after = encl_text.chop, '', ')'
|
500
492
|
end
|
501
493
|
end
|
502
494
|
if visible
|
503
495
|
# ((Tigers))
|
504
|
-
if (term = normalize_text
|
496
|
+
if (term = normalize_text encl_text, true).include? ';&'
|
505
497
|
if term.include? ' >> '
|
506
498
|
term, _, see = term.partition ' >> '
|
507
499
|
attrs = { 'see' => see }
|
@@ -510,12 +502,11 @@ module Substitutors
|
|
510
502
|
attrs = { 'see-also' => see_also }
|
511
503
|
end
|
512
504
|
end
|
513
|
-
#doc.register :indexterms, [term]
|
514
505
|
subbed_term = (Inline.new self, :indexterm, term, attributes: attrs, type: :visible).convert
|
515
506
|
else
|
516
507
|
# (((Tigers,Big cats)))
|
517
508
|
attrs = {}
|
518
|
-
if (terms = normalize_text
|
509
|
+
if (terms = normalize_text encl_text, true).include? ';&'
|
519
510
|
if terms.include? ' >> '
|
520
511
|
terms, _, see = terms.partition ' >> '
|
521
512
|
attrs['see'] = see
|
@@ -524,8 +515,7 @@ module Substitutors
|
|
524
515
|
attrs['see-also'] = see_also
|
525
516
|
end
|
526
517
|
end
|
527
|
-
attrs['terms'] =
|
528
|
-
#doc.register :indexterms, terms
|
518
|
+
attrs['terms'] = split_simple_csv terms
|
529
519
|
subbed_term = (Inline.new self, :indexterm, nil, attributes: attrs).convert
|
530
520
|
end
|
531
521
|
before ? %(#{before}#{subbed_term}#{after}) : subbed_term
|
@@ -545,7 +535,7 @@ module Substitutors
|
|
545
535
|
# NOTE if $4 is set, we're looking at a formal macro (e.g., https://example.org[])
|
546
536
|
if $4
|
547
537
|
prefix = '' if prefix == 'link:'
|
548
|
-
|
538
|
+
link_text = nil if (link_text = $4).empty?
|
549
539
|
else
|
550
540
|
# invalid macro syntax (link: prefix w/o trailing square brackets or enclosed in double quotes)
|
551
541
|
# FIXME we probably shouldn't even get here when the link: prefix is present; the regex is doing too much
|
@@ -553,12 +543,13 @@ module Substitutors
|
|
553
543
|
when 'link:', ?", ?'
|
554
544
|
next $&
|
555
545
|
end
|
556
|
-
text = ''
|
557
546
|
case $3
|
558
|
-
when ')'
|
559
|
-
# move trailing ) out of URL
|
547
|
+
when ')', '?', '!'
|
560
548
|
target = target.chop
|
561
|
-
suffix = ')'
|
549
|
+
if (suffix = $3) == ')' && (target.end_with? '.', '?', '!')
|
550
|
+
suffix = target[-1] + suffix
|
551
|
+
target = target.chop
|
552
|
+
end
|
562
553
|
# NOTE handle case when modified target is a URI scheme (e.g., http://)
|
563
554
|
next $& if target.end_with? '://'
|
564
555
|
when ';'
|
@@ -591,27 +582,37 @@ module Substitutors
|
|
591
582
|
end
|
592
583
|
|
593
584
|
attrs, link_opts = nil, { type: :link }
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
585
|
+
|
586
|
+
if link_text
|
587
|
+
new_link_text = link_text = link_text.gsub ESC_R_SB, R_SB if link_text.include? R_SB
|
588
|
+
if !doc.compat_mode && (link_text.include? '=')
|
589
|
+
# NOTE if an equals sign (=) is present, extract attributes from link text
|
590
|
+
link_text, attrs = extract_attributes_from_text link_text, ''
|
591
|
+
new_link_text = link_text
|
599
592
|
link_opts[:id] = attrs['id']
|
600
593
|
end
|
601
594
|
|
602
|
-
if
|
603
|
-
|
595
|
+
if link_text.end_with? '^'
|
596
|
+
new_link_text = link_text = link_text.chop
|
604
597
|
if attrs
|
605
598
|
attrs['window'] ||= '_blank'
|
606
599
|
else
|
607
600
|
attrs = { 'window' => '_blank' }
|
608
601
|
end
|
609
602
|
end
|
610
|
-
end
|
611
603
|
|
612
|
-
|
604
|
+
if new_link_text && new_link_text.empty?
|
605
|
+
# NOTE it's not possible for the URI scheme to be bare in this case
|
606
|
+
link_text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
|
607
|
+
bare = true
|
608
|
+
end
|
609
|
+
else
|
613
610
|
# NOTE it's not possible for the URI scheme to be bare in this case
|
614
|
-
|
611
|
+
link_text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
|
612
|
+
bare = true
|
613
|
+
end
|
614
|
+
|
615
|
+
if bare
|
615
616
|
if attrs
|
616
617
|
attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
|
617
618
|
else
|
@@ -621,7 +622,7 @@ module Substitutors
|
|
621
622
|
|
622
623
|
doc.register :links, (link_opts[:target] = target)
|
623
624
|
link_opts[:attributes] = attrs if attrs
|
624
|
-
%(#{prefix}#{(Inline.new self, :anchor,
|
625
|
+
%(#{prefix}#{(Inline.new self, :anchor, link_text, link_opts).convert}#{suffix})
|
625
626
|
end
|
626
627
|
end
|
627
628
|
|
@@ -637,12 +638,12 @@ module Substitutors
|
|
637
638
|
target = $2
|
638
639
|
end
|
639
640
|
attrs, link_opts = nil, { type: :link }
|
640
|
-
unless (
|
641
|
-
|
641
|
+
unless (link_text = $3).empty?
|
642
|
+
link_text = link_text.gsub ESC_R_SB, R_SB if link_text.include? R_SB
|
642
643
|
if mailto
|
643
|
-
if !doc.compat_mode && (
|
644
|
-
# NOTE if a comma (,) is present, extract attributes from text
|
645
|
-
|
644
|
+
if !doc.compat_mode && (link_text.include? ',')
|
645
|
+
# NOTE if a comma (,) is present, extract attributes from link text
|
646
|
+
link_text, attrs = extract_attributes_from_text link_text, ''
|
646
647
|
link_opts[:id] = attrs['id']
|
647
648
|
if attrs.key? 2
|
648
649
|
if attrs.key? 3
|
@@ -652,14 +653,14 @@ module Substitutors
|
|
652
653
|
end
|
653
654
|
end
|
654
655
|
end
|
655
|
-
elsif !doc.compat_mode && (
|
656
|
-
# NOTE if an equals sign (=) is present, extract attributes from text
|
657
|
-
|
656
|
+
elsif !doc.compat_mode && (link_text.include? '=')
|
657
|
+
# NOTE if an equals sign (=) is present, extract attributes from link text
|
658
|
+
link_text, attrs = extract_attributes_from_text link_text, ''
|
658
659
|
link_opts[:id] = attrs['id']
|
659
660
|
end
|
660
661
|
|
661
|
-
if
|
662
|
-
|
662
|
+
if link_text.end_with? '^'
|
663
|
+
link_text = link_text.chop
|
663
664
|
if attrs
|
664
665
|
attrs['window'] ||= '_blank'
|
665
666
|
else
|
@@ -668,17 +669,17 @@ module Substitutors
|
|
668
669
|
end
|
669
670
|
end
|
670
671
|
|
671
|
-
if
|
672
|
+
if link_text.empty?
|
672
673
|
# mailto is a special case, already processed
|
673
674
|
if mailto
|
674
|
-
|
675
|
+
link_text = mailto_text
|
675
676
|
else
|
676
677
|
if doc_attrs.key? 'hide-uri-scheme'
|
677
|
-
if (
|
678
|
-
|
678
|
+
if (link_text = target.sub UriSniffRx, '').empty?
|
679
|
+
link_text = target
|
679
680
|
end
|
680
681
|
else
|
681
|
-
|
682
|
+
link_text = target
|
682
683
|
end
|
683
684
|
if attrs
|
684
685
|
attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
|
@@ -691,7 +692,7 @@ module Substitutors
|
|
691
692
|
# QUESTION should a mailto be registered as an e-mail address?
|
692
693
|
doc.register :links, (link_opts[:target] = target)
|
693
694
|
link_opts[:attributes] = attrs if attrs
|
694
|
-
Inline.new(self, :anchor,
|
695
|
+
Inline.new(self, :anchor, link_text, link_opts).convert
|
695
696
|
end
|
696
697
|
end
|
697
698
|
|
@@ -738,15 +739,17 @@ module Substitutors
|
|
738
739
|
|
739
740
|
attrs = {}
|
740
741
|
if (refid = $1)
|
741
|
-
|
742
|
-
|
742
|
+
if refid.include? ','
|
743
|
+
refid, _, link_text = refid.partition ','
|
744
|
+
link_text = nil if (link_text = link_text.lstrip).empty?
|
745
|
+
end
|
743
746
|
else
|
744
747
|
macro = true
|
745
748
|
refid = $2
|
746
|
-
if (
|
747
|
-
|
748
|
-
# NOTE if an equals sign (=) is present, extract attributes from text
|
749
|
-
|
749
|
+
if (link_text = $3)
|
750
|
+
link_text = link_text.gsub ESC_R_SB, R_SB if link_text.include? R_SB
|
751
|
+
# NOTE if an equals sign (=) is present, extract attributes from link text
|
752
|
+
link_text, attrs = extract_attributes_from_text link_text if !doc.compat_mode && (link_text.include? '=')
|
750
753
|
end
|
751
754
|
end
|
752
755
|
|
@@ -800,7 +803,7 @@ module Substitutors
|
|
800
803
|
refid, path, target = nil, nil, '#'
|
801
804
|
end
|
802
805
|
else
|
803
|
-
refid, path = path, %(#{doc.attributes['relfileprefix']}#{path}#{src2src ? (doc.attributes.fetch 'relfilesuffix', doc.outfilesuffix) : ''})
|
806
|
+
refid, path = path, %(#{doc.attributes['relfileprefix'] || ''}#{path}#{src2src ? (doc.attributes.fetch 'relfilesuffix', doc.outfilesuffix) : ''})
|
804
807
|
if fragment
|
805
808
|
refid, target = %(#{refid}##{fragment}), %(#{path}##{fragment})
|
806
809
|
else
|
@@ -825,7 +828,7 @@ module Substitutors
|
|
825
828
|
attrs['path'] = path
|
826
829
|
attrs['fragment'] = fragment
|
827
830
|
attrs['refid'] = refid
|
828
|
-
Inline.new(self, :anchor,
|
831
|
+
Inline.new(self, :anchor, link_text, type: :xref, target: target, attributes: attrs).convert
|
829
832
|
end
|
830
833
|
end
|
831
834
|
|
@@ -837,7 +840,7 @@ module Substitutors
|
|
837
840
|
# footnoteref
|
838
841
|
if $1
|
839
842
|
if $3
|
840
|
-
id,
|
843
|
+
id, content = $3.split ',', 2
|
841
844
|
logger.warn %(found deprecated footnoteref macro: #{$&}; use footnote macro with target instead) unless doc.compat_mode
|
842
845
|
else
|
843
846
|
next $&
|
@@ -845,31 +848,31 @@ module Substitutors
|
|
845
848
|
# footnote
|
846
849
|
else
|
847
850
|
id = $2
|
848
|
-
|
851
|
+
content = $3
|
849
852
|
end
|
850
853
|
|
851
854
|
if id
|
852
855
|
if (footnote = doc.footnotes.find {|candidate| candidate.id == id })
|
853
|
-
index,
|
856
|
+
index, content = footnote.index, footnote.text
|
854
857
|
type, target, id = :xref, id, nil
|
855
|
-
elsif
|
856
|
-
|
858
|
+
elsif content
|
859
|
+
content = restore_passthroughs(normalize_text content, true, true)
|
857
860
|
index = doc.counter('footnote-number')
|
858
|
-
doc.register(:footnotes, Document::Footnote.new(index, id,
|
861
|
+
doc.register(:footnotes, Document::Footnote.new(index, id, content))
|
859
862
|
type, target = :ref, nil
|
860
863
|
else
|
861
864
|
logger.warn %(invalid footnote reference: #{id})
|
862
|
-
type, target,
|
865
|
+
type, target, content, id = :xref, id, id, nil
|
863
866
|
end
|
864
|
-
elsif
|
865
|
-
|
867
|
+
elsif content
|
868
|
+
content = restore_passthroughs(normalize_text content, true, true)
|
866
869
|
index = doc.counter('footnote-number')
|
867
|
-
doc.register(:footnotes, Document::Footnote.new(index, id,
|
870
|
+
doc.register(:footnotes, Document::Footnote.new(index, id, content))
|
868
871
|
type = target = nil
|
869
872
|
else
|
870
873
|
next $&
|
871
874
|
end
|
872
|
-
Inline.new(self, :footnote,
|
875
|
+
Inline.new(self, :footnote, content, attributes: { 'index' => index }, id: id, target: target, type: type).convert
|
873
876
|
end
|
874
877
|
end
|
875
878
|
|
@@ -946,8 +949,9 @@ module Substitutors
|
|
946
949
|
|
947
950
|
doc_attrs = @document.attributes
|
948
951
|
syntax_hl_name = syntax_hl.name
|
949
|
-
if (linenums_mode = (attr? 'linenums') ? (doc_attrs[%(#{syntax_hl_name}-linenums-mode)] || :table).to_sym : nil)
|
950
|
-
|
952
|
+
if (linenums_mode = (attr? 'linenums') ? (doc_attrs[%(#{syntax_hl_name}-linenums-mode)] || :table).to_sym : nil) &&
|
953
|
+
(start_line_number = (attr 'start', 1).to_i) < 1
|
954
|
+
start_line_number = 1
|
951
955
|
end
|
952
956
|
highlight_lines = resolve_lines_to_highlight source, (attr 'highlight'), start_line_number if attr? 'highlight'
|
953
957
|
|
@@ -984,7 +988,7 @@ module Substitutors
|
|
984
988
|
negate = true
|
985
989
|
end
|
986
990
|
if (delim = (entry.include? '..') ? '..' : ((entry.include? '-') ? '-' : nil))
|
987
|
-
from,
|
991
|
+
from, _, to = entry.partition delim
|
988
992
|
to = (source.count LF) + 1 if to.empty? || (to = to.to_i) < 0
|
989
993
|
if negate
|
990
994
|
lines -= (from.to_i..to).to_a
|
@@ -1118,7 +1122,7 @@ module Substitutors
|
|
1118
1122
|
end
|
1119
1123
|
subs = $2
|
1120
1124
|
content = normalize_text $3, nil, true
|
1121
|
-
# NOTE drop enclosing $ signs around latexmath for backwards compatibility with AsciiDoc
|
1125
|
+
# NOTE drop enclosing $ signs around latexmath for backwards compatibility with AsciiDoc.py
|
1122
1126
|
content = content.slice 1, content.length - 2 if type == :latexmath && (content.start_with? '$') && (content.end_with? '$')
|
1123
1127
|
subs = subs ? (resolve_pass_subs subs) : ((@document.basebackend? 'html') ? BASIC_SUBS : nil)
|
1124
1128
|
passthrus[passthru_key = passthrus.size] = { text: content, subs: subs, type: type }
|
@@ -1232,17 +1236,17 @@ module Substitutors
|
|
1232
1236
|
resolve_subs subs, :inline, nil, 'passthrough macro'
|
1233
1237
|
end
|
1234
1238
|
|
1235
|
-
# Public: Expand all groups in the subs list and return. If no subs are
|
1239
|
+
# Public: Expand all groups in the subs list and return. If no subs are resolved, return nil.
|
1236
1240
|
#
|
1237
|
-
# subs - The substitutions to expand; can be a Symbol, Symbol Array or
|
1241
|
+
# subs - The substitutions to expand; can be a Symbol, Symbol Array, or String
|
1242
|
+
# subject - The String to use in log messages to communicate the subject for which subs are being resolved (default: nil)
|
1238
1243
|
#
|
1239
1244
|
# Returns a Symbol Array of substitutions to pass to apply_subs or nil if no substitutions were resolved.
|
1240
|
-
def expand_subs subs
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
else
|
1245
|
+
def expand_subs subs, subject = nil
|
1246
|
+
case subs
|
1247
|
+
when ::Symbol
|
1248
|
+
subs == :none ? nil : SUB_GROUPS[subs] || [subs]
|
1249
|
+
when ::Array
|
1246
1250
|
expanded_subs = []
|
1247
1251
|
subs.each do |key|
|
1248
1252
|
unless key == :none
|
@@ -1253,8 +1257,9 @@ module Substitutors
|
|
1253
1257
|
end
|
1254
1258
|
end
|
1255
1259
|
end
|
1256
|
-
|
1257
1260
|
expanded_subs.empty? ? nil : expanded_subs
|
1261
|
+
else
|
1262
|
+
resolve_subs subs, :inline, nil, subject
|
1258
1263
|
end
|
1259
1264
|
end
|
1260
1265
|
|
@@ -1276,7 +1281,7 @@ module Substitutors
|
|
1276
1281
|
# NOTE :literal with listparagraph-option gets folded into text of list item later
|
1277
1282
|
default_subs = @context == :verse ? NORMAL_SUBS : VERBATIM_SUBS
|
1278
1283
|
when :raw
|
1279
|
-
# TODO make pass subs a compliance setting; AsciiDoc
|
1284
|
+
# TODO make pass subs a compliance setting; AsciiDoc.py performs :attributes and :macros on a pass block
|
1280
1285
|
default_subs = @context == :stem ? BASIC_SUBS : NO_SUBS
|
1281
1286
|
else
|
1282
1287
|
return @subs
|
@@ -1472,33 +1477,28 @@ module Substitutors
|
|
1472
1477
|
#
|
1473
1478
|
# Returns a Hash of attributes (role and id only)
|
1474
1479
|
def parse_quoted_text_attributes str
|
1475
|
-
return {} if (str = str.rstrip).empty?
|
1476
1480
|
# NOTE attributes are typically resolved after quoted text, so substitute eagerly
|
1477
1481
|
str = sub_attributes str if str.include? ATTR_REF_HEAD
|
1478
1482
|
# for compliance, only consider first positional attribute (very unlikely)
|
1479
1483
|
str = str.slice 0, (str.index ',') if str.include? ','
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1484
|
+
if (str = str.strip).empty?
|
1485
|
+
{}
|
1486
|
+
elsif (str.start_with? '.', '#') && Compliance.shorthand_property_syntax
|
1487
|
+
before, _, after = str.partition '#'
|
1488
|
+
attrs = {}
|
1489
|
+
if after.empty?
|
1490
|
+
attrs['role'] = (before.tr '.', ' ').lstrip if before.length > 1
|
1486
1491
|
else
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
roles.concat more_roles
|
1492
|
+
id, _, roles = after.partition '.'
|
1493
|
+
attrs['id'] = id unless id.empty?
|
1494
|
+
if roles.empty?
|
1495
|
+
attrs['role'] = (before.tr '.', ' ').lstrip if before.length > 1
|
1496
|
+
elsif before.length > 1
|
1497
|
+
attrs['role'] = ((before + '.' + roles).tr '.', ' ').lstrip
|
1498
|
+
else
|
1499
|
+
attrs['role'] = roles.tr '.', ' '
|
1500
|
+
end
|
1497
1501
|
end
|
1498
|
-
|
1499
|
-
attrs = {}
|
1500
|
-
attrs['id'] = id if id
|
1501
|
-
attrs['role'] = roles.join ' ' unless roles.empty?
|
1502
1502
|
attrs
|
1503
1503
|
else
|
1504
1504
|
{ 'role' => str }
|
@@ -1532,7 +1532,7 @@ module Substitutors
|
|
1532
1532
|
case c
|
1533
1533
|
when ','
|
1534
1534
|
if quote_open
|
1535
|
-
accum
|
1535
|
+
accum += c
|
1536
1536
|
else
|
1537
1537
|
values << accum.strip
|
1538
1538
|
accum = ''
|
@@ -1540,7 +1540,7 @@ module Substitutors
|
|
1540
1540
|
when '"'
|
1541
1541
|
quote_open = !quote_open
|
1542
1542
|
else
|
1543
|
-
accum
|
1543
|
+
accum += c
|
1544
1544
|
end
|
1545
1545
|
end
|
1546
1546
|
values << accum.strip
|