asciidoctor 1.5.5 → 1.5.6

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.

Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +216 -1
  3. data/CONTRIBUTING.adoc +2 -2
  4. data/Gemfile +20 -1
  5. data/LICENSE.adoc +1 -1
  6. data/README-fr.adoc +4 -3
  7. data/README-jp.adoc +11 -10
  8. data/README-zh_CN.adoc +4 -3
  9. data/README.adoc +17 -202
  10. data/Rakefile +41 -25
  11. data/asciidoctor.gemspec +9 -10
  12. data/data/locale/attributes.adoc +216 -34
  13. data/data/stylesheets/asciidoctor-default.css +23 -16
  14. data/features/step_definitions.rb +15 -19
  15. data/features/xref.feature +584 -20
  16. data/lib/asciidoctor.rb +292 -278
  17. data/lib/asciidoctor/abstract_block.rb +155 -94
  18. data/lib/asciidoctor/abstract_node.rb +108 -94
  19. data/lib/asciidoctor/attribute_list.rb +30 -22
  20. data/lib/asciidoctor/block.rb +7 -7
  21. data/lib/asciidoctor/cli/invoker.rb +47 -34
  22. data/lib/asciidoctor/cli/options.rb +22 -11
  23. data/lib/asciidoctor/converter.rb +3 -3
  24. data/lib/asciidoctor/converter/base.rb +2 -2
  25. data/lib/asciidoctor/converter/composite.rb +1 -1
  26. data/lib/asciidoctor/converter/docbook45.rb +2 -2
  27. data/lib/asciidoctor/converter/docbook5.rb +132 -87
  28. data/lib/asciidoctor/converter/factory.rb +0 -1
  29. data/lib/asciidoctor/converter/html5.rb +116 -98
  30. data/lib/asciidoctor/converter/manpage.rb +51 -52
  31. data/lib/asciidoctor/converter/template.rb +47 -36
  32. data/lib/asciidoctor/core_ext.rb +8 -2
  33. data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +4 -0
  34. data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +6 -0
  35. data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +5 -0
  36. data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +1 -1
  37. data/lib/asciidoctor/core_ext/1.8.7/string/{limit.rb → limit_bytesize.rb} +7 -6
  38. data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +6 -0
  39. data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +1 -1
  40. data/lib/asciidoctor/core_ext/nil_or_empty.rb +5 -5
  41. data/lib/asciidoctor/core_ext/regexp/is_match.rb +3 -0
  42. data/lib/asciidoctor/core_ext/string/{limit.rb → limit_bytesize.rb} +2 -2
  43. data/lib/asciidoctor/document.rb +216 -213
  44. data/lib/asciidoctor/extensions.rb +318 -185
  45. data/lib/asciidoctor/helpers.rb +35 -35
  46. data/lib/asciidoctor/inline.rb +32 -1
  47. data/lib/asciidoctor/list.rb +22 -6
  48. data/lib/asciidoctor/parser.rb +1008 -1038
  49. data/lib/asciidoctor/path_resolver.rb +46 -50
  50. data/lib/asciidoctor/reader.rb +275 -251
  51. data/lib/asciidoctor/section.rb +86 -58
  52. data/lib/asciidoctor/stylesheets.rb +6 -6
  53. data/lib/asciidoctor/substitutors.rb +567 -649
  54. data/lib/asciidoctor/table.rb +163 -108
  55. data/lib/asciidoctor/version.rb +1 -1
  56. data/man/asciidoctor.1 +18 -16
  57. data/man/asciidoctor.adoc +15 -13
  58. data/test/attributes_test.rb +138 -22
  59. data/test/blocks_test.rb +377 -97
  60. data/test/converter_test.rb +13 -0
  61. data/test/document_test.rb +244 -34
  62. data/test/extensions_test.rb +409 -42
  63. data/test/fixtures/asciidoc_index.txt +521 -0
  64. data/test/fixtures/basic-docinfo-footer.html +6 -0
  65. data/test/fixtures/basic-docinfo-footer.xml +8 -0
  66. data/test/fixtures/basic-docinfo.html +1 -0
  67. data/test/fixtures/basic-docinfo.xml +4 -0
  68. data/test/fixtures/basic.asciidoc +5 -0
  69. data/test/fixtures/chapter-a.adoc +3 -0
  70. data/test/fixtures/child-include.adoc +5 -0
  71. data/test/fixtures/circle.svg +9 -0
  72. data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
  73. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
  74. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
  75. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
  76. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
  77. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
  78. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
  79. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
  80. data/test/fixtures/custom-docinfodir/basic-docinfo.html +1 -0
  81. data/test/fixtures/custom-docinfodir/docinfo.html +1 -0
  82. data/test/fixtures/docinfo-footer.html +1 -0
  83. data/test/fixtures/docinfo-footer.xml +9 -0
  84. data/test/fixtures/docinfo.html +1 -0
  85. data/test/fixtures/docinfo.xml +3 -0
  86. data/test/fixtures/dot.gif +0 -0
  87. data/test/fixtures/encoding.asciidoc +13 -0
  88. data/test/fixtures/grandchild-include.adoc +3 -0
  89. data/test/fixtures/hello-asciidoctor.pdf +69 -0
  90. data/test/fixtures/include-file.asciidoc +24 -0
  91. data/test/fixtures/include-file.ml +3 -0
  92. data/test/fixtures/include-file.xml +5 -0
  93. data/test/fixtures/master.adoc +5 -0
  94. data/test/fixtures/mismatched-end-tag.adoc +7 -0
  95. data/test/fixtures/parent-include-restricted.adoc +5 -0
  96. data/test/fixtures/parent-include.adoc +5 -0
  97. data/test/fixtures/sample.asciidoc +26 -0
  98. data/test/fixtures/stylesheets/custom.css +3 -0
  99. data/test/fixtures/subs-docinfo.html +2 -0
  100. data/test/fixtures/subs.adoc +7 -0
  101. data/test/fixtures/tagged-class-enclosed.rb +26 -0
  102. data/test/fixtures/tagged-class.rb +23 -0
  103. data/test/fixtures/tip.gif +0 -0
  104. data/test/invoker_test.rb +82 -4
  105. data/test/links_test.rb +312 -37
  106. data/test/lists_test.rb +204 -25
  107. data/test/manpage_test.rb +191 -4
  108. data/test/options_test.rb +18 -1
  109. data/test/paragraphs_test.rb +32 -7
  110. data/test/parser_test.rb +150 -30
  111. data/test/paths_test.rb +47 -13
  112. data/test/preamble_test.rb +1 -1
  113. data/test/reader_test.rb +366 -126
  114. data/test/sections_test.rb +203 -56
  115. data/test/substitutions_test.rb +339 -131
  116. data/test/tables_test.rb +315 -15
  117. data/test/test_helper.rb +400 -0
  118. data/test/text_test.rb +5 -5
  119. metadata +110 -22
