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
@@ -1,232 +1,417 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# A module for defining converters that are used to convert {AbstractNode} objects in a parsed AsciiDoc document to an
|
4
|
+
# output (aka backend) format such as HTML or DocBook.
|
5
|
+
#
|
6
|
+
# A {Converter} is typically instantiated each time an AsciiDoc document is processed (i.e., parsed and converted).
|
7
|
+
# Implementing a custom converter entails:
|
8
|
+
#
|
9
|
+
# * Including the {Converter} module in a converter class and implementing the {Converter#convert} method or extending
|
10
|
+
# the {Converter::Base Base} class and implementing the dispatch methods that map to each node.
|
11
|
+
# * Optionally registering the converter with one or more backend names statically using the +register_for+ DSL method
|
12
|
+
# contributed by the {Converter::Config Config} module.
|
13
|
+
#
|
14
|
+
# Examples
|
15
|
+
#
|
16
|
+
# class TextConverter
|
17
|
+
# include Asciidoctor::Converter
|
18
|
+
# register_for 'text'
|
19
|
+
# def initialize *args
|
20
|
+
# super
|
21
|
+
# outfilesuffix '.txt'
|
22
|
+
# end
|
23
|
+
# def convert node, transform = node.node_name, opts = nil
|
24
|
+
# case transform
|
25
|
+
# when 'document', 'section'
|
26
|
+
# [node.title, node.content].join %(\n\n)
|
27
|
+
# when 'paragraph'
|
28
|
+
# (node.content.tr ?\n, ' ') << ?\n
|
29
|
+
# else
|
30
|
+
# (transform.start_with? 'inline_') ? node.text : node.content
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# puts Asciidoctor.convert_file 'sample.adoc', backend: :text, safe: :safe
|
35
|
+
#
|
36
|
+
# class Html5Converter < (Asciidoctor::Converter.for 'html5')
|
37
|
+
# register_for 'html5'
|
38
|
+
# def convert_paragraph node
|
39
|
+
# %(<p>#{node.content}</p>)
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# puts Asciidoctor.convert_file 'sample.adoc', safe: :safe
|
43
|
+
module Converter
|
44
|
+
autoload :CompositeConverter, %(#{__dir__}/converter/composite)
|
45
|
+
autoload :TemplateConverter, %(#{__dir__}/converter/template) unless RUBY_ENGINE == 'opal'
|
46
|
+
|
47
|
+
# Public: The String backend name that this converter is handling.
|
48
|
+
attr_reader :backend
|
49
|
+
|
50
|
+
# Public: Creates a new instance of this {Converter}.
|
51
|
+
#
|
52
|
+
# backend - The String backend name (aka format) to which this converter converts.
|
53
|
+
# opts - An options Hash (optional, default: {})
|
54
|
+
#
|
55
|
+
# Returns a new [Converter] instance.
|
56
|
+
def initialize backend, opts = {}
|
57
|
+
@backend = backend
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Converts an {AbstractNode} using the given transform.
|
61
|
+
#
|
62
|
+
# This method must be implemented by a concrete converter class.
|
63
|
+
#
|
64
|
+
# node - The concrete instance of AbstractNode to convert.
|
65
|
+
# transform - An optional String transform that hints at which transformation should be applied to this node. If a
|
66
|
+
# transform is not given, the transform is often derived from the value of the {AbstractNode#node_name}
|
67
|
+
# property. (optional, default: nil)
|
68
|
+
# opts - An optional Hash of options hints about how to convert the node. (optional, default: nil)
|
69
|
+
#
|
70
|
+
# Returns the [String] result.
|
71
|
+
def convert node, transform = nil, opts = nil
|
72
|
+
raise ::NotImplementedError, %(#{self.class} (backend: #{@backend}) must implement the ##{__method__} method)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Public: Reports whether the current converter is able to convert this node (by its transform name). Used by the
|
76
|
+
# {CompositeConverter} to select which converter to use to handle a given node. Returns true by default.
|
77
|
+
#
|
78
|
+
# transform - the String name of the node transformation (typically the node name).
|
79
|
+
#
|
80
|
+
# Returns a [Boolean] indicating whether this converter can handle the specified transform.
|
81
|
+
def handles? transform
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
# Public: Derive backend traits (basebackend, filetype, outfilesuffix, htmlsyntax) from the given backend.
|
86
|
+
#
|
87
|
+
# backend - the String backend from which to derive the traits
|
88
|
+
# basebackend - the String basebackend to use in favor of deriving one from the backend (optional, default: nil)
|
89
|
+
#
|
90
|
+
# Returns the backend traits for the given backend as a [Hash].
|
91
|
+
def self.derive_backend_traits backend, basebackend = nil
|
92
|
+
return {} unless backend
|
93
|
+
if (outfilesuffix = DEFAULT_EXTENSIONS[(basebackend ||= backend.sub TrailingDigitsRx, '')])
|
94
|
+
filetype = outfilesuffix.slice 1, outfilesuffix.length
|
95
|
+
else
|
96
|
+
outfilesuffix = %(.#{filetype = basebackend})
|
97
|
+
end
|
98
|
+
filetype == 'html' ?
|
99
|
+
{ basebackend: basebackend, filetype: filetype, htmlsyntax: 'html', outfilesuffix: outfilesuffix } :
|
100
|
+
{ basebackend: basebackend, filetype: filetype, outfilesuffix: outfilesuffix }
|
101
|
+
end
|
102
|
+
|
103
|
+
module BackendTraits
|
104
|
+
def basebackend value = nil
|
105
|
+
value ? ((backend_traits value)[:basebackend] = value) : backend_traits[:basebackend]
|
106
|
+
end
|
107
|
+
|
108
|
+
def filetype value = nil
|
109
|
+
value ? (backend_traits[:filetype] = value) : backend_traits[:filetype]
|
110
|
+
end
|
111
|
+
|
112
|
+
def htmlsyntax value = nil
|
113
|
+
value ? (backend_traits[:htmlsyntax] = value) : backend_traits[:htmlsyntax]
|
114
|
+
end
|
115
|
+
|
116
|
+
def outfilesuffix value = nil
|
117
|
+
value ? (backend_traits[:outfilesuffix] = value) : backend_traits[:outfilesuffix]
|
118
|
+
end
|
119
|
+
|
120
|
+
def supports_templates value = true
|
121
|
+
backend_traits[:supports_templates] = value
|
122
|
+
end
|
123
|
+
|
124
|
+
def supports_templates?
|
125
|
+
backend_traits[:supports_templates]
|
126
|
+
end
|
127
|
+
|
128
|
+
def init_backend_traits value = nil
|
129
|
+
@backend_traits = value || {}
|
130
|
+
end
|
131
|
+
|
132
|
+
def backend_traits basebackend = nil
|
133
|
+
@backend_traits ||= Converter.derive_backend_traits @backend, basebackend
|
134
|
+
end
|
135
|
+
|
136
|
+
alias backend_info backend_traits
|
137
|
+
|
138
|
+
# Deprecated: Use {Converter.derive_backend_traits} instead.
|
139
|
+
def self.derive_backend_traits backend, basebackend = nil
|
140
|
+
Converter.derive_backend_traits backend, basebackend
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# A module that contributes the +register_for+ method for registering a converter with the default registry.
|
145
|
+
module Config
|
146
|
+
# Public: Registers this {Converter} class with the default registry to handle the specified backend name(s).
|
147
|
+
#
|
148
|
+
# backends - One or more String backend names with which to associate this {Converter} class.
|
149
|
+
#
|
150
|
+
# Returns nothing.
|
151
|
+
def register_for *backends
|
152
|
+
Converter.register self, *(backends.map {|backend| backend.to_s })
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# A reusable module for registering and instantiating {Converter Converter} classes used to convert an {AbstractNode}
|
157
|
+
# to an output (aka backend) format such as HTML or DocBook.
|
6
158
|
#
|
7
|
-
#
|
159
|
+
# {Converter Converter} objects are instantiated by passing a String backend name and, optionally, an options Hash to
|
160
|
+
# the {Factory#create} method. The backend can be thought of as an intent to convert a document to a specified format.
|
8
161
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# * optionally associating the converter with one or more backends using
|
12
|
-
# the {#register_for} DSL method imported by the {Config Converter::Config} module
|
162
|
+
# Applications interact with the factory either through the global, static registry mixed into the {Converter
|
163
|
+
# Converter} module or a concrete class that includes this module such as {CustomFactory}. For example:
|
13
164
|
#
|
14
165
|
# Examples
|
15
166
|
#
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
#
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
167
|
+
# converter = Asciidoctor::Converter.create 'html5', htmlsyntax: 'xml'
|
168
|
+
module Factory
|
169
|
+
# Public: Create an instance of DefaultProxyFactory or CustomFactory, depending on whether the proxy_default keyword
|
170
|
+
# arg is set (true by default), and optionally seed it with the specified converters map. If proxy_default is set,
|
171
|
+
# entries in the proxy registry are preferred over matching entries from the default registry.
|
172
|
+
#
|
173
|
+
# converters - An optional Hash of converters to use in place of ones in the default registry. The keys are
|
174
|
+
# backend names and the values are converter classes or instances.
|
175
|
+
# proxy_default - A Boolean keyword arg indicating whether to proxy the default registry (optional, default: true).
|
176
|
+
#
|
177
|
+
# Returns a Factory instance (DefaultFactoryProxy or CustomFactory) seeded with the optional converters map.
|
178
|
+
def self.new converters = nil, proxy_default: true
|
179
|
+
proxy_default ? (DefaultFactoryProxy.new converters) : (CustomFactory.new converters)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Deprecated: Maps the old default factory instance holder to the Converter module.
|
183
|
+
def self.default *args
|
184
|
+
Converter
|
185
|
+
end
|
186
|
+
|
187
|
+
# Deprecated: Maps the create method on the old default factory instance holder to the Converter module.
|
188
|
+
def self.create backend, opts = {}
|
189
|
+
default.create backend, opts
|
190
|
+
end
|
191
|
+
|
192
|
+
# Public: Register a custom converter with this factory to handle conversion for the specified backends. If the
|
193
|
+
# backend is an asterisk (i.e., +*+), the converter will handle any backend for which a converter is not registered.
|
194
|
+
#
|
195
|
+
# converter - The Converter class to register.
|
196
|
+
# backends - One or more String backend names that this converter should be registered to handle.
|
197
|
+
#
|
198
|
+
# Returns nothing
|
199
|
+
def register converter, *backends
|
200
|
+
backends.each {|backend| backend == '*' ? (registry.default = converter) : (registry[backend] = converter) }
|
201
|
+
end
|
202
|
+
|
203
|
+
# Public: Lookup the custom converter registered with this factory to handle the specified backend.
|
204
|
+
#
|
205
|
+
# backend - The String backend name.
|
206
|
+
#
|
207
|
+
# Returns the [Converter] class registered to convert the specified backend or nil if no match is found.
|
208
|
+
def for backend
|
209
|
+
registry[backend]
|
210
|
+
end
|
211
|
+
|
212
|
+
# Public: Create a new Converter object that can be used to convert {AbstractNode}s to the format associated with
|
213
|
+
# the backend. This method accepts an optional Hash of options that are passed to the converter's constructor.
|
214
|
+
#
|
215
|
+
# If a custom Converter is found to convert the specified backend, it's instantiated (if necessary) and returned
|
216
|
+
# immediately. If a custom Converter is not found, an attempt is made to find a built-in converter. If the
|
217
|
+
# +:template_dirs+ key is found in the Hash passed as the second argument, a {CompositeConverter} is created that
|
218
|
+
# delegates to a {TemplateConverter} and, if found, the built-in converter. If the +:template_dirs+ key is not
|
219
|
+
# found, the built-in converter is returned or nil if no converter is found.
|
220
|
+
#
|
221
|
+
# backend - the String backend name.
|
222
|
+
# opts - a Hash of options to customize creation; also passed to the converter's constructor:
|
223
|
+
# :template_dirs - a String Array of directories used to instantiate a {TemplateConverter} (optional).
|
224
|
+
# :delegate_backend - a backend String of the last converter in the {CompositeConverter} chain (optional).
|
225
|
+
#
|
226
|
+
# Returns the [Converter] instance.
|
227
|
+
def create backend, opts = {}
|
228
|
+
if (converter = self.for backend)
|
229
|
+
converter = converter.new backend, opts if ::Class === converter
|
230
|
+
if (template_dirs = opts[:template_dirs]) && BackendTraits === converter && converter.supports_templates?
|
231
|
+
CompositeConverter.new backend, (TemplateConverter.new backend, template_dirs, opts), converter, backend_traits_source: converter
|
232
|
+
else
|
233
|
+
converter
|
234
|
+
end
|
235
|
+
elsif (template_dirs = opts[:template_dirs])
|
236
|
+
if (delegate_backend = opts[:delegate_backend]) && (converter = self.for delegate_backend)
|
237
|
+
converter = converter.new delegate_backend, opts if ::Class === converter
|
238
|
+
CompositeConverter.new backend, (TemplateConverter.new backend, template_dirs, opts), converter, backend_traits_source: converter
|
62
239
|
else
|
63
|
-
|
64
|
-
backends.include? name
|
65
|
-
end
|
240
|
+
TemplateConverter.new backend, template_dirs, opts
|
66
241
|
end
|
67
|
-
nil
|
68
242
|
end
|
69
243
|
end
|
70
244
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
245
|
+
# Public: Get the Hash of Converter classes keyed by backend name. Intended for testing only.
|
246
|
+
def converters
|
247
|
+
registry.merge
|
248
|
+
end
|
75
249
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
:htmlsyntax => syntax
|
93
|
-
}
|
250
|
+
private
|
251
|
+
|
252
|
+
def registry
|
253
|
+
raise ::NotImplementedError, %(#{Factory} subclass #{self.class} must implement the ##{__method__} method)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class CustomFactory
|
258
|
+
include Factory
|
259
|
+
|
260
|
+
def initialize seed_registry = nil
|
261
|
+
if seed_registry
|
262
|
+
seed_registry.default = seed_registry.delete '*'
|
263
|
+
@registry = seed_registry
|
264
|
+
else
|
265
|
+
@registry = {}
|
94
266
|
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Public: Unregister all Converter classes that are registered with this factory. Intended for testing only.
|
270
|
+
#
|
271
|
+
# Returns nothing.
|
272
|
+
def unregister_all
|
273
|
+
registry.clear.default = nil
|
274
|
+
end
|
275
|
+
|
276
|
+
private
|
277
|
+
|
278
|
+
attr_reader :registry
|
279
|
+
end
|
280
|
+
|
281
|
+
# Mixed into the {Converter} module to provide the global registry of converters that are registered statically.
|
282
|
+
#
|
283
|
+
# This registry includes built-in converters for {Html5Converter HTML 5}, {DocBook5Converter DocBook 5} and
|
284
|
+
# {ManPageConverter man(ual) page}, as well as any custom converters that have been discovered or explicitly
|
285
|
+
# registered. Converter registration is synchronized (where applicable) and is thus guaranteed to be thread safe.
|
286
|
+
module DefaultFactory
|
287
|
+
include Factory
|
288
|
+
|
289
|
+
private
|
290
|
+
|
291
|
+
@@registry = {}
|
95
292
|
|
96
|
-
|
97
|
-
|
98
|
-
|
293
|
+
def registry
|
294
|
+
@@registry
|
295
|
+
end
|
296
|
+
|
297
|
+
unless RUBY_ENGINE == 'opal' # the following block adds support for synchronization and lazy registration
|
298
|
+
public
|
299
|
+
|
300
|
+
def register converter, *backends
|
301
|
+
if @@mutex.owned?
|
302
|
+
backends.each {|backend| backend == '*' ? (@@catch_all = converter) : (@@registry = @@registry.merge backend => converter) }
|
99
303
|
else
|
100
|
-
|
304
|
+
@@mutex.synchronize { register converter, *backends }
|
101
305
|
end
|
102
306
|
end
|
103
307
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
backend_info[:basebackend]
|
308
|
+
def unregister_all
|
309
|
+
@@mutex.synchronize do
|
310
|
+
@@registry = @@registry.select {|backend| PROVIDED[backend] }
|
311
|
+
@@catch_all = nil
|
109
312
|
end
|
110
313
|
end
|
111
314
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
315
|
+
def for backend
|
316
|
+
@@registry.fetch backend do
|
317
|
+
PROVIDED[backend] ? (@@mutex.synchronize do
|
318
|
+
# require is thread-safe, so no reason to refetch
|
319
|
+
require PROVIDED[backend]
|
320
|
+
@@registry[backend]
|
321
|
+
end) : catch_all
|
117
322
|
end
|
118
323
|
end
|
119
324
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
325
|
+
PROVIDED = {
|
326
|
+
'docbook5' => %(#{__dir__}/converter/docbook5),
|
327
|
+
'html5' => %(#{__dir__}/converter/html5),
|
328
|
+
'manpage' => %(#{__dir__}/converter/manpage),
|
329
|
+
}
|
330
|
+
|
331
|
+
private
|
332
|
+
|
333
|
+
def catch_all
|
334
|
+
@@catch_all
|
126
335
|
end
|
127
336
|
|
128
|
-
|
129
|
-
|
337
|
+
@@catch_all = nil
|
338
|
+
@@mutex = ::Mutex.new
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
class DefaultFactoryProxy < CustomFactory
|
343
|
+
include DefaultFactory # inserts module into ancestors immediately after superclass
|
344
|
+
|
345
|
+
unless RUBY_ENGINE == 'opal'
|
346
|
+
def unregister_all
|
347
|
+
super
|
348
|
+
@registry.clear.default = nil
|
130
349
|
end
|
131
350
|
|
132
|
-
def
|
133
|
-
|
351
|
+
def for backend
|
352
|
+
@registry.fetch(backend) { super }
|
134
353
|
end
|
135
|
-
end
|
136
354
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
#
|
142
|
-
# Returns nothing
|
143
|
-
def included converter
|
144
|
-
converter.extend Config
|
355
|
+
private
|
356
|
+
|
357
|
+
def catch_all
|
358
|
+
@registry.default || super
|
145
359
|
end
|
146
360
|
end
|
361
|
+
end
|
147
362
|
|
148
|
-
|
149
|
-
|
363
|
+
# Internal: Mixes the {Config} module into any class that includes the {Converter} module. Additionally, mixes the
|
364
|
+
# {BackendTraits} method into instances of this class.
|
365
|
+
#
|
366
|
+
# into - The Class into which the {Converter} module is being included.
|
367
|
+
#
|
368
|
+
# Returns nothing.
|
369
|
+
def self.included into
|
370
|
+
into.send :include, BackendTraits
|
371
|
+
into.extend Config
|
372
|
+
end
|
373
|
+
private_class_method :included # use separate declaration for Ruby 2.0.x
|
374
|
+
|
375
|
+
# An abstract base class for defining converters that can be used to convert {AbstractNode} objects in a parsed
|
376
|
+
# AsciiDoc document to a backend format such as HTML or DocBook.
|
377
|
+
class Base
|
378
|
+
include Logging
|
379
|
+
include Converter
|
150
380
|
|
151
|
-
# Public:
|
381
|
+
# Public: Converts an {AbstractNode} by delegating to a method that matches the transform value.
|
152
382
|
#
|
153
|
-
#
|
154
|
-
# opts
|
383
|
+
# This method looks for a method whose name matches the transform prefixed with "convert_" to dispatch to. If the
|
384
|
+
# +opts+ argument is non-nil, this method assumes the dispatch method accepts two arguments, the node and an options
|
385
|
+
# Hash. The options Hash may be used by converters to delegate back to the top-level converter. Currently, this
|
386
|
+
# feature is used for the outline transform. If the +opts+ argument is nil, this method assumes the dispatch method
|
387
|
+
# accepts the node as its only argument.
|
155
388
|
#
|
156
|
-
#
|
157
|
-
def
|
158
|
-
|
159
|
-
|
389
|
+
# See {Converter#convert} for details about the arguments and return value.
|
390
|
+
def convert node, transform = node.node_name, opts = nil
|
391
|
+
opts ? (send 'convert_' + transform, node, opts) : (send 'convert_' + transform, node)
|
392
|
+
rescue
|
393
|
+
raise unless ::NoMethodError === (ex = $!) && ex.receiver == self && ex.name.to_s == transform
|
394
|
+
logger.warn %(missing convert handler for #{ex.name} node in #{@backend} backend (#{self.class}))
|
395
|
+
nil
|
160
396
|
end
|
161
397
|
|
162
|
-
|
163
|
-
|
164
|
-
#
|
165
|
-
# owner - The CompositeConverter instance
|
166
|
-
#
|
167
|
-
# Returns nothing
|
168
|
-
def composed owner
|
398
|
+
def handles? transform
|
399
|
+
respond_to? %(convert_#{transform})
|
169
400
|
end
|
170
|
-
=end
|
171
401
|
|
172
|
-
# Public: Converts
|
173
|
-
# with additional options. If a transform is not specified, implementations
|
174
|
-
# typically derive one from the {AbstractNode#node_name} property.
|
402
|
+
# Public: Converts the {AbstractNode} using only its converted content.
|
175
403
|
#
|
176
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
# the transform to select a template to render.
|
180
|
-
#
|
181
|
-
# node - The concrete instance of AbstractNode to convert
|
182
|
-
# transform - An optional String transform that hints at which transformation
|
183
|
-
# should be applied to this node. If a transform is not specified,
|
184
|
-
# the transform is typically derived from the value of the
|
185
|
-
# node's node_name property. (optional, default: nil)
|
186
|
-
# opts - An optional Hash of options that provide additional hints about
|
187
|
-
# how to convert the node. (optional, default: {})
|
188
|
-
#
|
189
|
-
# Returns the [String] result
|
190
|
-
def convert node, transform = nil, opts = {}
|
191
|
-
raise ::NotImplementedError
|
404
|
+
# Returns the converted [String] content.
|
405
|
+
def content_only node
|
406
|
+
node.content
|
192
407
|
end
|
193
408
|
|
194
|
-
|
195
|
-
|
196
|
-
# Alias for backward compatibility.
|
197
|
-
alias convert_with_options convert
|
198
|
-
end
|
199
|
-
|
200
|
-
# A module that can be used to mix the {#write} method into a {Converter}
|
201
|
-
# implementation to allow the converter to control how the output is written
|
202
|
-
# to disk.
|
203
|
-
module Writer
|
204
|
-
# Public: Writes the output to the specified target file name or stream.
|
205
|
-
#
|
206
|
-
# output - The output String to write
|
207
|
-
# target - The String file name or stream object to which the output should
|
208
|
-
# be written.
|
409
|
+
# Public: Skips conversion of the {AbstractNode}.
|
209
410
|
#
|
210
|
-
# Returns nothing
|
211
|
-
def
|
212
|
-
if target.respond_to? :write
|
213
|
-
target.write output.chomp
|
214
|
-
# ensure there's a trailing endline to be nice to terminals
|
215
|
-
target.write LF
|
216
|
-
else
|
217
|
-
::IO.write target, output
|
218
|
-
end
|
219
|
-
nil
|
220
|
-
end
|
411
|
+
# Returns nothing.
|
412
|
+
def skip node; end
|
221
413
|
end
|
222
414
|
|
223
|
-
|
224
|
-
|
225
|
-
# Public: Does not write output
|
226
|
-
def write output, target
|
227
|
-
end
|
228
|
-
end
|
415
|
+
extend DefaultFactory # exports static methods
|
416
|
+
end
|
229
417
|
end
|
230
|
-
|
231
|
-
require 'asciidoctor/converter/base'
|
232
|
-
require 'asciidoctor/converter/factory'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# NOTE remove once minimum required Ruby version is at least 2.4
|
3
|
+
# NOTE use `send :prepend` to be nice to Ruby 2.0
|
4
|
+
Float.send :prepend, (Module.new do
|
5
|
+
def truncate *args
|
6
|
+
if args.length == 1
|
7
|
+
if (precision = Integer args.shift) == 0
|
8
|
+
super
|
9
|
+
elsif precision > 0
|
10
|
+
precision_factor = 10.0 ** precision
|
11
|
+
(self * precision_factor).to_i / precision_factor
|
12
|
+
else
|
13
|
+
precision_factor = 10 ** precision.abs
|
14
|
+
(self / precision_factor).to_i * precision_factor
|
15
|
+
end
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end) if (Float.instance_method :truncate).arity == 0
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# NOTE remove once minimum required Ruby version is at least 2.6
|
3
|
+
# NOTE use `send :prepend` to be nice to Ruby 2.0
|
4
|
+
Hash.send :prepend, (Module.new do
|
5
|
+
def merge *args
|
6
|
+
(len = args.length) < 1 ? dup : (len > 1 ? args.inject(self) {|acc, arg| acc.merge arg } : (super args[0]))
|
7
|
+
end
|
8
|
+
end) if (Hash.instance_method :merge).arity == 1
|