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/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
|