@@ -11,7 +11,7 @@ module Asciidoctor
11
11
  # 2. The Parser parses the block-level content into an abstract syntax tree.
12
12
  # Custom blocks and block macros are processed by associated {BlockProcessor}s
13
13
  # and {BlockMacroProcessor}s, respectively.
14
- # 3. {Treeprocessor}s are run on the abstract syntax tree.
14
+ # 3. {TreeProcessor}s are run on the abstract syntax tree.
15
15
  # 4. Conversion of the document begins, at which point inline markup is processed
16
16
  # and converted. Custom inline macros are processed by associated {InlineMacroProcessor}s.
17
17
  # 5. {Postprocessor}s modify or replace the converted document.
@@ -73,8 +73,8 @@ module Extensions
73
73
  extend const_get :DSL if constants.grep :DSL
74
74
  end
75
75
  end
76
- alias :extend_dsl :use_dsl
77
- alias :include_dsl :use_dsl
76
+ alias extend_dsl use_dsl
77
+ alias include_dsl use_dsl
78
78
  end
79
79
 
80
80
  # Public: Get the configuration Hash for this processor instance.
@@ -89,7 +89,58 @@ module Extensions
89
89
  end
90
90
 
91
91
  def process *args
92
- raise ::NotImplementedError
92
+ raise ::NotImplementedError, %(Asciidoctor::Extensions::Processor subclass must implement ##{__method__} method)
93
+ end
94
+
95
+ # QUESTION should attributes be an option instead of a parameter?
96
+
97
+ # Public: Creates a new Section node.
98
+ #
99
+ # Creates a Section node in the same manner as the parser.
100
+ #
101
+ # parent - The parent Section (or Document) of this new Section.
102
+ # title - The String title of the new Section.
103
+ # attrs - A Hash of attributes to control how the section is built.
104
+ # Use the style attribute to set the name of a special section (ex. appendix).
105
+ # Use the id attribute to assign an explicit ID or set the value to false to
106
+ # disable automatic ID generation (when sectids document attribute is set).
107
+ # opts - An optional Hash of options (default: {}):
108
+ # :level - [Integer] The level to assign to this section; defaults to
109
+ # one greater than the parent level (optional).
110
+ # :numbered - [Boolean] A flag to force numbering, which falls back to the
111
+ # state of the sectnums document attribute (optional).
112
+ #
113
+ # Returns a [Section] node with all properties properly initialized.
114
+ def create_section parent, title, attrs, opts = {}
115
+ doc = parent.document
116
+ doctype, level = doc.doctype, (opts[:level] || parent.level + 1)
117
+ if (style = attrs.delete 'style')
118
+ if style == 'abstract' && doctype == 'book'
119
+ sectname, level = 'chapter', 1
120
+ else
121
+ sectname, special = style, true
122
+ level = 1 if level == 0
123
+ end
124
+ elsif doctype == 'book'
125
+ sectname = level == 0 ? 'part' : (level == 1 ? 'chapter' : 'section')
126
+ elsif doctype == 'manpage' && (title.casecmp 'synopsis') == 0
127
+ sectname, special = 'synopsis', true
128
+ else
129
+ sectname = 'section'
130
+ end
131
+ sect = Section.new parent, level, false
132
+ sect.title, sect.sectname = title, sectname
133
+ if special
134
+ sect.special = true
135
+ sect.numbered = true if opts.fetch :numbered, (style == 'appendix')
136
+ elsif opts.fetch :numbered, (level > 0 && (doc.attributes.key? 'sectnums'))
137
+ sect.numbered = sect.special ? (parent.context == :section && parent.numbered) : true
138
+ end
139
+ unless (id = attrs.delete 'id') == false
140
+ sect.id = attrs['id'] = id || ((doc.attributes.key? 'sectids') ? (Section.generate_id sect.title, doc) : nil)
141
+ end
142
+ sect.update_attributes attrs
143
+ sect
93
144
  end
94
145
 
95
146
  def create_block parent, context, source, attrs, opts = {}
@@ -129,7 +180,8 @@ module Extensions
129
180
  [:create_anchor, :create_inline, :anchor]
130
181
  ].each do |method_name, delegate_method_name, context|
131
182
  define_method method_name do |*args|
