asciidoctor 2.0.6 → 2.0.11
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/CHANGELOG.adoc +159 -6
- data/LICENSE +2 -1
- data/README-de.adoc +5 -5
- data/README-fr.adoc +4 -4
- data/README-jp.adoc +248 -183
- data/README-zh_CN.adoc +6 -6
- data/README.adoc +17 -11
- data/asciidoctor.gemspec +8 -8
- 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 +29 -26
- data/lib/asciidoctor.rb +94 -1098
- 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 +8 -8
- data/lib/asciidoctor/convert.rb +198 -0
- data/lib/asciidoctor/converter.rb +14 -13
- data/lib/asciidoctor/converter/docbook5.rb +9 -25
- data/lib/asciidoctor/converter/html5.rb +65 -42
- data/lib/asciidoctor/converter/manpage.rb +13 -12
- data/lib/asciidoctor/converter/template.rb +6 -3
- data/lib/asciidoctor/document.rb +40 -48
- 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 +117 -0
- data/lib/asciidoctor/parser.rb +29 -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 +62 -40
- 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 +18 -11
- data/lib/asciidoctor/table.rb +49 -20
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +17 -17
- data/man/asciidoctor.adoc +15 -14
- metadata +12 -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,13 +326,12 @@ 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
|
333
333
|
|
334
|
-
@parsed =
|
335
|
-
@header = @header_attributes = nil
|
334
|
+
@parsed = @reftexts = @header = @header_attributes = nil
|
336
335
|
@counters = {}
|
337
336
|
@attributes_modified = ::Set.new
|
338
337
|
@docinfo_processor_extensions = {}
|
@@ -340,51 +339,36 @@ class Document < AbstractBlock
|
|
340
339
|
(@options = options).freeze
|
341
340
|
|
342
341
|
attrs = @attributes
|
343
|
-
|
344
|
-
attrs['
|
345
|
-
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
|
+
|
346
348
|
if standalone
|
347
|
-
attrs['copycss'] = ''
|
348
349
|
# sync embedded attribute with :standalone option value
|
349
350
|
attr_overrides['embedded'] = nil
|
351
|
+
attrs['copycss'] = ''
|
352
|
+
attrs['iconfont-remote'] = ''
|
353
|
+
attrs['stylesheet'] = ''
|
354
|
+
attrs['webfonts'] = ''
|
350
355
|
else
|
351
|
-
attrs['notitle'] = ''
|
352
356
|
# sync embedded attribute with :standalone option value
|
353
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
|
354
365
|
end
|
355
|
-
attrs['stylesheet'] = ''
|
356
|
-
attrs['webfonts'] = ''
|
357
|
-
attrs['prewrap'] = ''
|
358
|
-
attrs['attribute-undefined'] = Compliance.attribute_undefined
|
359
|
-
attrs['attribute-missing'] = Compliance.attribute_missing
|
360
|
-
attrs['iconfont-remote'] = ''
|
361
|
-
|
362
|
-
# language strings
|
363
|
-
# TODO load these based on language settings
|
364
|
-
attrs['caution-caption'] = 'Caution'
|
365
|
-
attrs['important-caption'] = 'Important'
|
366
|
-
attrs['note-caption'] = 'Note'
|
367
|
-
attrs['tip-caption'] = 'Tip'
|
368
|
-
attrs['warning-caption'] = 'Warning'
|
369
|
-
attrs['example-caption'] = 'Example'
|
370
|
-
attrs['figure-caption'] = 'Figure'
|
371
|
-
#attrs['listing-caption'] = 'Listing'
|
372
|
-
attrs['table-caption'] = 'Table'
|
373
|
-
attrs['toc-title'] = 'Table of Contents'
|
374
|
-
#attrs['preface-title'] = 'Preface'
|
375
|
-
attrs['section-refsig'] = 'Section'
|
376
|
-
attrs['part-refsig'] = 'Part'
|
377
|
-
attrs['chapter-refsig'] = 'Chapter'
|
378
|
-
attrs['appendix-caption'] = attrs['appendix-refsig'] = 'Appendix'
|
379
|
-
attrs['untitled-label'] = 'Untitled'
|
380
|
-
attrs['version-label'] = 'Version'
|
381
|
-
attrs['last-update-label'] = 'Last updated'
|
382
366
|
|
383
367
|
attr_overrides['asciidoctor'] = ''
|
384
368
|
attr_overrides['asciidoctor-version'] = ::Asciidoctor::VERSION
|
385
369
|
|
386
370
|
attr_overrides['safe-mode-name'] = (safe_mode_name = SafeMode.name_for_value @safe)
|
387
|
-
attr_overrides[
|
371
|
+
attr_overrides[%(safe-mode-#{safe_mode_name})] = ''
|
388
372
|
attr_overrides['safe-mode-level'] = @safe
|
389
373
|
|
390
374
|
# the only way to set the max-include-depth attribute is via the API; default to 64 like AsciiDoc Python
|
@@ -506,10 +490,10 @@ class Document < AbstractBlock
|
|
506
490
|
::AsciidoctorJ::Extensions::ExtensionRegistry === ext_registry)
|
507
491
|
@extensions = ext_registry.activate self
|
508
492
|
end
|
509
|
-
elsif
|
493
|
+
elsif (ext_block = options[:extensions]).nil?
|
494
|
+
@extensions = Extensions::Registry.new.activate self unless Extensions.groups.empty?
|
495
|
+
elsif ::Proc === ext_block
|
510
496
|
@extensions = Extensions.create(&ext_block).activate self
|
511
|
-
elsif !Extensions.groups.empty?
|
512
|
-
@extensions = Extensions::Registry.new.activate self
|
513
497
|
end
|
514
498
|
end
|
515
499
|
|
@@ -607,24 +591,32 @@ class Document < AbstractBlock
|
|
607
591
|
when :refs
|
608
592
|
@catalog[:refs][value[0]] ||= (ref = value[1])
|
609
593
|
ref
|
610
|
-
#when :footnotes, :indexterms
|
611
594
|
when :footnotes
|
612
595
|
@catalog[type] << value
|
613
596
|
else
|
614
|
-
@catalog[type] << (type == :images ? (ImageReference.new value
|
597
|
+
@catalog[type] << (type == :images ? (ImageReference.new value, @attributes['imagesdir']) : value) if @options[:catalog_assets]
|
615
598
|
end
|
616
599
|
end
|
617
600
|
|
618
|
-
# Public: Scan
|
619
|
-
#
|
620
|
-
# If multiple references in the document have the same reference text, the first match in document order is used.
|
601
|
+
# Public: Scan registered references and return the ID of the first reference that matches the specified reference text.
|
621
602
|
#
|
622
603
|
# text - The String reference text to compare to the converted reference text of each registered reference.
|
623
604
|
#
|
624
605
|
# Returns the String ID of the first reference with matching reference text or nothing if no reference is found.
|
625
606
|
def resolve_id text
|
626
|
-
|
627
|
-
|
607
|
+
if @reftexts
|
608
|
+
@reftexts[text]
|
609
|
+
elsif @parsed
|
610
|
+
# @reftexts is set eagerly to prevent nested lazy init
|
611
|
+
(@reftexts = {}).tap {|accum| @catalog[:refs].each {|id, ref| accum[ref.xreftext] ||= id } }[text]
|
612
|
+
else
|
613
|
+
# @reftexts is set eagerly to prevent nested lazy init
|
614
|
+
resolved_id = nil
|
615
|
+
# NOTE short-circuit early since we're throwing away this table
|
616
|
+
(@reftexts = {}).tap {|accum| @catalog[:refs].each {|id, ref| (xreftext = ref.xreftext) == text ? (break (resolved_id = id)) : (accum[xreftext] ||= id) } }
|
617
|
+
@reftexts = nil
|
618
|
+
resolved_id
|
619
|
+
end
|
628
620
|
end
|
629
621
|
|
630
622
|
def footnotes?
|
@@ -765,7 +757,7 @@ class Document < AbstractBlock
|
|
765
757
|
end
|
766
758
|
|
767
759
|
def notitle
|
768
|
-
|
760
|
+
@attributes.key? 'notitle'
|
769
761
|
end
|
770
762
|
|
771
763
|
def noheader
|
@@ -1210,7 +1202,7 @@ class Document < AbstractBlock
|
|
1210
1202
|
when '', 'font'
|
1211
1203
|
else
|
1212
1204
|
attrs['icons'] = ''
|
1213
|
-
attrs['icontype'] = icons_val
|
1205
|
+
attrs['icontype'] = icons_val unless icons_val == 'image'
|
1214
1206
|
end
|
1215
1207
|
end
|
1216
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
@@ -0,0 +1,117 @@
|
|
1
|
+
module Asciidoctor
|
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
|
18
|
+
|
19
|
+
if (timings = options[:timings])
|
20
|
+
timings.start :read
|
21
|
+
end
|
22
|
+
|
23
|
+
if (logger = options[:logger]) && logger != LoggerManager.logger
|
24
|
+
LoggerManager.logger = logger
|
25
|
+
end
|
26
|
+
|
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
|
+
end
|
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
|
+
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 ' < '})
|
53
|
+
end
|
54
|
+
|
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
|
75
|
+
|
76
|
+
if timings
|
77
|
+
timings.record :read
|
78
|
+
timings.start :parse
|
79
|
+
end
|
80
|
+
|
81
|
+
options[:attributes] = attrs
|
82
|
+
doc = options[:parse] == false ? (Document.new source, options) : (Document.new source, options).parse
|
83
|
+
|
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
|
101
|
+
end
|
102
|
+
raise wrapped_ex
|
103
|
+
end
|
104
|
+
|
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
|
+
end
|
117
|
+
end
|