asciidoctor 1.5.6.2 → 1.5.7
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 +330 -143
- data/README-fr.adoc +441 -0
- data/README-jp.adoc +418 -0
- data/README-zh_CN.adoc +430 -0
- data/README.adoc +454 -0
- data/Rakefile +57 -0
- data/asciidoctor.gemspec +7 -1
- data/data/locale/attributes-ar.adoc +22 -0
- data/data/locale/attributes-bg.adoc +22 -0
- data/data/locale/attributes-ca.adoc +22 -0
- data/data/locale/attributes-cs.adoc +22 -0
- data/data/locale/attributes-da.adoc +22 -0
- data/data/locale/attributes-de.adoc +22 -0
- data/data/locale/attributes-en.adoc +23 -0
- data/data/locale/attributes-es.adoc +22 -0
- data/data/locale/attributes-fa.adoc +22 -0
- data/data/locale/attributes-fi.adoc +22 -0
- data/data/locale/attributes-fr.adoc +22 -0
- data/data/locale/attributes-hu.adoc +22 -0
- data/data/locale/attributes-id.adoc +22 -0
- data/data/locale/attributes-it.adoc +22 -0
- data/data/locale/attributes-ja.adoc +22 -0
- data/data/locale/attributes-kr.adoc +22 -0
- data/data/locale/attributes-nb.adoc +22 -0
- data/data/locale/attributes-nl.adoc +22 -0
- data/data/locale/attributes-nn.adoc +22 -0
- data/data/locale/attributes-pl.adoc +22 -0
- data/data/locale/attributes-pt.adoc +22 -0
- data/data/locale/attributes-pt_BR.adoc +22 -0
- data/data/locale/attributes-ro.adoc +22 -0
- data/data/locale/attributes-ru.adoc +22 -0
- data/data/locale/attributes-sr.adoc +22 -0
- data/data/locale/attributes-sr_Latn.adoc +22 -0
- data/data/locale/attributes-tr.adoc +22 -0
- data/data/locale/attributes-uk.adoc +22 -0
- data/data/locale/attributes-zh_CN.adoc +22 -0
- data/data/locale/attributes-zh_TW.adoc +22 -0
- data/data/locale/attributes.adoc +8 -649
- data/data/stylesheets/asciidoctor-default.css +77 -72
- data/features/xref.feature +366 -7
- data/lib/asciidoctor.rb +107 -93
- data/lib/asciidoctor/abstract_block.rb +247 -239
- data/lib/asciidoctor/abstract_node.rb +56 -58
- data/lib/asciidoctor/block.rb +3 -3
- data/lib/asciidoctor/callouts.rb +1 -1
- data/lib/asciidoctor/cli/invoker.rb +36 -9
- data/lib/asciidoctor/cli/options.rb +63 -25
- data/lib/asciidoctor/converter.rb +23 -13
- data/lib/asciidoctor/converter/base.rb +4 -0
- data/lib/asciidoctor/converter/docbook45.rb +16 -9
- data/lib/asciidoctor/converter/docbook5.rb +115 -97
- data/lib/asciidoctor/converter/factory.rb +29 -31
- data/lib/asciidoctor/converter/html5.rb +229 -192
- data/lib/asciidoctor/converter/manpage.rb +72 -50
- data/lib/asciidoctor/converter/template.rb +12 -12
- data/lib/asciidoctor/core_ext.rb +5 -1
- data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +6 -0
- data/lib/asciidoctor/document.rb +168 -77
- data/lib/asciidoctor/extensions.rb +79 -47
- data/lib/asciidoctor/helpers.rb +33 -11
- data/lib/asciidoctor/inline.rb +3 -2
- data/lib/asciidoctor/list.rb +2 -1
- data/lib/asciidoctor/logging.rb +122 -0
- data/lib/asciidoctor/parser.rb +406 -382
- data/lib/asciidoctor/path_resolver.rb +169 -162
- data/lib/asciidoctor/reader.rb +166 -121
- data/lib/asciidoctor/section.rb +45 -28
- data/lib/asciidoctor/stylesheets.rb +13 -5
- data/lib/asciidoctor/substitutors.rb +328 -254
- data/lib/asciidoctor/table.rb +105 -48
- data/lib/asciidoctor/timings.rb +34 -6
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +41 -23
- data/man/asciidoctor.adoc +14 -8
- data/test/api_test.rb +1004 -0
- data/test/attributes_test.rb +241 -50
- data/test/blocks_test.rb +549 -124
- data/test/converter_test.rb +170 -78
- data/test/document_test.rb +208 -767
- data/test/extensions_test.rb +188 -53
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +1 -1
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +1 -1
- data/test/fixtures/file-with-missing-include.adoc +1 -0
- data/test/fixtures/include-file.jsx +8 -0
- data/test/fixtures/lists.adoc +96 -0
- data/test/fixtures/other-chapters.adoc +11 -0
- data/test/fixtures/outer-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +5 -1
- data/test/fixtures/subdir/index.adoc +3 -0
- data/test/fixtures/subdir/inner-include.adoc +3 -0
- data/test/fixtures/subdir/middle-include.adoc +5 -0
- data/test/fixtures/tagged-class-enclosed.rb +0 -1
- data/test/fixtures/unclosed-tag.adoc +3 -0
- data/test/fixtures/unexpected-end-tag.adoc +4 -0
- data/test/invoker_test.rb +101 -40
- data/test/links_test.rb +266 -72
- data/test/lists_test.rb +243 -45
- data/test/logger_test.rb +211 -0
- data/test/manpage_test.rb +124 -6
- data/test/options_test.rb +46 -1
- data/test/paragraphs_test.rb +23 -10
- data/test/parser_test.rb +30 -1
- data/test/paths_test.rb +115 -33
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +337 -81
- data/test/sections_test.rb +656 -72
- data/test/substitutions_test.rb +182 -57
- data/test/tables_test.rb +324 -57
- data/test/test_helper.rb +77 -32
- data/test/text_test.rb +7 -7
- metadata +67 -3
data/lib/asciidoctor/reader.rb
CHANGED
@@ -2,21 +2,21 @@
|
|
2
2
|
module Asciidoctor
|
3
3
|
# Public: Methods for retrieving lines from AsciiDoc source files
|
4
4
|
class Reader
|
5
|
+
include Logging
|
6
|
+
|
5
7
|
class Cursor
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@
|
14
|
-
@path = path
|
15
|
-
@lineno = lineno
|
8
|
+
attr_reader :file, :dir, :path, :lineno
|
9
|
+
|
10
|
+
def initialize file, dir = nil, path = nil, lineno = 1
|
11
|
+
@file, @dir, @path, @lineno = file, dir, path, lineno
|
12
|
+
end
|
13
|
+
|
14
|
+
def advance num
|
15
|
+
@lineno += num
|
16
16
|
end
|
17
17
|
|
18
18
|
def line_info
|
19
|
-
%(#{path}: line #{lineno})
|
19
|
+
%(#{@path}: line #{@lineno})
|
20
20
|
end
|
21
21
|
|
22
22
|
alias to_s line_info
|
@@ -35,10 +35,14 @@ class Reader
|
|
35
35
|
# Public: Control whether lines are processed using Reader#process_line on first visit (default: true)
|
36
36
|
attr_accessor :process_lines
|
37
37
|
|
38
|
+
# Public: Indicates that the end of the reader was reached with a delimited block still open.
|
39
|
+
attr_accessor :unterminated
|
40
|
+
|
38
41
|
# Public: Initialize the Reader object
|
39
42
|
def initialize data = nil, cursor = nil, opts = {}
|
40
43
|
if !cursor
|
41
|
-
@file =
|
44
|
+
@file = nil
|
45
|
+
@dir = '.'
|
42
46
|
@path = '<stdin>'
|
43
47
|
@lineno = 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
|
44
48
|
elsif ::String === cursor
|
@@ -46,27 +50,22 @@ class Reader
|
|
46
50
|
@dir, @path = ::File.split @file
|
47
51
|
@lineno = 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
|
48
52
|
else
|
49
|
-
@file = cursor.file
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
@dir = ::File.dirname @file
|
56
|
-
@dir = nil if @dir == '.' # right?
|
57
|
-
end
|
58
|
-
|
59
|
-
unless cursor.path
|
60
|
-
@path = ::File.basename @file
|
61
|
-
end
|
53
|
+
if (@file = cursor.file)
|
54
|
+
@dir = cursor.dir || (::File.dirname @file)
|
55
|
+
@path = cursor.path || (::File.basename @file)
|
56
|
+
else
|
57
|
+
@dir = cursor.dir || '.'
|
58
|
+
@path = cursor.path || '<stdin>'
|
62
59
|
end
|
63
60
|
@lineno = cursor.lineno || 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
|
64
61
|
end
|
65
62
|
@lines = data ? (prepare_lines data, opts) : []
|
66
63
|
@source_lines = @lines.dup
|
64
|
+
@mark = nil
|
67
65
|
@look_ahead = 0
|
68
66
|
@process_lines = true
|
69
67
|
@unescape_next_line = false
|
68
|
+
@unterminated = nil
|
70
69
|
end
|
71
70
|
|
72
71
|
# Internal: Prepare the lines from the provided data
|
@@ -188,15 +187,15 @@ class Reader
|
|
188
187
|
# be processed and marked as such so that subsequent reads will not need to process
|
189
188
|
# the lines again.
|
190
189
|
#
|
191
|
-
# num - The positive Integer number of lines to peek
|
192
|
-
# direct - A Boolean indicating whether processing should be disabled when reading lines
|
190
|
+
# num - The positive Integer number of lines to peek or nil to peek all lines (default: nil).
|
191
|
+
# direct - A Boolean indicating whether processing should be disabled when reading lines (default: false).
|
193
192
|
#
|
194
193
|
# Returns A String Array of the next multiple lines of source data, or an empty Array
|
195
194
|
# if there are no more lines in this Reader.
|
196
|
-
def peek_lines num, direct = false
|
195
|
+
def peek_lines num = nil, direct = false
|
197
196
|
old_look_ahead = @look_ahead
|
198
197
|
result = []
|
199
|
-
num.times do
|
198
|
+
(num || MAX_INT).times do
|
200
199
|
if (line = direct ? shift : read_line)
|
201
200
|
result << line
|
202
201
|
else
|
@@ -331,44 +330,42 @@ class Reader
|
|
331
330
|
end
|
332
331
|
end
|
333
332
|
|
334
|
-
# Public: Skip consecutive lines
|
333
|
+
# Public: Skip consecutive comment lines and block comments.
|
335
334
|
#
|
336
335
|
# Examples
|
337
336
|
# @lines
|
338
337
|
# => ["// foo", "bar"]
|
339
338
|
#
|
340
339
|
# comment_lines = skip_comment_lines
|
341
|
-
# =>
|
340
|
+
# => nil
|
342
341
|
#
|
343
342
|
# @lines
|
344
343
|
# => ["bar"]
|
345
344
|
#
|
346
|
-
# Returns
|
345
|
+
# Returns nothing
|
347
346
|
def skip_comment_lines
|
348
|
-
return
|
347
|
+
return if empty?
|
349
348
|
|
350
|
-
comment_lines = []
|
351
349
|
while (next_line = peek_line) && !next_line.empty?
|
352
350
|
if next_line.start_with? '//'
|
353
351
|
if next_line.start_with? '///'
|
354
352
|
if (ll = next_line.length) > 3 && next_line == '/' * ll
|
355
|
-
|
356
|
-
comment_lines.push(*(read_lines_until(:terminator => next_line, :read_last_line => true, :skip_processing => true)))
|
353
|
+
read_lines_until :terminator => next_line, :skip_first_line => true, :read_last_line => true, :skip_processing => true, :context => :comment
|
357
354
|
else
|
358
355
|
break
|
359
356
|
end
|
360
357
|
else
|
361
|
-
|
358
|
+
shift
|
362
359
|
end
|
363
360
|
else
|
364
361
|
break
|
365
362
|
end
|
366
363
|
end
|
367
364
|
|
368
|
-
|
365
|
+
nil
|
369
366
|
end
|
370
367
|
|
371
|
-
# Public: Skip consecutive lines
|
368
|
+
# Public: Skip consecutive comment lines and return them.
|
372
369
|
#
|
373
370
|
# This method assumes the reader only contains simple lines (no blocks).
|
374
371
|
def skip_line_comments
|
@@ -402,8 +399,12 @@ class Reader
|
|
402
399
|
# a line for which the given block evals to true.
|
403
400
|
#
|
404
401
|
# options - an optional Hash of processing options:
|
402
|
+
# * :terminator may be used to specify the contents of the line
|
403
|
+
# at which the reader should stop
|
405
404
|
# * :break_on_blank_lines may be used to specify to break on
|
406
405
|
# blank lines
|
406
|
+
# * :break_on_list_continuation may be used to specify to break
|
407
|
+
# on a list continuation line
|
407
408
|
# * :skip_first_line may be used to tell the reader to advance
|
408
409
|
# beyond the first line before beginning the scan
|
409
410
|
# * :preserve_last_line may be used to specify that the String
|
@@ -412,6 +413,10 @@ class Reader
|
|
412
413
|
# * :read_last_line may be used to specify that the String
|
413
414
|
# causing the method to stop processing lines should be
|
414
415
|
# included in the lines being returned
|
416
|
+
# * :skip_line_comments may be used to look for and skip
|
417
|
+
# line comments
|
418
|
+
# * :skip_processing is used to disable line (pre)processing
|
419
|
+
# for the duration of this method
|
415
420
|
#
|
416
421
|
# Returns the Array of lines forming the next segment.
|
417
422
|
#
|
@@ -429,15 +434,12 @@ class Reader
|
|
429
434
|
# => ["First line", "Second line"]
|
430
435
|
def read_lines_until options = {}
|
431
436
|
result = []
|
432
|
-
shift if options[:skip_first_line]
|
433
437
|
if @process_lines && options[:skip_processing]
|
434
438
|
@process_lines = false
|
435
439
|
restore_process_lines = true
|
436
|
-
else
|
437
|
-
restore_process_lines = false
|
438
440
|
end
|
439
|
-
|
440
441
|
if (terminator = options[:terminator])
|
442
|
+
start_cursor = options[:cursor] || cursor
|
441
443
|
break_on_blank_lines = false
|
442
444
|
break_on_list_continuation = false
|
443
445
|
else
|
@@ -445,10 +447,8 @@ class Reader
|
|
445
447
|
break_on_list_continuation = options[:break_on_list_continuation]
|
446
448
|
end
|
447
449
|
skip_comments = options[:skip_line_comments]
|
448
|
-
line_read =
|
449
|
-
|
450
|
-
|
451
|
-
complete = false
|
450
|
+
complete = line_read = line_restored = nil
|
451
|
+
shift if options[:skip_first_line]
|
452
452
|
while !complete && (line = read_line)
|
453
453
|
complete = while true
|
454
454
|
break true if terminator && line == terminator
|
@@ -461,7 +461,6 @@ class Reader
|
|
461
461
|
break true if block_given? && (yield line)
|
462
462
|
break false
|
463
463
|
end
|
464
|
-
|
465
464
|
if complete
|
466
465
|
if options[:read_last_line]
|
467
466
|
result << line
|
@@ -478,11 +477,15 @@ class Reader
|
|
478
477
|
end
|
479
478
|
end
|
480
479
|
end
|
481
|
-
|
482
480
|
if restore_process_lines
|
483
481
|
@process_lines = true
|
484
482
|
@look_ahead -= 1 if line_restored && !terminator
|
485
483
|
end
|
484
|
+
if terminator && terminator != line && (context = options.fetch :context, terminator)
|
485
|
+
start_cursor = cursor_at_mark if start_cursor == :at_mark
|
486
|
+
logger.warn message_with_context %(unterminated #{context} block), :source_location => start_cursor
|
487
|
+
@unterminated = true
|
488
|
+
end
|
486
489
|
result
|
487
490
|
end
|
488
491
|
|
@@ -517,17 +520,37 @@ class Reader
|
|
517
520
|
Cursor.new @file, @dir, @path, @lineno
|
518
521
|
end
|
519
522
|
|
523
|
+
def cursor_at_line lineno
|
524
|
+
Cursor.new @file, @dir, @path, lineno
|
525
|
+
end
|
526
|
+
|
527
|
+
def cursor_at_mark
|
528
|
+
@mark ? Cursor.new(*@mark) : cursor
|
529
|
+
end
|
530
|
+
|
531
|
+
def cursor_before_mark
|
532
|
+
if @mark
|
533
|
+
m_file, m_dir, m_path, m_lineno = @mark
|
534
|
+
Cursor.new m_file, m_dir, m_path, m_lineno - 1
|
535
|
+
else
|
536
|
+
Cursor.new @file, @dir, @path, @lineno - 1
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
def cursor_at_prev_line
|
541
|
+
Cursor.new @file, @dir, @path, @lineno - 1
|
542
|
+
end
|
543
|
+
|
544
|
+
def mark
|
545
|
+
@mark = @file, @dir, @path, @lineno
|
546
|
+
end
|
547
|
+
|
520
548
|
# Public: Get information about the last line read, including file name and line number.
|
521
549
|
#
|
522
550
|
# Returns A String summary of the last line read
|
523
551
|
def line_info
|
524
552
|
%(#{@path}: line #{@lineno})
|
525
553
|
end
|
526
|
-
alias next_line_info line_info
|
527
|
-
|
528
|
-
def prev_line_info
|
529
|
-
%(#{@path}: line #{@lineno - 1})
|
530
|
-
end
|
531
554
|
|
532
555
|
# Public: Get a copy of the remaining Array of String lines managed by this Reader
|
533
556
|
#
|
@@ -550,16 +573,13 @@ class Reader
|
|
550
573
|
#
|
551
574
|
#
|
552
575
|
# Returns A string summary of this reader, which contains the path and line information
|
553
|
-
|
554
|
-
line_info
|
555
|
-
end
|
576
|
+
alias to_s line_info
|
556
577
|
end
|
557
578
|
|
558
579
|
# Public: Methods for retrieving lines from AsciiDoc source files, evaluating preprocessor
|
559
580
|
# directives as each line is read off the Array of lines.
|
560
581
|
class PreprocessorReader < Reader
|
561
582
|
attr_reader :include_stack
|
562
|
-
attr_reader :includes
|
563
583
|
|
564
584
|
# Public: Initialize the PreprocessorReader object
|
565
585
|
def initialize document, data = nil, cursor = nil, opts = {}
|
@@ -580,7 +600,7 @@ class PreprocessorReader < Reader
|
|
580
600
|
result = super
|
581
601
|
|
582
602
|
# QUESTION should this work for AsciiDoc table cell content? Currently it does not.
|
583
|
-
if @document &&
|
603
|
+
if @document && @document.attributes['skip-front-matter']
|
584
604
|
if (front_matter = skip_front_matter! result)
|
585
605
|
@document.attributes['front-matter'] = front_matter * LF
|
586
606
|
end
|
@@ -635,7 +655,7 @@ class PreprocessorReader < Reader
|
|
635
655
|
@look_ahead += 1
|
636
656
|
line[1..-1]
|
637
657
|
# QUESTION should we strip whitespace from raw attributes in Substitutors#parse_attributes? (check perf)
|
638
|
-
elsif preprocess_include_directive $2, $3
|
658
|
+
elsif preprocess_include_directive $2, $3
|
639
659
|
# peek again since the content has changed
|
640
660
|
nil
|
641
661
|
else
|
@@ -720,12 +740,12 @@ class PreprocessorReader < Reader
|
|
720
740
|
|
721
741
|
if keyword == 'endif'
|
722
742
|
if @conditional_stack.empty?
|
723
|
-
|
743
|
+
logger.error message_with_context %(unmatched macro: endif::#{target}[]), :source_location => cursor
|
724
744
|
elsif no_target || target == (pair = @conditional_stack[-1])[:target]
|
725
745
|
@conditional_stack.pop
|
726
746
|
@skipping = @conditional_stack.empty? ? false : @conditional_stack[-1][:skipping]
|
727
747
|
else
|
728
|
-
|
748
|
+
logger.error message_with_context %(mismatched macro: endif::#{target}[], expected endif::#{pair[:target]}[]), :source_location => cursor
|
729
749
|
end
|
730
750
|
return true
|
731
751
|
end
|
@@ -825,14 +845,14 @@ class PreprocessorReader < Reader
|
|
825
845
|
if ((expanded_target = target).include? ATTR_REF_HEAD) &&
|
826
846
|
(expanded_target = @document.sub_attributes target, :attribute_missing => 'drop-line').empty?
|
827
847
|
shift
|
828
|
-
if @document.attributes
|
848
|
+
if (@document.attributes['attribute-missing'] || Compliance.attribute_missing) == 'skip'
|
829
849
|
unshift %(Unresolved directive in #{@path} - include::#{target}[#{attrlist}])
|
830
850
|
end
|
831
851
|
true
|
832
852
|
elsif include_processors? && (ext = @include_processor_extensions.find {|candidate| candidate.instance.handles? expanded_target })
|
833
853
|
shift
|
834
854
|
# FIXME parse attributes only if requested by extension
|
835
|
-
ext.process_method[@document, self, expanded_target, AttributeList.new
|
855
|
+
ext.process_method[@document, self, expanded_target, attrlist ? (AttributeList.new attrlist).parse : {}]
|
836
856
|
true
|
837
857
|
# if running in SafeMode::SECURE or greater, don't process this directive
|
838
858
|
# however, be friendly and at least make it a link to the source document
|
@@ -841,48 +861,39 @@ class PreprocessorReader < Reader
|
|
841
861
|
replace_next_line %(link:#{expanded_target}[])
|
842
862
|
elsif (abs_maxdepth = @maxdepth[:abs]) > 0
|
843
863
|
if @include_stack.size >= abs_maxdepth
|
844
|
-
|
864
|
+
logger.error message_with_context %(maximum include depth of #{@maxdepth[:rel]} exceeded), :source_location => cursor
|
845
865
|
return
|
846
866
|
end
|
847
867
|
|
848
|
-
parsed_attributes = attrlist
|
868
|
+
parsed_attributes = attrlist ? (AttributeList.new attrlist).parse : {}
|
849
869
|
inc_path, target_type, relpath = resolve_include_path expanded_target, attrlist, parsed_attributes
|
850
870
|
return inc_path unless target_type
|
851
871
|
|
852
872
|
inc_linenos = inc_tags = nil
|
853
|
-
|
873
|
+
if attrlist
|
854
874
|
if parsed_attributes.key? 'lines'
|
855
875
|
inc_linenos = []
|
856
876
|
parsed_attributes['lines'].split(DataDelimiterRx).each do |linedef|
|
857
877
|
if linedef.include?('..')
|
858
878
|
from, to = linedef.split('..', 2).map {|it| it.to_i }
|
859
|
-
|
860
|
-
inc_linenos << from
|
861
|
-
inc_linenos << 1.0/0.0
|
862
|
-
else
|
863
|
-
inc_linenos.concat ::Range.new(from, to).to_a
|
864
|
-
end
|
879
|
+
inc_linenos += to < 0 ? [from, 1.0/0.0] : ::Range.new(from, to).to_a
|
865
880
|
else
|
866
881
|
inc_linenos << linedef.to_i
|
867
882
|
end
|
868
883
|
end
|
869
884
|
inc_linenos = inc_linenos.empty? ? nil : inc_linenos.sort.uniq
|
870
885
|
elsif parsed_attributes.key? 'tag'
|
871
|
-
unless (tag = parsed_attributes['tag']).empty?
|
872
|
-
|
873
|
-
inc_tags = { (tag.slice 1, tag.length) => false } unless tag == '!'
|
874
|
-
else
|
875
|
-
inc_tags = { tag => true }
|
876
|
-
end
|
886
|
+
unless (tag = parsed_attributes['tag']).empty? || tag == '!'
|
887
|
+
inc_tags = (tag.start_with? '!') ? { (tag.slice 1, tag.length) => false } : { tag => true }
|
877
888
|
end
|
878
889
|
elsif parsed_attributes.key? 'tags'
|
879
890
|
inc_tags = {}
|
880
891
|
parsed_attributes['tags'].split(DataDelimiterRx).each do |tagdef|
|
881
892
|
if tagdef.start_with? '!'
|
882
|
-
inc_tags[tagdef.slice 1, tagdef.length] = false
|
893
|
+
inc_tags[tagdef.slice 1, tagdef.length] = false
|
883
894
|
else
|
884
895
|
inc_tags[tagdef] = true
|
885
|
-
end unless tagdef.empty?
|
896
|
+
end unless tagdef.empty? || tagdef == '!'
|
886
897
|
end
|
887
898
|
inc_tags = nil if inc_tags.empty?
|
888
899
|
end
|
@@ -891,16 +902,16 @@ class PreprocessorReader < Reader
|
|
891
902
|
if inc_linenos
|
892
903
|
inc_lines, inc_offset, inc_lineno = [], nil, 0
|
893
904
|
begin
|
894
|
-
open(inc_path, '
|
905
|
+
open(inc_path, 'rb') do |f|
|
906
|
+
select_remaining = nil
|
895
907
|
f.each_line do |l|
|
896
908
|
inc_lineno += 1
|
897
|
-
select = inc_linenos[0]
|
898
|
-
if ::Float === select && select.infinite?
|
909
|
+
if select_remaining || (::Float === (select = inc_linenos[0]) && (select_remaining = select.infinite?))
|
899
910
|
# NOTE record line where we started selecting
|
900
911
|
inc_offset ||= inc_lineno
|
901
912
|
inc_lines << l
|
902
913
|
else
|
903
|
-
if
|
914
|
+
if select == inc_lineno
|
904
915
|
# NOTE record line where we started selecting
|
905
916
|
inc_offset ||= inc_lineno
|
906
917
|
inc_lines << l
|
@@ -911,12 +922,15 @@ class PreprocessorReader < Reader
|
|
911
922
|
end
|
912
923
|
end
|
913
924
|
rescue
|
914
|
-
|
925
|
+
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), :source_location => cursor
|
915
926
|
return replace_next_line %(Unresolved directive in #{@path} - include::#{expanded_target}[#{attrlist}])
|
916
927
|
end
|
917
928
|
shift
|
918
929
|
# FIXME not accounting for skipped lines in reader line numbering
|
919
|
-
|
930
|
+
if inc_offset
|
931
|
+
parsed_attributes['partial-option'] = true
|
932
|
+
push_include inc_lines, inc_path, relpath, inc_offset, parsed_attributes
|
933
|
+
end
|
920
934
|
elsif inc_tags
|
921
935
|
inc_lines, inc_offset, inc_lineno, tag_stack, tags_used, active_tag = [], nil, 0, [], ::Set.new, nil
|
922
936
|
if inc_tags.key? '**'
|
@@ -930,37 +944,35 @@ class PreprocessorReader < Reader
|
|
930
944
|
select = base_select = !(inc_tags.value? true)
|
931
945
|
wildcard = inc_tags.delete '*'
|
932
946
|
end
|
933
|
-
if (ext_idx = inc_path.rindex '.') && (circ_cmt = CIRCUMFIX_COMMENTS[inc_path.slice ext_idx, inc_path.length])
|
934
|
-
cmt_suffix_len = (tag_suffix = %([] #{circ_cmt[:suffix]})).length - 2
|
935
|
-
end
|
936
947
|
begin
|
937
|
-
open(inc_path, '
|
948
|
+
open(inc_path, 'rb') do |f|
|
949
|
+
dbl_co, dbl_sb = '::', '[]'
|
950
|
+
encoding = ::Encoding::UTF_8 if COERCE_ENCODING
|
938
951
|
f.each_line do |l|
|
939
952
|
inc_lineno += 1
|
940
953
|
# must force encoding since we're performing String operations on line
|
941
|
-
l.force_encoding
|
942
|
-
if (
|
943
|
-
(tag_suffix && (tl.end_with? tag_suffix) && (tl = tl.slice 0, tl.length - cmt_suffix_len))) &&
|
944
|
-
TagDirectiveRx =~ tl
|
954
|
+
l.force_encoding encoding if encoding
|
955
|
+
if (l.include? dbl_co) && (l.include? dbl_sb) && TagDirectiveRx =~ l
|
945
956
|
if $1 # end tag
|
946
957
|
if (this_tag = $2) == active_tag
|
947
958
|
tag_stack.pop
|
948
959
|
active_tag, select = tag_stack.empty? ? [nil, base_select] : tag_stack[-1]
|
949
960
|
elsif inc_tags.key? this_tag
|
961
|
+
include_cursor = create_include_cursor inc_path, expanded_target, inc_lineno
|
950
962
|
if (idx = tag_stack.rindex {|key, _| key == this_tag })
|
951
963
|
idx == 0 ? tag_stack.shift : (tag_stack.delete_at idx)
|
952
|
-
warn %(
|
964
|
+
logger.warn message_with_context %(mismatched end tag (expected '#{active_tag}' but found '#{this_tag}') at line #{inc_lineno} of include #{target_type}: #{inc_path}), :source_location => cursor, :include_location => include_cursor
|
953
965
|
else
|
954
|
-
warn %(
|
966
|
+
logger.warn message_with_context %(unexpected end tag '#{this_tag}' at line #{inc_lineno} of include #{target_type}: #{inc_path}), :source_location => cursor, :include_location => include_cursor
|
955
967
|
end
|
956
968
|
end
|
957
969
|
elsif inc_tags.key?(this_tag = $2)
|
958
970
|
tags_used << this_tag
|
959
971
|
# QUESTION should we prevent tag from being selected when enclosing tag is excluded?
|
960
|
-
tag_stack << [(active_tag = this_tag), (select = inc_tags[this_tag])]
|
972
|
+
tag_stack << [(active_tag = this_tag), (select = inc_tags[this_tag]), inc_lineno]
|
961
973
|
elsif !wildcard.nil?
|
962
974
|
select = active_tag && !select ? false : wildcard
|
963
|
-
tag_stack << [(active_tag = this_tag), select]
|
975
|
+
tag_stack << [(active_tag = this_tag), select, inc_lineno]
|
964
976
|
end
|
965
977
|
elsif select
|
966
978
|
# NOTE record the line where we started selecting
|
@@ -970,29 +982,35 @@ class PreprocessorReader < Reader
|
|
970
982
|
end
|
971
983
|
end
|
972
984
|
rescue
|
973
|
-
|
985
|
+
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), :source_location => cursor
|
974
986
|
return replace_next_line %(Unresolved directive in #{@path} - include::#{expanded_target}[#{attrlist}])
|
975
987
|
end
|
988
|
+
unless tag_stack.empty?
|
989
|
+
tag_stack.each do |tag_name, _, tag_lineno|
|
990
|
+
logger.warn message_with_context %(detected unclosed tag '#{tag_name}' starting at line #{tag_lineno} of include #{target_type}: #{inc_path}), :source_location => cursor, :include_location => (create_include_cursor inc_path, expanded_target, tag_lineno)
|
991
|
+
end
|
992
|
+
end
|
976
993
|
unless (missing_tags = inc_tags.keys.to_a - tags_used.to_a).empty?
|
977
|
-
warn %(
|
994
|
+
logger.warn message_with_context %(tag#{missing_tags.size > 1 ? 's' : ''} '#{missing_tags.join ', '}' not found in include #{target_type}: #{inc_path}), :source_location => cursor
|
978
995
|
end
|
979
996
|
shift
|
980
|
-
|
981
|
-
|
997
|
+
if inc_offset
|
998
|
+
parsed_attributes['partial-option'] = true unless base_select && wildcard && inc_tags.empty?
|
999
|
+
# FIXME not accounting for skipped lines in reader line numbering
|
1000
|
+
push_include inc_lines, inc_path, relpath, inc_offset, parsed_attributes
|
1001
|
+
end
|
982
1002
|
else
|
983
1003
|
begin
|
984
1004
|
# NOTE read content first so that we only advance cursor if IO operation succeeds
|
985
|
-
inc_content = target_type == :file ? (::IO.
|
1005
|
+
inc_content = target_type == :file ? (::IO.binread inc_path) : open(inc_path, 'rb') {|f| f.read }
|
986
1006
|
shift
|
987
1007
|
push_include inc_content, inc_path, relpath, 1, parsed_attributes
|
988
1008
|
rescue
|
989
|
-
|
1009
|
+
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), :source_location => cursor
|
990
1010
|
return replace_next_line %(Unresolved directive in #{@path} - include::#{expanded_target}[#{attrlist}])
|
991
1011
|
end
|
992
1012
|
end
|
993
1013
|
true
|
994
|
-
else
|
995
|
-
false
|
996
1014
|
end
|
997
1015
|
end
|
998
1016
|
|
@@ -1015,9 +1033,10 @@ class PreprocessorReader < Reader
|
|
1015
1033
|
# Returns An Array containing the resolved (absolute) include path, the target type, and the path
|
1016
1034
|
# relative to the outermost document. May also return a boolean to halt processing of the include.
|
1017
1035
|
def resolve_include_path target, attrlist, attributes
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1036
|
+
doc = @document
|
1037
|
+
if (Helpers.uriish? target) || (::String === @dir ? nil : (target = %(#{@dir}/#{target})))
|
1038
|
+
return replace_next_line %(link:#{target}[#{attrlist}]) unless doc.attr? 'allow-uri-read'
|
1039
|
+
if doc.attr? 'cache-uri'
|
1021
1040
|
# caching requires the open-uri-cached gem to be installed
|
1022
1041
|
# processing will be automatically aborted if these libraries can't be opened
|
1023
1042
|
Helpers.require_library 'open-uri/cached', 'open-uri-cached' unless defined? ::OpenURI::Cache
|
@@ -1025,17 +1044,22 @@ class PreprocessorReader < Reader
|
|
1025
1044
|
# autoload open-uri
|
1026
1045
|
::OpenURI
|
1027
1046
|
end
|
1028
|
-
[target, :uri, target]
|
1047
|
+
[(::URI.parse target), :uri, target]
|
1029
1048
|
else
|
1030
1049
|
# include file is resolved relative to dir of current include, or base_dir if within original docfile
|
1031
|
-
inc_path =
|
1050
|
+
inc_path = doc.normalize_system_path target, @dir, nil, :target_name => 'include file'
|
1032
1051
|
unless ::File.file? inc_path
|
1033
|
-
|
1034
|
-
|
1052
|
+
if attributes.key? 'optional-option'
|
1053
|
+
shift
|
1054
|
+
return true
|
1055
|
+
else
|
1056
|
+
logger.error message_with_context %(include file not found: #{inc_path}), :source_location => cursor
|
1057
|
+
return replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{attrlist}])
|
1058
|
+
end
|
1035
1059
|
end
|
1036
1060
|
# NOTE relpath is the path relative to the root document (or base_dir, if set)
|
1037
1061
|
# QUESTION should we move relative_path method to Document
|
1038
|
-
relpath =
|
1062
|
+
relpath = doc.path_resolver.relative_path inc_path, doc.base_dir
|
1039
1063
|
[inc_path, :file, relpath]
|
1040
1064
|
end
|
1041
1065
|
end
|
@@ -1056,20 +1080,29 @@ class PreprocessorReader < Reader
|
|
1056
1080
|
# Returns this Reader object.
|
1057
1081
|
def push_include data, file = nil, path = nil, lineno = 1, attributes = {}
|
1058
1082
|
@include_stack << [@lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines]
|
1059
|
-
if file
|
1060
|
-
|
1061
|
-
|
1083
|
+
if (@file = file)
|
1084
|
+
# NOTE if file is not a string, assume it's a URI
|
1085
|
+
if ::String === file
|
1086
|
+
@dir = ::File.dirname file
|
1087
|
+
elsif ::RUBY_ENGINE_OPAL
|
1088
|
+
@dir = ::URI.parse ::File.dirname(file = file.to_s)
|
1089
|
+
else
|
1090
|
+
# NOTE this intentionally throws an error if URI has no path
|
1091
|
+
(@dir = file.dup).path = (dir = ::File.dirname file.path) == '/' ? '' : dir
|
1092
|
+
file = file.to_s
|
1093
|
+
end
|
1094
|
+
path ||= ::File.basename file
|
1062
1095
|
# only process lines in AsciiDoc files
|
1063
1096
|
@process_lines = ASCIIDOC_EXTENSIONS[::File.extname file]
|
1064
1097
|
else
|
1065
|
-
@
|
1066
|
-
@dir = '.' # right?
|
1098
|
+
@dir = '.'
|
1067
1099
|
# we don't know what file type we have, so assume AsciiDoc
|
1068
1100
|
@process_lines = true
|
1069
1101
|
end
|
1070
1102
|
|
1071
1103
|
if path
|
1072
|
-
@
|
1104
|
+
@path = path
|
1105
|
+
@includes[Helpers.rootname path] = attributes['partial-option'] ? nil : true if @process_lines
|
1073
1106
|
else
|
1074
1107
|
@path = '<stdin>'
|
1075
1108
|
end
|
@@ -1108,6 +1141,18 @@ class PreprocessorReader < Reader
|
|
1108
1141
|
self
|
1109
1142
|
end
|
1110
1143
|
|
1144
|
+
def create_include_cursor file, path, lineno
|
1145
|
+
if ::String === file
|
1146
|
+
dir = ::File.dirname file
|
1147
|
+
elsif ::RUBY_ENGINE_OPAL
|
1148
|
+
dir = ::File.dirname(file = file.to_s)
|
1149
|
+
else
|
1150
|
+
dir = (dir = ::File.dirname file.path) == '' ? '/' : dir
|
1151
|
+
file = file.to_s
|
1152
|
+
end
|
1153
|
+
Cursor.new file, dir, path, lineno
|
1154
|
+
end
|
1155
|
+
|
1111
1156
|
def pop_include
|
1112
1157
|
if @include_stack.size > 0
|
1113
1158
|
@lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines = @include_stack.pop
|