asciidoctor 0.1.4 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +209 -25
- data/{LICENSE → LICENSE.adoc} +4 -3
- data/README.adoc +392 -395
- data/Rakefile +94 -137
- data/benchmark/benchmark.rb +127 -0
- data/benchmark/sample-data/mdbasics.adoc +334 -0
- data/bin/asciidoctor +5 -8
- data/bin/asciidoctor-safe +4 -8
- data/compat/asciidoc.conf +78 -11
- data/compat/font-awesome-3-compat.css +397 -0
- data/data/stylesheets/asciidoctor-default.css +399 -0
- data/data/stylesheets/coderay-asciidoctor.css +89 -0
- data/features/open_block.feature +92 -0
- data/features/pass_block.feature +66 -0
- data/features/step_definitions.rb +42 -0
- data/features/text_formatting.feature +55 -0
- data/features/xref.feature +116 -0
- data/lib/asciidoctor.rb +1155 -605
- data/lib/asciidoctor/abstract_block.rb +157 -71
- data/lib/asciidoctor/abstract_node.rb +150 -93
- data/lib/asciidoctor/attribute_list.rb +85 -90
- data/lib/asciidoctor/block.rb +51 -24
- data/lib/asciidoctor/callouts.rb +4 -7
- data/lib/asciidoctor/cli.rb +3 -0
- data/lib/asciidoctor/cli/invoker.rb +86 -76
- data/lib/asciidoctor/cli/options.rb +111 -61
- data/lib/asciidoctor/converter.rb +232 -0
- data/lib/asciidoctor/converter/base.rb +58 -0
- data/lib/asciidoctor/converter/composite.rb +66 -0
- data/lib/asciidoctor/converter/docbook45.rb +94 -0
- data/lib/asciidoctor/converter/docbook5.rb +684 -0
- data/lib/asciidoctor/converter/factory.rb +225 -0
- data/lib/asciidoctor/converter/html5.rb +1081 -0
- data/lib/asciidoctor/converter/template.rb +296 -0
- data/lib/asciidoctor/core_ext.rb +7 -0
- data/lib/asciidoctor/core_ext/object/nil_or_empty.rb +23 -0
- data/lib/asciidoctor/core_ext/string/chr.rb +6 -0
- data/lib/asciidoctor/core_ext/symbol/length.rb +6 -0
- data/lib/asciidoctor/document.rb +590 -304
- data/lib/asciidoctor/extensions.rb +1100 -308
- data/lib/asciidoctor/helpers.rb +109 -46
- data/lib/asciidoctor/inline.rb +16 -9
- data/lib/asciidoctor/list.rb +23 -15
- data/lib/asciidoctor/opal_ext.rb +4 -0
- data/lib/asciidoctor/opal_ext/comparable.rb +38 -0
- data/lib/asciidoctor/opal_ext/dir.rb +13 -0
- data/lib/asciidoctor/opal_ext/error.rb +2 -0
- data/lib/asciidoctor/opal_ext/file.rb +125 -0
- data/lib/asciidoctor/{lexer.rb → parser.rb} +646 -455
- data/lib/asciidoctor/path_resolver.rb +141 -77
- data/lib/asciidoctor/reader.rb +257 -187
- data/lib/asciidoctor/section.rb +12 -16
- data/lib/asciidoctor/stylesheets.rb +91 -0
- data/lib/asciidoctor/substitutors.rb +1548 -0
- data/lib/asciidoctor/table.rb +73 -57
- data/lib/asciidoctor/timings.rb +39 -0
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +22 -14
- data/man/asciidoctor.adoc +18 -10
- data/test/attributes_test.rb +314 -14
- data/test/blocks_test.rb +763 -118
- data/test/converter_test.rb +352 -0
- data/test/document_test.rb +518 -199
- data/test/extensions_test.rb +273 -103
- data/test/fixtures/asciidoc_index.txt +27 -13
- data/test/fixtures/basic-docinfo.xml +1 -1
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/docinfo.xml +1 -1
- data/test/fixtures/include-file.asciidoc +2 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/invoker_test.rb +173 -61
- data/test/links_test.rb +97 -21
- data/test/lists_test.rb +181 -22
- data/test/options_test.rb +86 -2
- data/test/paragraphs_test.rb +47 -5
- data/test/{lexer_test.rb → parser_test.rb} +128 -57
- data/test/paths_test.rb +36 -1
- data/test/preamble_test.rb +25 -17
- data/test/reader_test.rb +404 -249
- data/test/sections_test.rb +623 -58
- data/test/substitutions_test.rb +609 -132
- data/test/tables_test.rb +198 -24
- data/test/test_helper.rb +101 -31
- data/test/text_test.rb +88 -31
- metadata +160 -64
- data/Gemfile +0 -12
- data/Guardfile +0 -18
- data/asciidoctor.gemspec +0 -143
- data/lib/asciidoctor/backends/_stylesheets.rb +0 -466
- data/lib/asciidoctor/backends/base_template.rb +0 -114
- data/lib/asciidoctor/backends/docbook45.rb +0 -774
- data/lib/asciidoctor/backends/docbook5.rb +0 -103
- data/lib/asciidoctor/backends/html5.rb +0 -1214
- data/lib/asciidoctor/renderer.rb +0 -259
- data/lib/asciidoctor/substituters.rb +0 -1083
- data/test/fixtures/asciidoc.txt +0 -105
- data/test/fixtures/ascshort.txt +0 -32
- data/test/fixtures/list_elements.asciidoc +0 -10
- data/test/renderer_test.rb +0 -162
@@ -1,443 +1,1235 @@
|
|
1
1
|
module Asciidoctor
|
2
|
+
# Extensions provide a way to participate in the parsing and converting
|
3
|
+
# phases of the AsciiDoc processor or extend the AsciiDoc syntax.
|
4
|
+
#
|
5
|
+
# The various extensions participate in AsciiDoc processing as follows:
|
6
|
+
#
|
7
|
+
# 1. After the source lines are normalized, {Preprocessor}s modify or replace
|
8
|
+
# the source lines before parsing begins. {IncludeProcessor}s are used to
|
9
|
+
# process include directives for targets which they claim to handle.
|
10
|
+
# 2. The Parser parses the block-level content into an abstract syntax tree.
|
11
|
+
# Custom blocks and block macros are processed by associated {BlockProcessor}s
|
12
|
+
# and {BlockMacroProcessor}s, respectively.
|
13
|
+
# 3. {Treeprocessor}s are run on the abstract syntax tree.
|
14
|
+
# 4. Conversion of the document begins, at which point inline markup is processed
|
15
|
+
# and converted. Custom inline macros are processed by associated {InlineMacroProcessor}s.
|
16
|
+
# 5. {Postprocessor}s modify or replace the converted document.
|
17
|
+
# 6. The output is written to the output stream.
|
18
|
+
#
|
19
|
+
# Extensions may be registered globally using the {Extensions.register} method
|
20
|
+
# or added to a custom {Registry} instance and passed as an option to a single
|
21
|
+
# Asciidoctor processor.
|
2
22
|
module Extensions
|
3
|
-
|
23
|
+
|
24
|
+
# Public: An abstract base class for document and syntax processors.
|
25
|
+
#
|
26
|
+
# This class provides access to a class-level Hash for holding default
|
27
|
+
# configuration options defined using the {Processor.option} method. This
|
28
|
+
# style of default configuration is specific to the native Ruby environment
|
29
|
+
# and is only consulted inside the initializer. An overriding configuration
|
30
|
+
# Hash can be passed to the initializer. Once the processor is initialized,
|
31
|
+
# the configuration is accessed using the {Processor#config} instance variable.
|
32
|
+
#
|
33
|
+
# Instances of the Processor class provide convenience methods for creating
|
34
|
+
# AST nodes, such as Block and Inline, and for parsing child content.
|
35
|
+
class Processor
|
4
36
|
class << self
|
5
|
-
|
6
|
-
|
37
|
+
# Public: Get the static configuration for this processor class.
|
38
|
+
#
|
39
|
+
# Returns a configuration [Hash]
|
40
|
+
def config
|
41
|
+
@config ||= {}
|
42
|
+
end
|
43
|
+
|
44
|
+
# Public: Assigns a default value for the specified option that gets
|
45
|
+
# applied to all instances of this processor.
|
46
|
+
#
|
47
|
+
# Examples
|
48
|
+
#
|
49
|
+
# option :contexts, [:open, :paragraph]
|
50
|
+
#
|
51
|
+
# Returns nothing
|
52
|
+
def option key, default_value
|
53
|
+
config[key] = default_value
|
7
54
|
end
|
8
55
|
|
9
|
-
|
56
|
+
# Include the DSL class for this processor into this processor class or instance.
|
57
|
+
#
|
58
|
+
# This method automatically detects whether to use the include or extend keyword
|
59
|
+
# based on what is appropriate.
|
60
|
+
#
|
61
|
+
# Returns nothing
|
62
|
+
def use_dsl
|
63
|
+
if self.name.nil_or_empty?
|
64
|
+
# NOTE contants(false) doesn't exist in Ruby 1.8.7
|
65
|
+
#include const_get :DSL if constants(false).grep :DSL
|
66
|
+
include const_get :DSL if constants.grep :DSL
|
67
|
+
else
|
68
|
+
# NOTE contants(false) doesn't exist in Ruby 1.8.7
|
69
|
+
#extend const_get :DSL if constants(false).grep :DSL
|
70
|
+
extend const_get :DSL if constants.grep :DSL
|
71
|
+
end
|
10
72
|
end
|
73
|
+
alias :extend_dsl :use_dsl
|
74
|
+
alias :include_dsl :use_dsl
|
11
75
|
end
|
12
|
-
end
|
13
76
|
|
14
|
-
|
15
|
-
|
16
|
-
|
77
|
+
# Public: Get the configuration Hash for this processor instance.
|
78
|
+
attr_reader :config
|
79
|
+
|
80
|
+
def initialize config = {}
|
81
|
+
@config = self.class.config.merge config
|
17
82
|
end
|
18
83
|
|
19
|
-
def
|
20
|
-
@
|
84
|
+
def update_config config
|
85
|
+
@config.update config
|
21
86
|
end
|
22
87
|
|
23
|
-
|
24
|
-
|
25
|
-
# is name of block if block is given
|
26
|
-
# having a name makes it easier to unregister an extension
|
27
|
-
def register extension = nil, &block
|
28
|
-
if block_given?
|
29
|
-
registered << block
|
30
|
-
elsif extension
|
31
|
-
registered << resolve_class(extension)
|
32
|
-
end
|
88
|
+
def process *args
|
89
|
+
raise ::NotImplementedError
|
33
90
|
end
|
34
91
|
|
35
|
-
def
|
36
|
-
|
92
|
+
def create_block parent, context, source, attrs, opts = {}
|
93
|
+
Block.new parent, context, { :source => source, :attributes => attrs }.merge(opts)
|
37
94
|
end
|
38
95
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
96
|
+
def create_image_block parent, attrs, opts = {}
|
97
|
+
create_block parent, :image, nil, attrs, opts
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_inline parent, context, text, opts = {}
|
101
|
+
Inline.new parent, context, text, opts
|
102
|
+
end
|
103
|
+
|
104
|
+
# Public: Parses blocks in the content and attaches the block to the parent.
|
105
|
+
#
|
106
|
+
# Returns nothing
|
107
|
+
#--
|
108
|
+
# QUESTION is parse_content the right method name? should we wrap in open block automatically?
|
109
|
+
def parse_content parent, content, attributes = {}
|
110
|
+
reader = (content.is_a? Reader) ? reader : (Reader.new content)
|
111
|
+
while reader.has_more_lines?
|
112
|
+
block = Parser.next_block(reader, parent, attributes)
|
113
|
+
parent << block if block
|
48
114
|
end
|
115
|
+
nil
|
49
116
|
end
|
50
117
|
|
51
|
-
|
52
|
-
|
118
|
+
# TODO fill out remaining methods
|
119
|
+
[
|
120
|
+
[:create_paragraph, :create_block, :paragraph],
|
121
|
+
[:create_open_block, :create_block, :open],
|
122
|
+
[:create_example_block, :create_block, :example],
|
123
|
+
[:create_pass_block, :create_block, :pass],
|
124
|
+
[:create_listing_block, :create_block, :listing],
|
125
|
+
[:create_literal_block, :create_block, :literal],
|
126
|
+
[:create_anchor, :create_inline, :anchor]
|
127
|
+
].each do |method_name, delegate_method_name, context|
|
128
|
+
define_method method_name do |*args|
|
129
|
+
send delegate_method_name, *args.dup.insert(1, context)
|
130
|
+
end
|
53
131
|
end
|
54
132
|
end
|
55
133
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
@block_macro_processor_cache = {}
|
76
|
-
@inline_macros = {}
|
77
|
-
@inline_macro_processor_cache = {}
|
78
|
-
|
79
|
-
Extensions.registered.each do |extension|
|
80
|
-
if extension.is_a? Proc
|
81
|
-
register document, &extension
|
82
|
-
else
|
83
|
-
extension.activate self, document
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def preprocessor processor, position = :<<
|
89
|
-
processor = resolve_processor_class processor
|
90
|
-
if position == :<< || @preprocessors.empty?
|
91
|
-
@preprocessors.push processor
|
92
|
-
elsif position == :>>
|
93
|
-
@preprocessors.unshift processor
|
134
|
+
# Internal: Overlays a builder DSL for configuring the Processor instance.
|
135
|
+
# Includes a method to define configuration options and another to define the
|
136
|
+
# {Processor#process} method.
|
137
|
+
module ProcessorDsl
|
138
|
+
def option key, value
|
139
|
+
config[key] = value
|
140
|
+
end
|
141
|
+
|
142
|
+
def process *args, &block
|
143
|
+
# need to check for both block/proc and lambda
|
144
|
+
# TODO need test for this!
|
145
|
+
#if block_given? || (args.size == 1 && ((block = args[0]).is_a? ::Proc))
|
146
|
+
if block_given?
|
147
|
+
@process_block = block
|
148
|
+
elsif @process_block
|
149
|
+
# NOTE Proc automatically expands a single array argument
|
150
|
+
# ...but lambda doesn't (and we want to accept lambdas too)
|
151
|
+
# TODO need a test for this!
|
152
|
+
@process_block.call(*args)
|
94
153
|
else
|
95
|
-
|
154
|
+
raise ::NotImplementedError
|
96
155
|
end
|
97
156
|
end
|
157
|
+
#alias :process_with :process
|
98
158
|
|
99
|
-
def
|
100
|
-
|
159
|
+
def process_block_given?
|
160
|
+
defined? @process_block
|
101
161
|
end
|
162
|
+
end
|
102
163
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
164
|
+
# Public: Preprocessors are run after the source text is split into lines and
|
165
|
+
# normalized, but before parsing begins.
|
166
|
+
#
|
167
|
+
# Prior to invoking the preprocessor, Asciidoctor splits the source text into
|
168
|
+
# lines and normalizes them. The normalize process strips trailing whitespace
|
169
|
+
# from each line and leaves behind a line-feed character (i.e., "\n").
|
170
|
+
#
|
171
|
+
# Asciidoctor passes a reference to the Reader and a copy of the lines Array
|
172
|
+
# to the {Processor#process} method of an instance of each registered
|
173
|
+
# Preprocessor. The Preprocessor modifies the Array as necessary and either
|
174
|
+
# returns a reference to the same Reader or a reference to a new Reader.
|
175
|
+
#
|
176
|
+
# Preprocessor implementations must extend the Preprocessor class.
|
177
|
+
class Preprocessor < Processor
|
178
|
+
def process document, reader
|
179
|
+
raise ::NotImplementedError
|
107
180
|
end
|
181
|
+
end
|
182
|
+
Preprocessor::DSL = ProcessorDsl
|
108
183
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
184
|
+
# Public: Treeprocessors are run on the Document after the source has been
|
185
|
+
# parsed into an abstract syntax tree (AST), as represented by the Document
|
186
|
+
# object and its child Node objects (e.g., Section, Block, List, ListItem).
|
187
|
+
#
|
188
|
+
# Asciidoctor invokes the {Processor#process} method on an instance of each
|
189
|
+
# registered Treeprocessor.
|
190
|
+
#
|
191
|
+
# Treeprocessor implementations must extend Treeprocessor.
|
192
|
+
#--
|
193
|
+
# QUESTION should the treeprocessor get invoked after parse header too?
|
194
|
+
class Treeprocessor < Processor
|
195
|
+
def process document
|
196
|
+
raise ::NotImplementedError
|
197
|
+
end
|
198
|
+
end
|
199
|
+
Treeprocessor::DSL = ProcessorDsl
|
200
|
+
|
201
|
+
# Public: Postprocessors are run after the document is converted, but before
|
202
|
+
# it is written to the output stream.
|
203
|
+
#
|
204
|
+
# Asciidoctor passes a reference to the converted String to the {Processor#process}
|
205
|
+
# method of each registered Postprocessor. The Preprocessor modifies the
|
206
|
+
# String as necessary and returns the String replacement.
|
207
|
+
#
|
208
|
+
# The markup format in the String is determined by the backend used to convert
|
209
|
+
# the Document. The backend and be looked up using the backend method on the
|
210
|
+
# Document object, as well as various backend-related document attributes.
|
211
|
+
#
|
212
|
+
# TIP: Postprocessors can also be used to relocate assets needed by the published
|
213
|
+
# document.
|
214
|
+
#
|
215
|
+
# Postprocessor implementations must Postprocessor.
|
216
|
+
class Postprocessor < Processor
|
217
|
+
def process document, output
|
218
|
+
raise ::NotImplementedError
|
219
|
+
end
|
220
|
+
end
|
221
|
+
Postprocessor::DSL = ProcessorDsl
|
222
|
+
|
223
|
+
# Public: IncludeProcessors are used to process `include::<target>[]`
|
224
|
+
# directives in the source document.
|
225
|
+
#
|
226
|
+
# When Asciidoctor comes across a `include::<target>[]` directive in the
|
227
|
+
# source document, it iterates through the IncludeProcessors and delegates
|
228
|
+
# the work of reading the content to the first processor that identifies
|
229
|
+
# itself as capable of handling that target.
|
230
|
+
#
|
231
|
+
# IncludeProcessor implementations must extend IncludeProcessor.
|
232
|
+
#--
|
233
|
+
# TODO add file extension or regexp to shortcut handles?
|
234
|
+
class IncludeProcessor < Processor
|
235
|
+
def process document, reader, target, attributes
|
236
|
+
raise ::NotImplementedError
|
237
|
+
end
|
238
|
+
|
239
|
+
def handles? target
|
240
|
+
true
|
241
|
+
end
|
242
|
+
end
|
243
|
+
IncludeProcessor::DSL = ProcessorDsl
|
244
|
+
|
245
|
+
# Public: BlockProcessors are used to handle delimited blocks and paragraphs
|
246
|
+
# that have a custom name.
|
247
|
+
#
|
248
|
+
# When Asciidoctor encounters a delimited block or paragraph with an
|
249
|
+
# unrecognized name while parsing the document, it looks for a BlockProcessor
|
250
|
+
# registered to handle this name and, if found, invokes its {Processor#process}
|
251
|
+
# method to build a cooresponding node in the document tree.
|
252
|
+
#
|
253
|
+
# AsciiDoc example:
|
254
|
+
#
|
255
|
+
# [shout]
|
256
|
+
# Get a move on.
|
257
|
+
#
|
258
|
+
# Recognized options:
|
259
|
+
#
|
260
|
+
# * :named - The name of the block (required: true)
|
261
|
+
# * :contexts - The blocks contexts on which this style can be used (default: [:paragraph, :open]
|
262
|
+
# * :content_model - The structure of the content supported in this block (default: :compound)
|
263
|
+
# * :positional_attributes - A list of attribute names used to map positional attributes (default: nil)
|
264
|
+
# * ...
|
265
|
+
#
|
266
|
+
# BlockProcessor implementations must extend BlockProcessor.
|
267
|
+
class BlockProcessor < Processor
|
268
|
+
attr_accessor :name
|
269
|
+
|
270
|
+
def initialize name = nil, config = {}
|
271
|
+
super config
|
272
|
+
@name = name || @config[:name]
|
273
|
+
# assign fallbacks
|
274
|
+
case @config[:contexts]
|
275
|
+
when ::NilClass
|
276
|
+
@config[:contexts] ||= [:open, :paragraph].to_set
|
277
|
+
when ::Symbol
|
278
|
+
@config[:contexts] = [@config[:contexts]].to_set
|
115
279
|
else
|
116
|
-
@
|
280
|
+
@config[:contexts] = @config[:contexts].to_set
|
117
281
|
end
|
282
|
+
# QUESTION should the default content model be raw??
|
283
|
+
@config[:content_model] ||= :compound
|
118
284
|
end
|
119
285
|
|
120
|
-
def
|
121
|
-
|
286
|
+
def process parent, reader, attributes
|
287
|
+
raise ::NotImplementedError
|
122
288
|
end
|
289
|
+
end
|
290
|
+
|
291
|
+
module BlockProcessorDsl
|
292
|
+
include ProcessorDsl
|
123
293
|
|
124
|
-
|
125
|
-
|
126
|
-
|
294
|
+
# FIXME this isn't the prettiest thing
|
295
|
+
def named value
|
296
|
+
if self.is_a? Processor
|
297
|
+
@name = value
|
298
|
+
else
|
299
|
+
option :name, value
|
127
300
|
end
|
128
301
|
end
|
302
|
+
alias :match_name :named
|
303
|
+
alias :bind_to :named
|
129
304
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
305
|
+
def contexts *value
|
306
|
+
option :contexts, value.flatten
|
307
|
+
end
|
308
|
+
alias :on_contexts :contexts
|
309
|
+
alias :on_context :contexts
|
310
|
+
|
311
|
+
def content_model value
|
312
|
+
option :content_model, value
|
313
|
+
end
|
314
|
+
alias :parse_content_as :content_model
|
315
|
+
|
316
|
+
def positional_attributes *value
|
317
|
+
option :pos_attrs, value.flatten
|
318
|
+
end
|
319
|
+
alias :pos_attrs :positional_attributes
|
320
|
+
alias :name_attributes :positional_attributes
|
321
|
+
alias :name_positional_attributes :positional_attributes
|
322
|
+
|
323
|
+
def default_attrs value
|
324
|
+
option :default_attrs, value
|
325
|
+
end
|
326
|
+
alias :seed_attributes_with :default_attrs
|
327
|
+
end
|
328
|
+
BlockProcessor::DSL = BlockProcessorDsl
|
329
|
+
|
330
|
+
class MacroProcessor < Processor
|
331
|
+
attr_accessor :name
|
332
|
+
|
333
|
+
def initialize name = nil, config = {}
|
334
|
+
super config
|
335
|
+
@name = name || @config[:name]
|
336
|
+
@config[:content_model] ||= :attributes
|
337
|
+
end
|
338
|
+
|
339
|
+
def process parent, target, attributes
|
340
|
+
raise ::NotImplementedError
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
module MacroProcessorDsl
|
345
|
+
include ProcessorDsl
|
346
|
+
# QUESTION perhaps include a SyntaxDsl?
|
347
|
+
|
348
|
+
def named value
|
349
|
+
if self.is_a? Processor
|
350
|
+
@name = value
|
136
351
|
else
|
137
|
-
|
352
|
+
option :name, value
|
138
353
|
end
|
139
354
|
end
|
355
|
+
alias :match_name :named
|
356
|
+
alias :bind_to :named
|
140
357
|
|
141
|
-
def
|
142
|
-
|
358
|
+
def content_model value
|
359
|
+
option :content_model, value
|
143
360
|
end
|
361
|
+
alias :parse_content_as :content_model
|
144
362
|
|
145
|
-
def
|
146
|
-
|
147
|
-
|
148
|
-
|
363
|
+
def positional_attributes *value
|
364
|
+
option :pos_attrs, value.flatten
|
365
|
+
end
|
366
|
+
alias :pos_attrs :positional_attributes
|
367
|
+
alias :name_attributes :positional_attributes
|
368
|
+
alias :name_positional_attributes :positional_attributes
|
369
|
+
|
370
|
+
def default_attrs value
|
371
|
+
option :default_attrs, value
|
149
372
|
end
|
373
|
+
alias :seed_attributes_with :default_attrs
|
374
|
+
end
|
375
|
+
|
376
|
+
# Public: BlockMacroProcessors are used to handle block macros that have a
|
377
|
+
# custom name.
|
378
|
+
#
|
379
|
+
# BlockMacroProcessor implementations must extend BlockMacroProcessor.
|
380
|
+
class BlockMacroProcessor < MacroProcessor
|
381
|
+
end
|
382
|
+
BlockMacroProcessor::DSL = MacroProcessorDsl
|
150
383
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
384
|
+
# Public: InlineMacroProcessors are used to handle block macros that have a
|
385
|
+
# custom name.
|
386
|
+
#
|
387
|
+
# InlineMacroProcessor implementations must extend InlineMacroProcessor.
|
388
|
+
#--
|
389
|
+
# TODO break this out into different pattern types
|
390
|
+
# for example, FormalInlineMacro, ShortInlineMacro (no target) and other patterns
|
391
|
+
# FIXME for inline passthrough, we need to have some way to specify the text as a passthrough
|
392
|
+
class InlineMacroProcessor < MacroProcessor
|
393
|
+
def initialize name, config = {}
|
394
|
+
super
|
395
|
+
@config[:regexp] ||= (resolve_regexp @name, @config[:format])
|
396
|
+
end
|
397
|
+
|
398
|
+
def resolve_regexp name, format
|
399
|
+
# TODO memoize these regular expressions!
|
400
|
+
if format == :short
|
401
|
+
%r(\\?#{name}:\[((?:\\\]|[^\]])*?)\])
|
157
402
|
else
|
158
|
-
|
403
|
+
%r(\\?#{name}:(\S+?)\[((?:\\\]|[^\]])*?)\])
|
159
404
|
end
|
160
405
|
end
|
406
|
+
end
|
161
407
|
|
162
|
-
|
163
|
-
|
408
|
+
module InlineMacroProcessorDsl
|
409
|
+
include MacroProcessorDsl
|
410
|
+
|
411
|
+
def using_format value
|
412
|
+
option :format, value
|
164
413
|
end
|
165
414
|
|
166
|
-
def
|
167
|
-
|
168
|
-
processor.new(*args)
|
169
|
-
end
|
170
|
-
# QUESTION do we need/want the cache?
|
171
|
-
#@include_processors.map do |processor|
|
172
|
-
# @include_processor_cache[processor] ||= processor.new(*args)
|
173
|
-
#end
|
415
|
+
def match value
|
416
|
+
option :regexp, value
|
174
417
|
end
|
418
|
+
end
|
419
|
+
InlineMacroProcessor::DSL = InlineMacroProcessorDsl
|
175
420
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
421
|
+
# Public: Extension is a proxy object for an extension implementation such as
|
422
|
+
# a processor. It allows the preparation of the extension instance to be
|
423
|
+
# separated from its usage to provide consistency between different
|
424
|
+
# interfaces and avoid tight coupling with the extension type.
|
425
|
+
#
|
426
|
+
# The proxy encapsulates the extension kind (e.g., :block), its config Hash
|
427
|
+
# and the extension instance. This Proxy is what gets stored in the extension
|
428
|
+
# registry when activated.
|
429
|
+
#--
|
430
|
+
# QUESTION call this ExtensionInfo?
|
431
|
+
class Extension
|
432
|
+
attr :kind
|
433
|
+
attr :config
|
434
|
+
attr :instance
|
435
|
+
|
436
|
+
def initialize kind, instance, config
|
437
|
+
@kind = kind
|
438
|
+
@instance = instance
|
439
|
+
@config = config
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
# Public: A specialization of the Extension proxy that additionally stores a
|
444
|
+
# reference to the {Processor#process} method. By storing this reference, its
|
445
|
+
# possible to accomodate both concrete extension implementations and Procs.
|
446
|
+
class ProcessorExtension < Extension
|
447
|
+
attr :process_method
|
448
|
+
|
449
|
+
def initialize kind, instance, process_method = nil
|
450
|
+
super kind, instance, instance.config
|
451
|
+
@process_method = process_method || instance.method(:process)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
# Public: A Group is used to register one or more extensions with the Registry.
|
456
|
+
#
|
457
|
+
# The Group should be subclassed and registered with the Registry either by
|
458
|
+
# invoking the {Group.register} method or passing the subclass to the
|
459
|
+
# {Extensions.register} method. Extensions are registered with the Registry
|
460
|
+
# inside the {Group#activate} method.
|
461
|
+
class Group
|
462
|
+
class << self
|
463
|
+
def register name = nil
|
464
|
+
Extensions.register name, self
|
184
465
|
end
|
185
466
|
end
|
186
467
|
|
187
|
-
def
|
188
|
-
|
468
|
+
def activate registry
|
469
|
+
raise ::NotImplementedError
|
189
470
|
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# Public: The primary entry point into the extension system.
|
474
|
+
#
|
475
|
+
# Registry holds the extensions which have been registered and activated, has
|
476
|
+
# methods for registering or defining a processor and looks up extensions
|
477
|
+
# stored in the registry during parsing.
|
478
|
+
class Registry
|
479
|
+
# Public: Returns the {Asciidoctor::Document} on which the extensions in this registry are being used.
|
480
|
+
attr_reader :document
|
481
|
+
|
482
|
+
# Public: Returns the Array of {Group} classes, instances and/or Procs that have been registered.
|
483
|
+
attr_reader :groups
|
190
484
|
|
191
|
-
def
|
192
|
-
|
485
|
+
def initialize groups = {}
|
486
|
+
@groups = groups
|
487
|
+
@preprocessor_extensions = @treeprocessor_extensions = @postprocessor_extensions = @include_processor_extensions = nil
|
488
|
+
@block_extensions = @block_macro_extensions = @inline_macro_extensions = nil
|
489
|
+
@document = nil
|
193
490
|
end
|
194
491
|
|
195
|
-
#
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
492
|
+
# Public: Activates all the global extension {Group}s and the extension {Group}s
|
493
|
+
# associated with this registry.
|
494
|
+
#
|
495
|
+
# document - the {Asciidoctor::Document} on which the extensions are to be used.
|
496
|
+
#
|
497
|
+
# Returns the instance of this [Registry].
|
498
|
+
def activate document
|
499
|
+
@document = document
|
500
|
+
(Extensions.groups.values + @groups.values).each do |group|
|
501
|
+
case group
|
502
|
+
when ::Proc
|
503
|
+
case group.arity
|
504
|
+
when 0, -1
|
505
|
+
instance_exec(&group)
|
506
|
+
when 1
|
507
|
+
group.call self
|
201
508
|
end
|
509
|
+
when ::Class
|
510
|
+
group.new.activate self
|
202
511
|
else
|
203
|
-
|
204
|
-
return name
|
205
|
-
end
|
512
|
+
group.activate self
|
206
513
|
end
|
207
514
|
end
|
208
|
-
|
515
|
+
self
|
209
516
|
end
|
210
517
|
|
211
|
-
|
212
|
-
|
518
|
+
# Public: Registers a {Preprocessor} with the extension registry to process
|
519
|
+
# the AsciiDoc source before parsing begins.
|
520
|
+
#
|
521
|
+
# The Preprocessor may be one of four types:
|
522
|
+
#
|
523
|
+
# * A Preprocessor subclass
|
524
|
+
# * An instance of a Preprocessor subclass
|
525
|
+
# * The String name of a Preprocessor subclass
|
526
|
+
# * A method block (i.e., Proc) that conforms to the Preprocessor contract
|
527
|
+
#
|
528
|
+
# Unless the Preprocessor is passed as the method block, it must be the
|
529
|
+
# first argument to this method.
|
530
|
+
#
|
531
|
+
# Examples
|
532
|
+
#
|
533
|
+
# # as a Preprocessor subclass
|
534
|
+
# preprocessor FrontMatterPreprocessor
|
535
|
+
#
|
536
|
+
# # as an instance of a Preprocessor subclass
|
537
|
+
# preprocessor FrontMatterPreprocessor.new
|
538
|
+
#
|
539
|
+
# # as a name of a Preprocessor subclass
|
540
|
+
# preprocessor 'FrontMatterPreprocessor'
|
541
|
+
#
|
542
|
+
# # as a method block
|
543
|
+
# preprocessor do
|
544
|
+
# process |reader, lines|
|
545
|
+
# ...
|
546
|
+
# end
|
547
|
+
# end
|
548
|
+
#
|
549
|
+
# Returns the [Extension] stored in the registry that proxies the
|
550
|
+
# instance of this Preprocessor.
|
551
|
+
def preprocessor *args, &block
|
552
|
+
add_document_processor :preprocessor, args, &block
|
213
553
|
end
|
214
554
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
end
|
555
|
+
# Public: Checks whether any {Preprocessor} extensions have been registered.
|
556
|
+
#
|
557
|
+
# Returns a [Boolean] indicating whether any Preprocessor extensions are registered.
|
558
|
+
def preprocessors?
|
559
|
+
!!@preprocessor_extensions
|
221
560
|
end
|
222
561
|
|
223
|
-
|
224
|
-
|
225
|
-
|
562
|
+
# Public: Retrieves the {Extension} proxy objects for all
|
563
|
+
# Preprocessor instances in this registry.
|
564
|
+
#
|
565
|
+
# Returns an [Array] of Extension proxy objects.
|
566
|
+
def preprocessors
|
567
|
+
@preprocessor_extensions
|
226
568
|
end
|
227
569
|
|
228
|
-
|
229
|
-
|
570
|
+
# Public: Registers a {Treeprocessor} with the extension registry to process
|
571
|
+
# the AsciiDoc source after parsing is complete.
|
572
|
+
#
|
573
|
+
# The Treeprocessor may be one of four types:
|
574
|
+
#
|
575
|
+
# * A Treeprocessor subclass
|
576
|
+
# * An instance of a Treeprocessor subclass
|
577
|
+
# * The String name of a Treeprocessor subclass
|
578
|
+
# * A method block (i.e., Proc) that conforms to the Treeprocessor contract
|
579
|
+
#
|
580
|
+
# Unless the Treeprocessor is passed as the method block, it must be the
|
581
|
+
# first argument to this method.
|
582
|
+
#
|
583
|
+
# Examples
|
584
|
+
#
|
585
|
+
# # as a Treeprocessor subclass
|
586
|
+
# treeprocessor ShellTreeprocessor
|
587
|
+
#
|
588
|
+
# # as an instance of a Treeprocessor subclass
|
589
|
+
# treeprocessor ShellTreeprocessor.new
|
590
|
+
#
|
591
|
+
# # as a name of a Treeprocessor subclass
|
592
|
+
# treeprocessor 'ShellTreeprocessor'
|
593
|
+
#
|
594
|
+
# # as a method block
|
595
|
+
# treeprocessor do
|
596
|
+
# process |document|
|
597
|
+
# ...
|
598
|
+
# end
|
599
|
+
# end
|
600
|
+
#
|
601
|
+
# Returns the [Extension] stored in the registry that proxies the
|
602
|
+
# instance of this Treeprocessor.
|
603
|
+
def treeprocessor *args, &block
|
604
|
+
add_document_processor :treeprocessor, args, &block
|
230
605
|
end
|
231
606
|
|
232
|
-
|
233
|
-
|
607
|
+
# Public: Checks whether any {Treeprocessor} extensions have been registered.
|
608
|
+
#
|
609
|
+
# Returns a [Boolean] indicating whether any Treeprocessor extensions are registered.
|
610
|
+
def treeprocessors?
|
611
|
+
!!@treeprocessor_extensions
|
234
612
|
end
|
235
613
|
|
236
|
-
|
237
|
-
|
614
|
+
# Public: Retrieves the {Extension} proxy objects for all
|
615
|
+
# Treeprocessor instances in this registry.
|
616
|
+
#
|
617
|
+
# Returns an [Array] of Extension proxy objects.
|
618
|
+
def treeprocessors
|
619
|
+
@treeprocessor_extensions
|
238
620
|
end
|
239
621
|
|
240
|
-
#
|
241
|
-
|
242
|
-
|
243
|
-
|
622
|
+
# Public: Registers a {Postprocessor} with the extension registry to process
|
623
|
+
# the output after conversion is complete.
|
624
|
+
#
|
625
|
+
# The Postprocessor may be one of four types:
|
626
|
+
#
|
627
|
+
# * A Postprocessor subclass
|
628
|
+
# * An instance of a Postprocessor subclass
|
629
|
+
# * The String name of a Postprocessor subclass
|
630
|
+
# * A method block (i.e., Proc) that conforms to the Postprocessor contract
|
631
|
+
#
|
632
|
+
# Unless the Postprocessor is passed as the method block, it must be the
|
633
|
+
# first argument to this method.
|
634
|
+
#
|
635
|
+
# Examples
|
636
|
+
#
|
637
|
+
# # as a Postprocessor subclass
|
638
|
+
# postprocessor AnalyticsPostprocessor
|
639
|
+
#
|
640
|
+
# # as an instance of a Postprocessor subclass
|
641
|
+
# postprocessor AnalyticsPostprocessor.new
|
642
|
+
#
|
643
|
+
# # as a name of a Postprocessor subclass
|
644
|
+
# postprocessor 'AnalyticsPostprocessor'
|
645
|
+
#
|
646
|
+
# # as a method block
|
647
|
+
# postprocessor do
|
648
|
+
# process |document, output|
|
649
|
+
# ...
|
650
|
+
# end
|
651
|
+
# end
|
652
|
+
#
|
653
|
+
# Returns the [Extension] stored in the registry that proxies the
|
654
|
+
# instance of this Postprocessor.
|
655
|
+
def postprocessor *args, &block
|
656
|
+
add_document_processor :postprocessor, args, &block
|
244
657
|
end
|
245
658
|
|
246
|
-
|
247
|
-
|
659
|
+
# Public: Checks whether any {Postprocessor} extensions have been registered.
|
660
|
+
#
|
661
|
+
# Returns a [Boolean] indicating whether any Postprocessor extensions are registered.
|
662
|
+
def postprocessors?
|
663
|
+
!!@postprocessor_extensions
|
248
664
|
end
|
249
665
|
|
250
|
-
|
251
|
-
|
666
|
+
# Public: Retrieves the {Extension} proxy objects for all
|
667
|
+
# Postprocessor instances in this registry.
|
668
|
+
#
|
669
|
+
# Returns an [Array] of Extension proxy objects.
|
670
|
+
def postprocessors
|
671
|
+
@postprocessor_extensions
|
252
672
|
end
|
253
673
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
674
|
+
# Public: Registers an {IncludeProcessor} with the extension registry to have
|
675
|
+
# a shot at handling the include directive.
|
676
|
+
#
|
677
|
+
# The IncludeProcessor may be one of four types:
|
678
|
+
#
|
679
|
+
# * A IncludeProcessor subclass
|
680
|
+
# * An instance of a IncludeProcessor subclass
|
681
|
+
# * The String name of a IncludeProcessor subclass
|
682
|
+
# * A method block (i.e., Proc) that conforms to the IncludeProcessor contract
|
683
|
+
#
|
684
|
+
# Unless the IncludeProcessor is passed as the method block, it must be the
|
685
|
+
# first argument to this method.
|
686
|
+
#
|
687
|
+
# Examples
|
688
|
+
#
|
689
|
+
# # as an IncludeProcessor subclass
|
690
|
+
# include_processor GitIncludeProcessor
|
691
|
+
#
|
692
|
+
# # as an instance of a Postprocessor subclass
|
693
|
+
# include_processor GitIncludeProcessor.new
|
694
|
+
#
|
695
|
+
# # as a name of a Postprocessor subclass
|
696
|
+
# include_processor 'GitIncludeProcessor'
|
697
|
+
#
|
698
|
+
# # as a method block
|
699
|
+
# include_processor do
|
700
|
+
# process |document, output|
|
701
|
+
# ...
|
702
|
+
# end
|
703
|
+
# end
|
704
|
+
#
|
705
|
+
# Returns the [Extension] stored in the registry that proxies the
|
706
|
+
# instance of this IncludeProcessor.
|
707
|
+
def include_processor *args, &block
|
708
|
+
add_document_processor :include_processor, args, &block
|
258
709
|
end
|
259
710
|
|
260
|
-
|
261
|
-
|
711
|
+
# Public: Checks whether any {IncludeProcessor} extensions have been registered.
|
712
|
+
#
|
713
|
+
# Returns a [Boolean] indicating whether any IncludeProcessor extensions are registered.
|
714
|
+
def include_processors?
|
715
|
+
!!@include_processor_extensions
|
262
716
|
end
|
263
717
|
|
264
|
-
|
265
|
-
|
718
|
+
# Public: Retrieves the {Extension} proxy objects for all the
|
719
|
+
# IncludeProcessor instances stored in this registry.
|
720
|
+
#
|
721
|
+
# Returns an [Array] of Extension proxy objects.
|
722
|
+
def include_processors
|
723
|
+
@include_processor_extensions
|
266
724
|
end
|
267
725
|
|
268
|
-
|
269
|
-
|
726
|
+
# Public: Registers a {BlockProcessor} with the extension registry to
|
727
|
+
# process the block content (i.e., delimited block or paragraph) in the
|
728
|
+
# AsciiDoc source annotated with the specified block name (i.e., style).
|
729
|
+
#
|
730
|
+
# The BlockProcessor may be one of four types:
|
731
|
+
#
|
732
|
+
# * A BlockProcessor subclass
|
733
|
+
# * An instance of a BlockProcessor subclass
|
734
|
+
# * The String name of a BlockProcessor subclass
|
735
|
+
# * A method block (i.e., Proc) that conforms to the BlockProcessor contract
|
736
|
+
#
|
737
|
+
# Unless the BlockProcessor is passed as the method block, it must be the
|
738
|
+
# first argument to this method. The second argument is the name (coersed
|
739
|
+
# to a Symbol) of the AsciiDoc block content (i.e., delimited block or
|
740
|
+
# paragraph) that this processor is registered to handle. If a block name
|
741
|
+
# is not passed as an argument, it gets read from the name property of the
|
742
|
+
# BlockProcessor instance. If a name still cannot be determined, an error
|
743
|
+
# is raised.
|
744
|
+
#
|
745
|
+
# Examples
|
746
|
+
#
|
747
|
+
# # as a BlockProcessor subclass
|
748
|
+
# block ShoutBlock
|
749
|
+
#
|
750
|
+
# # as a BlockProcessor subclass with an explicit block name
|
751
|
+
# block ShoutBlock, :shout
|
752
|
+
#
|
753
|
+
# # as an instance of a BlockProcessor subclass
|
754
|
+
# block ShoutBlock.new
|
755
|
+
#
|
756
|
+
# # as an instance of a BlockProcessor subclass with an explicit block name
|
757
|
+
# block ShoutBlock.new, :shout
|
758
|
+
#
|
759
|
+
# # as a name of a BlockProcessor subclass
|
760
|
+
# block 'ShoutBlock'
|
761
|
+
#
|
762
|
+
# # as a name of a BlockProcessor subclass with an explicit block name
|
763
|
+
# block 'ShoutBlock', :shout
|
764
|
+
#
|
765
|
+
# # as a method block
|
766
|
+
# block do
|
767
|
+
# named :shout
|
768
|
+
# process |parent, reader, attrs|
|
769
|
+
# ...
|
770
|
+
# end
|
771
|
+
# end
|
772
|
+
#
|
773
|
+
# # as a method block with an explicit block name
|
774
|
+
# register :shout do
|
775
|
+
# process |parent, reader, attrs|
|
776
|
+
# ...
|
777
|
+
# end
|
778
|
+
# end
|
779
|
+
#
|
780
|
+
# Returns an instance of the [Extension] proxy object that is stored in the
|
781
|
+
# registry and manages the instance of this BlockProcessor.
|
782
|
+
def block *args, &block
|
783
|
+
add_syntax_processor :block, args, &block
|
270
784
|
end
|
271
785
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
786
|
+
# Public: Checks whether any {BlockProcessor} extensions have been registered.
|
787
|
+
#
|
788
|
+
# Returns a [Boolean] indicating whether any BlockProcessor extensions are registered.
|
789
|
+
def blocks?
|
790
|
+
!!@block_extensions
|
276
791
|
end
|
277
|
-
end
|
278
792
|
|
279
|
-
|
280
|
-
|
281
|
-
|
793
|
+
# Public: Checks whether any {BlockProcessor} extensions are registered to
|
794
|
+
# handle the specified block name appearing on the specified context.
|
795
|
+
#
|
796
|
+
# Returns the [Extension] proxy object for the BlockProcessor that matches
|
797
|
+
# the block name and context or false if no match is found.
|
798
|
+
def registered_for_block? name, context
|
799
|
+
if (ext = @block_extensions[name.to_sym])
|
800
|
+
(ext.config[:contexts].include? context) ? ext : false
|
801
|
+
else
|
802
|
+
false
|
803
|
+
end
|
282
804
|
end
|
283
|
-
end
|
284
805
|
|
285
|
-
|
286
|
-
|
287
|
-
#
|
288
|
-
# Prior to invoking the preprocessor, Asciidoctor splits the source text into
|
289
|
-
# lines and normalizes them. The normalize process strips trailing whitespace
|
290
|
-
# from each line and leaves behind a line-feed character (i.e., "\n").
|
291
|
-
#
|
292
|
-
# Asciidoctor passes a reference to the Reader and a copy of the lines Array
|
293
|
-
# to the process method of an instance of each registered Preprocessor. The
|
294
|
-
# Preprocessor modifies the Array as necessary and either returns a reference
|
295
|
-
# to the same Reader or a reference to a new one.
|
296
|
-
#
|
297
|
-
# Preprocessors must extend Asciidoctor::Extensions::Preprocessor.
|
298
|
-
class Preprocessor < Processor
|
299
|
-
# Public: Accepts the Reader and an Array of lines, modifies them as
|
300
|
-
# needed, then returns the Reader or a reference to a new one.
|
806
|
+
# Public: Retrieves the {Extension} proxy object for the BlockProcessor registered
|
807
|
+
# to handle block content with the name.
|
301
808
|
#
|
302
|
-
#
|
303
|
-
|
304
|
-
|
809
|
+
# name - the String or Symbol (coersed to a Symbol) macro name
|
810
|
+
#
|
811
|
+
# Returns the [Extension] object stored in the registry that proxies the
|
812
|
+
# corresponding BlockProcessor or nil if a match is not found.
|
813
|
+
def find_block_extension name
|
814
|
+
@block_extensions[name.to_sym]
|
305
815
|
end
|
306
|
-
end
|
307
816
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
817
|
+
# Public: Registers a {BlockMacroProcessor} with the extension registry to
|
818
|
+
# process a block macro with the specified name.
|
819
|
+
#
|
820
|
+
# The BlockMacroProcessor may be one of four types:
|
821
|
+
#
|
822
|
+
# * A BlockMacroProcessor subclass
|
823
|
+
# * An instance of a BlockMacroProcessor subclass
|
824
|
+
# * The String name of a BlockMacroProcessor subclass
|
825
|
+
# * A method block (i.e., Proc) that conforms to the BlockMacroProcessor contract
|
826
|
+
#
|
827
|
+
# Unless the BlockMacroProcessor is passed as the method block, it must be
|
828
|
+
# the first argument to this method. The second argument is the name
|
829
|
+
# (coersed to a Symbol) of the AsciiDoc block macro that this processor is
|
830
|
+
# registered to handle. If a block macro name is not passed as an argument,
|
831
|
+
# it gets read from the name property of the BlockMacroProcessor instance.
|
832
|
+
# If a name still cannot be determined, an error is raised.
|
833
|
+
#
|
834
|
+
# Examples
|
835
|
+
#
|
836
|
+
# # as a BlockMacroProcessor subclass
|
837
|
+
# block GistBlockMacro
|
838
|
+
#
|
839
|
+
# # as a BlockMacroProcessor subclass with an explicit macro name
|
840
|
+
# block GistBlockMacro, :gist
|
841
|
+
#
|
842
|
+
# # as an instance of a BlockMacroProcessor subclass
|
843
|
+
# block GistBlockMacro.new
|
844
|
+
#
|
845
|
+
# # as an instance of a BlockMacroProcessor subclass with an explicit macro name
|
846
|
+
# block GistBlockMacro.new, :gist
|
847
|
+
#
|
848
|
+
# # as a name of a BlockMacroProcessor subclass
|
849
|
+
# block 'GistBlockMacro'
|
850
|
+
#
|
851
|
+
# # as a name of a BlockMacroProcessor subclass with an explicit macro name
|
852
|
+
# block 'GistBlockMacro', :gist
|
853
|
+
#
|
854
|
+
# # as a method block
|
855
|
+
# block_macro do
|
856
|
+
# named :gist
|
857
|
+
# process |parent, target, attrs|
|
858
|
+
# ...
|
859
|
+
# end
|
860
|
+
# end
|
861
|
+
#
|
862
|
+
# # as a method block with an explicit macro name
|
863
|
+
# register :gist do
|
864
|
+
# process |parent, target, attrs|
|
865
|
+
# ...
|
866
|
+
# end
|
867
|
+
# end
|
868
|
+
#
|
869
|
+
# Returns an instance of the [Extension] proxy object that is stored in the
|
870
|
+
# registry and manages the instance of this BlockMacroProcessor.
|
871
|
+
def block_macro *args, &block
|
872
|
+
add_syntax_processor :block_macro, args, &block
|
320
873
|
end
|
321
|
-
end
|
322
874
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
# necessary and returns the String replacement.
|
329
|
-
#
|
330
|
-
# The markup format in the String is determined from the backend used to
|
331
|
-
# render the Document. The backend and be looked up using the backend method
|
332
|
-
# on the Document object, as well as various backend-related document
|
333
|
-
# attributes.
|
334
|
-
#
|
335
|
-
# Postprocessors can also be used to relocate assets needed by the published
|
336
|
-
# document.
|
337
|
-
#
|
338
|
-
# Postprocessors must extend Asciidoctor::Extensions::Postprocessor.
|
339
|
-
class Postprocessor < Processor
|
340
|
-
def process output
|
341
|
-
output
|
875
|
+
# Public: Checks whether any {BlockMacroProcessor} extensions have been registered.
|
876
|
+
#
|
877
|
+
# Returns a [Boolean] indicating whether any BlockMacroProcessor extensions are registered.
|
878
|
+
def block_macros?
|
879
|
+
!!@block_macro_extensions
|
342
880
|
end
|
343
|
-
end
|
344
881
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
output
|
882
|
+
# Public: Checks whether any {BlockMacroProcessor} extensions are registered to
|
883
|
+
# handle the block macro with the specified name.
|
884
|
+
#
|
885
|
+
# name - the String or Symbol (coersed to a Symbol) macro name
|
886
|
+
#
|
887
|
+
# Returns the [Extension] proxy object for the BlockMacroProcessor that matches
|
888
|
+
# the macro name or false if no match is found.
|
889
|
+
#--
|
890
|
+
# TODO only allow blank target if format is :short
|
891
|
+
def registered_for_block_macro? name
|
892
|
+
(ext = @block_macro_extensions[name.to_sym]) ? ext : false
|
357
893
|
end
|
358
|
-
end
|
359
894
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
end
|
895
|
+
# Public: Retrieves the {Extension} proxy object for the BlockMacroProcessor registered
|
896
|
+
# to handle a block macro with the specified name.
|
897
|
+
#
|
898
|
+
# name - the String or Symbol (coersed to a Symbol) macro name
|
899
|
+
#
|
900
|
+
# Returns the [Extension] object stored in the registry that proxies the
|
901
|
+
# cooresponding BlockMacroProcessor or nil if a match is not found.
|
902
|
+
def find_block_macro_extension name
|
903
|
+
@block_macro_extensions[name.to_sym]
|
904
|
+
end
|
371
905
|
|
372
|
-
|
373
|
-
|
374
|
-
|
906
|
+
# Public: Registers a {InlineMacroProcessor} with the extension registry to
|
907
|
+
# process an inline macro with the specified name.
|
908
|
+
#
|
909
|
+
# The InlineMacroProcessor may be one of four types:
|
910
|
+
#
|
911
|
+
# * An InlineMacroProcessor subclass
|
912
|
+
# * An instance of an InlineMacroProcessor subclass
|
913
|
+
# * The String name of an InlineMacroProcessor subclass
|
914
|
+
# * A method block (i.e., Proc) that conforms to the InlineMacroProcessor contract
|
915
|
+
#
|
916
|
+
# Unless the InlineMacroProcessor is passed as the method block, it must be
|
917
|
+
# the first argument to this method. The second argument is the name
|
918
|
+
# (coersed to a Symbol) of the AsciiDoc block macro that this processor is
|
919
|
+
# registered to handle. If a block macro name is not passed as an argument,
|
920
|
+
# it gets read from the name property of the InlineMacroProcessor instance.
|
921
|
+
# If a name still cannot be determined, an error is raised.
|
922
|
+
#
|
923
|
+
# Examples
|
924
|
+
#
|
925
|
+
# # as an InlineMacroProcessor subclass
|
926
|
+
# block ChromeInlineMacro
|
927
|
+
#
|
928
|
+
# # as an InlineMacroProcessor subclass with an explicit macro name
|
929
|
+
# block ChromeInineMacro, :chrome
|
930
|
+
#
|
931
|
+
# # as an instance of an InlineMacroProcessor subclass
|
932
|
+
# block ChromeInlineMacro.new
|
933
|
+
#
|
934
|
+
# # as an instance of an InlineMacroProcessor subclass with an explicit macro name
|
935
|
+
# block ChromeInlineMacro.new, :chrome
|
936
|
+
#
|
937
|
+
# # as a name of an InlineMacroProcessor subclass
|
938
|
+
# block 'ChromeInlineMacro'
|
939
|
+
#
|
940
|
+
# # as a name of an InlineMacroProcessor subclass with an explicit macro name
|
941
|
+
# block 'ChromeInineMacro', :chrome
|
942
|
+
#
|
943
|
+
# # as a method block
|
944
|
+
# inline_macro do
|
945
|
+
# named :chrome
|
946
|
+
# process |parent, target, attrs|
|
947
|
+
# ...
|
948
|
+
# end
|
949
|
+
# end
|
950
|
+
#
|
951
|
+
# # as a method block with an explicit macro name
|
952
|
+
# register :chrome do
|
953
|
+
# process |parent, target, attrs|
|
954
|
+
# ...
|
955
|
+
# end
|
956
|
+
# end
|
957
|
+
#
|
958
|
+
# Returns an instance of the [Extension] proxy object that is stored in the
|
959
|
+
# registry and manages the instance of this InlineMacroProcessor.
|
960
|
+
def inline_macro *args, &block
|
961
|
+
add_syntax_processor :inline_macro, args, &block
|
375
962
|
end
|
376
963
|
|
377
|
-
|
378
|
-
|
379
|
-
|
964
|
+
# Public: Checks whether any {InlineMacroProcessor} extensions have been registered.
|
965
|
+
#
|
966
|
+
# Returns a [Boolean] indicating whether any IncludeMacroProcessor extensions are registered.
|
967
|
+
def inline_macros?
|
968
|
+
!!@inline_macro_extensions
|
969
|
+
end
|
380
970
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
971
|
+
# Public: Checks whether any {InlineMacroProcessor} extensions are registered to
|
972
|
+
# handle the inline macro with the specified name.
|
973
|
+
#
|
974
|
+
# name - the String or Symbol (coersed to a Symbol) macro name
|
975
|
+
#
|
976
|
+
# Returns the [Extension] proxy object for the InlineMacroProcessor that matches
|
977
|
+
# the macro name or false if no match is found.
|
978
|
+
def registered_for_inline_macro? name
|
979
|
+
(ext = @inline_macro_extensions[name.to_sym]) ? ext : false
|
389
980
|
end
|
390
981
|
|
391
|
-
|
392
|
-
|
982
|
+
# Public: Retrieves the {Extension} proxy object for the InlineMacroProcessor registered
|
983
|
+
# to handle an inline macro with the specified name.
|
984
|
+
#
|
985
|
+
# name - the String or Symbol (coersed to a Symbol) macro name
|
986
|
+
#
|
987
|
+
# Returns the [Extension] object stored in the registry that proxies the
|
988
|
+
# cooresponding InlineMacroProcessor or nil if a match is not found.
|
989
|
+
def find_inline_macro_extension name
|
990
|
+
@inline_macro_extensions[name.to_sym]
|
393
991
|
end
|
394
|
-
end
|
395
992
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
993
|
+
# Public: Retrieves the {Extension} proxy objects for all
|
994
|
+
# InlineMacroProcessor instances in this registry.
|
995
|
+
#
|
996
|
+
# Returns an [Array] of Extension proxy objects.
|
997
|
+
def inline_macros
|
998
|
+
@inline_macro_extensions.values
|
999
|
+
end
|
1000
|
+
|
1001
|
+
private
|
1002
|
+
|
1003
|
+
def add_document_processor kind, args, &block
|
1004
|
+
kind_name = kind.to_s.tr '_', ' '
|
1005
|
+
kind_class_symbol = kind_name.split(' ').map {|word| %(#{word.chr.upcase}#{word[1..-1]}) }.join.to_sym
|
1006
|
+
kind_class = Extensions.const_get kind_class_symbol
|
1007
|
+
kind_java_class = (defined? ::AsciidoctorJ) ? (::AsciidoctorJ::Extensions.const_get kind_class_symbol) : nil
|
1008
|
+
kind_store = instance_variable_get(%(@#{kind}_extensions).to_sym) || instance_variable_set(%(@#{kind}_extensions).to_sym, [])
|
1009
|
+
# style 1: specified as block
|
1010
|
+
extension = if block_given?
|
1011
|
+
config = resolve_args args, 1
|
1012
|
+
# TODO if block arity is 0, assume block is process method
|
1013
|
+
processor = kind_class.new config
|
1014
|
+
class << processor
|
1015
|
+
include_dsl
|
1016
|
+
end
|
1017
|
+
processor.instance_exec(&block)
|
1018
|
+
processor.freeze
|
1019
|
+
unless processor.process_block_given?
|
1020
|
+
raise ::ArgumentError.new %(No block specified to process #{kind_name} extension at #{block.source_location})
|
1021
|
+
end
|
1022
|
+
ProcessorExtension.new kind, processor
|
1023
|
+
else
|
1024
|
+
processor, config = resolve_args args, 2
|
1025
|
+
# style 2: specified as class or class name
|
1026
|
+
if (processor.is_a? ::Class) || ((processor.is_a? ::String) && (processor = Extensions.class_for_name processor))
|
1027
|
+
unless processor < kind_class || (kind_java_class && processor < kind_java_class)
|
1028
|
+
raise ::ArgumentError.new %(Invalid type for #{kind_name} extension: #{processor})
|
1029
|
+
end
|
1030
|
+
processor_instance = processor.new config
|
1031
|
+
processor_instance.freeze
|
1032
|
+
ProcessorExtension.new kind, processor_instance
|
1033
|
+
# style 3: specified as instance
|
1034
|
+
elsif (processor.is_a? kind_class) || (kind_java_class && (processor.is_a? kind_java_class))
|
1035
|
+
processor.update_config config
|
1036
|
+
processor.freeze
|
1037
|
+
ProcessorExtension.new kind, processor
|
1038
|
+
else
|
1039
|
+
raise ::ArgumentError.new %(Invalid arguments specified for registering #{kind_name} extension: #{args})
|
1040
|
+
end
|
400
1041
|
end
|
401
1042
|
|
402
|
-
|
403
|
-
|
1043
|
+
if extension.config[:position] == :>>
|
1044
|
+
kind_store.unshift extension
|
1045
|
+
else
|
1046
|
+
kind_store << extension
|
404
1047
|
end
|
405
1048
|
end
|
406
1049
|
|
407
|
-
|
408
|
-
|
409
|
-
|
1050
|
+
def add_syntax_processor kind, args, &block
|
1051
|
+
kind_name = kind.to_s.tr '_', ' '
|
1052
|
+
kind_class_basename = kind_name.split(' ').map {|word| %(#{word.chr.upcase}#{word[1..-1]}) }.join
|
1053
|
+
kind_class_symbol = %(#{kind_class_basename}Processor).to_sym
|
1054
|
+
kind_class = Extensions.const_get kind_class_symbol
|
1055
|
+
kind_java_class = (defined? ::AsciidoctorJ) ? (::AsciidoctorJ::Extensions.const_get kind_class_symbol) : nil
|
1056
|
+
kind_store = instance_variable_get(%(@#{kind}_extensions).to_sym) || instance_variable_set(%(@#{kind}_extensions).to_sym, {})
|
1057
|
+
# style 1: specified as block
|
1058
|
+
if block_given?
|
1059
|
+
name, config = resolve_args args, 2
|
1060
|
+
processor = kind_class.new as_symbol(name), config
|
1061
|
+
class << processor
|
1062
|
+
include_dsl
|
1063
|
+
end
|
1064
|
+
if block.arity == 1
|
1065
|
+
yield processor
|
1066
|
+
else
|
1067
|
+
processor.instance_exec(&block)
|
1068
|
+
end
|
1069
|
+
unless (name = as_symbol processor.name)
|
1070
|
+
raise ::ArgumentError.new %(No name specified for #{kind_name} extension at #{block.source_location})
|
1071
|
+
end
|
1072
|
+
unless processor.process_block_given?
|
1073
|
+
raise ::NoMethodError.new %(No block specified to process #{kind_name} extension at #{block.source_location})
|
1074
|
+
end
|
1075
|
+
processor.freeze
|
1076
|
+
kind_store[name] = ProcessorExtension.new kind, processor
|
1077
|
+
else
|
1078
|
+
processor, name, config = resolve_args args, 3
|
1079
|
+
# style 2: specified as class or class name
|
1080
|
+
if (processor.is_a? ::Class) || ((processor.is_a? ::String) && (processor = Extensions.class_for_name processor))
|
1081
|
+
unless processor < kind_class || (kind_java_class && processor < kind_java_class)
|
1082
|
+
raise ::ArgumentError.new %(Class specified for #{kind_name} extension does not inherit from #{kind_class}: #{processor})
|
1083
|
+
end
|
1084
|
+
processor_instance = processor.new as_symbol(name), config
|
1085
|
+
unless (name = as_symbol processor_instance.name)
|
1086
|
+
raise ::ArgumentError.new %(No name specified for #{kind_name} extension: #{processor})
|
1087
|
+
end
|
1088
|
+
processor.freeze
|
1089
|
+
kind_store[name] = ProcessorExtension.new kind, processor_instance
|
1090
|
+
# style 3: specified as instance
|
1091
|
+
elsif (processor.is_a? kind_class) || (kind_java_class && (processor.is_a? kind_java_class))
|
1092
|
+
processor.update_config config
|
1093
|
+
# TODO need a test for this override!
|
1094
|
+
unless (name = name ? (processor.name = as_symbol name) : (as_symbol processor.name))
|
1095
|
+
raise ::ArgumentError.new %(No name specified for #{kind_name} extension: #{processor})
|
1096
|
+
end
|
1097
|
+
processor.freeze
|
1098
|
+
kind_store[name] = ProcessorExtension.new kind, processor
|
1099
|
+
else
|
1100
|
+
raise ::ArgumentError.new %(Invalid arguments specified for registering #{kind_name} extension: #{args})
|
1101
|
+
end
|
1102
|
+
end
|
1103
|
+
end
|
410
1104
|
|
411
|
-
def
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
1105
|
+
def resolve_args args, expect
|
1106
|
+
opts = (args[-1].is_a? ::Hash) ? args.pop : {}
|
1107
|
+
return opts if expect == 1
|
1108
|
+
num_args = args.size
|
1109
|
+
if (missing = expect - 1 - num_args) > 0
|
1110
|
+
args.fill nil, num_args, missing
|
1111
|
+
elsif missing < 0
|
1112
|
+
args.pop(-missing)
|
1113
|
+
end
|
1114
|
+
args << opts
|
1115
|
+
args
|
416
1116
|
end
|
417
1117
|
|
418
|
-
def
|
419
|
-
nil
|
1118
|
+
def as_symbol name
|
1119
|
+
name ? ((name.is_a? ::Symbol) ? name : name.to_sym) : nil
|
420
1120
|
end
|
421
1121
|
end
|
422
1122
|
|
423
|
-
class
|
424
|
-
|
1123
|
+
class << self
|
1124
|
+
def generate_name
|
1125
|
+
%(extgrp#{next_auto_id})
|
1126
|
+
end
|
425
1127
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
1128
|
+
def next_auto_id
|
1129
|
+
@auto_id ||= -1
|
1130
|
+
@auto_id += 1
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
def groups
|
1134
|
+
@groups ||= {}
|
432
1135
|
end
|
433
1136
|
|
434
|
-
def
|
435
|
-
if
|
436
|
-
|
1137
|
+
def build_registry name = nil, &block
|
1138
|
+
if block_given?
|
1139
|
+
name ||= generate_name
|
1140
|
+
Registry.new({ name => block })
|
437
1141
|
else
|
438
|
-
|
1142
|
+
Registry.new
|
439
1143
|
end
|
440
1144
|
end
|
1145
|
+
|
1146
|
+
# Public: Registers an extension Group that subsequently registers a
|
1147
|
+
# collection of extensions.
|
1148
|
+
#
|
1149
|
+
# Registers the extension Group specified under the given name. If a name is
|
1150
|
+
# not given, one is calculated by appending the next value in a 0-based
|
1151
|
+
# index to the string "extgrp". For instance, the first unnamed extension
|
1152
|
+
# group to be registered is assigned the name "extgrp0" if a name is not
|
1153
|
+
# specified.
|
1154
|
+
#
|
1155
|
+
# The names are not yet used, but are intended for selectively activating
|
1156
|
+
# extensions in the future.
|
1157
|
+
#
|
1158
|
+
# If the extension group argument is a String or a Symbol, it gets resolved
|
1159
|
+
# to a Class before being registered.
|
1160
|
+
#
|
1161
|
+
# name - The name under which this extension group is registered (optional, default: nil)
|
1162
|
+
# group - A block (Proc), a Class, a String or Symbol name of a Class or
|
1163
|
+
# an Object instance of a Class.
|
1164
|
+
#
|
1165
|
+
# Examples
|
1166
|
+
#
|
1167
|
+
# Asciidoctor::Extensions.register UmlExtensions
|
1168
|
+
#
|
1169
|
+
# Asciidoctor::Extensions.register :uml, UmlExtensions
|
1170
|
+
#
|
1171
|
+
# Asciidoctor::Extensions.register do
|
1172
|
+
# block_processor :plantuml, PlantUmlBlock
|
1173
|
+
# end
|
1174
|
+
#
|
1175
|
+
# Asciidoctor::Extensions.register :uml do
|
1176
|
+
# block_processor :plantuml, PlantUmlBlock
|
1177
|
+
# end
|
1178
|
+
#
|
1179
|
+
# Returns the [Proc, Class or Object] instance, matching the type passed to this method.
|
1180
|
+
def register *args, &block
|
1181
|
+
argc = args.length
|
1182
|
+
resolved_group = if block_given?
|
1183
|
+
block
|
1184
|
+
elsif !(group = args.pop)
|
1185
|
+
raise ::ArgumentError.new %(Extension group to register not specified)
|
1186
|
+
else
|
1187
|
+
# QUESTION should we instantiate the group class here or defer until
|
1188
|
+
# activation??
|
1189
|
+
case group
|
1190
|
+
when ::Class
|
1191
|
+
group
|
1192
|
+
when ::String
|
1193
|
+
class_for_name group
|
1194
|
+
when ::Symbol
|
1195
|
+
class_for_name group.to_s
|
1196
|
+
else
|
1197
|
+
group
|
1198
|
+
end
|
1199
|
+
end
|
1200
|
+
name = args.pop || generate_name
|
1201
|
+
unless args.empty?
|
1202
|
+
raise ::ArgumentError.new %(Wrong number of arguments (#{argc} for 1..2))
|
1203
|
+
end
|
1204
|
+
groups[name] = resolved_group
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
def unregister_all
|
1208
|
+
@groups = {}
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
# unused atm, but tested
|
1212
|
+
def resolve_class object
|
1213
|
+
(object.is_a? ::Class) ? object : (class_for_name object.to_s)
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
# Public: Resolves the Class object for the qualified name.
|
1217
|
+
#
|
1218
|
+
# Returns Class
|
1219
|
+
def class_for_name qualified_name
|
1220
|
+
resolved_class = ::Object
|
1221
|
+
qualified_name.split('::').each do |name|
|
1222
|
+
if name.empty?
|
1223
|
+
# do nothing
|
1224
|
+
elsif resolved_class.const_defined? name
|
1225
|
+
resolved_class = resolved_class.const_get name
|
1226
|
+
else
|
1227
|
+
raise %(Could not resolve class for name: #{qualified_name})
|
1228
|
+
end
|
1229
|
+
end
|
1230
|
+
resolved_class
|
1231
|
+
end
|
441
1232
|
end
|
1233
|
+
|
442
1234
|
end
|
443
1235
|
end
|