asciidoctor 1.5.6 → 1.5.6.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +52 -1
- data/README-fr.adoc +21 -15
- data/README-jp.adoc +20 -8
- data/README-zh_CN.adoc +21 -9
- data/README.adoc +10 -7
- data/Rakefile +11 -4
- data/lib/asciidoctor.rb +30 -27
- data/lib/asciidoctor/abstract_node.rb +0 -5
- data/lib/asciidoctor/converter/html5.rb +1 -1
- data/lib/asciidoctor/extensions.rb +4 -5
- data/lib/asciidoctor/parser.rb +96 -105
- data/lib/asciidoctor/path_resolver.rb +7 -7
- data/lib/asciidoctor/reader.rb +88 -84
- data/lib/asciidoctor/substitutors.rb +19 -14
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +5 -5
- data/man/asciidoctor.adoc +1 -1
- data/test/attributes_test.rb +20 -20
- data/test/blocks_test.rb +51 -16
- data/test/converter_test.rb +10 -10
- data/test/document_test.rb +84 -69
- data/test/extensions_test.rb +48 -5
- data/test/invoker_test.rb +21 -21
- data/test/links_test.rb +4 -4
- data/test/lists_test.rb +10 -10
- data/test/paragraphs_test.rb +1 -1
- data/test/parser_test.rb +4 -4
- data/test/paths_test.rb +7 -0
- data/test/reader_test.rb +46 -12
- data/test/sections_test.rb +64 -42
- data/test/substitutions_test.rb +34 -14
- data/test/tables_test.rb +2 -2
- data/test/text_test.rb +12 -10
- metadata +3 -3
@@ -458,16 +458,16 @@ class PathResolver
|
|
458
458
|
|
459
459
|
# Public: Calculate the relative path to this absolute filename from the specified base directory
|
460
460
|
#
|
461
|
-
# If either the filename or the base_directory are not absolute paths,
|
461
|
+
# If either the filename or the base_directory are not absolute paths, or the
|
462
|
+
# filename is not contained within the base directory, no work is done.
|
462
463
|
#
|
463
|
-
# filename -
|
464
|
-
# base_directory -
|
464
|
+
# filename - [String] an absolute filename.
|
465
|
+
# base_directory - [String] an absolute base directory.
|
465
466
|
#
|
466
|
-
# Return the relative path
|
467
|
+
# Return the [String] relative path of the filename calculated from the base directory.
|
467
468
|
def relative_path filename, base_directory
|
468
|
-
if (is_root? filename) && (
|
469
|
-
|
470
|
-
filename[offset..-1]
|
469
|
+
if (is_root? filename) && (filename.start_with? base_directory)
|
470
|
+
filename.slice base_directory.length + 1, filename.length
|
471
471
|
else
|
472
472
|
filename
|
473
473
|
end
|
data/lib/asciidoctor/reader.rb
CHANGED
@@ -64,7 +64,6 @@ class Reader
|
|
64
64
|
end
|
65
65
|
@lines = data ? (prepare_lines data, opts) : []
|
66
66
|
@source_lines = @lines.dup
|
67
|
-
@eof = @lines.empty?
|
68
67
|
@look_ahead = 0
|
69
68
|
@process_lines = true
|
70
69
|
@unescape_next_line = false
|
@@ -121,8 +120,26 @@ class Reader
|
|
121
120
|
#
|
122
121
|
# Returns True if there are more lines, False if there are not.
|
123
122
|
def has_more_lines?
|
124
|
-
|
123
|
+
if @lines.empty?
|
124
|
+
@look_ahead = 0
|
125
|
+
false
|
126
|
+
else
|
127
|
+
true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Public: Check whether this reader is empty (contains no lines)
|
132
|
+
#
|
133
|
+
# Returns true if there are no more lines to peek, otherwise false.
|
134
|
+
def empty?
|
135
|
+
if @lines.empty?
|
136
|
+
@look_ahead = 0
|
137
|
+
true
|
138
|
+
else
|
139
|
+
false
|
140
|
+
end
|
125
141
|
end
|
142
|
+
alias eof? empty?
|
126
143
|
|
127
144
|
# Public: Peek at the next line and check if it's empty (i.e., whitespace only)
|
128
145
|
#
|
@@ -154,19 +171,14 @@ class Reader
|
|
154
171
|
def peek_line direct = false
|
155
172
|
if direct || @look_ahead > 0
|
156
173
|
@unescape_next_line ? @lines[0][1..-1] : @lines[0]
|
157
|
-
elsif @
|
158
|
-
@eof = true
|
174
|
+
elsif @lines.empty?
|
159
175
|
@look_ahead = 0
|
160
176
|
nil
|
161
177
|
else
|
162
178
|
# FIXME the problem with this approach is that we aren't
|
163
179
|
# retaining the modified line (hence the @unescape_next_line tweak)
|
164
|
-
# perhaps we need a stack of
|
165
|
-
|
166
|
-
peek_line
|
167
|
-
else
|
168
|
-
line
|
169
|
-
end
|
180
|
+
# perhaps we need a stack of proxied lines
|
181
|
+
(line = process_line @lines[0]) ? line : peek_line
|
170
182
|
end
|
171
183
|
end
|
172
184
|
|
@@ -183,11 +195,11 @@ class Reader
|
|
183
195
|
#
|
184
196
|
# Returns A String Array of the next multiple lines of source data, or an empty Array
|
185
197
|
# if there are no more lines in this Reader.
|
186
|
-
def peek_lines num
|
198
|
+
def peek_lines num, direct = false
|
187
199
|
old_look_ahead = @look_ahead
|
188
200
|
result = []
|
189
201
|
num.times do
|
190
|
-
if (line = read_line
|
202
|
+
if (line = direct ? shift : read_line)
|
191
203
|
result << line
|
192
204
|
else
|
193
205
|
@lineno -= 1 if direct
|
@@ -205,17 +217,11 @@ class Reader
|
|
205
217
|
|
206
218
|
# Public: Get the next line of source data. Consumes the line returned.
|
207
219
|
#
|
208
|
-
# direct - A Boolean flag to bypasses the check for more lines and immediately
|
209
|
-
# returns the first element of the internal @lines Array. (default: false)
|
210
|
-
#
|
211
220
|
# Returns the String of the next line of the source data if data is present.
|
212
221
|
# Returns nothing if there is no more data.
|
213
|
-
def read_line
|
214
|
-
|
215
|
-
|
216
|
-
else
|
217
|
-
nil
|
218
|
-
end
|
222
|
+
def read_line
|
223
|
+
# has_more_lines? triggers preprocessor
|
224
|
+
shift if @look_ahead > 0 || has_more_lines?
|
219
225
|
end
|
220
226
|
|
221
227
|
# Public: Get the remaining lines of source data.
|
@@ -228,6 +234,7 @@ class Reader
|
|
228
234
|
# Returns the lines read as a String Array
|
229
235
|
def read_lines
|
230
236
|
lines = []
|
237
|
+
# has_more_lines? triggers preprocessor
|
231
238
|
while has_more_lines?
|
232
239
|
lines << shift
|
233
240
|
end
|
@@ -246,12 +253,9 @@ class Reader
|
|
246
253
|
|
247
254
|
# Public: Advance to the next line by discarding the line at the front of the stack
|
248
255
|
#
|
249
|
-
# direct - A Boolean flag to bypasses the check for more lines and immediately
|
250
|
-
# returns the first element of the internal @lines Array. (default: true)
|
251
|
-
#
|
252
256
|
# Returns a Boolean indicating whether there was a line to discard.
|
253
|
-
def advance
|
254
|
-
|
257
|
+
def advance
|
258
|
+
shift ? true : false
|
255
259
|
end
|
256
260
|
|
257
261
|
# Public: Push the String line onto the beginning of the Array of source data.
|
@@ -294,42 +298,39 @@ class Reader
|
|
294
298
|
#
|
295
299
|
# Returns nothing.
|
296
300
|
def replace_next_line replacement
|
297
|
-
|
301
|
+
shift
|
298
302
|
unshift replacement
|
299
303
|
nil
|
300
304
|
end
|
301
305
|
# deprecated
|
302
306
|
alias replace_line replace_next_line
|
303
307
|
|
304
|
-
# Public:
|
308
|
+
# Public: Skip blank lines at the cursor.
|
305
309
|
#
|
306
310
|
# Examples
|
307
311
|
#
|
308
|
-
#
|
312
|
+
# reader.lines
|
309
313
|
# => ["", "", "Foo", "Bar", ""]
|
310
|
-
#
|
311
|
-
# skip_blank_lines
|
314
|
+
# reader.skip_blank_lines
|
312
315
|
# => 2
|
313
|
-
#
|
314
|
-
# @lines
|
316
|
+
# reader.lines
|
315
317
|
# => ["Foo", "Bar", ""]
|
316
318
|
#
|
317
|
-
# Returns
|
319
|
+
# Returns the [Integer] number of lines skipped or nothing if all lines have
|
320
|
+
# been consumed (even if lines were skipped by this method).
|
318
321
|
def skip_blank_lines
|
319
|
-
return
|
322
|
+
return if empty?
|
320
323
|
|
321
324
|
num_skipped = 0
|
322
325
|
# optimized code for shortest execution path
|
323
326
|
while (next_line = peek_line)
|
324
327
|
if next_line.empty?
|
325
|
-
|
328
|
+
shift
|
326
329
|
num_skipped += 1
|
327
330
|
else
|
328
331
|
return num_skipped
|
329
332
|
end
|
330
333
|
end
|
331
|
-
|
332
|
-
num_skipped
|
333
334
|
end
|
334
335
|
|
335
336
|
# Public: Skip consecutive lines containing line comments and return them.
|
@@ -345,19 +346,22 @@ class Reader
|
|
345
346
|
# => ["bar"]
|
346
347
|
#
|
347
348
|
# Returns the Array of lines that were skipped
|
348
|
-
def skip_comment_lines
|
349
|
-
return [] if
|
349
|
+
def skip_comment_lines
|
350
|
+
return [] if empty?
|
350
351
|
|
351
352
|
comment_lines = []
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
353
|
+
while (next_line = peek_line) && !next_line.empty?
|
354
|
+
if next_line.start_with? '//'
|
355
|
+
if next_line.start_with? '///'
|
356
|
+
if (ll = next_line.length) > 3 && next_line == '/' * ll
|
357
|
+
comment_lines << shift
|
358
|
+
comment_lines.push(*(read_lines_until(:terminator => next_line, :read_last_line => true, :skip_processing => true)))
|
359
|
+
else
|
360
|
+
break
|
361
|
+
end
|
362
|
+
else
|
363
|
+
comment_lines << shift
|
364
|
+
end
|
361
365
|
else
|
362
366
|
break
|
363
367
|
end
|
@@ -367,13 +371,15 @@ class Reader
|
|
367
371
|
end
|
368
372
|
|
369
373
|
# Public: Skip consecutive lines that are line comments and return them.
|
374
|
+
#
|
375
|
+
# This method assumes the reader only contains simple lines (no blocks).
|
370
376
|
def skip_line_comments
|
371
|
-
return [] if
|
377
|
+
return [] if empty?
|
372
378
|
|
373
379
|
comment_lines = []
|
374
380
|
# optimized code for shortest execution path
|
375
|
-
while (next_line = peek_line)
|
376
|
-
if
|
381
|
+
while (next_line = peek_line) && !next_line.empty?
|
382
|
+
if (next_line.start_with? '//')
|
377
383
|
comment_lines << shift
|
378
384
|
else
|
379
385
|
break
|
@@ -389,19 +395,10 @@ class Reader
|
|
389
395
|
def terminate
|
390
396
|
@lineno += @lines.size
|
391
397
|
@lines.clear
|
392
|
-
@eof = true
|
393
398
|
@look_ahead = 0
|
394
399
|
nil
|
395
400
|
end
|
396
401
|
|
397
|
-
# Public: Check whether this reader is empty (contains no lines)
|
398
|
-
#
|
399
|
-
# Returns true if there are no more lines to peek, otherwise false.
|
400
|
-
def eof?
|
401
|
-
!has_more_lines?
|
402
|
-
end
|
403
|
-
alias empty? eof?
|
404
|
-
|
405
402
|
# Public: Return all the lines from `@lines` until we (1) run out them,
|
406
403
|
# (2) find a blank line with :break_on_blank_lines => true, or (3) find
|
407
404
|
# a line for which the given block evals to true.
|
@@ -434,7 +431,7 @@ class Reader
|
|
434
431
|
# => ["First line", "Second line"]
|
435
432
|
def read_lines_until options = {}
|
436
433
|
result = []
|
437
|
-
|
434
|
+
shift if options[:skip_first_line]
|
438
435
|
if @process_lines && options[:skip_processing]
|
439
436
|
@process_lines = false
|
440
437
|
restore_process_lines = true
|
@@ -477,7 +474,7 @@ class Reader
|
|
477
474
|
line_restored = true
|
478
475
|
end
|
479
476
|
else
|
480
|
-
unless skip_comments && (line.start_with? '//') && (
|
477
|
+
unless skip_comments && (line.start_with? '//') && !(line.start_with? '///')
|
481
478
|
result << line
|
482
479
|
line_read = true
|
483
480
|
end
|
@@ -495,6 +492,7 @@ class Reader
|
|
495
492
|
#
|
496
493
|
# This method can be used directly when you've already called peek_line
|
497
494
|
# and determined that you do, in fact, want to pluck that line off the stack.
|
495
|
+
# Use read_line if the line hasn't (or many not have been) visited yet.
|
498
496
|
#
|
499
497
|
# Returns The String line at the top of the stack
|
500
498
|
def shift
|
@@ -507,7 +505,6 @@ class Reader
|
|
507
505
|
def unshift line
|
508
506
|
@lineno -= 1
|
509
507
|
@look_ahead += 1
|
510
|
-
@eof = false
|
511
508
|
@lines.unshift line
|
512
509
|
end
|
513
510
|
|
@@ -515,7 +512,6 @@ class Reader
|
|
515
512
|
def unshift_all lines
|
516
513
|
@lineno -= lines.size
|
517
514
|
@look_ahead += lines.size
|
518
|
-
@eof = false
|
519
515
|
@lines.unshift(*lines)
|
520
516
|
end
|
521
517
|
|
@@ -609,7 +605,7 @@ class PreprocessorReader < Reader
|
|
609
605
|
|
610
606
|
if line.empty?
|
611
607
|
@look_ahead += 1
|
612
|
-
return
|
608
|
+
return line
|
613
609
|
end
|
614
610
|
|
615
611
|
# NOTE highly optimized
|
@@ -622,7 +618,7 @@ class PreprocessorReader < Reader
|
|
622
618
|
line[1..-1]
|
623
619
|
elsif preprocess_conditional_directive $2, $3, $4, $5
|
624
620
|
# move the pointer past the conditional line
|
625
|
-
|
621
|
+
shift
|
626
622
|
# treat next line as uncharted territory
|
627
623
|
nil
|
628
624
|
else
|
@@ -632,7 +628,7 @@ class PreprocessorReader < Reader
|
|
632
628
|
line
|
633
629
|
end
|
634
630
|
elsif @skipping
|
635
|
-
|
631
|
+
shift
|
636
632
|
nil
|
637
633
|
elsif (line.start_with? 'inc', '\\inc') && IncludeDirectiveRx =~ line
|
638
634
|
# if escaped, mark as processed and return line unescaped
|
@@ -656,7 +652,7 @@ class PreprocessorReader < Reader
|
|
656
652
|
line
|
657
653
|
end
|
658
654
|
elsif @skipping
|
659
|
-
|
655
|
+
shift
|
660
656
|
nil
|
661
657
|
else
|
662
658
|
# NOTE optimization to inline super
|
@@ -665,6 +661,17 @@ class PreprocessorReader < Reader
|
|
665
661
|
end
|
666
662
|
end
|
667
663
|
|
664
|
+
# (see Reader#has_more_lines?)
|
665
|
+
def has_more_lines?
|
666
|
+
peek_line ? true : false
|
667
|
+
end
|
668
|
+
|
669
|
+
# (see Reader#empty?)
|
670
|
+
def empty?
|
671
|
+
peek_line ? false : true
|
672
|
+
end
|
673
|
+
alias eof? empty?
|
674
|
+
|
668
675
|
# Public: Override the Reader#peek_line method to pop the include
|
669
676
|
# stack if the last line has been reached and there's at least
|
670
677
|
# one include on the stack.
|
@@ -814,15 +821,15 @@ class PreprocessorReader < Reader
|
|
814
821
|
#
|
815
822
|
# Returns a Boolean indicating whether the line under the cursor has changed.
|
816
823
|
def preprocess_include_directive raw_target, raw_attributes
|
817
|
-
if ((target = raw_target).include?
|
824
|
+
if ((target = raw_target).include? ATTR_REF_HEAD) &&
|
818
825
|
(target = @document.sub_attributes raw_target, :attribute_missing => 'drop-line').empty?
|
819
|
-
|
826
|
+
shift
|
820
827
|
if @document.attributes.fetch('attribute-missing', Compliance.attribute_missing) == 'skip'
|
821
828
|
unshift %(Unresolved directive in #{@path} - include::#{raw_target}[#{raw_attributes}])
|
822
829
|
end
|
823
830
|
true
|
824
831
|
elsif include_processors? && (ext = @include_processor_extensions.find {|candidate| candidate.instance.handles? target })
|
825
|
-
|
832
|
+
shift
|
826
833
|
# FIXME parse attributes only if requested by extension
|
827
834
|
ext.process_method[@document, self, target, AttributeList.new(raw_attributes).parse]
|
828
835
|
true
|
@@ -867,9 +874,9 @@ class PreprocessorReader < Reader
|
|
867
874
|
replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
|
868
875
|
return true
|
869
876
|
end
|
870
|
-
# NOTE relpath is the path relative to the
|
871
|
-
#
|
872
|
-
relpath = PathResolver.new.relative_path inc_path, @document.base_dir
|
877
|
+
# NOTE relpath is the path relative to the root document (or base_dir, if set)
|
878
|
+
# QUESTION should we move relative_path method to Document
|
879
|
+
relpath = (@path_resolver ||= PathResolver.new).relative_path inc_path, @document.base_dir
|
873
880
|
end
|
874
881
|
|
875
882
|
inc_linenos, inc_tags, attributes = nil, nil, {}
|
@@ -940,7 +947,7 @@ class PreprocessorReader < Reader
|
|
940
947
|
replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
|
941
948
|
return true
|
942
949
|
end
|
943
|
-
|
950
|
+
shift
|
944
951
|
# FIXME not accounting for skipped lines in reader line numbering
|
945
952
|
push_include inc_lines, inc_path, relpath, inc_offset, attributes if inc_offset
|
946
953
|
elsif inc_tags
|
@@ -1003,14 +1010,14 @@ class PreprocessorReader < Reader
|
|
1003
1010
|
unless (missing_tags = inc_tags.keys.to_a - tags_used.to_a).empty?
|
1004
1011
|
warn %(asciidoctor: WARNING: #{line_info}: tag#{missing_tags.size > 1 ? 's' : nil} '#{missing_tags * ','}' not found in include #{target_type}: #{inc_path})
|
1005
1012
|
end
|
1006
|
-
|
1013
|
+
shift
|
1007
1014
|
# FIXME not accounting for skipped lines in reader line numbering
|
1008
1015
|
push_include inc_lines, inc_path, relpath, inc_offset, attributes if inc_offset
|
1009
1016
|
else
|
1010
1017
|
begin
|
1011
1018
|
# NOTE read content first so that we only advance cursor if IO operation succeeds
|
1012
1019
|
inc_content = target_type == :file ? (::IO.read inc_path) : open(inc_path, 'r') {|f| f.read }
|
1013
|
-
|
1020
|
+
shift
|
1014
1021
|
push_include inc_content, inc_path, relpath, 1, attributes
|
1015
1022
|
rescue
|
1016
1023
|
warn %(asciidoctor: WARNING: #{line_info}: include #{target_type} not readable: #{inc_path})
|
@@ -1053,8 +1060,7 @@ class PreprocessorReader < Reader
|
|
1053
1060
|
end
|
1054
1061
|
|
1055
1062
|
if path
|
1056
|
-
@includes << Helpers.rootname(path)
|
1057
|
-
@path = path
|
1063
|
+
@includes << Helpers.rootname(@path = path)
|
1058
1064
|
else
|
1059
1065
|
@path = '<stdin>'
|
1060
1066
|
end
|
@@ -1088,7 +1094,6 @@ class PreprocessorReader < Reader
|
|
1088
1094
|
# FIXME kind of a hack
|
1089
1095
|
#Document::AttributeEntry.new('infile', @file).save_to_next_block @document
|
1090
1096
|
#Document::AttributeEntry.new('indir', @dir).save_to_next_block @document
|
1091
|
-
@eof = false
|
1092
1097
|
@look_ahead = 0
|
1093
1098
|
end
|
1094
1099
|
self
|
@@ -1100,10 +1105,9 @@ class PreprocessorReader < Reader
|
|
1100
1105
|
# FIXME kind of a hack
|
1101
1106
|
#Document::AttributeEntry.new('infile', @file).save_to_next_block @document
|
1102
1107
|
#Document::AttributeEntry.new('indir', ::File.dirname(@file)).save_to_next_block @document
|
1103
|
-
@eof = @lines.empty?
|
1104
1108
|
@look_ahead = 0
|
1109
|
+
nil
|
1105
1110
|
end
|
1106
|
-
nil
|
1107
1111
|
end
|
1108
1112
|
|
1109
1113
|
def include_depth
|
@@ -1197,7 +1201,7 @@ class PreprocessorReader < Reader
|
|
1197
1201
|
|
1198
1202
|
# QUESTION should we substitute first?
|
1199
1203
|
# QUESTION should we also require string to be single quoted (like block attribute values?)
|
1200
|
-
val = @document.sub_attributes val, :attribute_missing => 'drop' if val.include?
|
1204
|
+
val = @document.sub_attributes val, :attribute_missing => 'drop' if val.include? ATTR_REF_HEAD
|
1201
1205
|
|
1202
1206
|
if quoted
|
1203
1207
|
val
|
@@ -77,12 +77,12 @@ module Substitutors
|
|
77
77
|
# Public: Apply the specified substitutions to the source.
|
78
78
|
#
|
79
79
|
# source - The String or String Array of text to process; must not be nil.
|
80
|
-
# subs - The substitutions to perform; can be a Symbol
|
81
|
-
# expand - A Boolean to control whether substitution aliases are expanded (default:
|
80
|
+
# subs - The substitutions to perform; can be a Symbol, Symbol Array or nil (default: NORMAL_SUBS).
|
81
|
+
# expand - A Boolean (or nil) to control whether substitution aliases are expanded (default: nil).
|
82
82
|
#
|
83
83
|
# Returns a String or String Array with substitutions applied, matching the type of source argument.
|
84
|
-
def apply_subs source, subs = NORMAL_SUBS, expand =
|
85
|
-
if source.empty? || subs
|
84
|
+
def apply_subs source, subs = NORMAL_SUBS, expand = nil
|
85
|
+
if source.empty? || !subs
|
86
86
|
return source
|
87
87
|
elsif expand
|
88
88
|
if ::Symbol === subs
|
@@ -101,6 +101,8 @@ module Substitutors
|
|
101
101
|
return source
|
102
102
|
end
|
103
103
|
end
|
104
|
+
elsif subs.empty?
|
105
|
+
return source
|
104
106
|
end
|
105
107
|
|
106
108
|
text = (multiline = ::Array === source) ? source * LF : source
|
@@ -117,7 +119,7 @@ module Substitutors
|
|
117
119
|
when :quotes
|
118
120
|
text = sub_quotes text
|
119
121
|
when :attributes
|
120
|
-
text = sub_attributes(text.split LF, -1) * LF if text.include?
|
122
|
+
text = sub_attributes(text.split LF, -1) * LF if text.include? ATTR_REF_HEAD
|
121
123
|
when :replacements
|
122
124
|
text = sub_replacements text
|
123
125
|
when :macros
|
@@ -190,9 +192,9 @@ module Substitutors
|
|
190
192
|
if (boundary = m[4]) # $$, ++, or +++
|
191
193
|
# skip ++ in compat mode, handled as normal quoted text
|
192
194
|
if compat_mode && boundary == '++'
|
193
|
-
next m[2]
|
194
|
-
%(#{m[1]}#{m[3]}++#{extract_passthroughs m[5]}++) :
|
195
|
-
%(#{m[1]}
|
195
|
+
next m[2] ?
|
196
|
+
%(#{m[1]}[#{m[2]}]#{m[3]}++#{extract_passthroughs m[5]}++) :
|
197
|
+
%(#{m[1]}#{m[3]}++#{extract_passthroughs m[5]}++)
|
196
198
|
end
|
197
199
|
|
198
200
|
attributes = m[2]
|
@@ -504,7 +506,7 @@ module Substitutors
|
|
504
506
|
$&
|
505
507
|
end
|
506
508
|
end
|
507
|
-
} if line.include?
|
509
|
+
} if line.include? ATTR_REF_HEAD
|
508
510
|
|
509
511
|
result << line unless reject || (reject_if_empty && line.empty?)
|
510
512
|
end
|
@@ -649,7 +651,7 @@ module Substitutors
|
|
649
651
|
# alias match for Ruby 1.8.7 compat
|
650
652
|
m = $~
|
651
653
|
# honor the escape
|
652
|
-
if (captured =
|
654
|
+
if (captured = $&).start_with? RS
|
653
655
|
next captured[1..-1]
|
654
656
|
end
|
655
657
|
|
@@ -658,7 +660,10 @@ module Substitutors
|
|
658
660
|
else
|
659
661
|
type, posattrs = 'image', ['alt', 'width', 'height']
|
660
662
|
end
|
661
|
-
target = m[1]
|
663
|
+
if (target = m[1]).include? ATTR_REF_HEAD
|
664
|
+
# TODO remove this special case once titles use normal substitution order
|
665
|
+
target = sub_attributes target
|
666
|
+
end
|
662
667
|
@document.register(:images, target) unless type == 'icon'
|
663
668
|
attrs = parse_attributes(m[2], posattrs, :unescape_input => true)
|
664
669
|
attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
|
@@ -1096,7 +1101,7 @@ module Substitutors
|
|
1096
1101
|
# Returns The converted String text for the quoted text region
|
1097
1102
|
def convert_quoted_text(match, type, scope)
|
1098
1103
|
if match[0].start_with? RS
|
1099
|
-
if scope == :constrained &&
|
1104
|
+
if scope == :constrained && (attrs = match[2])
|
1100
1105
|
unescaped_attrs = %([#{attrs}])
|
1101
1106
|
else
|
1102
1107
|
return match[0][1..-1]
|
@@ -1130,7 +1135,7 @@ module Substitutors
|
|
1130
1135
|
# Returns a Hash of attributes (role and id only)
|
1131
1136
|
def parse_quoted_text_attributes str
|
1132
1137
|
# NOTE attributes are typically resolved after quoted text, so substitute eagerly
|
1133
|
-
str = sub_attributes str if str.include?
|
1138
|
+
str = sub_attributes str if str.include? ATTR_REF_HEAD
|
1134
1139
|
# for compliance, only consider first positional attribute
|
1135
1140
|
str = str.slice 0, (str.index ',') if str.include? ','
|
1136
1141
|
|
@@ -1173,7 +1178,7 @@ module Substitutors
|
|
1173
1178
|
def parse_attributes(attrline, posattrs = ['role'], opts = {})
|
1174
1179
|
return unless attrline
|
1175
1180
|
return {} if attrline.empty?
|
1176
|
-
attrline = @document.sub_attributes(attrline) if opts[:sub_input] && (attrline.include?
|
1181
|
+
attrline = @document.sub_attributes(attrline) if opts[:sub_input] && (attrline.include? ATTR_REF_HEAD)
|
1177
1182
|
attrline = unescape_bracketed_text(attrline) if opts[:unescape_input]
|
1178
1183
|
# substitutions are only performed on attribute values if block is not nil
|
1179
1184
|
block = opts.fetch(:sub_result, true) ? self : nil
|