asciidoctor 1.5.8 → 2.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +11 -0
- data/CHANGELOG.adoc +628 -45
- data/LICENSE +2 -1
- data/README-de.adoc +28 -38
- data/README-fr.adoc +30 -43
- data/README-jp.adoc +255 -201
- data/README-zh_CN.adoc +40 -44
- data/README.adoc +170 -143
- data/asciidoctor.gemspec +22 -34
- data/bin/asciidoctor +5 -4
- data/data/locale/attributes-ar.adoc +4 -3
- data/data/locale/attributes-be.adoc +23 -0
- data/data/locale/attributes-bg.adoc +4 -3
- data/data/locale/attributes-ca.adoc +6 -5
- data/data/locale/attributes-cs.adoc +4 -3
- data/data/locale/attributes-da.adoc +6 -5
- data/data/locale/attributes-de.adoc +6 -5
- data/data/locale/attributes-en.adoc +4 -4
- data/data/locale/attributes-es.adoc +6 -5
- data/data/locale/attributes-fa.adoc +4 -3
- data/data/locale/attributes-fi.adoc +4 -3
- data/data/locale/attributes-fr.adoc +8 -7
- data/data/locale/attributes-hu.adoc +4 -3
- data/data/locale/attributes-id.adoc +4 -3
- data/data/locale/attributes-it.adoc +6 -5
- data/data/locale/attributes-ja.adoc +4 -3
- data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
- data/data/locale/attributes-nb.adoc +4 -3
- data/data/locale/attributes-nl.adoc +6 -5
- data/data/locale/attributes-nn.adoc +4 -3
- data/data/locale/attributes-pl.adoc +8 -7
- data/data/locale/attributes-pt.adoc +6 -5
- data/data/locale/attributes-pt_BR.adoc +6 -5
- data/data/locale/attributes-ro.adoc +4 -3
- data/data/locale/attributes-ru.adoc +6 -5
- data/data/locale/attributes-sr.adoc +4 -4
- data/data/locale/attributes-sr_Latn.adoc +4 -4
- data/data/locale/attributes-sv.adoc +4 -4
- data/data/locale/attributes-th.adoc +23 -0
- data/data/locale/attributes-tr.adoc +4 -3
- data/data/locale/attributes-uk.adoc +6 -5
- data/data/locale/attributes-vi.adoc +23 -0
- data/data/locale/attributes-zh_CN.adoc +4 -3
- data/data/locale/attributes-zh_TW.adoc +4 -3
- data/data/reference/syntax.adoc +296 -0
- data/data/stylesheets/asciidoctor-default.css +120 -114
- data/data/stylesheets/coderay-asciidoctor.css +15 -17
- data/lib/asciidoctor/abstract_block.rb +146 -140
- data/lib/asciidoctor/abstract_node.rb +152 -170
- data/lib/asciidoctor/attribute_list.rb +77 -89
- data/lib/asciidoctor/block.rb +29 -28
- data/lib/asciidoctor/callouts.rb +4 -2
- data/lib/asciidoctor/cli/invoker.rb +20 -24
- data/lib/asciidoctor/cli/options.rb +107 -96
- data/lib/asciidoctor/cli.rb +3 -2
- data/lib/asciidoctor/convert.rb +199 -0
- data/lib/asciidoctor/converter/composite.rb +40 -48
- data/lib/asciidoctor/converter/docbook5.rb +627 -644
- data/lib/asciidoctor/converter/html5.rb +1053 -951
- data/lib/asciidoctor/converter/manpage.rb +581 -532
- data/lib/asciidoctor/converter/template.rb +232 -271
- data/lib/asciidoctor/converter.rb +370 -185
- data/lib/asciidoctor/core_ext/float/truncate.rb +20 -0
- data/lib/asciidoctor/core_ext/hash/merge.rb +8 -0
- data/lib/asciidoctor/core_ext/match_data/names.rb +7 -0
- data/lib/asciidoctor/core_ext/nil_or_empty.rb +1 -0
- data/lib/asciidoctor/core_ext/regexp/is_match.rb +4 -2
- data/lib/asciidoctor/core_ext.rb +8 -17
- data/lib/asciidoctor/document.rb +503 -461
- data/lib/asciidoctor/extensions.rb +127 -174
- data/lib/asciidoctor/helpers.rb +184 -107
- data/lib/asciidoctor/inline.rb +9 -12
- data/lib/asciidoctor/list.rb +11 -29
- data/lib/asciidoctor/load.rb +119 -0
- data/lib/asciidoctor/logging.rb +22 -17
- data/lib/asciidoctor/parser.rb +673 -719
- data/lib/asciidoctor/path_resolver.rb +48 -33
- data/lib/asciidoctor/reader.rb +383 -338
- data/lib/asciidoctor/rouge_ext.rb +39 -0
- data/lib/asciidoctor/rx.rb +723 -0
- data/lib/asciidoctor/section.rb +17 -16
- data/lib/asciidoctor/stylesheets.rb +19 -37
- data/lib/asciidoctor/substitutors.rb +926 -1022
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +88 -0
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +34 -0
- data/lib/asciidoctor/syntax_highlighter/html_pipeline.rb +10 -0
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +30 -0
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +157 -0
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +143 -0
- data/lib/asciidoctor/syntax_highlighter.rb +253 -0
- data/lib/asciidoctor/table.rb +152 -114
- data/lib/asciidoctor/timings.rb +7 -5
- data/lib/asciidoctor/version.rb +2 -1
- data/lib/asciidoctor/writer.rb +30 -0
- data/lib/asciidoctor.rb +266 -1340
- data/man/asciidoctor.1 +49 -47
- data/man/asciidoctor.adoc +54 -45
- metadata +50 -245
- data/CONTRIBUTING.adoc +0 -185
- data/Gemfile +0 -60
- data/Rakefile +0 -129
- data/bin/asciidoctor-safe +0 -15
- data/features/open_block.feature +0 -92
- data/features/pass_block.feature +0 -66
- data/features/step_definitions.rb +0 -49
- data/features/text_formatting.feature +0 -57
- data/features/xref.feature +0 -1039
- data/lib/asciidoctor/converter/base.rb +0 -59
- data/lib/asciidoctor/converter/docbook45.rb +0 -93
- data/lib/asciidoctor/converter/factory.rb +0 -226
- data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +0 -5
- data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +0 -4
- data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +0 -5
- data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/string/limit_bytesize.rb +0 -29
- data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +0 -6
- data/lib/asciidoctor/core_ext/string/limit_bytesize.rb +0 -10
- data/test/api_test.rb +0 -1240
- data/test/attribute_list_test.rb +0 -242
- data/test/attributes_test.rb +0 -1623
- data/test/blocks_test.rb +0 -3870
- data/test/converter_test.rb +0 -470
- data/test/document_test.rb +0 -1853
- data/test/extensions_test.rb +0 -1560
- data/test/fixtures/asciidoc_index.txt +0 -521
- data/test/fixtures/basic-docinfo-footer.html +0 -6
- data/test/fixtures/basic-docinfo-footer.xml +0 -8
- data/test/fixtures/basic-docinfo.html +0 -1
- data/test/fixtures/basic-docinfo.xml +0 -4
- data/test/fixtures/basic.asciidoc +0 -5
- data/test/fixtures/chapter-a.adoc +0 -3
- data/test/fixtures/child-include.adoc +0 -5
- data/test/fixtures/circle.svg +0 -9
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +0 -6
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +0 -6
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +0 -3
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +0 -5
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +0 -1
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +0 -6
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +0 -3
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +0 -5
- data/test/fixtures/custom-docinfodir/basic-docinfo.html +0 -1
- data/test/fixtures/custom-docinfodir/docinfo.html +0 -1
- data/test/fixtures/docinfo-footer.html +0 -1
- data/test/fixtures/docinfo-footer.xml +0 -9
- data/test/fixtures/docinfo.html +0 -1
- data/test/fixtures/docinfo.xml +0 -3
- data/test/fixtures/doctime-localtime.adoc +0 -2
- data/test/fixtures/dot.gif +0 -0
- data/test/fixtures/encoding.asciidoc +0 -13
- data/test/fixtures/file-with-missing-include.adoc +0 -1
- data/test/fixtures/grandchild-include.adoc +0 -3
- data/test/fixtures/hello-asciidoctor.pdf +0 -69
- data/test/fixtures/include-file.asciidoc +0 -24
- data/test/fixtures/include-file.jsx +0 -8
- data/test/fixtures/include-file.ml +0 -3
- data/test/fixtures/include-file.xml +0 -5
- data/test/fixtures/lists.adoc +0 -96
- data/test/fixtures/master.adoc +0 -5
- data/test/fixtures/mismatched-end-tag.adoc +0 -7
- data/test/fixtures/other-chapters.adoc +0 -11
- data/test/fixtures/outer-include.adoc +0 -5
- data/test/fixtures/parent-include-restricted.adoc +0 -5
- data/test/fixtures/parent-include.adoc +0 -5
- data/test/fixtures/sample.asciidoc +0 -30
- data/test/fixtures/section-a.adoc +0 -4
- data/test/fixtures/stylesheets/custom.css +0 -3
- data/test/fixtures/subdir/index.adoc +0 -3
- data/test/fixtures/subdir/inner-include.adoc +0 -3
- data/test/fixtures/subdir/middle-include.adoc +0 -5
- data/test/fixtures/subs-docinfo.html +0 -2
- data/test/fixtures/subs.adoc +0 -6
- data/test/fixtures/tagged-class-enclosed.rb +0 -25
- data/test/fixtures/tagged-class.rb +0 -23
- data/test/fixtures/tip.gif +0 -0
- data/test/fixtures/unclosed-tag.adoc +0 -3
- data/test/fixtures/unexpected-end-tag.adoc +0 -4
- data/test/invoker_test.rb +0 -745
- data/test/links_test.rb +0 -855
- data/test/lists_test.rb +0 -5151
- data/test/logger_test.rb +0 -211
- data/test/manpage_test.rb +0 -660
- data/test/options_test.rb +0 -262
- data/test/paragraphs_test.rb +0 -562
- data/test/parser_test.rb +0 -742
- data/test/paths_test.rb +0 -395
- data/test/preamble_test.rb +0 -173
- data/test/reader_test.rb +0 -2161
- data/test/sections_test.rb +0 -3575
- data/test/substitutions_test.rb +0 -2066
- data/test/tables_test.rb +0 -2036
- data/test/test_helper.rb +0 -447
- data/test/text_test.rb +0 -309
data/lib/asciidoctor/reader.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
3
|
# Public: Methods for retrieving lines from AsciiDoc source files
|
4
4
|
class Reader
|
@@ -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 =
|
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
|
@@ -69,47 +68,6 @@ class Reader
|
|
69
68
|
@saved = nil
|
70
69
|
end
|
71
70
|
|
72
|
-
# Internal: Prepare the lines from the provided data
|
73
|
-
#
|
74
|
-
# This method strips whitespace from the end of every line of
|
75
|
-
# the source data and appends a LF (i.e., Unix endline). This
|
76
|
-
# whitespace substitution is very important to how Asciidoctor
|
77
|
-
# works.
|
78
|
-
#
|
79
|
-
# Any leading or trailing blank lines are also removed.
|
80
|
-
#
|
81
|
-
# data - A String Array of input data to be normalized
|
82
|
-
# opts - A Hash of options to control what cleansing is done
|
83
|
-
#
|
84
|
-
# Returns The String lines extracted from the data
|
85
|
-
def prepare_lines data, opts = {}
|
86
|
-
if ::String === data
|
87
|
-
if opts[:normalize]
|
88
|
-
Helpers.normalize_lines_from_string data
|
89
|
-
else
|
90
|
-
data.split LF, -1
|
91
|
-
end
|
92
|
-
elsif opts[:normalize]
|
93
|
-
Helpers.normalize_lines_array data
|
94
|
-
else
|
95
|
-
data.drop 0
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Internal: Processes a previously unvisited line
|
100
|
-
#
|
101
|
-
# By default, this method marks the line as processed
|
102
|
-
# by incrementing the look_ahead counter and returns
|
103
|
-
# the line unmodified.
|
104
|
-
#
|
105
|
-
# Returns The String line the Reader should make available to the next
|
106
|
-
# invocation of Reader#read_line or nil if the Reader should drop the line,
|
107
|
-
# advance to the next line and process it.
|
108
|
-
def process_line line
|
109
|
-
@look_ahead += 1 if @process_lines
|
110
|
-
line
|
111
|
-
end
|
112
|
-
|
113
71
|
# Public: Check whether there are any lines left to read.
|
114
72
|
#
|
115
73
|
# If a previous call to this method resulted in a value of false,
|
@@ -168,7 +126,7 @@ class Reader
|
|
168
126
|
# Returns nothing if there is no more data.
|
169
127
|
def peek_line direct = false
|
170
128
|
if direct || @look_ahead > 0
|
171
|
-
@unescape_next_line ? ((line = @lines[
|
129
|
+
@unescape_next_line ? ((line = @lines[-1]).slice 1, line.length) : @lines[-1]
|
172
130
|
elsif @lines.empty?
|
173
131
|
@look_ahead = 0
|
174
132
|
nil
|
@@ -176,7 +134,7 @@ class Reader
|
|
176
134
|
# FIXME the problem with this approach is that we aren't
|
177
135
|
# retaining the modified line (hence the @unescape_next_line tweak)
|
178
136
|
# perhaps we need a stack of proxied lines
|
179
|
-
(
|
137
|
+
(process_line @lines[-1]) || peek_line
|
180
138
|
end
|
181
139
|
end
|
182
140
|
|
@@ -233,9 +191,7 @@ class Reader
|
|
233
191
|
def read_lines
|
234
192
|
lines = []
|
235
193
|
# has_more_lines? triggers preprocessor
|
236
|
-
while has_more_lines?
|
237
|
-
lines << shift
|
238
|
-
end
|
194
|
+
lines << shift while has_more_lines?
|
239
195
|
lines
|
240
196
|
end
|
241
197
|
alias readlines read_lines
|
@@ -282,7 +238,6 @@ class Reader
|
|
282
238
|
# Returns nothing.
|
283
239
|
def unshift_lines lines_to_restore
|
284
240
|
unshift_all lines_to_restore
|
285
|
-
nil
|
286
241
|
end
|
287
242
|
alias restore_lines unshift_lines
|
288
243
|
|
@@ -351,7 +306,7 @@ class Reader
|
|
351
306
|
if next_line.start_with? '//'
|
352
307
|
if next_line.start_with? '///'
|
353
308
|
if (ll = next_line.length) > 3 && next_line == '/' * ll
|
354
|
-
read_lines_until :
|
309
|
+
read_lines_until terminator: next_line, skip_first_line: true, read_last_line: true, skip_processing: true, context: :comment
|
355
310
|
else
|
356
311
|
break
|
357
312
|
end
|
@@ -375,7 +330,7 @@ class Reader
|
|
375
330
|
comment_lines = []
|
376
331
|
# optimized code for shortest execution path
|
377
332
|
while (next_line = peek_line) && !next_line.empty?
|
378
|
-
if
|
333
|
+
if next_line.start_with? '//'
|
379
334
|
comment_lines << shift
|
380
335
|
else
|
381
336
|
break
|
@@ -396,7 +351,7 @@ class Reader
|
|
396
351
|
end
|
397
352
|
|
398
353
|
# Public: Return all the lines from `@lines` until we (1) run out them,
|
399
|
-
# (2) find a blank line with :
|
354
|
+
# (2) find a blank line with `break_on_blank_lines: true`, or (3) find
|
400
355
|
# a line for which the given block evals to true.
|
401
356
|
#
|
402
357
|
# options - an optional Hash of processing options:
|
@@ -429,7 +384,7 @@ class Reader
|
|
429
384
|
# "\n",
|
430
385
|
# "Third line\n",
|
431
386
|
# ]
|
432
|
-
# reader = Reader.new data, nil, :
|
387
|
+
# reader = Reader.new data, nil, normalize: true
|
433
388
|
#
|
434
389
|
# reader.read_lines_until
|
435
390
|
# => ["First line", "Second line"]
|
@@ -448,34 +403,22 @@ class Reader
|
|
448
403
|
break_on_list_continuation = options[:break_on_list_continuation]
|
449
404
|
end
|
450
405
|
skip_comments = options[:skip_line_comments]
|
451
|
-
|
406
|
+
line_read = line_restored = nil
|
452
407
|
shift if options[:skip_first_line]
|
453
|
-
while
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
if break_on_list_continuation && line_read && line == LIST_CONTINUATION
|
459
|
-
options[:preserve_last_line] = true
|
460
|
-
break true
|
461
|
-
end
|
462
|
-
break true if block_given? && (yield line)
|
463
|
-
break false
|
464
|
-
end
|
465
|
-
if complete
|
466
|
-
if options[:read_last_line]
|
467
|
-
result << line
|
468
|
-
line_read = true
|
469
|
-
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]
|
470
413
|
if options[:preserve_last_line]
|
471
414
|
unshift line
|
472
415
|
line_restored = true
|
473
416
|
end
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
417
|
+
break
|
418
|
+
end
|
419
|
+
unless skip_comments && (line.start_with? '//') && !(line.start_with? '///')
|
420
|
+
result << line
|
421
|
+
line_read = true
|
479
422
|
end
|
480
423
|
end
|
481
424
|
if restore_process_lines
|
@@ -484,7 +427,7 @@ class Reader
|
|
484
427
|
end
|
485
428
|
if terminator && terminator != line && (context = options.fetch :context, terminator)
|
486
429
|
start_cursor = cursor_at_mark if start_cursor == :at_mark
|
487
|
-
logger.warn message_with_context %(unterminated #{context} block), :
|
430
|
+
logger.warn message_with_context %(unterminated #{context} block), source_location: start_cursor
|
488
431
|
@unterminated = true
|
489
432
|
end
|
490
433
|
result
|
@@ -500,21 +443,37 @@ class Reader
|
|
500
443
|
def shift
|
501
444
|
@lineno += 1
|
502
445
|
@look_ahead -= 1 unless @look_ahead == 0
|
503
|
-
@lines.
|
446
|
+
@lines.pop
|
504
447
|
end
|
505
448
|
|
506
449
|
# Internal: Restore the line to the stack and decrement the lineno
|
507
450
|
def unshift line
|
508
451
|
@lineno -= 1
|
509
452
|
@look_ahead += 1
|
510
|
-
@lines.
|
453
|
+
@lines.push line
|
454
|
+
nil
|
511
455
|
end
|
512
456
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
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
|
518
477
|
end
|
519
478
|
|
520
479
|
def cursor
|
@@ -557,12 +516,12 @@ class Reader
|
|
557
516
|
#
|
558
517
|
# Returns A copy of the String Array of lines remaining in this Reader
|
559
518
|
def lines
|
560
|
-
@lines.
|
519
|
+
@lines.reverse
|
561
520
|
end
|
562
521
|
|
563
522
|
# Public: Get a copy of the remaining lines managed by this Reader joined as a String
|
564
523
|
def string
|
565
|
-
@lines.join LF
|
524
|
+
@lines.reverse.join LF
|
566
525
|
end
|
567
526
|
|
568
527
|
# Public: Get the source lines for this Reader joined as a String
|
@@ -570,15 +529,19 @@ class Reader
|
|
570
529
|
@source_lines.join LF
|
571
530
|
end
|
572
531
|
|
532
|
+
# Internal: Save the state of the reader at cursor
|
573
533
|
def save
|
574
|
-
|
575
|
-
|
576
|
-
|
534
|
+
@saved = {}.tap do |accum|
|
535
|
+
instance_variables.each do |name|
|
536
|
+
unless name == :@saved || name == :@source_lines
|
537
|
+
accum[name] = ::Array === (val = instance_variable_get name) ? (val.drop 0) : val
|
538
|
+
end
|
539
|
+
end
|
577
540
|
end
|
578
|
-
@saved = accum
|
579
541
|
nil
|
580
542
|
end
|
581
543
|
|
544
|
+
# Internal: Restore the state of the reader at cursor
|
582
545
|
def restore_save
|
583
546
|
if @saved
|
584
547
|
@saved.each do |name, val|
|
@@ -588,15 +551,60 @@ class Reader
|
|
588
551
|
end
|
589
552
|
end
|
590
553
|
|
554
|
+
# Internal: Discard a previous saved state
|
591
555
|
def discard_save
|
592
556
|
@saved = nil
|
593
557
|
end
|
594
558
|
|
595
|
-
|
559
|
+
def to_s
|
560
|
+
%(#<#{self.class}@#{object_id} {path: #{@path.inspect}, line: #{@lineno}}>)
|
561
|
+
end
|
562
|
+
|
563
|
+
private
|
564
|
+
|
565
|
+
# Internal: Prepare the source data for parsing.
|
566
|
+
#
|
567
|
+
# Converts the source data into an Array of lines ready for parsing. If the +:normalize+ option is set, this method
|
568
|
+
# coerces the encoding of each line to UTF-8 and strips trailing whitespace, including the newline. (This whitespace
|
569
|
+
# cleaning is very important to how Asciidoctor works). Subclasses may choose to perform additional preparation.
|
596
570
|
#
|
571
|
+
# data - A String Array or String of source data to be normalized.
|
572
|
+
# opts - A Hash of options to control how lines are prepared.
|
573
|
+
# :normalize - Enables line normalization, which coerces the encoding to UTF-8 and removes trailing whitespace;
|
574
|
+
# :rstrip removes all trailing whitespace; :chomp removes trailing newline only (optional, not set).
|
597
575
|
#
|
598
|
-
# Returns A
|
599
|
-
|
576
|
+
# Returns A String Array of source lines. If the source data is an Array, this method returns a copy.
|
577
|
+
def prepare_lines data, opts = {}
|
578
|
+
if (normalize = opts[:normalize])
|
579
|
+
::Array === data ? (Helpers.prepare_source_array data, normalize != :chomp) : (Helpers.prepare_source_string data, normalize != :chomp)
|
580
|
+
elsif ::Array === data
|
581
|
+
data.drop 0
|
582
|
+
elsif data
|
583
|
+
data.chomp.split LF, -1
|
584
|
+
else
|
585
|
+
[]
|
586
|
+
end
|
587
|
+
rescue
|
588
|
+
if (::Array === data ? data.join : data.to_s).valid_encoding?
|
589
|
+
raise
|
590
|
+
else
|
591
|
+
raise ::ArgumentError, 'source is either binary or contains invalid Unicode data'
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
# Internal: Processes a previously unvisited line
|
596
|
+
#
|
597
|
+
# By default, this method marks the line as processed
|
598
|
+
# by incrementing the look_ahead counter and returns
|
599
|
+
# the line unmodified.
|
600
|
+
#
|
601
|
+
# Returns The String line the Reader should make available to the next
|
602
|
+
# invocation of Reader#read_line or nil if the Reader should drop the line,
|
603
|
+
# advance to the next line and process it.
|
604
|
+
def process_line line
|
605
|
+
@look_ahead += 1 if @process_lines
|
606
|
+
line
|
607
|
+
end
|
600
608
|
end
|
601
609
|
|
602
610
|
# Public: Methods for retrieving lines from AsciiDoc source files, evaluating preprocessor
|
@@ -608,10 +616,13 @@ class PreprocessorReader < Reader
|
|
608
616
|
def initialize document, data = nil, cursor = nil, opts = {}
|
609
617
|
@document = document
|
610
618
|
super data, cursor, opts
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
619
|
+
if (default_include_depth = (document.attributes['max-include-depth'] || 64).to_i) > 0
|
620
|
+
# track absolute max depth, current max depth for comparing to include stack size, and relative max depth for reporting
|
621
|
+
@maxdepth = { abs: default_include_depth, curr: default_include_depth, rel: default_include_depth }
|
622
|
+
else
|
623
|
+
# if @maxdepth is not set, built-in include functionality is disabled
|
624
|
+
@maxdepth = nil
|
625
|
+
end
|
615
626
|
@include_stack = []
|
616
627
|
@includes = document.catalog[:includes]
|
617
628
|
@skipping = false
|
@@ -619,24 +630,183 @@ class PreprocessorReader < Reader
|
|
619
630
|
@include_processor_extensions = nil
|
620
631
|
end
|
621
632
|
|
633
|
+
# (see Reader#has_more_lines?)
|
634
|
+
def has_more_lines?
|
635
|
+
peek_line ? true : false
|
636
|
+
end
|
637
|
+
|
638
|
+
# (see Reader#empty?)
|
639
|
+
def empty?
|
640
|
+
peek_line ? false : true
|
641
|
+
end
|
642
|
+
alias eof? empty?
|
643
|
+
|
644
|
+
# Public: Override the Reader#peek_line method to pop the include
|
645
|
+
# stack if the last line has been reached and there's at least
|
646
|
+
# one include on the stack.
|
647
|
+
#
|
648
|
+
# Returns the next line of the source data as a String if there are lines remaining
|
649
|
+
# in the current include context or a parent include context.
|
650
|
+
# Returns nothing if there are no more lines remaining and the include stack is empty.
|
651
|
+
def peek_line direct = false
|
652
|
+
if (line = super)
|
653
|
+
line
|
654
|
+
elsif @include_stack.empty?
|
655
|
+
nil
|
656
|
+
else
|
657
|
+
pop_include
|
658
|
+
peek_line direct
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
# Public: Push source onto the front of the reader and switch the context
|
663
|
+
# based on the file, document-relative path and line information given.
|
664
|
+
#
|
665
|
+
# This method is typically used in an IncludeProcessor to add source
|
666
|
+
# read from the target specified.
|
667
|
+
#
|
668
|
+
# Examples
|
669
|
+
#
|
670
|
+
# path = 'partial.adoc'
|
671
|
+
# file = File.expand_path path
|
672
|
+
# data = File.read file
|
673
|
+
# reader.push_include data, file, path
|
674
|
+
#
|
675
|
+
# Returns this Reader object.
|
676
|
+
def push_include data, file = nil, path = nil, lineno = 1, attributes = {}
|
677
|
+
@include_stack << [@lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines]
|
678
|
+
if (@file = file)
|
679
|
+
# NOTE if file is not a string, assume it's a URI
|
680
|
+
if ::String === file
|
681
|
+
@dir = ::File.dirname file
|
682
|
+
elsif RUBY_ENGINE_OPAL
|
683
|
+
@dir = ::URI.parse ::File.dirname(file = file.to_s)
|
684
|
+
else
|
685
|
+
# NOTE this intentionally throws an error if URI has no path
|
686
|
+
(@dir = file.dup).path = (dir = ::File.dirname file.path) == '/' ? '' : dir
|
687
|
+
file = file.to_s
|
688
|
+
end
|
689
|
+
@path = (path ||= ::File.basename file)
|
690
|
+
# only process lines in AsciiDoc files
|
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
|
+
@includes[path.slice 0, (path.rindex '.')] = attributes['partial-option'] ? nil : true
|
694
|
+
end
|
695
|
+
else
|
696
|
+
@dir = '.'
|
697
|
+
# we don't know what file type we have, so assume AsciiDoc
|
698
|
+
@process_lines = true
|
699
|
+
if (@path = path)
|
700
|
+
# NOTE registering the include with a nil value tracks it while not making it visible to interdocument xrefs
|
701
|
+
@includes[Helpers.rootname path] = attributes['partial-option'] ? nil : true
|
702
|
+
else
|
703
|
+
@path = '<stdin>'
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
@lineno = lineno
|
708
|
+
|
709
|
+
if @maxdepth && (attributes.key? 'depth')
|
710
|
+
if (rel_maxdepth = attributes['depth'].to_i) > 0
|
711
|
+
if (curr_maxdepth = @include_stack.size + rel_maxdepth) > (abs_maxdepth = @maxdepth[:abs])
|
712
|
+
# if relative depth exceeds absolute max depth, effectively ignore relative depth request
|
713
|
+
curr_maxdepth = rel_maxdepth = abs_maxdepth
|
714
|
+
end
|
715
|
+
@maxdepth = { abs: abs_maxdepth, curr: curr_maxdepth, rel: rel_maxdepth }
|
716
|
+
else
|
717
|
+
@maxdepth = { abs: @maxdepth[:abs], curr: @include_stack.size, rel: 0 }
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
# effectively fill the buffer
|
722
|
+
if (@lines = prepare_lines data, normalize: @process_lines || :chomp, condense: false, indent: attributes['indent']).empty?
|
723
|
+
pop_include
|
724
|
+
else
|
725
|
+
# FIXME we eventually want to handle leveloffset without affecting the lines
|
726
|
+
if attributes.key? 'leveloffset'
|
727
|
+
@lines = [((leveloffset = @document.attr 'leveloffset') ? %(:leveloffset: #{leveloffset}) : ':leveloffset!:'), ''] + @lines.reverse + ['', %(:leveloffset: #{attributes['leveloffset']})]
|
728
|
+
# compensate for these extra lines at the top
|
729
|
+
@lineno -= 2
|
730
|
+
else
|
731
|
+
@lines.reverse!
|
732
|
+
end
|
733
|
+
|
734
|
+
# FIXME kind of a hack
|
735
|
+
#Document::AttributeEntry.new('infile', @file).save_to_next_block @document
|
736
|
+
#Document::AttributeEntry.new('indir', @dir).save_to_next_block @document
|
737
|
+
@look_ahead = 0
|
738
|
+
end
|
739
|
+
self
|
740
|
+
end
|
741
|
+
|
742
|
+
def include_depth
|
743
|
+
@include_stack.size
|
744
|
+
end
|
745
|
+
|
746
|
+
# Public: Reports whether pushing an include on the include stack exceeds the max include depth.
|
747
|
+
#
|
748
|
+
# Returns nil if no max depth is set and includes are disabled (max-include-depth=0), false if the current max depth
|
749
|
+
# will not be exceeded, and the relative max include depth if the current max depth will be exceed.
|
750
|
+
def exceeds_max_depth?
|
751
|
+
@maxdepth && @include_stack.size >= @maxdepth[:curr] && @maxdepth[:rel]
|
752
|
+
end
|
753
|
+
alias exceeded_max_depth? exceeds_max_depth?
|
754
|
+
|
755
|
+
# TODO Document this override
|
756
|
+
# also, we now have the field in the super class, so perhaps
|
757
|
+
# just implement the logic there?
|
758
|
+
def shift
|
759
|
+
if @unescape_next_line
|
760
|
+
@unescape_next_line = false
|
761
|
+
(line = super).slice 1, line.length
|
762
|
+
else
|
763
|
+
super
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
def include_processors?
|
768
|
+
if @include_processor_extensions.nil?
|
769
|
+
if @document.extensions? && @document.extensions.include_processors?
|
770
|
+
!!(@include_processor_extensions = @document.extensions.include_processors)
|
771
|
+
else
|
772
|
+
@include_processor_extensions = false
|
773
|
+
end
|
774
|
+
else
|
775
|
+
@include_processor_extensions != false
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
def create_include_cursor file, path, lineno
|
780
|
+
if ::String === file
|
781
|
+
dir = ::File.dirname file
|
782
|
+
elsif RUBY_ENGINE_OPAL
|
783
|
+
dir = ::File.dirname(file = file.to_s)
|
784
|
+
else
|
785
|
+
dir = (dir = ::File.dirname file.path) == '' ? '/' : dir
|
786
|
+
file = file.to_s
|
787
|
+
end
|
788
|
+
Cursor.new file, dir, path, lineno
|
789
|
+
end
|
790
|
+
|
791
|
+
def to_s
|
792
|
+
%(#<#{self.class}@#{object_id} {path: #{@path.inspect}, line: #{@lineno}, include depth: #{@include_stack.size}, include stack: [#{@include_stack.map {|inc| inc.to_s }.join ', '}]}>)
|
793
|
+
end
|
794
|
+
|
795
|
+
private
|
796
|
+
|
622
797
|
def prepare_lines data, opts = {}
|
623
798
|
result = super
|
624
799
|
|
625
800
|
# QUESTION should this work for AsciiDoc table cell content? Currently it does not.
|
626
|
-
if @document && @document.attributes['skip-front-matter']
|
627
|
-
|
628
|
-
@document.attributes['front-matter'] = front_matter.join LF
|
629
|
-
end
|
801
|
+
if @document && @document.attributes['skip-front-matter'] && (front_matter = skip_front_matter! result)
|
802
|
+
@document.attributes['front-matter'] = front_matter.join LF
|
630
803
|
end
|
631
804
|
|
632
805
|
if opts.fetch :condense, true
|
633
|
-
result.shift && @lineno += 1 while (first = result[0]) && first.empty?
|
634
806
|
result.pop while (last = result[-1]) && last.empty?
|
635
807
|
end
|
636
808
|
|
637
|
-
if opts[:indent]
|
638
|
-
Parser.adjust_indentation! result, opts[:indent], (@document.attr 'tabsize')
|
639
|
-
end
|
809
|
+
Parser.adjust_indentation! result, opts[:indent].to_i, (@document.attr 'tabsize').to_i if opts[:indent]
|
640
810
|
|
641
811
|
result
|
642
812
|
end
|
@@ -702,35 +872,6 @@ class PreprocessorReader < Reader
|
|
702
872
|
end
|
703
873
|
end
|
704
874
|
|
705
|
-
# (see Reader#has_more_lines?)
|
706
|
-
def has_more_lines?
|
707
|
-
peek_line ? true : false
|
708
|
-
end
|
709
|
-
|
710
|
-
# (see Reader#empty?)
|
711
|
-
def empty?
|
712
|
-
peek_line ? false : true
|
713
|
-
end
|
714
|
-
alias eof? empty?
|
715
|
-
|
716
|
-
# Public: Override the Reader#peek_line method to pop the include
|
717
|
-
# stack if the last line has been reached and there's at least
|
718
|
-
# one include on the stack.
|
719
|
-
#
|
720
|
-
# Returns the next line of the source data as a String if there are lines remaining
|
721
|
-
# in the current include context or a parent include context.
|
722
|
-
# Returns nothing if there are no more lines remaining and the include stack is empty.
|
723
|
-
def peek_line direct = false
|
724
|
-
if (line = super)
|
725
|
-
line
|
726
|
-
elsif @include_stack.empty?
|
727
|
-
nil
|
728
|
-
else
|
729
|
-
pop_include
|
730
|
-
peek_line direct
|
731
|
-
end
|
732
|
-
end
|
733
|
-
|
734
875
|
# Internal: Preprocess the directive to conditionally include or exclude content.
|
735
876
|
#
|
736
877
|
# Preprocess the conditional directive (ifdef, ifndef, ifeval, endif) under
|
@@ -755,30 +896,28 @@ class PreprocessorReader < Reader
|
|
755
896
|
# attributes are case insensitive
|
756
897
|
target = target.downcase unless (no_target = target.empty?)
|
757
898
|
|
758
|
-
# must have a target before brackets if ifdef or ifndef
|
759
|
-
# must not have text between brackets if endif
|
760
|
-
# skip line if it doesn't meet this criteria
|
761
|
-
# QUESTION should we warn for these bogus declarations?
|
762
|
-
return false if (no_target && (keyword == 'ifdef' || keyword == 'ifndef')) || (text && keyword == 'endif')
|
763
|
-
|
764
899
|
if keyword == 'endif'
|
765
|
-
if
|
766
|
-
logger.error message_with_context %(
|
900
|
+
if text
|
901
|
+
logger.error message_with_context %(malformed preprocessor directive - text not permitted: endif::#{target}[#{text}]), source_location: cursor
|
902
|
+
elsif @conditional_stack.empty?
|
903
|
+
logger.error message_with_context %(unmatched preprocessor directive: endif::#{target}[]), source_location: cursor
|
767
904
|
elsif no_target || target == (pair = @conditional_stack[-1])[:target]
|
768
905
|
@conditional_stack.pop
|
769
906
|
@skipping = @conditional_stack.empty? ? false : @conditional_stack[-1][:skipping]
|
770
907
|
else
|
771
|
-
logger.error message_with_context %(mismatched
|
908
|
+
logger.error message_with_context %(mismatched preprocessor directive: endif::#{target}[], expected endif::#{pair[:target]}[]), source_location: cursor
|
772
909
|
end
|
773
910
|
return true
|
774
|
-
|
775
|
-
|
776
|
-
if @skipping
|
911
|
+
elsif @skipping
|
777
912
|
skip = false
|
778
913
|
else
|
779
914
|
# QUESTION any way to wrap ifdef & ifndef logic up together?
|
780
915
|
case keyword
|
781
916
|
when 'ifdef'
|
917
|
+
if no_target
|
918
|
+
logger.error message_with_context %(malformed preprocessor directive - missing target: ifdef::[#{text}]), source_location: cursor
|
919
|
+
return true
|
920
|
+
end
|
782
921
|
case delimiter
|
783
922
|
when ','
|
784
923
|
# skip if no attribute is defined
|
@@ -791,6 +930,10 @@ class PreprocessorReader < Reader
|
|
791
930
|
skip = !@document.attributes.key?(target)
|
792
931
|
end
|
793
932
|
when 'ifndef'
|
933
|
+
if no_target
|
934
|
+
logger.error message_with_context %(malformed preprocessor directive - missing target: ifndef::[#{text}]), source_location: cursor
|
935
|
+
return true
|
936
|
+
end
|
794
937
|
case delimiter
|
795
938
|
when ','
|
796
939
|
# skip if any attribute is defined
|
@@ -803,20 +946,22 @@ class PreprocessorReader < Reader
|
|
803
946
|
skip = @document.attributes.key?(target)
|
804
947
|
end
|
805
948
|
when 'ifeval'
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
949
|
+
if no_target
|
950
|
+
# the text in brackets must match a conditional expression
|
951
|
+
if text && EvalExpressionRx =~ text.strip
|
952
|
+
# NOTE assignments must happen before call to resolve_expr_val for compatibility with Opal
|
953
|
+
lhs = $1
|
954
|
+
# regex enforces a restricted set of math-related operations (==, !=, <=, >=, <, >)
|
955
|
+
op = $2
|
956
|
+
rhs = $3
|
957
|
+
skip = ((resolve_expr_val lhs).send op, (resolve_expr_val rhs)) ? false : true rescue true
|
958
|
+
else
|
959
|
+
logger.error message_with_context %(malformed preprocessor directive - #{text ? 'invalid expression' : 'missing expression'}: ifeval::[#{text}]), source_location: cursor
|
960
|
+
return true
|
961
|
+
end
|
818
962
|
else
|
819
|
-
|
963
|
+
logger.error message_with_context %(malformed preprocessor directive - target not permitted: ifeval::#{target}[#{text}]), source_location: cursor
|
964
|
+
return true
|
820
965
|
end
|
821
966
|
end
|
822
967
|
end
|
@@ -824,7 +969,7 @@ class PreprocessorReader < Reader
|
|
824
969
|
# conditional inclusion block
|
825
970
|
if keyword == 'ifeval' || !text
|
826
971
|
@skipping = true if skip
|
827
|
-
@conditional_stack << {:
|
972
|
+
@conditional_stack << { target: target, skip: skip, skipping: @skipping }
|
828
973
|
# single line conditional inclusion
|
829
974
|
else
|
830
975
|
unless @skipping || skip
|
@@ -867,40 +1012,64 @@ class PreprocessorReader < Reader
|
|
867
1012
|
def preprocess_include_directive target, attrlist
|
868
1013
|
doc = @document
|
869
1014
|
if ((expanded_target = target).include? ATTR_REF_HEAD) &&
|
870
|
-
(expanded_target = doc.sub_attributes target, :attribute_missing
|
871
|
-
|
872
|
-
|
873
|
-
|
1015
|
+
(expanded_target = doc.sub_attributes target, attribute_missing: ((attr_missing = doc.attributes['attribute-missing'] || Compliance.attribute_missing) == 'warn' ? 'drop-line' : attr_missing)).empty?
|
1016
|
+
if attr_missing == 'drop-line' && (doc.sub_attributes target + ' ', attribute_missing: 'drop-line', drop_line_severity: :ignore).empty?
|
1017
|
+
logger.info { message_with_context %(include dropped due to missing attribute: include::#{target}[#{attrlist}]), source_location: cursor }
|
1018
|
+
shift
|
1019
|
+
true
|
1020
|
+
elsif (doc.parse_attributes attrlist, [], sub_input: true)['optional-option']
|
1021
|
+
logger.info { message_with_context %(optional include dropped #{attr_missing == 'warn' && (doc.sub_attributes target + ' ', attribute_missing: 'drop-line', drop_line_severity: :ignore).empty? ? 'due to missing attribute' : 'because resolved target is blank'}: include::#{target}[#{attrlist}]), source_location: cursor }
|
1022
|
+
shift
|
1023
|
+
true
|
1024
|
+
else
|
1025
|
+
logger.warn message_with_context %(include dropped #{attr_missing == 'warn' && (doc.sub_attributes target + ' ', attribute_missing: 'drop-line', drop_line_severity: :ignore).empty? ? 'due to missing attribute' : 'because resolved target is blank'}: include::#{target}[#{attrlist}]), source_location: cursor
|
1026
|
+
# QUESTION should this line include target or expanded_target (or escaped target?)
|
1027
|
+
replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{attrlist}])
|
874
1028
|
end
|
875
|
-
true
|
876
1029
|
elsif include_processors? && (ext = @include_processor_extensions.find {|candidate| candidate.instance.handles? expanded_target })
|
877
1030
|
shift
|
878
1031
|
# FIXME parse attributes only if requested by extension
|
879
|
-
ext.process_method[doc, self, expanded_target, (doc.parse_attributes attrlist, [], :
|
1032
|
+
ext.process_method[doc, self, expanded_target, (doc.parse_attributes attrlist, [], sub_input: true)]
|
880
1033
|
true
|
881
1034
|
# if running in SafeMode::SECURE or greater, don't process this directive
|
882
1035
|
# however, be friendly and at least make it a link to the source document
|
883
1036
|
elsif doc.safe >= SafeMode::SECURE
|
884
1037
|
# FIXME we don't want to use a link macro if we are in a verbatim context
|
885
1038
|
replace_next_line %(link:#{expanded_target}[])
|
886
|
-
elsif
|
887
|
-
if @include_stack.size >=
|
888
|
-
logger.error message_with_context %(maximum include depth of #{@maxdepth[:rel]} exceeded), :
|
1039
|
+
elsif @maxdepth
|
1040
|
+
if @include_stack.size >= @maxdepth[:curr]
|
1041
|
+
logger.error message_with_context %(maximum include depth of #{@maxdepth[:rel]} exceeded), source_location: cursor
|
889
1042
|
return
|
890
1043
|
end
|
891
1044
|
|
892
|
-
parsed_attrs = doc.parse_attributes attrlist, [], :
|
1045
|
+
parsed_attrs = doc.parse_attributes attrlist, [], sub_input: true
|
893
1046
|
inc_path, target_type, relpath = resolve_include_path expanded_target, attrlist, parsed_attrs
|
894
|
-
|
1047
|
+
case target_type
|
1048
|
+
when :file
|
1049
|
+
reader = ::File.method :open
|
1050
|
+
read_mode = FILE_READ_MODE
|
1051
|
+
when :uri
|
1052
|
+
reader = ::OpenURI.method :open_uri
|
1053
|
+
read_mode = URI_READ_MODE
|
1054
|
+
else
|
1055
|
+
# NOTE if target_type is not set, inc_path is a boolean to skip over (false) or reevaluate (true) the current line
|
1056
|
+
return inc_path
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
if (enc = parsed_attrs['encoding']) && (::Encoding.find enc rescue nil)
|
1060
|
+
(read_mode_params = read_mode.split ':')[1] = enc
|
1061
|
+
read_mode = read_mode_params.join ':'
|
1062
|
+
end unless RUBY_ENGINE_OPAL
|
895
1063
|
|
896
1064
|
inc_linenos = inc_tags = nil
|
1065
|
+
# NOTE attrlist is nil if missing from include directive
|
897
1066
|
if attrlist
|
898
1067
|
if parsed_attrs.key? 'lines'
|
899
1068
|
inc_linenos = []
|
900
1069
|
(split_delimited_value parsed_attrs['lines']).each do |linedef|
|
901
1070
|
if linedef.include? '..'
|
902
|
-
from, to = linedef.
|
903
|
-
inc_linenos += (to.empty? || (to = to.to_i) < 0) ? [from.to_i,
|
1071
|
+
from, _, to = linedef.partition '..'
|
1072
|
+
inc_linenos += (to.empty? || (to = to.to_i) < 0) ? [from.to_i, ::Float::INFINITY] : (from.to_i..to).to_a
|
904
1073
|
else
|
905
1074
|
inc_linenos << linedef.to_i
|
906
1075
|
end
|
@@ -926,7 +1095,7 @@ class PreprocessorReader < Reader
|
|
926
1095
|
if inc_linenos
|
927
1096
|
inc_lines, inc_offset, inc_lineno = [], nil, 0
|
928
1097
|
begin
|
929
|
-
|
1098
|
+
reader.call inc_path, read_mode do |f|
|
930
1099
|
select_remaining = nil
|
931
1100
|
f.each_line do |l|
|
932
1101
|
inc_lineno += 1
|
@@ -946,54 +1115,58 @@ class PreprocessorReader < Reader
|
|
946
1115
|
end
|
947
1116
|
end
|
948
1117
|
rescue
|
949
|
-
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), :
|
1118
|
+
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), source_location: cursor
|
950
1119
|
return replace_next_line %(Unresolved directive in #{@path} - include::#{expanded_target}[#{attrlist}])
|
951
1120
|
end
|
952
1121
|
shift
|
953
1122
|
# FIXME not accounting for skipped lines in reader line numbering
|
954
1123
|
if inc_offset
|
955
|
-
parsed_attrs['partial-option'] =
|
1124
|
+
parsed_attrs['partial-option'] = ''
|
956
1125
|
push_include inc_lines, inc_path, relpath, inc_offset, parsed_attrs
|
957
1126
|
end
|
958
1127
|
elsif inc_tags
|
959
|
-
inc_lines, inc_offset, inc_lineno, tag_stack,
|
1128
|
+
inc_lines, inc_offset, inc_lineno, tag_stack, tags_selected, active_tag = [], nil, 0, [], ::Set.new, nil
|
960
1129
|
if inc_tags.key? '**'
|
1130
|
+
select = base_select = inc_tags.delete '**'
|
961
1131
|
if inc_tags.key? '*'
|
962
|
-
select = base_select = (inc_tags.delete '**')
|
963
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 '*')
|
964
1139
|
else
|
965
|
-
select = base_select =
|
1140
|
+
select = base_select = false
|
1141
|
+
wildcard = inc_tags.delete '*'
|
966
1142
|
end
|
967
1143
|
else
|
968
1144
|
select = base_select = !(inc_tags.value? true)
|
969
|
-
wildcard = inc_tags.delete '*'
|
970
1145
|
end
|
971
1146
|
begin
|
972
|
-
|
1147
|
+
reader.call inc_path, read_mode do |f|
|
973
1148
|
dbl_co, dbl_sb = '::', '[]'
|
974
|
-
encoding = ::Encoding::UTF_8 if COERCE_ENCODING
|
975
1149
|
f.each_line do |l|
|
976
1150
|
inc_lineno += 1
|
977
|
-
# must force encoding since we're performing String operations on line
|
978
|
-
l.force_encoding encoding if encoding
|
979
1151
|
if (l.include? dbl_co) && (l.include? dbl_sb) && TagDirectiveRx =~ l
|
1152
|
+
this_tag = $2
|
980
1153
|
if $1 # end tag
|
981
|
-
if
|
1154
|
+
if this_tag == active_tag
|
982
1155
|
tag_stack.pop
|
983
1156
|
active_tag, select = tag_stack.empty? ? [nil, base_select] : tag_stack[-1]
|
984
1157
|
elsif inc_tags.key? this_tag
|
985
1158
|
include_cursor = create_include_cursor inc_path, expanded_target, inc_lineno
|
986
|
-
if (idx = tag_stack.rindex {|key
|
1159
|
+
if (idx = tag_stack.rindex {|key,| key == this_tag })
|
987
1160
|
idx == 0 ? tag_stack.shift : (tag_stack.delete_at idx)
|
988
|
-
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}), :
|
1161
|
+
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
|
989
1162
|
else
|
990
|
-
logger.warn message_with_context %(unexpected end tag '#{this_tag}' at line #{inc_lineno} of include #{target_type}: #{inc_path}), :
|
1163
|
+
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
|
991
1164
|
end
|
992
1165
|
end
|
993
|
-
elsif inc_tags.key?
|
994
|
-
|
1166
|
+
elsif inc_tags.key? this_tag
|
1167
|
+
tags_selected << this_tag if (select = inc_tags[this_tag])
|
995
1168
|
# QUESTION should we prevent tag from being selected when enclosing tag is excluded?
|
996
|
-
tag_stack << [(active_tag = this_tag),
|
1169
|
+
tag_stack << [(active_tag = this_tag), select, inc_lineno]
|
997
1170
|
elsif !wildcard.nil?
|
998
1171
|
select = active_tag && !select ? false : wildcard
|
999
1172
|
tag_stack << [(active_tag = this_tag), select, inc_lineno]
|
@@ -1006,31 +1179,31 @@ class PreprocessorReader < Reader
|
|
1006
1179
|
end
|
1007
1180
|
end
|
1008
1181
|
rescue
|
1009
|
-
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), :
|
1182
|
+
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), source_location: cursor
|
1010
1183
|
return replace_next_line %(Unresolved directive in #{@path} - include::#{expanded_target}[#{attrlist}])
|
1011
1184
|
end
|
1012
1185
|
unless tag_stack.empty?
|
1013
1186
|
tag_stack.each do |tag_name, _, tag_lineno|
|
1014
|
-
logger.warn message_with_context %(detected unclosed tag '#{tag_name}' starting at line #{tag_lineno} of include #{target_type}: #{inc_path}), :
|
1187
|
+
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)
|
1015
1188
|
end
|
1016
1189
|
end
|
1017
|
-
unless (missing_tags = inc_tags.keys
|
1018
|
-
logger.warn message_with_context %(tag#{missing_tags.size > 1 ? 's' : ''} '#{missing_tags.join ', '}' not found in include #{target_type}: #{inc_path}), :
|
1190
|
+
unless (missing_tags = inc_tags.keep_if {|_, v| v }.keys - tags_selected.to_a).empty?
|
1191
|
+
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
|
1019
1192
|
end
|
1020
1193
|
shift
|
1021
1194
|
if inc_offset
|
1022
|
-
parsed_attrs['partial-option'] =
|
1195
|
+
parsed_attrs['partial-option'] = '' unless base_select && wildcard != false && inc_tags.empty?
|
1023
1196
|
# FIXME not accounting for skipped lines in reader line numbering
|
1024
1197
|
push_include inc_lines, inc_path, relpath, inc_offset, parsed_attrs
|
1025
1198
|
end
|
1026
1199
|
else
|
1027
1200
|
begin
|
1028
|
-
# NOTE read content
|
1029
|
-
inc_content =
|
1201
|
+
# NOTE read content before shift so cursor is only advanced if IO operation succeeds
|
1202
|
+
inc_content = reader.call(inc_path, read_mode) {|f| f.read }
|
1030
1203
|
shift
|
1031
1204
|
push_include inc_content, inc_path, relpath, 1, parsed_attrs
|
1032
1205
|
rescue
|
1033
|
-
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), :
|
1206
|
+
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), source_location: cursor
|
1034
1207
|
return replace_next_line %(Unresolved directive in #{@path} - include::#{expanded_target}[#{attrlist}])
|
1035
1208
|
end
|
1036
1209
|
end
|
@@ -1064,20 +1237,21 @@ class PreprocessorReader < Reader
|
|
1064
1237
|
# caching requires the open-uri-cached gem to be installed
|
1065
1238
|
# processing will be automatically aborted if these libraries can't be opened
|
1066
1239
|
Helpers.require_library 'open-uri/cached', 'open-uri-cached' unless defined? ::OpenURI::Cache
|
1067
|
-
elsif
|
1240
|
+
elsif !RUBY_ENGINE_OPAL
|
1068
1241
|
# autoload open-uri
|
1069
1242
|
::OpenURI
|
1070
1243
|
end
|
1071
1244
|
[(::URI.parse target), :uri, target]
|
1072
1245
|
else
|
1073
1246
|
# include file is resolved relative to dir of current include, or base_dir if within original docfile
|
1074
|
-
inc_path = doc.normalize_system_path target, @dir, nil, :
|
1247
|
+
inc_path = doc.normalize_system_path target, @dir, nil, target_name: 'include file'
|
1075
1248
|
unless ::File.file? inc_path
|
1076
|
-
if attributes
|
1249
|
+
if attributes['optional-option']
|
1250
|
+
logger.info { message_with_context %(optional include dropped because include file not found: #{inc_path}), source_location: cursor }
|
1077
1251
|
shift
|
1078
1252
|
return true
|
1079
1253
|
else
|
1080
|
-
logger.error message_with_context %(include file not found: #{inc_path}), :
|
1254
|
+
logger.error message_with_context %(include file not found: #{inc_path}), source_location: cursor
|
1081
1255
|
return replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{attrlist}])
|
1082
1256
|
end
|
1083
1257
|
end
|
@@ -1088,97 +1262,8 @@ class PreprocessorReader < Reader
|
|
1088
1262
|
end
|
1089
1263
|
end
|
1090
1264
|
|
1091
|
-
# Public: Push source onto the front of the reader and switch the context
|
1092
|
-
# based on the file, document-relative path and line information given.
|
1093
|
-
#
|
1094
|
-
# This method is typically used in an IncludeProcessor to add source
|
1095
|
-
# read from the target specified.
|
1096
|
-
#
|
1097
|
-
# Examples
|
1098
|
-
#
|
1099
|
-
# path = 'partial.adoc'
|
1100
|
-
# file = File.expand_path path
|
1101
|
-
# data = IO.read file
|
1102
|
-
# reader.push_include data, file, path
|
1103
|
-
#
|
1104
|
-
# Returns this Reader object.
|
1105
|
-
def push_include data, file = nil, path = nil, lineno = 1, attributes = {}
|
1106
|
-
@include_stack << [@lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines]
|
1107
|
-
if (@file = file)
|
1108
|
-
# NOTE if file is not a string, assume it's a URI
|
1109
|
-
if ::String === file
|
1110
|
-
@dir = ::File.dirname file
|
1111
|
-
elsif ::RUBY_ENGINE_OPAL
|
1112
|
-
@dir = ::URI.parse ::File.dirname(file = file.to_s)
|
1113
|
-
else
|
1114
|
-
# NOTE this intentionally throws an error if URI has no path
|
1115
|
-
(@dir = file.dup).path = (dir = ::File.dirname file.path) == '/' ? '' : dir
|
1116
|
-
file = file.to_s
|
1117
|
-
end
|
1118
|
-
path ||= ::File.basename file
|
1119
|
-
# only process lines in AsciiDoc files
|
1120
|
-
@process_lines = ASCIIDOC_EXTENSIONS[::File.extname file]
|
1121
|
-
else
|
1122
|
-
@dir = '.'
|
1123
|
-
# we don't know what file type we have, so assume AsciiDoc
|
1124
|
-
@process_lines = true
|
1125
|
-
end
|
1126
|
-
|
1127
|
-
if path
|
1128
|
-
@path = path
|
1129
|
-
@includes[Helpers.rootname path] = attributes['partial-option'] ? nil : true if @process_lines
|
1130
|
-
else
|
1131
|
-
@path = '<stdin>'
|
1132
|
-
end
|
1133
|
-
|
1134
|
-
@lineno = lineno
|
1135
|
-
|
1136
|
-
if attributes.key? 'depth'
|
1137
|
-
depth = attributes['depth'].to_i
|
1138
|
-
depth = 1 if depth <= 0
|
1139
|
-
@maxdepth = {:abs => (@include_stack.size - 1) + depth, :rel => depth}
|
1140
|
-
end
|
1141
|
-
|
1142
|
-
# effectively fill the buffer
|
1143
|
-
if (@lines = prepare_lines data, :normalize => true, :condense => false, :indent => attributes['indent']).empty?
|
1144
|
-
pop_include
|
1145
|
-
else
|
1146
|
-
# FIXME we eventually want to handle leveloffset without affecting the lines
|
1147
|
-
if attributes.key? 'leveloffset'
|
1148
|
-
@lines.unshift ''
|
1149
|
-
@lines.unshift %(:leveloffset: #{attributes['leveloffset']})
|
1150
|
-
@lines << ''
|
1151
|
-
if (old_leveloffset = @document.attr 'leveloffset')
|
1152
|
-
@lines << %(:leveloffset: #{old_leveloffset})
|
1153
|
-
else
|
1154
|
-
@lines << ':leveloffset!:'
|
1155
|
-
end
|
1156
|
-
# compensate for these extra lines
|
1157
|
-
@lineno -= 2
|
1158
|
-
end
|
1159
|
-
|
1160
|
-
# FIXME kind of a hack
|
1161
|
-
#Document::AttributeEntry.new('infile', @file).save_to_next_block @document
|
1162
|
-
#Document::AttributeEntry.new('indir', @dir).save_to_next_block @document
|
1163
|
-
@look_ahead = 0
|
1164
|
-
end
|
1165
|
-
self
|
1166
|
-
end
|
1167
|
-
|
1168
|
-
def create_include_cursor file, path, lineno
|
1169
|
-
if ::String === file
|
1170
|
-
dir = ::File.dirname file
|
1171
|
-
elsif ::RUBY_ENGINE_OPAL
|
1172
|
-
dir = ::File.dirname(file = file.to_s)
|
1173
|
-
else
|
1174
|
-
dir = (dir = ::File.dirname file.path) == '' ? '/' : dir
|
1175
|
-
file = file.to_s
|
1176
|
-
end
|
1177
|
-
Cursor.new file, dir, path, lineno
|
1178
|
-
end
|
1179
|
-
|
1180
1265
|
def pop_include
|
1181
|
-
|
1266
|
+
unless @include_stack.empty?
|
1182
1267
|
@lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines = @include_stack.pop
|
1183
1268
|
# FIXME kind of a hack
|
1184
1269
|
#Document::AttributeEntry.new('infile', @file).save_to_next_block @document
|
@@ -1188,30 +1273,6 @@ class PreprocessorReader < Reader
|
|
1188
1273
|
end
|
1189
1274
|
end
|
1190
1275
|
|
1191
|
-
def include_depth
|
1192
|
-
@include_stack.size
|
1193
|
-
end
|
1194
|
-
|
1195
|
-
def exceeded_max_depth?
|
1196
|
-
if (abs_maxdepth = @maxdepth[:abs]) > 0 && @include_stack.size >= abs_maxdepth
|
1197
|
-
@maxdepth[:rel]
|
1198
|
-
else
|
1199
|
-
false
|
1200
|
-
end
|
1201
|
-
end
|
1202
|
-
|
1203
|
-
# TODO Document this override
|
1204
|
-
# also, we now have the field in the super class, so perhaps
|
1205
|
-
# just implement the logic there?
|
1206
|
-
def shift
|
1207
|
-
if @unescape_next_line
|
1208
|
-
@unescape_next_line = false
|
1209
|
-
(line = super).slice 1, line.length
|
1210
|
-
else
|
1211
|
-
super
|
1212
|
-
end
|
1213
|
-
end
|
1214
|
-
|
1215
1276
|
# Private: Split delimited value on comma (if found), otherwise semi-colon
|
1216
1277
|
def split_delimited_value val
|
1217
1278
|
(val.include? ',') ? (val.split ',') : (val.split ';')
|
@@ -1284,7 +1345,7 @@ class PreprocessorReader < Reader
|
|
1284
1345
|
|
1285
1346
|
# QUESTION should we substitute first?
|
1286
1347
|
# QUESTION should we also require string to be single quoted (like block attribute values?)
|
1287
|
-
val = @document.sub_attributes val, :
|
1348
|
+
val = @document.sub_attributes val, attribute_missing: 'drop' if val.include? ATTR_REF_HEAD
|
1288
1349
|
|
1289
1350
|
if quoted
|
1290
1351
|
val
|
@@ -1304,21 +1365,5 @@ class PreprocessorReader < Reader
|
|
1304
1365
|
val.to_i
|
1305
1366
|
end
|
1306
1367
|
end
|
1307
|
-
|
1308
|
-
def include_processors?
|
1309
|
-
if @include_processor_extensions.nil?
|
1310
|
-
if @document.extensions? && @document.extensions.include_processors?
|
1311
|
-
!!(@include_processor_extensions = @document.extensions.include_processors)
|
1312
|
-
else
|
1313
|
-
@include_processor_extensions = false
|
1314
|
-
end
|
1315
|
-
else
|
1316
|
-
@include_processor_extensions != false
|
1317
|
-
end
|
1318
|
-
end
|
1319
|
-
|
1320
|
-
def to_s
|
1321
|
-
%(#<#{self.class}@#{object_id} {path: #{@path.inspect}, line #: #{@lineno}, include depth: #{@include_stack.size}, include stack: [#{@include_stack.map {|inc| inc.to_s }.join ', '}]}>)
|
1322
|
-
end
|
1323
1368
|
end
|
1324
1369
|
end
|