asciidoctor 2.0.7 → 2.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +169 -7
- data/LICENSE +2 -1
- data/README-de.adoc +5 -15
- data/README-fr.adoc +4 -14
- data/README-jp.adoc +234 -186
- data/README-zh_CN.adoc +7 -17
- data/README.adoc +18 -18
- data/asciidoctor.gemspec +4 -4
- data/data/locale/attributes-ar.adoc +4 -3
- 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 +4 -4
- 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 +6 -5
- data/data/locale/attributes-hu.adoc +4 -3
- data/data/locale/attributes-id.adoc +4 -3
- data/data/locale/attributes-it.adoc +4 -3
- 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 +4 -3
- 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-tr.adoc +4 -3
- data/data/locale/attributes-uk.adoc +6 -5
- data/data/locale/attributes-zh_CN.adoc +4 -3
- data/data/locale/attributes-zh_TW.adoc +4 -3
- data/data/stylesheets/asciidoctor-default.css +33 -30
- data/lib/asciidoctor.rb +89 -791
- data/lib/asciidoctor/abstract_block.rb +19 -11
- data/lib/asciidoctor/abstract_node.rb +21 -15
- data/lib/asciidoctor/attribute_list.rb +59 -67
- data/lib/asciidoctor/cli/invoker.rb +2 -0
- data/lib/asciidoctor/cli/options.rb +3 -3
- data/lib/asciidoctor/convert.rb +167 -162
- data/lib/asciidoctor/converter.rb +14 -13
- data/lib/asciidoctor/converter/docbook5.rb +10 -26
- data/lib/asciidoctor/converter/html5.rb +62 -43
- data/lib/asciidoctor/converter/manpage.rb +13 -12
- data/lib/asciidoctor/converter/template.rb +6 -3
- data/lib/asciidoctor/document.rb +25 -41
- data/lib/asciidoctor/extensions.rb +3 -3
- data/lib/asciidoctor/helpers.rb +38 -39
- data/lib/asciidoctor/inline.rb +1 -1
- data/lib/asciidoctor/load.rb +101 -101
- data/lib/asciidoctor/parser.rb +30 -25
- data/lib/asciidoctor/path_resolver.rb +35 -25
- data/lib/asciidoctor/reader.rb +14 -7
- data/lib/asciidoctor/rx.rb +722 -0
- data/lib/asciidoctor/substitutors.rb +61 -39
- data/lib/asciidoctor/syntax_highlighter.rb +22 -8
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +1 -1
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +12 -4
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +2 -3
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +15 -7
- data/lib/asciidoctor/table.rb +52 -23
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +6 -6
- data/man/asciidoctor.adoc +4 -3
- metadata +10 -9
@@ -34,8 +34,8 @@ class Converter::TemplateConverter < Converter::Base
|
|
34
34
|
}
|
35
35
|
|
36
36
|
begin
|
37
|
-
require 'concurrent/
|
38
|
-
@caches = { scans: ::Concurrent::
|
37
|
+
require 'concurrent/map' unless defined? ::Concurrent::Map
|
38
|
+
@caches = { scans: ::Concurrent::Map.new, templates: ::Concurrent::Map.new }
|
39
39
|
rescue ::LoadError
|
40
40
|
@caches = { scans: {}, templates: {} }
|
41
41
|
end
|
@@ -71,7 +71,7 @@ class Converter::TemplateConverter < Converter::Base
|
|
71
71
|
end
|
72
72
|
case opts[:template_cache]
|
73
73
|
when true
|
74
|
-
logger.warn 'optional gem \'concurrent-ruby\' is not available. This gem is recommended when using the default template cache.' unless defined? ::Concurrent::
|
74
|
+
logger.warn 'optional gem \'concurrent-ruby\' is not available. This gem is recommended when using the default template cache.' unless defined? ::Concurrent::Map
|
75
75
|
@caches = self.class.caches
|
76
76
|
when ::Hash
|
77
77
|
@caches = opts[:template_cache]
|
@@ -257,6 +257,9 @@ class Converter::TemplateConverter < Converter::Base
|
|
257
257
|
if !name || name == 'erb'
|
258
258
|
require 'erb' unless defined? ::ERB.version
|
259
259
|
[::Tilt::ERBTemplate, {}]
|
260
|
+
elsif name == 'erubi'
|
261
|
+
Helpers.require_library 'erubi' unless defined? ::Erubis::Engine
|
262
|
+
[::Tilt::ErubiTemplate, {}]
|
260
263
|
elsif name == 'erubis'
|
261
264
|
Helpers.require_library 'erubis' unless defined? ::Erubis::FastEruby
|
262
265
|
[::Tilt::ErubisTemplate, { engine_class: ::Erubis::FastEruby }]
|
data/lib/asciidoctor/document.rb
CHANGED
@@ -326,7 +326,7 @@ class Document < AbstractBlock
|
|
326
326
|
@sourcemap = options[:sourcemap]
|
327
327
|
@timings = options.delete :timings
|
328
328
|
@path_resolver = PathResolver.new
|
329
|
-
initialize_extensions = (defined? ::Asciidoctor::Extensions) ?
|
329
|
+
initialize_extensions = (defined? ::Asciidoctor::Extensions) || (options.key? :extensions) ? ::Asciidoctor::Extensions : nil
|
330
330
|
@extensions = nil # initialize furthur down if initialize_extensions is true
|
331
331
|
options[:standalone] = options[:header_footer] if (options.key? :header_footer) && !(options.key? :standalone)
|
332
332
|
end
|
@@ -339,51 +339,36 @@ class Document < AbstractBlock
|
|
339
339
|
(@options = options).freeze
|
340
340
|
|
341
341
|
attrs = @attributes
|
342
|
-
|
343
|
-
attrs['
|
344
|
-
attrs
|
342
|
+
attrs['attribute-undefined'] = Compliance.attribute_undefined
|
343
|
+
attrs['attribute-missing'] = Compliance.attribute_missing
|
344
|
+
attrs.update DEFAULT_ATTRIBUTES
|
345
|
+
# TODO if lang attribute is set, @safe mode < SafeMode::SERVER, and !parent_doc,
|
346
|
+
# load attributes from data/locale/attributes-<lang>.adoc
|
347
|
+
|
345
348
|
if standalone
|
346
|
-
attrs['copycss'] = ''
|
347
349
|
# sync embedded attribute with :standalone option value
|
348
350
|
attr_overrides['embedded'] = nil
|
351
|
+
attrs['copycss'] = ''
|
352
|
+
attrs['iconfont-remote'] = ''
|
353
|
+
attrs['stylesheet'] = ''
|
354
|
+
attrs['webfonts'] = ''
|
349
355
|
else
|
350
|
-
attrs['notitle'] = ''
|
351
356
|
# sync embedded attribute with :standalone option value
|
352
357
|
attr_overrides['embedded'] = ''
|
358
|
+
if (attr_overrides.key? 'showtitle') && (attr_overrides.keys & %w(notitle showtitle))[-1] == 'showtitle'
|
359
|
+
attr_overrides['notitle'] = { nil => '', false => '@', '@' => false}[attr_overrides['showtitle']]
|
360
|
+
elsif attr_overrides.key? 'notitle'
|
361
|
+
attr_overrides['showtitle'] = { nil => '', false => '@', '@' => false}[attr_overrides['notitle']]
|
362
|
+
else
|
363
|
+
attrs['notitle'] = ''
|
364
|
+
end
|
353
365
|
end
|
354
|
-
attrs['stylesheet'] = ''
|
355
|
-
attrs['webfonts'] = ''
|
356
|
-
attrs['prewrap'] = ''
|
357
|
-
attrs['attribute-undefined'] = Compliance.attribute_undefined
|
358
|
-
attrs['attribute-missing'] = Compliance.attribute_missing
|
359
|
-
attrs['iconfont-remote'] = ''
|
360
|
-
|
361
|
-
# language strings
|
362
|
-
# TODO load these based on language settings
|
363
|
-
attrs['caution-caption'] = 'Caution'
|
364
|
-
attrs['important-caption'] = 'Important'
|
365
|
-
attrs['note-caption'] = 'Note'
|
366
|
-
attrs['tip-caption'] = 'Tip'
|
367
|
-
attrs['warning-caption'] = 'Warning'
|
368
|
-
attrs['example-caption'] = 'Example'
|
369
|
-
attrs['figure-caption'] = 'Figure'
|
370
|
-
#attrs['listing-caption'] = 'Listing'
|
371
|
-
attrs['table-caption'] = 'Table'
|
372
|
-
attrs['toc-title'] = 'Table of Contents'
|
373
|
-
#attrs['preface-title'] = 'Preface'
|
374
|
-
attrs['section-refsig'] = 'Section'
|
375
|
-
attrs['part-refsig'] = 'Part'
|
376
|
-
attrs['chapter-refsig'] = 'Chapter'
|
377
|
-
attrs['appendix-caption'] = attrs['appendix-refsig'] = 'Appendix'
|
378
|
-
attrs['untitled-label'] = 'Untitled'
|
379
|
-
attrs['version-label'] = 'Version'
|
380
|
-
attrs['last-update-label'] = 'Last updated'
|
381
366
|
|
382
367
|
attr_overrides['asciidoctor'] = ''
|
383
368
|
attr_overrides['asciidoctor-version'] = ::Asciidoctor::VERSION
|
384
369
|
|
385
370
|
attr_overrides['safe-mode-name'] = (safe_mode_name = SafeMode.name_for_value @safe)
|
386
|
-
attr_overrides[
|
371
|
+
attr_overrides[%(safe-mode-#{safe_mode_name})] = ''
|
387
372
|
attr_overrides['safe-mode-level'] = @safe
|
388
373
|
|
389
374
|
# the only way to set the max-include-depth attribute is via the API; default to 64 like AsciiDoc Python
|
@@ -505,10 +490,10 @@ class Document < AbstractBlock
|
|
505
490
|
::AsciidoctorJ::Extensions::ExtensionRegistry === ext_registry)
|
506
491
|
@extensions = ext_registry.activate self
|
507
492
|
end
|
508
|
-
elsif
|
493
|
+
elsif (ext_block = options[:extensions]).nil?
|
494
|
+
@extensions = Extensions::Registry.new.activate self unless Extensions.groups.empty?
|
495
|
+
elsif ::Proc === ext_block
|
509
496
|
@extensions = Extensions.create(&ext_block).activate self
|
510
|
-
elsif !Extensions.groups.empty?
|
511
|
-
@extensions = Extensions::Registry.new.activate self
|
512
497
|
end
|
513
498
|
end
|
514
499
|
|
@@ -606,11 +591,10 @@ class Document < AbstractBlock
|
|
606
591
|
when :refs
|
607
592
|
@catalog[:refs][value[0]] ||= (ref = value[1])
|
608
593
|
ref
|
609
|
-
#when :footnotes, :indexterms
|
610
594
|
when :footnotes
|
611
595
|
@catalog[type] << value
|
612
596
|
else
|
613
|
-
@catalog[type] << (type == :images ? (ImageReference.new value
|
597
|
+
@catalog[type] << (type == :images ? (ImageReference.new value, @attributes['imagesdir']) : value) if @options[:catalog_assets]
|
614
598
|
end
|
615
599
|
end
|
616
600
|
|
@@ -773,7 +757,7 @@ class Document < AbstractBlock
|
|
773
757
|
end
|
774
758
|
|
775
759
|
def notitle
|
776
|
-
|
760
|
+
@attributes.key? 'notitle'
|
777
761
|
end
|
778
762
|
|
779
763
|
def noheader
|
@@ -1218,7 +1202,7 @@ class Document < AbstractBlock
|
|
1218
1202
|
when '', 'font'
|
1219
1203
|
else
|
1220
1204
|
attrs['icons'] = ''
|
1221
|
-
attrs['icontype'] = icons_val
|
1205
|
+
attrs['icontype'] = icons_val unless icons_val == 'image'
|
1222
1206
|
end
|
1223
1207
|
end
|
1224
1208
|
|
@@ -425,7 +425,7 @@ module Extensions
|
|
425
425
|
# TIP: Postprocessors can also be used to relocate assets needed by the published
|
426
426
|
# document.
|
427
427
|
#
|
428
|
-
# Postprocessor implementations must Postprocessor.
|
428
|
+
# Postprocessor implementations must extend Postprocessor.
|
429
429
|
class Postprocessor < Processor
|
430
430
|
def process document, output
|
431
431
|
raise ::NotImplementedError, %(#{Postprocessor} subclass #{self.class} must implement the ##{__method__} method)
|
@@ -610,7 +610,7 @@ module Extensions
|
|
610
610
|
#--
|
611
611
|
# TODO break this out into different pattern types
|
612
612
|
# for example, FullInlineMacro, ShortInlineMacro (no target) and other patterns
|
613
|
-
# FIXME for inline
|
613
|
+
# FIXME for inline macro, we need to have some way to specify the text as a passthrough
|
614
614
|
class InlineMacroProcessor < MacroProcessor
|
615
615
|
@@rx_cache = {}
|
616
616
|
|
@@ -622,7 +622,7 @@ module Extensions
|
|
622
622
|
|
623
623
|
def resolve_regexp name, format
|
624
624
|
raise ::ArgumentError, %(invalid name for inline macro: #{name}) unless MacroNameRx.match? name
|
625
|
-
@@rx_cache[[name, format]] ||= /\\?#{name}:#{format == :short ? '(){0}' : '(\S+?)'}\[(
|
625
|
+
@@rx_cache[[name, format]] ||= /\\?#{name}:#{format == :short ? '(){0}' : '(\S+?)'}\[(|#{CC_ANY}*?[^\\])\]/
|
626
626
|
end
|
627
627
|
end
|
628
628
|
|
data/lib/asciidoctor/helpers.rb
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
module Asciidoctor
|
3
3
|
# Internal: Except where noted, a module that contains internal helper functions.
|
4
4
|
module Helpers
|
5
|
-
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Public: Require the specified library using Kernel#require.
|
6
8
|
#
|
7
9
|
# Attempts to load the library specified in the first argument using the
|
8
10
|
# Kernel#require. Rescues the LoadError if the library is not available and
|
@@ -21,7 +23,7 @@ module Helpers
|
|
21
23
|
# Otherwise, if on_failure is :abort, Kernel#raise is called with an appropriate message.
|
22
24
|
# Otherwise, if on_failure is :warn, Kernel#warn is called with an appropriate message and nil returned.
|
23
25
|
# Otherwise, nil is returned.
|
24
|
-
def
|
26
|
+
def require_library name, gem_name = true, on_failure = :abort
|
25
27
|
require name
|
26
28
|
rescue ::LoadError
|
27
29
|
include Logging unless include? Logging
|
@@ -54,25 +56,27 @@ module Helpers
|
|
54
56
|
# If a BOM is found at the beginning of the data, a best attempt is made to
|
55
57
|
# encode it to UTF-8 from the specified source encoding.
|
56
58
|
#
|
57
|
-
# data
|
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)
|
58
62
|
#
|
59
63
|
# returns a String Array of prepared lines
|
60
|
-
def
|
64
|
+
def prepare_source_array data, trim_end = true
|
61
65
|
return [] if data.empty?
|
62
66
|
if (leading_2_bytes = (leading_bytes = (first = data[0]).unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
|
63
67
|
data[0] = first.byteslice 2, first.bytesize
|
64
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
|
65
|
-
return data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).rstrip }
|
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 }
|
66
70
|
elsif leading_2_bytes == BOM_BYTES_UTF_16BE
|
67
71
|
data[0] = first.byteslice 2, first.bytesize
|
68
|
-
return data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).rstrip }
|
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 }
|
69
73
|
elsif leading_bytes == BOM_BYTES_UTF_8
|
70
74
|
data[0] = first.byteslice 3, first.bytesize
|
71
75
|
end
|
72
76
|
if first.encoding == UTF_8
|
73
|
-
data.map {|line| line.rstrip }
|
77
|
+
trim_end ? data.map {|line| line.rstrip } : data.map {|line| line.chomp }
|
74
78
|
else
|
75
|
-
data.map {|line| (line.encode UTF_8).rstrip }
|
79
|
+
trim_end ? data.map {|line| (line.encode UTF_8).rstrip } : data.map {|line| (line.encode UTF_8).chomp }
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
@@ -84,10 +88,12 @@ module Helpers
|
|
84
88
|
# If a BOM is found at the beginning of the data, a best attempt is made to
|
85
89
|
# encode it to UTF-8 from the specified source encoding.
|
86
90
|
#
|
87
|
-
# 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)
|
88
94
|
#
|
89
95
|
# returns a String Array of prepared lines
|
90
|
-
def
|
96
|
+
def prepare_source_string data, trim_end = true
|
91
97
|
return [] if data.nil_or_empty?
|
92
98
|
if (leading_2_bytes = (leading_bytes = data.unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
|
93
99
|
data = (data.byteslice 2, data.bytesize).encode UTF_8, ::Encoding::UTF_16LE
|
@@ -99,7 +105,11 @@ module Helpers
|
|
99
105
|
elsif data.encoding != UTF_8
|
100
106
|
data = data.encode UTF_8
|
101
107
|
end
|
102
|
-
|
108
|
+
if trim_end
|
109
|
+
[].tap {|lines| data.each_line {|line| lines << line.rstrip } }
|
110
|
+
else
|
111
|
+
[].tap {|lines| data.each_line {|line| lines << line.chomp } }
|
112
|
+
end
|
103
113
|
end
|
104
114
|
|
105
115
|
# Internal: Efficiently checks whether the specified String resembles a URI
|
@@ -110,29 +120,17 @@ module Helpers
|
|
110
120
|
# str - the String to check
|
111
121
|
#
|
112
122
|
# returns true if the String is a URI, false if it is not
|
113
|
-
def
|
123
|
+
def uriish? str
|
114
124
|
(str.include? ':') && (UriSniffRx.match? str)
|
115
125
|
end
|
116
126
|
|
117
|
-
# Internal: Efficiently retrieves the URI prefix of the specified String
|
118
|
-
#
|
119
|
-
# Uses the Asciidoctor::UriSniffRx regex to match the URI prefix in the
|
120
|
-
# specified String (e.g., http://), if present.
|
121
|
-
#
|
122
|
-
# str - the String to check
|
123
|
-
#
|
124
|
-
# returns the string URI prefix if the string is a URI, otherwise nil
|
125
|
-
def self.uri_prefix str
|
126
|
-
(str.include? ':') && UriSniffRx =~ str ? $& : nil
|
127
|
-
end
|
128
|
-
|
129
127
|
# Internal: Encode a URI component String for safe inclusion in a URI.
|
130
128
|
#
|
131
129
|
# str - the URI component String to encode
|
132
130
|
#
|
133
131
|
# Returns the String with all reserved URI characters encoded (e.g., /, &, =, space, etc).
|
134
132
|
if RUBY_ENGINE == 'opal'
|
135
|
-
def
|
133
|
+
def encode_uri_component str
|
136
134
|
# patch necessary to adhere with RFC-3986 (and thus CGI.escape)
|
137
135
|
# see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Description
|
138
136
|
%x(
|
@@ -143,17 +141,17 @@ module Helpers
|
|
143
141
|
end
|
144
142
|
else
|
145
143
|
CGI = ::CGI
|
146
|
-
def
|
144
|
+
def encode_uri_component str
|
147
145
|
CGI.escape str
|
148
146
|
end
|
149
147
|
end
|
150
148
|
|
151
|
-
# Internal:
|
149
|
+
# Internal: Apply URI path encoding to spaces in the specified string (i.e., convert spaces to %20).
|
152
150
|
#
|
153
151
|
# str - the String to encode
|
154
152
|
#
|
155
|
-
# Returns the String with all spaces replaced with %20.
|
156
|
-
def
|
153
|
+
# Returns the specified String with all spaces replaced with %20.
|
154
|
+
def encode_spaces_in_uri str
|
157
155
|
(str.include? ' ') ? (str.gsub ' ', '%20') : str
|
158
156
|
end
|
159
157
|
|
@@ -167,7 +165,7 @@ module Helpers
|
|
167
165
|
# # => "part1/chapter1"
|
168
166
|
#
|
169
167
|
# Returns the String filename with the file extension removed
|
170
|
-
def
|
168
|
+
def rootname filename
|
171
169
|
if (last_dot_idx = filename.rindex '.')
|
172
170
|
(filename.index '/', last_dot_idx) ? filename : (filename.slice 0, last_dot_idx)
|
173
171
|
else
|
@@ -190,7 +188,7 @@ module Helpers
|
|
190
188
|
# # => "tiger"
|
191
189
|
#
|
192
190
|
# Returns the String filename with leading directories removed and, if specified, the extension removed
|
193
|
-
def
|
191
|
+
def basename filename, drop_ext = nil
|
194
192
|
if drop_ext
|
195
193
|
::File.basename filename, (drop_ext == true ? (extname filename) : drop_ext)
|
196
194
|
else
|
@@ -203,7 +201,7 @@ module Helpers
|
|
203
201
|
# path - The path String to check; expects a posix path
|
204
202
|
#
|
205
203
|
# Returns true if the path has a file extension, false otherwise
|
206
|
-
def
|
204
|
+
def extname? path
|
207
205
|
(last_dot_idx = path.rindex '.') && !(path.index '/', last_dot_idx)
|
208
206
|
end
|
209
207
|
|
@@ -217,7 +215,7 @@ module Helpers
|
|
217
215
|
#
|
218
216
|
# Returns the String file extension (with the leading dot included) or the fallback value if the path has no file extension.
|
219
217
|
if ::File::ALT_SEPARATOR
|
220
|
-
def
|
218
|
+
def extname path, fallback = ''
|
221
219
|
if (last_dot_idx = path.rindex '.')
|
222
220
|
(path.index '/', last_dot_idx) || (path.index ::File::ALT_SEPARATOR, last_dot_idx) ? fallback : (path.slice last_dot_idx, path.length)
|
223
221
|
else
|
@@ -225,7 +223,7 @@ module Helpers
|
|
225
223
|
end
|
226
224
|
end
|
227
225
|
else
|
228
|
-
def
|
226
|
+
def extname path, fallback = ''
|
229
227
|
if (last_dot_idx = path.rindex '.')
|
230
228
|
(path.index '/', last_dot_idx) ? fallback : (path.slice last_dot_idx, path.length)
|
231
229
|
else
|
@@ -235,7 +233,7 @@ module Helpers
|
|
235
233
|
end
|
236
234
|
|
237
235
|
# Internal: Make a directory, ensuring all parent directories exist.
|
238
|
-
def
|
236
|
+
def mkdir_p dir
|
239
237
|
unless ::File.directory? dir
|
240
238
|
unless (parent_dir = ::File.dirname dir) == '.'
|
241
239
|
mkdir_p parent_dir
|
@@ -252,13 +250,14 @@ module Helpers
|
|
252
250
|
'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, 'XC' => 90,
|
253
251
|
'L' => 50, 'XL' => 40, 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1
|
254
252
|
}
|
253
|
+
private_constant :ROMAN_NUMERALS
|
255
254
|
|
256
255
|
# Internal: Converts an integer to a Roman numeral.
|
257
256
|
#
|
258
257
|
# val - the [Integer] value to convert
|
259
258
|
#
|
260
259
|
# Returns the [String] roman numeral for this integer
|
261
|
-
def
|
260
|
+
def int_to_roman val
|
262
261
|
ROMAN_NUMERALS.map do |l, i|
|
263
262
|
repeat, val = val.divmod i
|
264
263
|
l * repeat
|
@@ -272,7 +271,7 @@ module Helpers
|
|
272
271
|
# current - the value to increment as a String or Integer
|
273
272
|
#
|
274
273
|
# returns the next value in the sequence according to the current value's type
|
275
|
-
def
|
274
|
+
def nextval current
|
276
275
|
if ::Integer === current
|
277
276
|
current + 1
|
278
277
|
else
|
@@ -291,14 +290,14 @@ module Helpers
|
|
291
290
|
#
|
292
291
|
# Returns a Class if the specified object is a Class (but not a Module) or
|
293
292
|
# a String that resolves to a Class; otherwise, nil
|
294
|
-
def
|
293
|
+
def resolve_class object
|
295
294
|
::Class === object ? object : (::String === object ? (class_for_name object) : nil)
|
296
295
|
end
|
297
296
|
|
298
297
|
# Internal: Resolves a Class object (not a Module) for the qualified name.
|
299
298
|
#
|
300
299
|
# Returns Class
|
301
|
-
def
|
300
|
+
def class_for_name qualified_name
|
302
301
|
raise unless ::Class === (resolved = ::Object.const_get qualified_name, false)
|
303
302
|
resolved
|
304
303
|
rescue
|
data/lib/asciidoctor/inline.rb
CHANGED
data/lib/asciidoctor/load.rb
CHANGED
@@ -1,117 +1,117 @@
|
|
1
1
|
module Asciidoctor
|
2
|
-
|
2
|
+
class << self
|
3
|
+
# Public: Parse the AsciiDoc source input into a {Document}
|
4
|
+
#
|
5
|
+
# Accepts input as an IO (or StringIO), String or String Array object. If the
|
6
|
+
# input is a File, the object is expected to be opened for reading and is not
|
7
|
+
# closed afterwards by this method. Information about the file (filename,
|
8
|
+
# directory name, etc) gets assigned to attributes on the Document object.
|
9
|
+
#
|
10
|
+
# input - the AsciiDoc source as a IO, String or Array.
|
11
|
+
# options - a String, Array or Hash of options to control processing (default: {})
|
12
|
+
# String and Array values are converted into a Hash.
|
13
|
+
# See {Document#initialize} for details about these options.
|
14
|
+
#
|
15
|
+
# Returns the Document
|
16
|
+
def load input, options = {}
|
17
|
+
options = options.merge
|
3
18
|
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
19
|
+
if (timings = options[:timings])
|
20
|
+
timings.start :read
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
if (logger = options[:logger]) && logger != LoggerManager.logger
|
24
|
+
LoggerManager.logger = logger
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
27
|
+
if !(attrs = options[:attributes])
|
28
|
+
attrs = {}
|
29
|
+
elsif ::Hash === attrs
|
30
|
+
attrs = attrs.merge
|
31
|
+
elsif (defined? ::Java::JavaUtil::Map) && ::Java::JavaUtil::Map === attrs
|
32
|
+
attrs = attrs.dup
|
33
|
+
elsif ::Array === attrs
|
34
|
+
attrs = {}.tap do |accum|
|
35
|
+
attrs.each do |entry|
|
36
|
+
k, _, v = entry.partition '='
|
37
|
+
accum[k] = v
|
38
|
+
end
|
39
39
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
elsif ::String === attrs
|
41
|
+
# condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null
|
42
|
+
attrs = {}.tap do |accum|
|
43
|
+
attrs.gsub(SpaceDelimiterRx, '\1' + NULL).gsub(EscapedSpaceRx, '\1').split(NULL).each do |entry|
|
44
|
+
k, _, v = entry.partition '='
|
45
|
+
accum[k] = v
|
46
|
+
end
|
47
47
|
end
|
48
|
+
elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
|
49
|
+
# coerce attrs to a real Hash
|
50
|
+
attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } }
|
51
|
+
else
|
52
|
+
raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '})
|
48
53
|
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
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
55
|
+
if ::File === input
|
56
|
+
options[:input_mtime] = input.mtime
|
57
|
+
# NOTE defer setting infile and indir until we get a better sense of their purpose
|
58
|
+
# TODO cli checks if input path can be read and is file, but might want to add check to API too
|
59
|
+
attrs['docfile'] = input_path = ::File.absolute_path input.path
|
60
|
+
attrs['docdir'] = ::File.dirname input_path
|
61
|
+
attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = Helpers.extname input_path)
|
62
|
+
source = input.read
|
63
|
+
elsif input.respond_to? :read
|
64
|
+
# NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either
|
65
|
+
# just fail the rewind operation silently to handle all cases
|
66
|
+
input.rewind rescue nil
|
67
|
+
source = input.read
|
68
|
+
elsif ::String === input
|
69
|
+
source = input
|
70
|
+
elsif ::Array === input
|
71
|
+
source = input.drop 0
|
72
|
+
elsif input
|
73
|
+
raise ::ArgumentError, %(unsupported input type: #{input.class})
|
74
|
+
end
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
76
|
+
if timings
|
77
|
+
timings.record :read
|
78
|
+
timings.start :parse
|
79
|
+
end
|
81
80
|
|
82
|
-
|
83
|
-
|
81
|
+
options[:attributes] = attrs
|
82
|
+
doc = options[:parse] == false ? (Document.new source, options) : (Document.new source, options).parse
|
84
83
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
84
|
+
timings.record :parse if timings
|
85
|
+
doc
|
86
|
+
rescue => ex
|
87
|
+
begin
|
88
|
+
context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
|
89
|
+
if ex.respond_to? :exception
|
90
|
+
# The original message must be explicitly preserved when wrapping a Ruby exception
|
91
|
+
wrapped_ex = ex.exception %(#{context} - #{ex.message})
|
92
|
+
# JRuby automatically sets backtrace; MRI did not until 2.6
|
93
|
+
wrapped_ex.set_backtrace ex.backtrace
|
94
|
+
else
|
95
|
+
# Likely a Java exception class
|
96
|
+
wrapped_ex = ex.class.new context, ex
|
97
|
+
wrapped_ex.stack_trace = ex.stack_trace
|
98
|
+
end
|
99
|
+
rescue
|
100
|
+
wrapped_ex = ex
|
99
101
|
end
|
100
|
-
|
101
|
-
wrapped_ex = ex
|
102
|
+
raise wrapped_ex
|
102
103
|
end
|
103
|
-
raise wrapped_ex
|
104
|
-
end
|
105
104
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
105
|
+
# Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
|
106
|
+
#
|
107
|
+
# input - the String AsciiDoc source filename
|
108
|
+
# options - a String, Array or Hash of options to control processing (default: {})
|
109
|
+
# String and Array values are converted into a Hash.
|
110
|
+
# See Asciidoctor::Document#initialize for details about options.
|
111
|
+
#
|
112
|
+
# Returns the Asciidoctor::Document
|
113
|
+
def load_file filename, options = {}
|
114
|
+
::File.open(filename, FILE_READ_MODE) {|file| load file, options }
|
115
|
+
end
|
116
116
|
end
|
117
117
|
end
|