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