132
- send delegate_method_name, *args.dup.insert(1, context)
183
+ args.unshift args.shift, context
184
+ send delegate_method_name, *args
133
185
  end
134
186
  end
135
187
  end
@@ -143,63 +195,151 @@ module Extensions
143
195
  end
144
196
 
145
197
  def process *args, &block
146
- # need to check for both block/proc and lambda
147
- # TODO need test for this!
148
- #if block_given? || (args.size == 1 && ::Proc === (block = args[0]))
149
198
  if block_given?
199
+ raise ::ArgumentError, %(wrong number of arguments (given #{args.size}, expected 0)) unless args.empty?
150
200
  @process_block = block
151
- elsif @process_block
201
+ # TODO enable if we want to support passing proc or lambda as argument instead of block
202
+ #elsif ::Proc === args[0]
203
+ # block = args.shift
204
+ # raise ::ArgumentError, %(wrong number of arguments (given #{args.size}, expected 0)) unless args.empty?
205
+ # @process_block = block
206
+ elsif defined? @process_block
152
207
  # NOTE Proc automatically expands a single array argument
153
208
  # ...but lambda doesn't (and we want to accept lambdas too)
154
209
  # TODO need a test for this!
155
210
  @process_block.call(*args)
156
211
  else
212
+ # TODO add exception message here
157
213
  raise ::NotImplementedError
158
214
  end
159
215
  end
160
- #alias :process_with :process
161
216
 
162
217
  def process_block_given?
163
218
  defined? @process_block
164
219
  end
165
220
  end
166
221
 
222
+ module SyntaxDsl
223
+ include ProcessorDsl
224
+
225
+ def named value
226
+ # NOTE due to how processors get initialized, we must defer this assignment in some scenarios
227
+ if Processor === self
228
+ @name = value
229
+ else
230
+ option :name, value
231
+ end
232
+ end
233
+ # NOTE match_name may get deprecated
234
+ alias match_name named
235
+
236
+ def content_model value
237
+ option :content_model, value
238
+ end
239
+ alias parse_content_as content_model
240
+ alias parses_content_as content_model
241
+ #alias parse_as content_model
242
+ #alias parsed_as content_model
243
+
244
+ def positional_attrs *value
245
+ option :pos_attrs, value.flatten
246
+ end
247
+ alias name_attributes positional_attrs
248
+ alias name_positional_attributes positional_attrs
249
+
250
+ def default_attrs value
251
+ option :default_attrs, value
252
+ end
253
+
254
+ def resolves_attributes *args
255
+ # NOTE assume true as default value; rewrap single-argument string or symbol
256
+ if (args = args.fetch 0, true).respond_to? :to_sym
257
+ args = [args]
258
+ end unless args.size > 1
259
+ case args
260
+ when true
261
+ option :pos_attrs, []
262
+ option :default_attrs, {}
263
+ when ::Array
264
+ names, defaults = [], {}
265
+ args.each do |arg|
266
+ if (arg = arg.to_s).include? '='
267
+ name, value = arg.split '=', 2
268
+ if name.include? ':'
269
+ idx, name = name.split ':', 2
270
+ idx = idx == '@' ? names.size : idx.to_i
271
+ names[idx] = name
272
+ end
273
+ defaults[name] = value
274
+ elsif arg.include? ':'
275
+ idx, name = arg.split ':', 2
276
+ idx = idx == '@' ? names.size : idx.to_i
277
+ names[idx] = name
278
+ else
279
+ names << arg
280
+ end
281
+ end
282
+ option :pos_attrs, names.compact
283
+ option :default_attrs, defaults
284
+ when ::Hash
285
+ names, defaults = [], {}
286
+ args.each do |key, val|
287
+ if (name = key.to_s).include? ':'
288
+ idx, name = name.split ':', 2
289
+ idx = idx == '@' ? names.size : idx.to_i
290
+ names[idx] = name
291
+ end
292
+ defaults[name] = val if val
293
+ end
294
+ option :pos_attrs, names.compact
295
+ option :default_attrs, defaults
296
+ else
297
+ raise ::ArgumentError, %(unsupported attributes specification for macro: #{args.inspect})
298
+ end
299
+ end
300
+ # NOTE we may decide to drop this alias
301
+ alias resolve_attributes resolves_attributes
302
+ end
303
+
167
304
  # Public: Preprocessors are run after the source text is split into lines and
168
305
  # normalized, but before parsing begins.
169
306
  #
170
307
  # Prior to invoking the preprocessor, Asciidoctor splits the source text into
171
308
  # lines and normalizes them. The normalize process strips trailing whitespace
172
- # from each line and leaves behind a line-feed character (i.e., "\n").
309
+ # and the end of line character sequence from each line.
173
310
  #
174
- # Asciidoctor passes a reference to the Reader and a copy of the lines Array
175
- # to the {Processor#process} method of an instance of each registered
176
- # Preprocessor. The Preprocessor modifies the Array as necessary and either
177
- # returns a reference to the same Reader or a reference to a new Reader.
311
+ # Asciidoctor passes the document and the document's Reader to the
312
+ # {Processor#process} method of the Preprocessor instance. The Preprocessor
313
+ # can modify the Reader as necessary and either return the same Reader (or
314
+ # falsy, which is equivalent) or a reference to a substitute Reader.
178
315
  #
179
316
  # Preprocessor implementations must extend the Preprocessor class.
180
317
  class Preprocessor < Processor
181
318
  def process document, reader
182
- raise ::NotImplementedError
319
+ raise ::NotImplementedError, %(Asciidoctor::Extensions::Preprocessor subclass must implement ##{__method__} method)
183
320
  end
184
321
  end
185
322
  Preprocessor::DSL = ProcessorDsl
186
323
 
187
- # Public: Treeprocessors are run on the Document after the source has been
324
+ # Public: TreeProcessors are run on the Document after the source has been
188
325
  # parsed into an abstract syntax tree (AST), as represented by the Document
189
326
  # object and its child Node objects (e.g., Section, Block, List, ListItem).
190
327
  #
191
328
  # Asciidoctor invokes the {Processor#process} method on an instance of each
192
- # registered Treeprocessor.
329
+ # registered TreeProcessor.
193
330
  #
194
- # Treeprocessor implementations must extend Treeprocessor.
331
+ # TreeProcessor implementations must extend TreeProcessor.
195
332
  #--
196
- # QUESTION should the treeprocessor get invoked after parse header too?
197
- class Treeprocessor < Processor
333
+ # QUESTION should the tree processor get invoked after parse header too?
334
+ class TreeProcessor < Processor
198
335
  def process document
199
- raise ::NotImplementedError
336
+ raise ::NotImplementedError, %(Asciidoctor::Extensions::TreeProcessor subclass must implement ##{__method__} method)
200
337
  end
201
338
  end
202
- Treeprocessor::DSL = ProcessorDsl
339
+ TreeProcessor::DSL = ProcessorDsl
340
+
341
+ # Alias deprecated class name for backwards compatibility
342
+ Treeprocessor = TreeProcessor
203
343
 
204
344
  # Public: Postprocessors are run after the document is converted, but before
205
345
  # it is written to the output stream.
@@ -218,7 +358,7 @@ module Extensions
218
358
  # Postprocessor implementations must Postprocessor.
219
359
  class Postprocessor < Processor
220
360
  def process document, output
221
- raise ::NotImplementedError
361
+ raise ::NotImplementedError, %(Asciidoctor::Extensions::Postprocessor subclass must implement ##{__method__} method)
222
362
  end
223
363
  end
224
364
  Postprocessor::DSL = ProcessorDsl
@@ -233,17 +373,37 @@ module Extensions
233
373
  #
234
374
  # IncludeProcessor implementations must extend IncludeProcessor.
235
375
  #--
236
- # TODO add file extension or regexp to shortcut handles?
376
+ # TODO add file extension or regexp as shortcut for handles? method
237
377
  class IncludeProcessor < Processor
238
378
  def process document, reader, target, attributes
239
- raise ::NotImplementedError
379
+ raise ::NotImplementedError, %(Asciidoctor::Extensions::IncludeProcessor subclass must implement ##{__method__} method)
240
380
  end
241
381
 
242
382
  def handles? target
243
383
  true
244
384
  end
245
385
  end
246
- IncludeProcessor::DSL = ProcessorDsl
386
+
387
+ module IncludeProcessorDsl
388
+ include ProcessorDsl
389
+
390
+ def handles? *args, &block
391
+ if block_given?
392
+ raise ::ArgumentError, %(wrong number of arguments (given #{args.size}, expected 0)) unless args.empty?
393
+ @handles_block = block
394
+ # TODO enable if we want to support passing proc or lambda as argument instead of block
395
+ #elsif ::Proc === args[0]
396
+ # block = args.shift
397
+ # raise ::ArgumentError, %(wrong number of arguments (given #{args.size}, expected 0)) unless args.empty?
398
+ # @handles_block = block
399
+ elsif defined? @handles_block
400
+ @handles_block.call args[0]
401
+ else
402
+ true
403
+ end
404
+ end
405
+ end
406
+ IncludeProcessor::DSL = IncludeProcessorDsl
247
407
 
248
408
  # Public: DocinfoProcessors are used to add additional content to
249
409
  # the header and/or footer of the generated document.
@@ -262,7 +422,7 @@ module Extensions
262
422
  end
263
423
 
264
424
  def process document
265
- raise ::NotImplementedError
425
+ raise ::NotImplementedError, %(Asciidoctor::Extensions::DocinfoProcessor subclass must implement ##{__method__} method)
266
426
  end
267
427
  end
268
428
 
@@ -281,7 +441,7 @@ module Extensions
281
441
  # When Asciidoctor encounters a delimited block or paragraph with an
282
442
  # unrecognized name while parsing the document, it looks for a BlockProcessor
283
443
  # registered to handle this name and, if found, invokes its {Processor#process}
284
- # method to build a cooresponding node in the document tree.
444
+ # method to build a corresponding node in the document tree.
285
445
  #
286
446
  # AsciiDoc example:
287
447
  #
@@ -293,7 +453,8 @@ module Extensions
293
453
  # * :named - The name of the block (required: true)
294
454
  # * :contexts - The blocks contexts on which this style can be used (default: [:paragraph, :open]
295
455
  # * :content_model - The structure of the content supported in this block (default: :compound)
296
- # * :positional_attributes - A list of attribute names used to map positional attributes (default: nil)
456
+ # * :pos_attrs - A list of attribute names used to map positional attributes (default: nil)
457
+ # * :default_attrs - A hash of attribute names and values used to seed the attributes hash (default: nil)
297
458
  # * ...
298
459
  #
299
460
  # BlockProcessor implementations must extend BlockProcessor.
@@ -317,46 +478,19 @@ module Extensions
317
478
  end
318
479
 
319
480
  def process parent, reader, attributes
320
- raise ::NotImplementedError
481
+ raise ::NotImplementedError, %(Asciidoctor::Extensions::BlockProcessor subclass must implement ##{__method__} method)
321
482
  end
322
483
  end
323
484
 
324
485
  module BlockProcessorDsl
325
- include ProcessorDsl
326
-
327
- # FIXME this isn't the prettiest thing
328
- def named value
329
- if Processor === self
330
- @name = value
331
- else
332
- option :name, value
333
- end
334
- end
335
- alias :match_name :named
336
- alias :bind_to :named
486
+ include SyntaxDsl
337
487
 
338
488
  def contexts *value
339
489
  option :contexts, value.flatten
340
490
  end
341
- alias :on_contexts :contexts
342
- alias :on_context :contexts
343
-
344
- def content_model value
345
- option :content_model, value
346
- end
347
- alias :parse_content_as :content_model
348
-
349
- def positional_attributes *value
350
- option :pos_attrs, value.flatten
351
- end
352
- alias :pos_attrs :positional_attributes
353
- alias :name_attributes :positional_attributes
354
- alias :name_positional_attributes :positional_attributes
355
-
356
- def default_attrs value
357
- option :default_attrs, value
358
- end
359
- alias :seed_attributes_with :default_attrs
491
+ alias on_contexts contexts
492
+ alias on_context contexts
493
+ alias bound_to contexts
360
494
  end
361
495
  BlockProcessor::DSL = BlockProcessorDsl
362
496
 
@@ -370,40 +504,23 @@ module Extensions
370
504
  end
371
505
 
372
506
  def process parent, target, attributes
373
- raise ::NotImplementedError
507
+ raise ::NotImplementedError, %(Asciidoctor::Extensions::MacroProcessor subclass must implement ##{__method__} method)
374
508
  end
375
509
  end
376
510
 
377
511
  module MacroProcessorDsl
378
- include ProcessorDsl
379
- # QUESTION perhaps include a SyntaxDsl?
512
+ include SyntaxDsl
380
513
 
381
- def named value
382
- if Processor === self
383
- @name = value
384
- else
385
- option :name, value
514
+ def resolves_attributes *args
515
+ if args.size == 1 && !args[0]
516
+ option :content_model, :text
517
+ return
386
518
  end
519
+ super
520
+ option :content_model, :attributes
387
521
  end
388
- alias :match_name :named
389
- alias :bind_to :named
390
-
391
- def content_model value
392
- option :content_model, value
393
- end
394
- alias :parse_content_as :content_model
395
-
396
- def positional_attributes *value
397
- option :pos_attrs, value.flatten
398
- end
399
- alias :pos_attrs :positional_attributes
400
- alias :name_attributes :positional_attributes
401
- alias :name_positional_attributes :positional_attributes
402
-
403
- def default_attrs value
404
- option :default_attrs, value
405
- end
406
- alias :seed_attributes_with :default_attrs
522
+ # NOTE we may decide to drop this alias
523
+ alias resolve_attributes resolves_attributes
407
524
  end
408
525
 
409
526
  # Public: BlockMacroProcessors are used to handle block macros that have a
@@ -420,35 +537,36 @@ module Extensions
420
537
  # InlineMacroProcessor implementations must extend InlineMacroProcessor.
421
538
  #--
422
539
  # TODO break this out into different pattern types
423
- # for example, FormalInlineMacro, ShortInlineMacro (no target) and other patterns
540
+ # for example, FullInlineMacro, ShortInlineMacro (no target) and other patterns
424
541
  # FIXME for inline passthrough, we need to have some way to specify the text as a passthrough
425
542
  class InlineMacroProcessor < MacroProcessor
543
+ @@rx_cache = {}
544
+
426
545
  # Lookup the regexp option, resolving it first if necessary.
427
546
  # Once this method is called, the regexp is considered frozen.
428
547
  def regexp
429
- @config[:regexp] ||= (resolve_regexp @name, @config[:format])
548
+ @config[:regexp] ||= resolve_regexp @name.to_s, @config[:format]
430
549
  end
431
550
 
432
551
  def resolve_regexp name, format
433
- # TODO memoize these regular expressions!
434
- if format == :short
435
- %r(\\?#{name}:\[((?:\\\]|[^\]])*?)\])
436
- else
437
- %r(\\?#{name}:(\S+?)\[((?:\\\]|[^\]])*?)\])
438
- end
552
+ raise ::ArgumentError, %(invalid name for inline macro: #{name}) unless MacroNameRx.match? name
553
+ @@rx_cache[[name, format]] ||= /\\?#{name}:#{format == :short ? '(){0}' : '(\S+?)'}\[(|.*?[^\\])\]/
439
554
  end
440
555
  end
441
556
 
442
557
  module InlineMacroProcessorDsl
443
558
  include MacroProcessorDsl
444
559
 
445
- def using_format value
560
+ def with_format value
446
561
  option :format, value
447
562
  end
563
+ alias using_format with_format
448
564
 
449
- def match value
565
+ def matches value
450
566
  option :regexp, value
451
567
  end
568
+ alias match matches
569
+ alias matching matches
452
570
  end
453
571
  InlineMacroProcessor::DSL = InlineMacroProcessorDsl
454
572
 
@@ -463,9 +581,9 @@ module Extensions
463
581
  #--
464
582
  # QUESTION call this ExtensionInfo?
465
583
  class Extension
466
- attr :kind
467
- attr :config
468
- attr :instance
584
+ attr_reader :kind
585
+ attr_reader :config
586
+ attr_reader :instance
469
587
 
470
588
  def initialize kind, instance, config
471
589
  @kind = kind
@@ -478,7 +596,7 @@ module Extensions
478
596
  # reference to the {Processor#process} method. By storing this reference, its
479
597
  # possible to accomodate both concrete extension implementations and Procs.
480
598
  class ProcessorExtension < Extension
481
- attr :process_method
599
+ attr_reader :process_method
482
600
 
483
601
  def initialize kind, instance, process_method = nil
484
602
  super kind, instance, instance.config
@@ -518,7 +636,7 @@ module Extensions
518
636
 
519
637
  def initialize groups = {}
520
638
  @groups = groups
521
- @preprocessor_extensions = @treeprocessor_extensions = @postprocessor_extensions = @include_processor_extensions = @docinfo_processor_extensions =nil
639
+ @preprocessor_extensions = @tree_processor_extensions = @postprocessor_extensions = @include_processor_extensions = @docinfo_processor_extensions = nil
522
640
  @block_extensions = @block_macro_extensions = @inline_macro_extensions = nil
523
641
  @document = nil
524
642
  end
@@ -575,7 +693,7 @@ module Extensions
575
693
  #
576
694
  # # as a method block
577
695
  # preprocessor do
578
- # process |reader, lines|
696
+ # process |doc, reader|
579
697
  # ...
580
698
  # end
581
699
  # end
@@ -601,58 +719,63 @@ module Extensions
601
719
  @preprocessor_extensions
602
720
  end
603
721
 
604
- # Public: Registers a {Treeprocessor} with the extension registry to process
722
+ # Public: Registers a {TreeProcessor} with the extension registry to process
605
723
  # the AsciiDoc source after parsing is complete.
606
724
  #
607
- # The Treeprocessor may be one of four types:
725
+ # The TreeProcessor may be one of four types:
608
726
  #
609
- # * A Treeprocessor subclass
610
- # * An instance of a Treeprocessor subclass
611
- # * The String name of a Treeprocessor subclass
612
- # * A method block (i.e., Proc) that conforms to the Treeprocessor contract
727
+ # * A TreeProcessor subclass
728
+ # * An instance of a TreeProcessor subclass
729
+ # * The String name of a TreeProcessor subclass
730
+ # * A method block (i.e., Proc) that conforms to the TreeProcessor contract
613
731
  #
614
- # Unless the Treeprocessor is passed as the method block, it must be the
732
+ # Unless the TreeProcessor is passed as the method block, it must be the
615
733
  # first argument to this method.
616
734
  #
617
735
  # Examples
618
736
  #
619
- # # as a Treeprocessor subclass
620
- # treeprocessor ShellTreeprocessor
737
+ # # as a TreeProcessor subclass
738
+ # tree_processor ShellTreeProcessor
621
739
  #
622
- # # as an instance of a Treeprocessor subclass
623
- # treeprocessor ShellTreeprocessor.new
740
+ # # as an instance of a TreeProcessor subclass
741
+ # tree_processor ShellTreeProcessor.new
624
742
  #
625
- # # as a name of a Treeprocessor subclass
626
- # treeprocessor 'ShellTreeprocessor'
743
+ # # as a name of a TreeProcessor subclass
744
+ # tree_processor 'ShellTreeProcessor'
627
745
  #
628
746
  # # as a method block
629
- # treeprocessor do
747
+ # tree_processor do
630
748
  # process |document|
631
749
  # ...
632
750
  # end
633
751
  # end
634
752
  #
635
753
  # Returns the [Extension] stored in the registry that proxies the
636
- # instance of this Treeprocessor.
637
- def treeprocessor *args, &block
638
- add_document_processor :treeprocessor, args, &block
754
+ # instance of this TreeProcessor.
755
+ def tree_processor *args, &block
756
+ add_document_processor :tree_processor, args, &block
639
757
  end
640
758
 
641
- # Public: Checks whether any {Treeprocessor} extensions have been registered.
759
+ # Public: Checks whether any {TreeProcessor} extensions have been registered.
642
760
  #
643
- # Returns a [Boolean] indicating whether any Treeprocessor extensions are registered.
644
- def treeprocessors?
645
- !!@treeprocessor_extensions
761
+ # Returns a [Boolean] indicating whether any TreeProcessor extensions are registered.
762
+ def tree_processors?
763
+ !!@tree_processor_extensions
646
764
  end
647
765
 
648
766
  # Public: Retrieves the {Extension} proxy objects for all
649
- # Treeprocessor instances in this registry.
767
+ # TreeProcessor instances in this registry.
650
768
  #
651
769
  # Returns an [Array] of Extension proxy objects.
652
- def treeprocessors
653
- @treeprocessor_extensions
770
+ def tree_processors
771
+ @tree_processor_extensions
654
772
  end
655
773
 
774
+ # Alias deprecated methods for backwards compatibility
775
+ alias treeprocessor tree_processor
776
+ alias treeprocessors? tree_processors?
777
+ alias treeprocessors tree_processors
778
+
656
779
  # Public: Registers a {Postprocessor} with the extension registry to process
657
780
  # the output after conversion is complete.
658
781
  #
@@ -1109,7 +1232,7 @@ module Extensions
1109
1232
 
1110
1233
  def add_document_processor kind, args, &block
1111
1234
  kind_name = kind.to_s.tr '_', ' '
1112
- kind_class_symbol = kind_name.split(' ').map {|word| %(#{word.chr.upcase}#{word[1..-1]}) }.join.to_sym
1235
+ kind_class_symbol = kind_name.split.map {|it| it.capitalize }.join.to_sym
1113
1236
  kind_class = Extensions.const_get kind_class_symbol
1114
1237
  kind_java_class = (defined? ::AsciidoctorJ) ? (::AsciidoctorJ::Extensions.const_get kind_class_symbol) : nil
1115
1238
  kind_store = instance_variable_get(%(@#{kind}_extensions).to_sym) || instance_variable_set(%(@#{kind}_extensions).to_sym, [])
@@ -1127,17 +1250,17 @@ module Extensions
1127
1250
  processor.instance_exec(&block)
1128
1251
  processor.freeze
1129
1252
  unless processor.process_block_given?
1130
- raise ::ArgumentError.new %(No block specified to process #{kind_name} extension at #{block.source_location})
1253
+ raise ::ArgumentError, %(No block specified to process #{kind_name} extension at #{block.source_location})
1131
1254
  end
1132
1255
  ProcessorExtension.new kind, processor
1133
1256
  else
1134
1257
  processor, config = resolve_args args, 2
1135
- # style 2: specified as class or class name
1136
- if ::Class === processor || (::String === processor && (processor = Extensions.class_for_name processor))
1137
- unless processor < kind_class || (kind_java_class && processor < kind_java_class)
1138
- raise ::ArgumentError.new %(Invalid type for #{kind_name} extension: #{processor})
1258
+ # style 2: specified as Class or String class name
1259
+ if (processor_class = Extensions.resolve_class processor)
1260
+ unless processor_class < kind_class || (kind_java_class && processor_class < kind_java_class)
1261
+ raise ::ArgumentError, %(Invalid type for #{kind_name} extension: #{processor})
1139
1262
  end
1140
- processor_instance = processor.new config
1263
+ processor_instance = processor_class.new config
1141
1264
  processor_instance.freeze
1142
1265
  ProcessorExtension.new kind, processor_instance
1143
1266
  # style 3: specified as instance
@@ -1146,7 +1269,7 @@ module Extensions
1146
1269
  processor.freeze
1147
1270
  ProcessorExtension.new kind, processor
1148
1271
  else
1149
- raise ::ArgumentError.new %(Invalid arguments specified for registering #{kind_name} extension: #{args})
1272
+ raise ::ArgumentError, %(Invalid arguments specified for registering #{kind_name} extension: #{args})
1150
1273
  end
1151
1274
  end
1152
1275
 
@@ -1159,8 +1282,7 @@ module Extensions
1159
1282
 
1160
1283
  def add_syntax_processor kind, args, &block
1161
1284
  kind_name = kind.to_s.tr '_', ' '
1162
- kind_class_basename = kind_name.split(' ').map {|word| %(#{word.chr.upcase}#{word[1..-1]}) }.join
1163
- kind_class_symbol = %(#{kind_class_basename}Processor).to_sym
1285
+ kind_class_symbol = (kind_name.split.map {|it| it.capitalize }.push 'Processor').join.to_sym
1164
1286
  kind_class = Extensions.const_get kind_class_symbol
1165
1287
  kind_java_class = (defined? ::AsciidoctorJ) ? (::AsciidoctorJ::Extensions.const_get kind_class_symbol) : nil
1166
1288
  kind_store = instance_variable_get(%(@#{kind}_extensions).to_sym) || instance_variable_set(%(@#{kind}_extensions).to_sym, {})
@@ -1180,37 +1302,37 @@ module Extensions
1180
1302
  processor.instance_exec(&block)
1181
1303
  end
1182
1304
  unless (name = as_symbol processor.name)
1183
- raise ::ArgumentError.new %(No name specified for #{kind_name} extension at #{block.source_location})
1305
+ raise ::ArgumentError, %(No name specified for #{kind_name} extension at #{block.source_location})
1184
1306
  end
1185
1307
  unless processor.process_block_given?
1186
- raise ::NoMethodError.new %(No block specified to process #{kind_name} extension at #{block.source_location})
1308
+ raise ::NoMethodError, %(No block specified to process #{kind_name} extension at #{block.source_location})
1187
1309
  end
1188
1310
  processor.freeze
1189
1311
  kind_store[name] = ProcessorExtension.new kind, processor
1190
1312
  else
1191
1313
  processor, name, config = resolve_args args, 3
1192
- # style 2: specified as class or class name
1193
- if ::Class === processor || (::String === processor && (processor = Extensions.class_for_name processor))
1194
- unless processor < kind_class || (kind_java_class && processor < kind_java_class)
1195
- raise ::ArgumentError.new %(Class specified for #{kind_name} extension does not inherit from #{kind_class}: #{processor})
1314
+ # style 2: specified as Class or String class name
1315
+ if (processor_class = Extensions.resolve_class processor)
1316
+ unless processor_class < kind_class || (kind_java_class && processor_class < kind_java_class)
1317
+ raise ::ArgumentError, %(Class specified for #{kind_name} extension does not inherit from #{kind_class}: #{processor})
1196
1318
  end
1197
- processor_instance = processor.new as_symbol(name), config
1319
+ processor_instance = processor_class.new as_symbol(name), config
1198
1320
  unless (name = as_symbol processor_instance.name)
1199
- raise ::ArgumentError.new %(No name specified for #{kind_name} extension: #{processor})
1321
+ raise ::ArgumentError, %(No name specified for #{kind_name} extension: #{processor})
1200
1322
  end
1201
- processor.freeze
1323
+ processor_instance.freeze
1202
1324
  kind_store[name] = ProcessorExtension.new kind, processor_instance
1203
1325
  # style 3: specified as instance
1204
1326
  elsif kind_class === processor || (kind_java_class && kind_java_class === processor)
1205
1327
  processor.update_config config
1206
1328
  # TODO need a test for this override!
1207
1329
  unless (name = name ? (processor.name = as_symbol name) : (as_symbol processor.name))
1208
- raise ::ArgumentError.new %(No name specified for #{kind_name} extension: #{processor})
1330
+ raise ::ArgumentError, %(No name specified for #{kind_name} extension: #{processor})
1209
1331
  end
1210
1332
  processor.freeze
1211
1333
  kind_store[name] = ProcessorExtension.new kind, processor
1212
1334
  else
1213
- raise ::ArgumentError.new %(Invalid arguments specified for registering #{kind_name} extension: #{args})
1335
+ raise ::ArgumentError, %(Invalid arguments specified for registering #{kind_name} extension: #{args})
1214
1336
  end
1215
1337
  end
1216
1338
  end
@@ -1218,9 +1340,8 @@ module Extensions
1218
1340
  def resolve_args args, expect
1219
1341
  opts = ::Hash === args[-1] ? args.pop : {}
1220
1342
  return opts if expect == 1
1221
- num_args = args.size
1222
- if (missing = expect - 1 - num_args) > 0
1223
- args.fill nil, num_args, missing
1343
+ if (missing = expect - 1 - args.size) > 0
1344
+ args += (::Array.new missing)
1224
1345
  elsif missing < 0
1225
1346
  args.pop(-missing)
1226
1347
  end
@@ -1247,14 +1368,15 @@ module Extensions
1247
1368
  @groups ||= {}
1248
1369
  end
1249
1370
 
1250
- def build_registry name = nil, &block
1371
+ def create name = nil, &block
1251
1372
  if block_given?
1252
- name ||= generate_name
1253
- Registry.new({ name => block })
1373
+ Registry.new({ (name || generate_name) => block })
1254
1374
  else
1255
1375
  Registry.new
1256
1376
  end
1257
1377
  end
1378
+ # Deprecated: Use create instead of build_registry
1379
+ alias build_registry create
1258
1380
 
1259
1381
  # Public: Registers an extension Group that subsequently registers a
1260
1382
  # collection of extensions.
@@ -1291,56 +1413,67 @@ module Extensions
1291
1413
  #
1292
1414
  # Returns the [Proc, Class or Object] instance, matching the type passed to this method.
1293
1415
  def register *args, &block
1294
- argc = args.length
1295
- resolved_group = if block_given?
1296
- block
1297
- elsif !(group = args.pop)
1298
- raise ::ArgumentError.new %(Extension group to register not specified)
1416
+ argc = args.size
1417
+ if block_given?
1418
+ resolved_group = block
1419
+ elsif (group = args.pop)
1420
+ # QUESTION should we instantiate the group class here or defer until activation??
1421
+ resolved_group = (resolve_class group) || group
1299
1422
  else
1300
- # QUESTION should we instantiate the group class here or defer until
1301
- # activation??
1302
- case group
1303
- when ::Class
1304
- group
1305
- when ::String
1306
- class_for_name group
1307
- when ::Symbol
1308
- class_for_name group.to_s
1309
- else
1310
- group
1311
- end
1423
+ raise ::ArgumentError, %(Extension group to register not specified)
1312
1424
  end
1313
1425
  name = args.pop || generate_name
1314
1426
  unless args.empty?
1315
- raise ::ArgumentError.new %(Wrong number of arguments (#{argc} for 1..2))
1427
+ raise ::ArgumentError, %(Wrong number of arguments (#{argc} for 1..2))
1316
1428
  end
1317
1429
  groups[name] = resolved_group
1318
1430
  end
1319
1431
 
1432
+ # Public: Unregister all statically-registered extension groups.
1433
+ #
1434
+ # Returns nothing
1320
1435
  def unregister_all
1321
1436
  @groups = {}
1437
+ nil
1322
1438
  end
1323
1439
 
1324
- # unused atm, but tested
1440
+ # Public: Unregister statically-registered extension groups by name.
1441
+ #
1442
+ # names - one or more Symbol or String group names to unregister
1443
+ #
1444
+ # Returns nothing
1445
+ def unregister *names
1446
+ names.each {|group| @groups.delete group.to_sym }
1447
+ nil
1448
+ end
1449
+
1450
+ # Internal: Resolve the specified object as a Class
1451
+ #
1452
+ # object - The object to resolve as a Class
1453
+ #
1454
+ # Returns a Class if the specified object is a Class (but not a Module) or
1455
+ # a String that resolves to a Class; otherwise, nil
1325
1456
  def resolve_class object
1326
- ::Class === object ? object : (class_for_name object.to_s)
1457
+ case object
1458
+ when ::Class
1459
+ object
1460
+ when ::String
1461
+ class_for_name object
1462
+ end
1327
1463
  end
1328
1464
 
1329
1465
  # Public: Resolves the Class object for the qualified name.
1330
1466
  #
1331
1467
  # Returns Class
1332
1468
  def class_for_name qualified_name
1333
- resolved_class = ::Object
1334
- qualified_name.split('::').each do |name|
1335
- if name.empty?
1336
- # do nothing
1337
- elsif resolved_class.const_defined? name
1338
- resolved_class = resolved_class.const_get name
1339
- else
1340
- raise %(Could not resolve class for name: #{qualified_name})
1469
+ resolved = ::Object
1470
+ (qualified_name.split '::').each do |name|
1471
+ unless name.empty? || ((resolved.const_defined? name) && ::Module === (resolved = resolved.const_get name))
1472
+ raise ::NameError, %(Could not resolve class for name: #{qualified_name})
1341
1473
  end
1342
1474
  end
1343
- resolved_class
1475
+ raise ::NameError, %(Could not resolve class for name: #{qualified_name}) unless ::Class === resolved
1476
+ resolved
1344
1477
  end
1345
1478
  end
1346
1479