asciidoctor 1.5.8 → 2.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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/helpers.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
|
+
# Internal: Except where noted, a module that contains internal helper functions.
|
3
4
|
module Helpers
|
4
|
-
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Public: Require the specified library using Kernel#require.
|
5
8
|
#
|
6
9
|
# Attempts to load the library specified in the first argument using the
|
7
10
|
# Kernel#require. Rescues the LoadError if the library is not available and
|
8
11
|
# passes a message to Kernel#raise if on_failure is :abort or Kernel#warn if
|
9
12
|
# on_failure is :warn to communicate to the user that processing is being
|
10
13
|
# aborted or functionality is disabled, respectively. If a gem_name is
|
11
|
-
# specified, the message communicates that a required gem is not
|
14
|
+
# specified, the message communicates that a required gem is not available.
|
12
15
|
#
|
13
16
|
# name - the String name of the library to require.
|
14
17
|
# gem_name - a Boolean that indicates whether this library is provided by a RubyGem,
|
@@ -20,108 +23,96 @@ module Helpers
|
|
20
23
|
# Otherwise, if on_failure is :abort, Kernel#raise is called with an appropriate message.
|
21
24
|
# Otherwise, if on_failure is :warn, Kernel#warn is called with an appropriate message and nil returned.
|
22
25
|
# Otherwise, nil is returned.
|
23
|
-
def
|
26
|
+
def require_library name, gem_name = true, on_failure = :abort
|
24
27
|
require name
|
25
|
-
rescue ::LoadError
|
28
|
+
rescue ::LoadError
|
26
29
|
include Logging unless include? Logging
|
27
30
|
if gem_name
|
28
31
|
gem_name = name if gem_name == true
|
29
32
|
case on_failure
|
30
33
|
when :abort
|
31
|
-
|
34
|
+
details = $!.path == gem_name ? '' : %[ (reason: #{$!.path ? %(cannot load '#{$!.path}') : $!.message})]
|
35
|
+
raise ::LoadError, %(asciidoctor: FAILED: required gem '#{gem_name}' is not available#{details}. Processing aborted.)
|
32
36
|
when :warn
|
33
|
-
|
37
|
+
details = $!.path == gem_name ? '' : %[ (reason: #{$!.path ? %(cannot load '#{$!.path}') : $!.message})]
|
38
|
+
logger.warn %(optional gem '#{gem_name}' is not available#{details}. Functionality disabled.)
|
34
39
|
end
|
35
40
|
else
|
36
41
|
case on_failure
|
37
42
|
when :abort
|
38
|
-
raise ::LoadError, %(asciidoctor: FAILED: #{
|
43
|
+
raise ::LoadError, %(asciidoctor: FAILED: #{$!.message.chomp '.'}. Processing aborted.)
|
39
44
|
when :warn
|
40
|
-
logger.warn %(#{
|
45
|
+
logger.warn %(#{$!.message.chomp '.'}. Functionality disabled.)
|
41
46
|
end
|
42
47
|
end
|
43
48
|
nil
|
44
49
|
end
|
45
50
|
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
# HACK Ruby messes up trailing whitespace on UTF-16LE, so reencode whole document first
|
74
|
-
data = data.join
|
75
|
-
return (((data.force_encoding ::Encoding::UTF_16LE).slice 1, data.length).encode utf8).each_line.map {|line| line.rstrip }
|
76
|
-
elsif leading_2_bytes == BOM_BYTES_UTF_16BE
|
77
|
-
data[0] = (first_line.force_encoding ::Encoding::UTF_16BE).slice 1, first_line.length
|
78
|
-
return data.map {|line| ((line.force_encoding ::Encoding::UTF_16BE).encode utf8).rstrip }
|
79
|
-
elsif leading_bytes == BOM_BYTES_UTF_8
|
80
|
-
data[0] = (first_line.force_encoding utf8).slice 1, first_line.length
|
81
|
-
end
|
82
|
-
|
83
|
-
data.map {|line| line.encoding == utf8 ? line.rstrip : (line.force_encoding utf8).rstrip }
|
51
|
+
# Internal: Prepare the source data Array for parsing.
|
52
|
+
#
|
53
|
+
# Encodes the data to UTF-8, if necessary, and removes any trailing
|
54
|
+
# whitespace from every line.
|
55
|
+
#
|
56
|
+
# If a BOM is found at the beginning of the data, a best attempt is made to
|
57
|
+
# encode it to UTF-8 from the specified source encoding.
|
58
|
+
#
|
59
|
+
# data - the source data Array to prepare (no nil entries allowed)
|
60
|
+
# trim_end - whether to trim whitespace from the end of each line;
|
61
|
+
# (true cleans all whitespace; false only removes trailing newline) (default: true)
|
62
|
+
#
|
63
|
+
# returns a String Array of prepared lines
|
64
|
+
def prepare_source_array data, trim_end = true
|
65
|
+
return [] if data.empty?
|
66
|
+
if (leading_2_bytes = (leading_bytes = (first = data[0]).unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
|
67
|
+
data[0] = first.byteslice 2, first.bytesize
|
68
|
+
# NOTE you can't split a UTF-16LE string using .lines when encoding is UTF-8; doing so will cause this line to fail
|
69
|
+
return trim_end ? data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).rstrip } : data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).chomp }
|
70
|
+
elsif leading_2_bytes == BOM_BYTES_UTF_16BE
|
71
|
+
data[0] = first.byteslice 2, first.bytesize
|
72
|
+
return trim_end ? data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).rstrip } : data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).chomp }
|
73
|
+
elsif leading_bytes == BOM_BYTES_UTF_8
|
74
|
+
data[0] = first.byteslice 3, first.bytesize
|
75
|
+
end
|
76
|
+
if first.encoding == UTF_8
|
77
|
+
trim_end ? data.map {|line| line.rstrip } : data.map {|line| line.chomp }
|
84
78
|
else
|
85
|
-
|
86
|
-
data[0] = first_line.slice 3, first_line.length if leading_bytes == BOM_BYTES_UTF_8
|
87
|
-
data.map {|line| line.rstrip }
|
79
|
+
trim_end ? data.map {|line| (line.encode UTF_8).rstrip } : data.map {|line| (line.encode UTF_8).chomp }
|
88
80
|
end
|
89
81
|
end
|
90
82
|
|
91
|
-
#
|
83
|
+
# Internal: Prepare the source data String for parsing.
|
92
84
|
#
|
93
|
-
#
|
94
|
-
#
|
85
|
+
# Encodes the data to UTF-8, if necessary, splits it into an array, and
|
86
|
+
# removes any trailing whitespace from every line.
|
95
87
|
#
|
96
|
-
# If a BOM is
|
97
|
-
#
|
88
|
+
# If a BOM is found at the beginning of the data, a best attempt is made to
|
89
|
+
# encode it to UTF-8 from the specified source encoding.
|
98
90
|
#
|
99
|
-
# data
|
91
|
+
# data - the source data String to prepare
|
92
|
+
# trim_end - whether to trim whitespace from the end of each line;
|
93
|
+
# (true cleans all whitespace; false only removes trailing newline) (default: true)
|
100
94
|
#
|
101
|
-
# returns a String Array of
|
102
|
-
def
|
95
|
+
# returns a String Array of prepared lines
|
96
|
+
def prepare_source_string data, trim_end = true
|
103
97
|
return [] if data.nil_or_empty?
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
98
|
+
if (leading_2_bytes = (leading_bytes = data.unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
|
99
|
+
data = (data.byteslice 2, data.bytesize).encode UTF_8, ::Encoding::UTF_16LE
|
100
|
+
elsif leading_2_bytes == BOM_BYTES_UTF_16BE
|
101
|
+
data = (data.byteslice 2, data.bytesize).encode UTF_8, ::Encoding::UTF_16BE
|
102
|
+
elsif leading_bytes == BOM_BYTES_UTF_8
|
103
|
+
data = data.byteslice 3, data.bytesize
|
104
|
+
data = data.encode UTF_8 unless data.encoding == UTF_8
|
105
|
+
elsif data.encoding != UTF_8
|
106
|
+
data = data.encode UTF_8
|
107
|
+
end
|
108
|
+
if trim_end
|
109
|
+
[].tap {|lines| data.each_line {|line| lines << line.rstrip } }
|
117
110
|
else
|
118
|
-
|
119
|
-
data = data.slice 3, data.length if leading_bytes == BOM_BYTES_UTF_8
|
111
|
+
[].tap {|lines| data.each_line {|line| lines << line.chomp } }
|
120
112
|
end
|
121
|
-
data.each_line.map {|line| line.rstrip }
|
122
113
|
end
|
123
114
|
|
124
|
-
#
|
115
|
+
# Internal: Efficiently checks whether the specified String resembles a URI
|
125
116
|
#
|
126
117
|
# Uses the Asciidoctor::UriSniffRx regex to check whether the String begins
|
127
118
|
# with a URI prefix (e.g., http://). No validation of the URI is performed.
|
@@ -129,46 +120,57 @@ module Helpers
|
|
129
120
|
# str - the String to check
|
130
121
|
#
|
131
122
|
# returns true if the String is a URI, false if it is not
|
132
|
-
def
|
123
|
+
def uriish? str
|
133
124
|
(str.include? ':') && (UriSniffRx.match? str)
|
134
125
|
end
|
135
126
|
|
136
|
-
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
127
|
+
# Internal: Encode a URI component String for safe inclusion in a URI.
|
128
|
+
#
|
129
|
+
# str - the URI component String to encode
|
130
|
+
#
|
131
|
+
# Returns the String with all reserved URI characters encoded (e.g., /, &, =, space, etc).
|
132
|
+
if RUBY_ENGINE == 'opal'
|
133
|
+
def encode_uri_component str
|
134
|
+
# patch necessary to adhere with RFC-3986 (and thus CGI.escape)
|
135
|
+
# see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Description
|
136
|
+
%x(
|
137
|
+
return encodeURIComponent(str).replace(/%20|[!'()*]/g, function (m) {
|
138
|
+
return m === '%20' ? '+' : '%' + m.charCodeAt(0).toString(16)
|
139
|
+
})
|
140
|
+
)
|
141
|
+
end
|
142
|
+
else
|
143
|
+
CGI = ::CGI
|
144
|
+
def encode_uri_component str
|
145
|
+
CGI.escape str
|
146
|
+
end
|
146
147
|
end
|
147
148
|
|
148
|
-
#
|
149
|
-
REGEXP_ENCODE_URI_CHARS = /[^\w\-.!~*';:@=+$,()\[\]]/
|
150
|
-
|
151
|
-
# Public: Encode a String for inclusion in a URI.
|
149
|
+
# Internal: Apply URI path encoding to spaces in the specified string (i.e., convert spaces to %20).
|
152
150
|
#
|
153
|
-
# str - the String to
|
151
|
+
# str - the String to encode
|
154
152
|
#
|
155
|
-
# Returns the String with all
|
156
|
-
def
|
157
|
-
str.
|
153
|
+
# Returns the specified String with all spaces replaced with %20.
|
154
|
+
def encode_spaces_in_uri str
|
155
|
+
(str.include? ' ') ? (str.gsub ' ', '%20') : str
|
158
156
|
end
|
159
157
|
|
160
158
|
# Public: Removes the file extension from filename and returns the result
|
161
159
|
#
|
162
|
-
# filename - The String file name to process
|
160
|
+
# filename - The String file name to process; expected to be a posix path
|
163
161
|
#
|
164
162
|
# Examples
|
165
163
|
#
|
166
|
-
# Helpers.rootname
|
164
|
+
# Helpers.rootname 'part1/chapter1.adoc'
|
167
165
|
# # => "part1/chapter1"
|
168
166
|
#
|
169
167
|
# Returns the String filename with the file extension removed
|
170
|
-
def
|
171
|
-
|
168
|
+
def rootname filename
|
169
|
+
if (last_dot_idx = filename.rindex '.')
|
170
|
+
(filename.index '/', last_dot_idx) ? filename : (filename.slice 0, last_dot_idx)
|
171
|
+
else
|
172
|
+
filename
|
173
|
+
end
|
172
174
|
end
|
173
175
|
|
174
176
|
# Public: Retrieves the basename of the filename, optionally removing the extension, if present
|
@@ -179,22 +181,59 @@ module Helpers
|
|
179
181
|
#
|
180
182
|
# Examples
|
181
183
|
#
|
182
|
-
# Helpers.basename
|
184
|
+
# Helpers.basename 'images/tiger.png', true
|
183
185
|
# # => "tiger"
|
184
186
|
#
|
185
|
-
# Helpers.basename
|
187
|
+
# Helpers.basename 'images/tiger.png', '.png'
|
186
188
|
# # => "tiger"
|
187
189
|
#
|
188
190
|
# Returns the String filename with leading directories removed and, if specified, the extension removed
|
189
|
-
def
|
191
|
+
def basename filename, drop_ext = nil
|
190
192
|
if drop_ext
|
191
|
-
::File.basename filename, (drop_ext == true ? (
|
193
|
+
::File.basename filename, (drop_ext == true ? (extname filename) : drop_ext)
|
192
194
|
else
|
193
195
|
::File.basename filename
|
194
196
|
end
|
195
197
|
end
|
196
198
|
|
197
|
-
|
199
|
+
# Public: Returns whether this path has a file extension.
|
200
|
+
#
|
201
|
+
# path - The path String to check; expects a posix path
|
202
|
+
#
|
203
|
+
# Returns true if the path has a file extension, false otherwise
|
204
|
+
def extname? path
|
205
|
+
(last_dot_idx = path.rindex '.') && !(path.index '/', last_dot_idx)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Public: Retrieves the file extension of the specified path. The file extension is the portion of the path in the
|
209
|
+
# last path segment starting from the last period.
|
210
|
+
#
|
211
|
+
# This method differs from File.extname in that it gives us control over the fallback value and is more efficient.
|
212
|
+
#
|
213
|
+
# path - The path String in which to look for a file extension
|
214
|
+
# fallback - The fallback String to return if no file extension is present (optional, default: '')
|
215
|
+
#
|
216
|
+
# Returns the String file extension (with the leading dot included) or the fallback value if the path has no file extension.
|
217
|
+
if ::File::ALT_SEPARATOR
|
218
|
+
def extname path, fallback = ''
|
219
|
+
if (last_dot_idx = path.rindex '.')
|
220
|
+
(path.index '/', last_dot_idx) || (path.index ::File::ALT_SEPARATOR, last_dot_idx) ? fallback : (path.slice last_dot_idx, path.length)
|
221
|
+
else
|
222
|
+
fallback
|
223
|
+
end
|
224
|
+
end
|
225
|
+
else
|
226
|
+
def extname path, fallback = ''
|
227
|
+
if (last_dot_idx = path.rindex '.')
|
228
|
+
(path.index '/', last_dot_idx) ? fallback : (path.slice last_dot_idx, path.length)
|
229
|
+
else
|
230
|
+
fallback
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Internal: Make a directory, ensuring all parent directories exist.
|
236
|
+
def mkdir_p dir
|
198
237
|
unless ::File.directory? dir
|
199
238
|
unless (parent_dir = ::File.dirname dir) == '.'
|
200
239
|
mkdir_p parent_dir
|
@@ -211,17 +250,55 @@ module Helpers
|
|
211
250
|
'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, 'XC' => 90,
|
212
251
|
'L' => 50, 'XL' => 40, 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1
|
213
252
|
}
|
253
|
+
private_constant :ROMAN_NUMERALS
|
214
254
|
|
215
|
-
# Converts an integer to a Roman numeral.
|
255
|
+
# Internal: Converts an integer to a Roman numeral.
|
216
256
|
#
|
217
257
|
# val - the [Integer] value to convert
|
218
258
|
#
|
219
259
|
# Returns the [String] roman numeral for this integer
|
220
|
-
def
|
221
|
-
ROMAN_NUMERALS.map
|
260
|
+
def int_to_roman val
|
261
|
+
ROMAN_NUMERALS.map do |l, i|
|
222
262
|
repeat, val = val.divmod i
|
223
263
|
l * repeat
|
224
|
-
|
264
|
+
end.join
|
265
|
+
end
|
266
|
+
|
267
|
+
# Internal: Get the next value in the sequence.
|
268
|
+
#
|
269
|
+
# Handles both integer and character sequences.
|
270
|
+
#
|
271
|
+
# current - the value to increment as a String or Integer
|
272
|
+
#
|
273
|
+
# returns the next value in the sequence according to the current value's type
|
274
|
+
def nextval current
|
275
|
+
if ::Integer === current
|
276
|
+
current + 1
|
277
|
+
elsif (intval = current.to_i).to_s == current.to_s
|
278
|
+
intval + 1
|
279
|
+
else
|
280
|
+
current.succ
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
# Internal: Resolve the specified object as a Class
|
285
|
+
#
|
286
|
+
# object - The Object to resolve as a Class
|
287
|
+
#
|
288
|
+
# Returns a Class if the specified object is a Class (but not a Module) or
|
289
|
+
# a String that resolves to a Class; otherwise, nil
|
290
|
+
def resolve_class object
|
291
|
+
::Class === object ? object : (::String === object ? (class_for_name object) : nil)
|
292
|
+
end
|
293
|
+
|
294
|
+
# Internal: Resolves a Class object (not a Module) for the qualified name.
|
295
|
+
#
|
296
|
+
# Returns Class
|
297
|
+
def class_for_name qualified_name
|
298
|
+
raise unless ::Class === (resolved = ::Object.const_get qualified_name, false)
|
299
|
+
resolved
|
300
|
+
rescue
|
301
|
+
raise ::NameError, %(Could not resolve class for name: #{qualified_name})
|
225
302
|
end
|
226
303
|
end
|
227
304
|
end
|
data/lib/asciidoctor/inline.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
3
|
# Public: Methods for managing inline elements in AsciiDoc block
|
4
4
|
class Inline < AbstractNode
|
5
5
|
# Public: Get the text of this inline element
|
6
|
-
|
6
|
+
attr_accessor :text
|
7
7
|
|
8
8
|
# Public: Get the type (qualifier) of this inline element
|
9
9
|
attr_reader :type
|
@@ -12,19 +12,12 @@ class Inline < AbstractNode
|
|
12
12
|
attr_accessor :target
|
13
13
|
|
14
14
|
def initialize(parent, context, text = nil, opts = {})
|
15
|
-
super(parent, context)
|
15
|
+
super(parent, context, opts)
|
16
16
|
@node_name = %(inline_#{context})
|
17
|
-
|
18
17
|
@text = text
|
19
|
-
|
20
18
|
@id = opts[:id]
|
21
19
|
@type = opts[:type]
|
22
20
|
@target = opts[:target]
|
23
|
-
|
24
|
-
# value of attributes option for inline nodes may be nil
|
25
|
-
if (attrs = opts[:attributes])
|
26
|
-
@attributes = attrs.dup
|
27
|
-
end
|
28
21
|
end
|
29
22
|
|
30
23
|
def block?
|
@@ -39,21 +32,25 @@ class Inline < AbstractNode
|
|
39
32
|
converter.convert self
|
40
33
|
end
|
41
34
|
|
42
|
-
#
|
35
|
+
# Deprecated: Use {Inline#convert} instead.
|
43
36
|
alias render convert
|
44
37
|
|
45
38
|
# Public: Returns the converted alt text for this inline image.
|
46
39
|
#
|
47
40
|
# Returns the [String] value of the alt attribute.
|
48
41
|
def alt
|
49
|
-
attr 'alt'
|
42
|
+
(attr 'alt') || ''
|
50
43
|
end
|
51
44
|
|
45
|
+
# For a reference node (:ref or :bibref), the text is the reftext (and the reftext attribute is not set).
|
46
|
+
#
|
52
47
|
# (see AbstractNode#reftext?)
|
53
48
|
def reftext?
|
54
49
|
@text && (@type == :ref || @type == :bibref)
|
55
50
|
end
|
56
51
|
|
52
|
+
# For a reference node (:ref or :bibref), the text is the reftext (and the reftext attribute is not set).
|
53
|
+
#
|
57
54
|
# (see AbstractNode#reftext)
|
58
55
|
def reftext
|
59
56
|
(val = @text) ? (apply_reftext_subs val) : nil
|
data/lib/asciidoctor/list.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
3
|
# Public: Methods for managing AsciiDoc lists (ordered, unordered and description lists)
|
4
4
|
class List < AbstractBlock
|
@@ -31,7 +31,7 @@ class List < AbstractBlock
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
#
|
34
|
+
# Deprecated: Use {List#convert} instead.
|
35
35
|
alias render convert
|
36
36
|
|
37
37
|
def to_s
|
@@ -41,6 +41,9 @@ class List < AbstractBlock
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Public: Methods for managing items for AsciiDoc olists, ulist, and dlists.
|
44
|
+
#
|
45
|
+
# In a description list (dlist), each item is a tuple that consists of a 2-item Array of ListItem terms and a ListItem
|
46
|
+
# description (i.e., [[term, term, ...], desc]. If a description is not set, then the second entry in the tuple is nil.
|
44
47
|
class ListItem < AbstractBlock
|
45
48
|
|
46
49
|
# A contextual alias for the list parent node; counterpart to the items alias on List
|
@@ -63,7 +66,7 @@ class ListItem < AbstractBlock
|
|
63
66
|
# Public: A convenience method that checks whether the text of this list item
|
64
67
|
# is not blank (i.e., not nil or empty string).
|
65
68
|
def text?
|
66
|
-
|
69
|
+
@text.nil_or_empty? ? false : true
|
67
70
|
end
|
68
71
|
|
69
72
|
# Public: Get the String text of this ListItem with substitutions applied.
|
@@ -77,12 +80,8 @@ class ListItem < AbstractBlock
|
|
77
80
|
@text && (apply_subs @text, @subs)
|
78
81
|
end
|
79
82
|
|
80
|
-
# Public: Set the String text
|
81
|
-
|
82
|
-
# Returns the new String text assigned to this ListItem
|
83
|
-
def text= val
|
84
|
-
@text = val
|
85
|
-
end
|
83
|
+
# Public: Set the String text assigned to this ListItem
|
84
|
+
attr_writer :text
|
86
85
|
|
87
86
|
# Check whether this list item has simple content (no nested blocks aside from a single outline list).
|
88
87
|
# Primarily relevant for outline lists.
|
@@ -100,33 +99,16 @@ class ListItem < AbstractBlock
|
|
100
99
|
!simple?
|
101
100
|
end
|
102
101
|
|
103
|
-
#
|
104
|
-
#
|
105
|
-
# Here are the rules for when a folding occurs:
|
106
|
-
#
|
107
|
-
# Given: this list item has at least one block
|
108
|
-
# When: the first block is a paragraph that's not connected by a list continuation
|
109
|
-
# Or: the first block is an indented paragraph that's adjacent (wrapped line)
|
110
|
-
# Or: the first block is an indented paragraph that's not connected by a list continuation
|
111
|
-
# Then: then drop the first block and fold it's content (buffer) into the list text
|
102
|
+
# Internal: Fold the adjacent paragraph block into the list item text
|
112
103
|
#
|
113
104
|
# Returns nothing
|
114
|
-
def fold_first
|
115
|
-
|
116
|
-
((first_block.context == :paragraph && !continuation_connects_first_block) ||
|
117
|
-
((content_adjacent || !continuation_connects_first_block) && first_block.context == :literal &&
|
118
|
-
first_block.option?('listparagraph')))
|
119
|
-
|
120
|
-
block = blocks.shift
|
121
|
-
block.lines.unshift @text unless @text.nil_or_empty?
|
122
|
-
@text = block.source
|
123
|
-
end
|
105
|
+
def fold_first
|
106
|
+
@text = @text.nil_or_empty? ? @blocks.shift.source : %(#{@text}#{LF}#{@blocks.shift.source})
|
124
107
|
nil
|
125
108
|
end
|
126
109
|
|
127
110
|
def to_s
|
128
111
|
%(#<#{self.class}@#{object_id} {list_context: #{parent.context.inspect}, text: #{@text.inspect}, blocks: #{(@blocks || []).size}}>)
|
129
112
|
end
|
130
|
-
|
131
113
|
end
|
132
114
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Asciidoctor
|
3
|
+
class << self
|
4
|
+
# Public: Parse the AsciiDoc source input into a {Document}
|
5
|
+
#
|
6
|
+
# Accepts input as an IO (or StringIO), String or String Array object. If the
|
7
|
+
# input is a File, the object is expected to be opened for reading and is not
|
8
|
+
# closed afterwards by this method. Information about the file (filename,
|
9
|
+
# directory name, etc) gets assigned to attributes on the Document object.
|
10
|
+
#
|
11
|
+
# input - the AsciiDoc source as a IO, String or Array.
|
12
|
+
# options - a String, Array or Hash of options to control processing (default: {})
|
13
|
+
# String and Array values are converted into a Hash.
|
14
|
+
# See {Document#initialize} for details about these options.
|
15
|
+
#
|
16
|
+
# Returns the Document
|
17
|
+
def load input, options = {}
|
18
|
+
options = options.merge
|
19
|
+
|
20
|
+
if (timings = options[:timings])
|
21
|
+
timings.start :read
|
22
|
+
end
|
23
|
+
|
24
|
+
if (options.key? :logger) && (logger = options[:logger]) != LoggerManager.logger
|
25
|
+
LoggerManager.logger = logger || NullLogger.new
|
26
|
+
end
|
27
|
+
|
28
|
+
if !(attrs = options[:attributes])
|
29
|
+
attrs = {}
|
30
|
+
elsif ::Hash === attrs
|
31
|
+
attrs = attrs.merge
|
32
|
+
elsif (defined? ::Java::JavaUtil::Map) && ::Java::JavaUtil::Map === attrs
|
33
|
+
attrs = attrs.dup
|
34
|
+
elsif ::Array === attrs
|
35
|
+
attrs = {}.tap do |accum|
|
36
|
+
attrs.each do |entry|
|
37
|
+
k, _, v = entry.partition '='
|
38
|
+
accum[k] = v
|
39
|
+
end
|
40
|
+
end
|
41
|
+
elsif ::String === attrs
|
42
|
+
# condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null
|
43
|
+
attrs = {}.tap do |accum|
|
44
|
+
attrs.gsub(SpaceDelimiterRx, '\1' + NULL).gsub(EscapedSpaceRx, '\1').split(NULL).each do |entry|
|
45
|
+
k, _, v = entry.partition '='
|
46
|
+
accum[k] = v
|
47
|
+
end
|
48
|
+
end
|
49
|
+
elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
|
50
|
+
# coerce attrs to a real Hash
|
51
|
+
attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } }
|
52
|
+
else
|
53
|
+
raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '})
|
54
|
+
end
|
55
|
+
|
56
|
+
if ::File === input
|
57
|
+
# File#mtime on JRuby 9.1 for Windows doesn't honor TZ environment variable; see https://github.com/jruby/jruby/issues/6659
|
58
|
+
options[:input_mtime] = RUBY_ENGINE == 'jruby' ? (::Time.at input.mtime.to_i) : input.mtime
|
59
|
+
# NOTE defer setting infile and indir until we get a better sense of their purpose
|
60
|
+
# TODO cli checks if input path can be read and is file, but might want to add check to API too
|
61
|
+
attrs['docfile'] = input_path = ::File.absolute_path input.path
|
62
|
+
attrs['docdir'] = ::File.dirname input_path
|
63
|
+
attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = Helpers.extname input_path)
|
64
|
+
source = input.read
|
65
|
+
elsif input.respond_to? :read
|
66
|
+
# NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either
|
67
|
+
# just fail the rewind operation silently to handle all cases
|
68
|
+
input.rewind rescue nil
|
69
|
+
source = input.read
|
70
|
+
elsif ::String === input
|
71
|
+
source = input
|
72
|
+
elsif ::Array === input
|
73
|
+
source = input.drop 0
|
74
|
+
elsif input
|
75
|
+
raise ::ArgumentError, %(unsupported input type: #{input.class})
|
76
|
+
end
|
77
|
+
|
78
|
+
if timings
|
79
|
+
timings.record :read
|
80
|
+
timings.start :parse
|
81
|
+
end
|
82
|
+
|
83
|
+
options[:attributes] = attrs
|
84
|
+
doc = options[:parse] == false ? (Document.new source, options) : (Document.new source, options).parse
|
85
|
+
|
86
|
+
timings.record :parse if timings
|
87
|
+
doc
|
88
|
+
rescue => e
|
89
|
+
begin
|
90
|
+
context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
|
91
|
+
if e.respond_to? :exception
|
92
|
+
# The original message must be explicitly preserved when wrapping a Ruby exception
|
93
|
+
wrapped_e = e.exception %(#{context} - #{e.message})
|
94
|
+
# JRuby automatically sets backtrace; MRI did not until 2.6
|
95
|
+
wrapped_e.set_backtrace e.backtrace
|
96
|
+
else
|
97
|
+
# Likely a Java exception class
|
98
|
+
wrapped_e = e.class.new context, e
|
99
|
+
wrapped_e.stack_trace = e.stack_trace
|
100
|
+
end
|
101
|
+
rescue
|
102
|
+
wrapped_e = e
|
103
|
+
end
|
104
|
+
raise wrapped_e
|
105
|
+
end
|
106
|
+
|
107
|
+
# Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
|
108
|
+
#
|
109
|
+
# input - the String AsciiDoc source filename
|
110
|
+
# options - a String, Array or Hash of options to control processing (default: {})
|
111
|
+
# String and Array values are converted into a Hash.
|
112
|
+
# See Asciidoctor::Document#initialize for details about options.
|
113
|
+
#
|
114
|
+
# Returns the Asciidoctor::Document
|
115
|
+
def load_file filename, options = {}
|
116
|
+
::File.open(filename, FILE_READ_MODE) {|file| load file, options }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|