"Fossies" - the Fresh Open Source Software Archive

Member "asciidoctor-2.0.10/lib/asciidoctor/extensions.rb" (1 Jun 2019, 56248 Bytes) of package /linux/www/asciidoctor-2.0.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Ruby source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "extensions.rb": 2.0.7_vs_2.0.8.

    1 # frozen_string_literal: true
    2 (require 'asciidoctor' unless defined? Asciidoctor.load) unless RUBY_ENGINE == 'opal'
    3 
    4 module Asciidoctor
    5 # Extensions provide a way to participate in the parsing and converting
    6 # phases of the AsciiDoc processor or extend the AsciiDoc syntax.
    7 #
    8 # The various extensions participate in AsciiDoc processing as follows:
    9 #
   10 # 1. After the source lines are normalized, {Preprocessor}s modify or replace
   11 #    the source lines before parsing begins.  {IncludeProcessor}s are used to
   12 #    process include directives for targets which they claim to handle.
   13 # 2. The Parser parses the block-level content into an abstract syntax tree.
   14 #    Custom blocks and block macros are processed by associated {BlockProcessor}s
   15 #    and {BlockMacroProcessor}s, respectively.
   16 # 3. {TreeProcessor}s are run on the abstract syntax tree.
   17 # 4. Conversion of the document begins, at which point inline markup is processed
   18 #    and converted. Custom inline macros are processed by associated {InlineMacroProcessor}s.
   19 # 5. {Postprocessor}s modify or replace the converted document.
   20 # 6. The output is written to the output stream.
   21 #
   22 # Extensions may be registered globally using the {Extensions.register} method
   23 # or added to a custom {Registry} instance and passed as an option to a single
   24 # Asciidoctor processor.
   25 module Extensions
   26 
   27   # Public: An abstract base class for document and syntax processors.
   28   #
   29   # This class provides access to a class-level Hash for holding default
   30   # configuration options defined using the {Processor.option} method. This
   31   # style of default configuration is specific to the native Ruby environment
   32   # and is only consulted inside the initializer. An overriding configuration
   33   # Hash can be passed to the initializer. Once the processor is initialized,
   34   # the configuration is accessed using the {Processor#config} instance variable.
   35   #
   36   # Instances of the Processor class provide convenience methods for creating
   37   # AST nodes, such as Block and Inline, and for parsing child content.
   38   class Processor
   39     class << self
   40       # Public: Get the static configuration for this processor class.
   41       #
   42       # Returns a configuration [Hash]
   43       def config
   44         @config ||= {}
   45       end
   46 
   47       # Public: Assigns a default value for the specified option that gets
   48       # applied to all instances of this processor.
   49       #
   50       # Examples
   51       #
   52       #   option :contexts, [:open, :paragraph]
   53       #
   54       # Returns nothing
   55       def option key, default_value
   56         config[key] = default_value
   57       end
   58 
   59       # Mixes the DSL class for this processor into this processor class or instance.
   60       #
   61       # This method automatically detects whether to use the include or extend keyword to mix in the module.
   62       #
   63       # NOTE Inspiration for this DSL design comes from https://corcoran.io/2013/09/04/simple-pattern-ruby-dsl/
   64       #
   65       # Returns self
   66       def enable_dsl
   67         if const_defined? :DSL
   68           if singleton_class?
   69             include const_get :DSL
   70           else
   71             extend const_get :DSL
   72           end
   73         end
   74       end
   75       alias use_dsl enable_dsl
   76     end
   77 
   78     # Public: Get the configuration Hash for this processor instance.
   79     attr_reader :config
   80 
   81     def initialize config = {}
   82       @config = self.class.config.merge config
   83     end
   84 
   85     def update_config config
   86       @config.update config
   87     end
   88 
   89     def process *args
   90       raise ::NotImplementedError, %(#{Processor} subclass #{self.class} must implement the ##{__method__} method)
   91     end
   92 
   93     # QUESTION should attributes be an option instead of a parameter?
   94 
   95     # Public: Creates a new Section node.
   96     #
   97     # Creates a Section node in the same manner as the parser.
   98     #
   99     # parent - The parent Section (or Document) of this new Section.
  100     # title  - The String title of the new Section.
  101     # attrs  - A Hash of attributes to control how the section is built.
  102     #          Use the style attribute to set the name of a special section (ex. appendix).
  103     #          Use the id attribute to assign an explicit ID or set the value to false to
  104     #          disable automatic ID generation (when sectids document attribute is set).
  105     # opts   - An optional Hash of options (default: {}):
  106     #          :level    - [Integer] The level to assign to this section; defaults to
  107     #                      one greater than the parent level (optional).
  108     #          :numbered - [Boolean] A flag to force numbering, which falls back to the
  109     #                      state of the sectnums document attribute (optional).
  110     #
  111     # Returns a [Section] node with all properties properly initialized.
  112     def create_section parent, title, attrs, opts = {}
  113       doc = parent.document
  114       book = (doctype = doc.doctype) == 'book'
  115       level = opts[:level] || parent.level + 1
  116       if (style = attrs.delete 'style')
  117         if book && style == 'abstract'
  118           sectname, level = 'chapter', 1
  119         else
  120           sectname, special = style, true
  121           level = 1 if level == 0
  122         end
  123       elsif book
  124         sectname = level == 0 ? 'part' : (level > 1 ? 'section' : 'chapter')
  125       elsif doctype == 'manpage' && (title.casecmp 'synopsis') == 0
  126         sectname, special = 'synopsis', true
  127       else
  128         sectname = 'section'
  129       end
  130       sect = Section.new parent, level
  131       sect.title, sect.sectname = title, sectname
  132       if special
  133         sect.special = true
  134         if opts.fetch :numbered, (style == 'appendix')
  135           sect.numbered = true
  136         elsif !(opts.key? :numbered) && (doc.attr? 'sectnums', 'all')
  137           sect.numbered = book && level == 1 ? :chapter : true
  138         end
  139       elsif level > 0
  140         if opts.fetch :numbered, (doc.attr? 'sectnums')
  141           sect.numbered = sect.special ? parent.numbered && true : true
  142         end
  143       else
  144         sect.numbered = true if opts.fetch :numbered, (book && (doc.attr? 'partnums'))
  145       end
  146       if (id = attrs['id']) == false
  147         attrs.delete 'id'
  148       else
  149         sect.id = attrs['id'] = id || ((doc.attr? 'sectids') ? (Section.generate_id sect.title, doc) : nil)
  150       end
  151       sect.update_attributes attrs
  152       sect
  153     end
  154 
  155     def create_block parent, context, source, attrs, opts = {}
  156       Block.new parent, context, { source: source, attributes: attrs }.merge(opts)
  157     end
  158 
  159     # Public: Creates a list node and links it to the specified parent.
  160     #
  161     # parent - The parent Block (Block, Section, or Document) of this new list block.
  162     # context - The list context (e.g., :ulist, :olist, :colist, :dlist)
  163     # attrs  - A Hash of attributes to set on this list block
  164     #
  165     # Returns a [List] node with all properties properly initialized.
  166     def create_list parent, context, attrs = nil
  167       list = List.new parent, context
  168       list.update_attributes attrs if attrs
  169       list
  170     end
  171 
  172     # Public: Creates a list item node and links it to the specified parent.
  173     #
  174     # parent - The parent List of this new list item block.
  175     # text   - The text of the list item.
  176     #
  177     # Returns a [ListItem] node with all properties properly initialized.
  178     def create_list_item parent, text = nil
  179       ListItem.new parent, text
  180     end
  181 
  182     # Public: Creates an image block node and links it to the specified parent.
  183     #
  184     # parent - The parent Block (Block, Section, or Document) of this new image block.
  185     # attrs  - A Hash of attributes to control how the image block is built.
  186     #          Use the target attribute to set the source of the image.
  187     #          Use the alt attribute to specify an alternative text for the image.
  188     # opts   - An optional Hash of options (default: {})
  189     #
  190     # Returns a [Block] node with all properties properly initialized.
  191     def create_image_block parent, attrs, opts = {}
  192       unless (target = attrs['target'])
  193         raise ::ArgumentError, 'Unable to create an image block, target attribute is required'
  194       end
  195       attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
  196       title = (attrs.key? 'title') ? (attrs.delete 'title') : nil
  197       block = create_block parent, :image, nil, attrs, opts
  198       if title
  199         block.title = title
  200         block.assign_caption (attrs.delete 'caption'), 'figure'
  201       end
  202       block
  203     end
  204 
  205     def create_inline parent, context, text, opts = {}
  206       Inline.new parent, context, text, context == :quoted ? ({ type: :unquoted }.merge opts) : opts
  207     end
  208 
  209     # Public: Parses blocks in the content and attaches the block to the parent.
  210     #
  211     # Returns The parent node into which the blocks are parsed.
  212     #--
  213     # QUESTION is parse_content the right method name? should we wrap in open block automatically?
  214     def parse_content parent, content, attributes = nil
  215       reader = Reader === content ? content : (Reader.new content)
  216       Parser.parse_blocks reader, parent, attributes
  217       parent
  218     end
  219 
  220     # Public: Parses the attrlist String into a Hash of attributes
  221     #
  222     # block    - the current AbstractBlock or the parent AbstractBlock if there is no current block (used for applying subs)
  223     # attrlist - the list of attributes as a String
  224     # opts     - an optional Hash of options to control processing:
  225     #            :positional_attributes - an Array of attribute names to map positional arguments to (optional, default: false)
  226     #            :sub_attributes - enables attribute substitution on the attrlist argument (optional, default: false)
  227     #
  228     # Returns a Hash of parsed attributes
  229     def parse_attributes block, attrlist, opts = {}
  230       return {} if attrlist ? attrlist.empty? : true
  231       attrlist = block.sub_attributes attrlist if opts[:sub_attributes] && (attrlist.include? ATTR_REF_HEAD)
  232       (AttributeList.new attrlist).parse (opts[:positional_attributes] || [])
  233     end
  234 
  235     # TODO fill out remaining methods
  236     [
  237       [:create_paragraph,     :create_block,  :paragraph],
  238       [:create_open_block,    :create_block,  :open],
  239       [:create_example_block, :create_block,  :example],
  240       [:create_pass_block,    :create_block,  :pass],
  241       [:create_listing_block, :create_block,  :listing],
  242       [:create_literal_block, :create_block,  :literal],
  243       [:create_anchor,        :create_inline, :anchor],
  244       [:create_inline_pass,   :create_inline, :quoted],
  245     ].each do |method_name, delegate_method_name, context|
  246       define_method method_name do |*args|
  247         args.unshift args.shift, context
  248         send delegate_method_name, *args
  249       end
  250     end
  251   end
  252 
  253   # Internal: Overlays a builder DSL for configuring the Processor instance.
  254   # Includes a method to define configuration options and another to define the
  255   # {Processor#process} method.
  256   module ProcessorDsl
  257     def option key, value
  258       config[key] = value
  259     end
  260 
  261     def process *args, &block
  262       if block_given?
  263         raise ::ArgumentError, %(wrong number of arguments (given #{args.size}, expected 0)) unless args.empty?
  264         unless block.binding && self == block.binding.receiver
  265           # NOTE remap self in process method to processor instance
  266           context = self
  267           block.define_singleton_method(:call) {|*m_args| context.instance_exec(*m_args, &block) }
  268         end
  269         @process_block = block
  270       # TODO enable if we want to support passing proc or lambda as argument instead of block
  271       #elsif ::Proc === args[0]
  272       #  raise ::ArgumentError, %(wrong number of arguments (given #{args.size - 1}, expected 0)) unless args.size == 1
  273       #  @process_block = args.shift
  274       elsif defined? @process_block
  275         @process_block.call(*args)
  276       else
  277         raise ::NotImplementedError, %(#{self.class} ##{__method__} method called before being registered)
  278       end
  279     end
  280 
  281     def process_block_given?
  282       defined? @process_block
  283     end
  284   end
  285 
  286   module DocumentProcessorDsl
  287     include ProcessorDsl
  288 
  289     def prefer
  290       option :position, :>>
  291     end
  292   end
  293 
  294   module SyntaxProcessorDsl
  295     include ProcessorDsl
  296 
  297     def named value
  298       # NOTE due to how processors get initialized, we must defer this assignment in some scenarios
  299       if Processor === self
  300         @name = value
  301       else
  302         option :name, value
  303       end
  304     end
  305 
  306     def content_model value
  307       option :content_model, value
  308     end
  309     alias parse_content_as content_model
  310 
  311     def positional_attributes *value
  312       option :positional_attrs, value.flatten
  313     end
  314     alias name_positional_attributes positional_attributes
  315     # NOTE positional_attrs alias is deprecated
  316     alias positional_attrs positional_attributes
  317 
  318     def default_attributes value
  319       option :default_attrs, value
  320     end
  321     # NOTE default_attrs alias is deprecated
  322     alias default_attrs default_attributes
  323 
  324     def resolve_attributes *args
  325       # NOTE assume true as default value; rewrap single-argument string or symbol
  326       if (args = args.fetch 0, true).respond_to? :to_sym
  327         args = [args]
  328       end unless args.size > 1
  329       case args
  330       when true
  331         option :positional_attrs, []
  332         option :default_attrs, {}
  333       when ::Array
  334         names, defaults = [], {}
  335         args.each do |arg|
  336           if (arg = arg.to_s).include? '='
  337             name, _, value = arg.partition '='
  338             if name.include? ':'
  339               idx, _, name = name.partition ':'
  340               idx = idx == '@' ? names.size : idx.to_i
  341               names[idx] = name
  342             end
  343             defaults[name] = value
  344           elsif arg.include? ':'
  345             idx, _, name = arg.partition ':'
  346             idx = idx == '@' ? names.size : idx.to_i
  347             names[idx] = name
  348           else
  349             names << arg
  350           end
  351         end
  352         option :positional_attrs, names.compact
  353         option :default_attrs, defaults
  354       when ::Hash
  355         names, defaults = [], {}
  356         args.each do |key, val|
  357           if (name = key.to_s).include? ':'
  358             idx, _, name = name.partition ':'
  359             idx = idx == '@' ? names.size : idx.to_i
  360             names[idx] = name
  361           end
  362           defaults[name] = val if val
  363         end
  364         option :positional_attrs, names.compact
  365         option :default_attrs, defaults
  366       else
  367         raise ::ArgumentError, %(unsupported attributes specification for macro: #{args.inspect})
  368       end
  369     end
  370     # NOTE resolves_attributes alias is deprecated
  371     alias resolves_attributes resolve_attributes
  372   end
  373 
  374   # Public: Preprocessors are run after the source text is split into lines and
  375   # normalized, but before parsing begins.
  376   #
  377   # Prior to invoking the preprocessor, Asciidoctor splits the source text into
  378   # lines and normalizes them. The normalize process strips trailing whitespace
  379   # and the end of line character sequence from each line.
  380   #
  381   # Asciidoctor passes the document and the document's Reader to the
  382   # {Processor#process} method of the Preprocessor instance. The Preprocessor
  383   # can modify the Reader as necessary and either return the same Reader (or
  384   # falsy, which is equivalent) or a reference to a substitute Reader.
  385   #
  386   # Preprocessor implementations must extend the Preprocessor class.
  387   class Preprocessor < Processor
  388     def process document, reader
  389       raise ::NotImplementedError, %(#{Preprocessor} subclass #{self.class} must implement the ##{__method__} method)
  390     end
  391   end
  392   Preprocessor::DSL = DocumentProcessorDsl
  393 
  394   # Public: TreeProcessors are run on the Document after the source has been
  395   # parsed into an abstract syntax tree (AST), as represented by the Document
  396   # object and its child Node objects (e.g., Section, Block, List, ListItem).
  397   #
  398   # Asciidoctor invokes the {Processor#process} method on an instance of each
  399   # registered TreeProcessor.
  400   #
  401   # TreeProcessor implementations must extend TreeProcessor.
  402   #--
  403   # QUESTION should the tree processor get invoked after parse header too?
  404   class TreeProcessor < Processor
  405     def process document
  406       raise ::NotImplementedError, %(#{TreeProcessor} subclass #{self.class} must implement the ##{__method__} method)
  407     end
  408   end
  409   TreeProcessor::DSL = DocumentProcessorDsl
  410 
  411   # Alias deprecated class name for backwards compatibility
  412   Treeprocessor = TreeProcessor
  413 
  414   # Public: Postprocessors are run after the document is converted, but before
  415   # it is written to the output stream.
  416   #
  417   # Asciidoctor passes a reference to the converted String to the {Processor#process}
  418   # method of each registered Postprocessor. The Preprocessor modifies the
  419   # String as necessary and returns the String replacement.
  420   #
  421   # The markup format in the String is determined by the backend used to convert
  422   # the Document. The backend and be looked up using the backend method on the
  423   # Document object, as well as various backend-related document attributes.
  424   #
  425   # TIP: Postprocessors can also be used to relocate assets needed by the published
  426   # document.
  427   #
  428   # Postprocessor implementations must Postprocessor.
  429   class Postprocessor < Processor
  430     def process document, output
  431       raise ::NotImplementedError, %(#{Postprocessor} subclass #{self.class} must implement the ##{__method__} method)
  432     end
  433   end
  434   Postprocessor::DSL = DocumentProcessorDsl
  435 
  436   # Public: IncludeProcessors are used to process `include::<target>[]`
  437   # directives in the source document.
  438   #
  439   # When Asciidoctor comes across a `include::<target>[]` directive in the
  440   # source document, it iterates through the IncludeProcessors and delegates
  441   # the work of reading the content to the first processor that identifies
  442   # itself as capable of handling that target.
  443   #
  444   # IncludeProcessor implementations must extend IncludeProcessor.
  445   #--
  446   # TODO add file extension or regexp as shortcut for handles? method
  447   class IncludeProcessor < Processor
  448     def process document, reader, target, attributes
  449       raise ::NotImplementedError, %(#{IncludeProcessor} subclass #{self.class} must implement the ##{__method__} method)
  450     end
  451 
  452     def handles? target
  453       true
  454     end
  455   end
  456 
  457   module IncludeProcessorDsl
  458     include DocumentProcessorDsl
  459 
  460     def handles? *args, &block
  461       if block_given?
  462         raise ::ArgumentError, %(wrong number of arguments (given #{args.size}, expected 0)) unless args.empty?
  463         @handles_block = block
  464       # TODO enable if we want to support passing proc or lambda as argument instead of block
  465       #elsif ::Proc === args[0]
  466       #  block = args.shift
  467       #  raise ::ArgumentError, %(wrong number of arguments (given #{args.size}, expected 0)) unless args.empty?
  468       #  @handles_block = block
  469       elsif defined? @handles_block
  470         @handles_block.call args[0]
  471       else
  472         true
  473       end
  474     end
  475   end
  476   IncludeProcessor::DSL = IncludeProcessorDsl
  477 
  478   # Public: DocinfoProcessors are used to add additional content to
  479   # the header and/or footer of the generated document.
  480   #
  481   # The placement of docinfo content is controlled by the converter.
  482   #
  483   # DocinfoProcessors implementations must extend DocinfoProcessor.
  484   # If a location is not specified, the DocinfoProcessor is assumed
  485   # to add content to the header.
  486   class DocinfoProcessor < Processor
  487     def initialize config = {}
  488       super config
  489       @config[:location] ||= :head
  490     end
  491 
  492     def process document
  493       raise ::NotImplementedError, %(#{DocinfoProcessor} subclass #{self.class} must implement the ##{__method__} method)
  494     end
  495   end
  496 
  497   module DocinfoProcessorDsl
  498     include DocumentProcessorDsl
  499 
  500     def at_location value
  501       option :location, value
  502     end
  503   end
  504   DocinfoProcessor::DSL = DocinfoProcessorDsl
  505 
  506   # Public: BlockProcessors are used to handle delimited blocks and paragraphs
  507   # that have a custom name.
  508   #
  509   # When Asciidoctor encounters a delimited block or paragraph with an
  510   # unrecognized name while parsing the document, it looks for a BlockProcessor
  511   # registered to handle this name and, if found, invokes its {Processor#process}
  512   # method to build a corresponding node in the document tree.
  513   #
  514   # AsciiDoc example:
  515   #
  516   #   [shout]
  517   #   Get a move on.
  518   #
  519   # Recognized options:
  520   #
  521   # * :named - The name of the block (required: true)
  522   # * :contexts - The blocks contexts on which this style can be used (default: [:paragraph, :open]
  523   # * :content_model - The structure of the content supported in this block (default: :compound)
  524   # * :positional_attrs - A list of attribute names used to map positional attributes (default: nil)
  525   # * :default_attrs - A hash of attribute names and values used to seed the attributes hash (default: nil)
  526   # * ...
  527   #
  528   # BlockProcessor implementations must extend BlockProcessor.
  529   class BlockProcessor < Processor
  530     attr_accessor :name
  531 
  532     def initialize name = nil, config = {}
  533       super config
  534       @name = name || @config[:name]
  535       # assign fallbacks
  536       case @config[:contexts]
  537       when ::NilClass
  538         @config[:contexts] ||= [:open, :paragraph].to_set
  539       when ::Symbol
  540         @config[:contexts] = [@config[:contexts]].to_set
  541       else
  542         @config[:contexts] = @config[:contexts].to_set
  543       end
  544       # QUESTION should the default content model be raw??
  545       @config[:content_model] ||= :compound
  546     end
  547 
  548     def process parent, reader, attributes
  549       raise ::NotImplementedError, %(#{BlockProcessor} subclass #{self.class} must implement the ##{__method__} method)
  550     end
  551   end
  552 
  553   module BlockProcessorDsl
  554     include SyntaxProcessorDsl
  555 
  556     def contexts *value
  557       option :contexts, value.flatten.to_set
  558     end
  559     alias on_contexts contexts
  560     alias on_context contexts
  561     alias bind_to contexts
  562   end
  563   BlockProcessor::DSL = BlockProcessorDsl
  564 
  565   class MacroProcessor < Processor
  566     attr_accessor :name
  567 
  568     def initialize name = nil, config = {}
  569       super config
  570       @name = name || @config[:name]
  571       @config[:content_model] ||= :attributes
  572     end
  573 
  574     def process parent, target, attributes
  575       raise ::NotImplementedError, %(#{MacroProcessor} subclass #{self.class} must implement the ##{__method__} method)
  576     end
  577   end
  578 
  579   module MacroProcessorDsl
  580     include SyntaxProcessorDsl
  581 
  582     def resolve_attributes *args
  583       if args.size == 1 && !args[0]
  584         option :content_model, :text
  585       else
  586         super
  587         option :content_model, :attributes
  588       end
  589     end
  590     # NOTE resolves_attributes alias is deprecated
  591     alias resolves_attributes resolve_attributes
  592   end
  593 
  594   # Public: BlockMacroProcessors are used to handle block macros that have a
  595   # custom name.
  596   #
  597   # BlockMacroProcessor implementations must extend BlockMacroProcessor.
  598   class BlockMacroProcessor < MacroProcessor
  599     def name
  600       raise ::ArgumentError, %(invalid name for block macro: #{@name}) unless MacroNameRx.match? @name.to_s
  601       @name
  602     end
  603   end
  604   BlockMacroProcessor::DSL = MacroProcessorDsl
  605 
  606   # Public: InlineMacroProcessors are used to handle block macros that have a
  607   # custom name.
  608   #
  609   # InlineMacroProcessor implementations must extend InlineMacroProcessor.
  610   #--
  611   # TODO break this out into different pattern types
  612   # for example, FullInlineMacro, ShortInlineMacro (no target) and other patterns
  613   # FIXME for inline passthrough, we need to have some way to specify the text as a passthrough
  614   class InlineMacroProcessor < MacroProcessor
  615     @@rx_cache = {}
  616 
  617     # Lookup the regexp option, resolving it first if necessary.
  618     # Once this method is called, the regexp is considered frozen.
  619     def regexp
  620       @config[:regexp] ||= resolve_regexp @name.to_s, @config[:format]
  621     end
  622 
  623     def resolve_regexp name, format
  624       raise ::ArgumentError, %(invalid name for inline macro: #{name}) unless MacroNameRx.match? name
  625       @@rx_cache[[name, format]] ||= /\\?#{name}:#{format == :short ? '(){0}' : '(\S+?)'}\[(|#{CC_ANY}*?[^\\])\]/
  626     end
  627   end
  628 
  629   module InlineMacroProcessorDsl
  630     include MacroProcessorDsl
  631 
  632     def format value
  633       option :format, value
  634     end
  635     alias match_format format
  636     # NOTE using_format alias is deprecated
  637     alias using_format format
  638 
  639     def match value
  640       option :regexp, value
  641     end
  642   end
  643   InlineMacroProcessor::DSL = InlineMacroProcessorDsl
  644 
  645   # Public: Extension is a proxy object for an extension implementation such as
  646   # a processor. It allows the preparation of the extension instance to be
  647   # separated from its usage to provide consistency between different
  648   # interfaces and avoid tight coupling with the extension type.
  649   #
  650   # The proxy encapsulates the extension kind (e.g., :block), its config Hash
  651   # and the extension instance. This Proxy is what gets stored in the extension
  652   # registry when activated.
  653   #--
  654   # QUESTION call this ExtensionInfo?
  655   class Extension
  656     attr_reader :kind
  657     attr_reader :config
  658     attr_reader :instance
  659 
  660     def initialize kind, instance, config
  661       @kind = kind
  662       @instance = instance
  663       @config = config
  664     end
  665   end
  666 
  667   # Public: A specialization of the Extension proxy that additionally stores a
  668   # reference to the {Processor#process} method. By storing this reference, its
  669   # possible to accomodate both concrete extension implementations and Procs.
  670   class ProcessorExtension < Extension
  671     attr_reader :process_method
  672 
  673     def initialize kind, instance, process_method = nil
  674       super kind, instance, instance.config
  675       @process_method = process_method || (instance.method :process)
  676     end
  677   end
  678 
  679   # Public: A Group is used to register one or more extensions with the Registry.
  680   #
  681   # The Group should be subclassed and registered with the Registry either by
  682   # invoking the {Group.register} method or passing the subclass to the
  683   # {Extensions.register} method. Extensions are registered with the Registry
  684   # inside the {Group#activate} method.
  685   class Group
  686     class << self
  687       def register name = nil
  688         Extensions.register name, self
  689       end
  690     end
  691 
  692     def activate registry
  693       raise ::NotImplementedError
  694     end
  695   end
  696 
  697   # Public: The primary entry point into the extension system.
  698   #
  699   # Registry holds the extensions which have been registered and activated, has
  700   # methods for registering or defining a processor and looks up extensions
  701   # stored in the registry during parsing.
  702   class Registry
  703     # Public: Returns the {Asciidoctor::Document} on which the extensions in this registry are being used.
  704     attr_reader :document
  705 
  706     # Public: Returns the Hash of {Group} classes, instances, and/or Procs that have been registered with this registry.
  707     attr_reader :groups
  708 
  709     def initialize groups = {}
  710       @groups = groups
  711       @preprocessor_extensions = @tree_processor_extensions = @postprocessor_extensions = @include_processor_extensions = @docinfo_processor_extensions = @block_extensions = @block_macro_extensions = @inline_macro_extensions = nil
  712       @document = nil
  713     end
  714 
  715     # Public: Activates all the global extension {Group}s and the extension {Group}s
  716     # associated with this registry.
  717     #
  718     # document - the {Asciidoctor::Document} on which the extensions are to be used.
  719     #
  720     # Returns the instance of this [Registry].
  721     def activate document
  722       @document = document
  723       unless (ext_groups = Extensions.groups.values + @groups.values).empty?
  724         ext_groups.each do |group|
  725           case group
  726           when ::Proc
  727             case group.arity
  728             when 0, -1
  729               instance_exec(&group)
  730             when 1
  731               group.call self
  732             end
  733           when ::Class
  734             group.new.activate self
  735           else
  736             group.activate self
  737           end
  738         end
  739       end
  740       self
  741     end
  742 
  743     # Public: Registers a {Preprocessor} with the extension registry to process
  744     # the AsciiDoc source before parsing begins.
  745     #
  746     # The Preprocessor may be one of four types:
  747     #
  748     # * A Preprocessor subclass
  749     # * An instance of a Preprocessor subclass
  750     # * The String name of a Preprocessor subclass
  751     # * A method block (i.e., Proc) that conforms to the Preprocessor contract
  752     #
  753     # Unless the Preprocessor is passed as the method block, it must be the
  754     # first argument to this method.
  755     #
  756     # Examples
  757     #
  758     #   # as a Preprocessor subclass
  759     #   preprocessor FrontMatterPreprocessor
  760     #
  761     #   # as an instance of a Preprocessor subclass
  762     #   preprocessor FrontMatterPreprocessor.new
  763     #
  764     #   # as a name of a Preprocessor subclass
  765     #   preprocessor 'FrontMatterPreprocessor'
  766     #
  767     #   # as a method block
  768     #   preprocessor do
  769     #     process do |doc, reader|
  770     #       ...
  771     #     end
  772     #   end
  773     #
  774     # Returns the [Extension] stored in the registry that proxies the
  775     # instance of this Preprocessor.
  776     def preprocessor *args, &block
  777       add_document_processor :preprocessor, args, &block
  778     end
  779 
  780     # Public: Checks whether any {Preprocessor} extensions have been registered.
  781     #
  782     # Returns a [Boolean] indicating whether any Preprocessor extensions are registered.
  783     def preprocessors?
  784       !!@preprocessor_extensions
  785     end
  786 
  787     # Public: Retrieves the {Extension} proxy objects for all
  788     # Preprocessor instances in this registry.
  789     #
  790     # Returns an [Array] of Extension proxy objects.
  791     def preprocessors
  792       @preprocessor_extensions
  793     end
  794 
  795     # Public: Registers a {TreeProcessor} with the extension registry to process
  796     # the AsciiDoc source after parsing is complete.
  797     #
  798     # The TreeProcessor may be one of four types:
  799     #
  800     # * A TreeProcessor subclass
  801     # * An instance of a TreeProcessor subclass
  802     # * The String name of a TreeProcessor subclass
  803     # * A method block (i.e., Proc) that conforms to the TreeProcessor contract
  804     #
  805     # Unless the TreeProcessor is passed as the method block, it must be the
  806     # first argument to this method.
  807     #
  808     # Examples
  809     #
  810     #   # as a TreeProcessor subclass
  811     #   tree_processor ShellTreeProcessor
  812     #
  813     #   # as an instance of a TreeProcessor subclass
  814     #   tree_processor ShellTreeProcessor.new
  815     #
  816     #   # as a name of a TreeProcessor subclass
  817     #   tree_processor 'ShellTreeProcessor'
  818     #
  819     #   # as a method block
  820     #   tree_processor do
  821     #     process do |document|
  822     #       ...
  823     #     end
  824     #   end
  825     #
  826     # Returns the [Extension] stored in the registry that proxies the
  827     # instance of this TreeProcessor.
  828     def tree_processor *args, &block
  829       add_document_processor :tree_processor, args, &block
  830     end
  831 
  832     # Public: Checks whether any {TreeProcessor} extensions have been registered.
  833     #
  834     # Returns a [Boolean] indicating whether any TreeProcessor extensions are registered.
  835     def tree_processors?
  836       !!@tree_processor_extensions
  837     end
  838 
  839     # Public: Retrieves the {Extension} proxy objects for all
  840     # TreeProcessor instances in this registry.
  841     #
  842     # Returns an [Array] of Extension proxy objects.
  843     def tree_processors
  844       @tree_processor_extensions
  845     end
  846 
  847     # Alias deprecated methods for backwards compatibility
  848     alias treeprocessor tree_processor
  849     alias treeprocessors? tree_processors?
  850     alias treeprocessors tree_processors
  851 
  852     # Public: Registers a {Postprocessor} with the extension registry to process
  853     # the output after conversion is complete.
  854     #
  855     # The Postprocessor may be one of four types:
  856     #
  857     # * A Postprocessor subclass
  858     # * An instance of a Postprocessor subclass
  859     # * The String name of a Postprocessor subclass
  860     # * A method block (i.e., Proc) that conforms to the Postprocessor contract
  861     #
  862     # Unless the Postprocessor is passed as the method block, it must be the
  863     # first argument to this method.
  864     #
  865     # Examples
  866     #
  867     #   # as a Postprocessor subclass
  868     #   postprocessor AnalyticsPostprocessor
  869     #
  870     #   # as an instance of a Postprocessor subclass
  871     #   postprocessor AnalyticsPostprocessor.new
  872     #
  873     #   # as a name of a Postprocessor subclass
  874     #   postprocessor 'AnalyticsPostprocessor'
  875     #
  876     #   # as a method block
  877     #   postprocessor do
  878     #     process do |document, output|
  879     #       ...
  880     #     end
  881     #   end
  882     #
  883     # Returns the [Extension] stored in the registry that proxies the
  884     # instance of this Postprocessor.
  885     def postprocessor *args, &block
  886       add_document_processor :postprocessor, args, &block
  887     end
  888 
  889     # Public: Checks whether any {Postprocessor} extensions have been registered.
  890     #
  891     # Returns a [Boolean] indicating whether any Postprocessor extensions are registered.
  892     def postprocessors?
  893       !!@postprocessor_extensions
  894     end
  895 
  896     # Public: Retrieves the {Extension} proxy objects for all
  897     # Postprocessor instances in this registry.
  898     #
  899     # Returns an [Array] of Extension proxy objects.
  900     def postprocessors
  901       @postprocessor_extensions
  902     end
  903 
  904     # Public: Registers an {IncludeProcessor} with the extension registry to have
  905     # a shot at handling the include directive.
  906     #
  907     # The IncludeProcessor may be one of four types:
  908     #
  909     # * A IncludeProcessor subclass
  910     # * An instance of a IncludeProcessor subclass
  911     # * The String name of a IncludeProcessor subclass
  912     # * A method block (i.e., Proc) that conforms to the IncludeProcessor contract
  913     #
  914     # Unless the IncludeProcessor is passed as the method block, it must be the
  915     # first argument to this method.
  916     #
  917     # Examples
  918     #
  919     #   # as an IncludeProcessor subclass
  920     #   include_processor GitIncludeProcessor
  921     #
  922     #   # as an instance of a Postprocessor subclass
  923     #   include_processor GitIncludeProcessor.new
  924     #
  925     #   # as a name of a Postprocessor subclass
  926     #   include_processor 'GitIncludeProcessor'
  927     #
  928     #   # as a method block
  929     #   include_processor do
  930     #     process do |document, output|
  931     #       ...
  932     #     end
  933     #   end
  934     #
  935     # Returns the [Extension] stored in the registry that proxies the
  936     # instance of this IncludeProcessor.
  937     def include_processor *args, &block
  938       add_document_processor :include_processor, args, &block
  939     end
  940 
  941     # Public: Checks whether any {IncludeProcessor} extensions have been registered.
  942     #
  943     # Returns a [Boolean] indicating whether any IncludeProcessor extensions are registered.
  944     def include_processors?
  945       !!@include_processor_extensions
  946     end
  947 
  948     # Public: Retrieves the {Extension} proxy objects for all the
  949     # IncludeProcessor instances stored in this registry.
  950     #
  951     # Returns an [Array] of Extension proxy objects.
  952     def include_processors
  953       @include_processor_extensions
  954     end
  955 
  956     # Public: Registers an {DocinfoProcessor} with the extension registry to
  957     # add additionnal docinfo to the document.
  958     #
  959     # The DocinfoProcessor may be one of four types:
  960     #
  961     # * A DocinfoProcessor subclass
  962     # * An instance of a DocinfoProcessor subclass
  963     # * The String name of a DocinfoProcessor subclass
  964     # * A method block (i.e., Proc) that conforms to the DocinfoProcessor contract
  965     #
  966     # Unless the DocinfoProcessor is passed as the method block, it must be the
  967     # first argument to this method.
  968     #
  969     # Examples
  970     #
  971     #   # as an DocinfoProcessor subclass
  972     #   docinfo_processor MetaRobotsDocinfoProcessor
  973     #
  974     #   # as an instance of a DocinfoProcessor subclass with an explicit location
  975     #   docinfo_processor JQueryDocinfoProcessor.new, location: :footer
  976     #
  977     #   # as a name of a DocinfoProcessor subclass
  978     #   docinfo_processor 'MetaRobotsDocinfoProcessor'
  979     #
  980     #   # as a method block
  981     #   docinfo_processor do
  982     #     process do |doc|
  983     #       at_location :footer
  984     #       'footer content'
  985     #     end
  986     #   end
  987     #
  988     # Returns the [Extension] stored in the registry that proxies the
  989     # instance of this DocinfoProcessor.
  990     def docinfo_processor *args, &block
  991       add_document_processor :docinfo_processor, args, &block
  992     end
  993 
  994     # Public: Checks whether any {DocinfoProcessor} extensions have been registered.
  995     #
  996     # location - A Symbol for selecting docinfo extensions at a given location (:head or :footer) (default: nil)
  997     #
  998     # Returns a [Boolean] indicating whether any DocinfoProcessor extensions are registered.
  999     def docinfo_processors? location = nil
 1000       if @docinfo_processor_extensions
 1001         if location
 1002           @docinfo_processor_extensions.any? {|ext| ext.config[:location] == location }
 1003         else
 1004           true
 1005         end
 1006       else
 1007         false
 1008       end
 1009     end
 1010 
 1011     # Public: Retrieves the {Extension} proxy objects for all the
 1012     # DocinfoProcessor instances stored in this registry.
 1013     #
 1014     # location - A Symbol for selecting docinfo extensions at a given location (:head or :footer) (default: nil)
 1015     #
 1016     # Returns an [Array] of Extension proxy objects.
 1017     def docinfo_processors location = nil
 1018       if @docinfo_processor_extensions
 1019         if location
 1020           @docinfo_processor_extensions.select {|ext| ext.config[:location] == location }
 1021         else
 1022           @docinfo_processor_extensions
 1023         end
 1024       else
 1025         nil
 1026       end
 1027     end
 1028 
 1029     # Public: Registers a {BlockProcessor} with the extension registry to
 1030     # process the block content (i.e., delimited block or paragraph) in the
 1031     # AsciiDoc source annotated with the specified block name (i.e., style).
 1032     #
 1033     # The BlockProcessor may be one of four types:
 1034     #
 1035     # * A BlockProcessor subclass
 1036     # * An instance of a BlockProcessor subclass
 1037     # * The String name of a BlockProcessor subclass
 1038     # * A method block (i.e., Proc) that conforms to the BlockProcessor contract
 1039     #
 1040     # Unless the BlockProcessor is passed as the method block, it must be the
 1041     # first argument to this method. The second argument is the name (coersed
 1042     # to a Symbol) of the AsciiDoc block content (i.e., delimited block or
 1043     # paragraph) that this processor is registered to handle. If a block name
 1044     # is not passed as an argument, it gets read from the name property of the
 1045     # BlockProcessor instance. If a name still cannot be determined, an error
 1046     # is raised.
 1047     #
 1048     # Examples
 1049     #
 1050     #   # as a BlockProcessor subclass
 1051     #   block ShoutBlock
 1052     #
 1053     #   # as a BlockProcessor subclass with an explicit block name
 1054     #   block ShoutBlock, :shout
 1055     #
 1056     #   # as an instance of a BlockProcessor subclass
 1057     #   block ShoutBlock.new
 1058     #
 1059     #   # as an instance of a BlockProcessor subclass with an explicit block name
 1060     #   block ShoutBlock.new, :shout
 1061     #
 1062     #   # as a name of a BlockProcessor subclass
 1063     #   block 'ShoutBlock'
 1064     #
 1065     #   # as a name of a BlockProcessor subclass with an explicit block name
 1066     #   block 'ShoutBlock', :shout
 1067     #
 1068     #   # as a method block
 1069     #   block do
 1070     #     named :shout
 1071     #     process do |parent, reader, attrs|
 1072     #       ...
 1073     #     end
 1074     #   end
 1075     #
 1076     #   # as a method block with an explicit block name
 1077     #   block :shout do
 1078     #     process do |parent, reader, attrs|
 1079     #       ...
 1080     #     end
 1081     #   end
 1082     #
 1083     # Returns an instance of the [Extension] proxy object that is stored in the
 1084     # registry and manages the instance of this BlockProcessor.
 1085     def block *args, &block
 1086       add_syntax_processor :block, args, &block
 1087     end
 1088 
 1089     # Public: Checks whether any {BlockProcessor} extensions have been registered.
 1090     #
 1091     # Returns a [Boolean] indicating whether any BlockProcessor extensions are registered.
 1092     def blocks?
 1093       !!@block_extensions
 1094     end
 1095 
 1096     # Public: Checks whether any {BlockProcessor} extensions are registered to
 1097     # handle the specified block name appearing on the specified context.
 1098     #
 1099     # Returns the [Extension] proxy object for the BlockProcessor that matches
 1100     # the block name and context or false if no match is found.
 1101     def registered_for_block? name, context
 1102       if (ext = @block_extensions[name.to_sym])
 1103         (ext.config[:contexts].include? context) ? ext : false
 1104       else
 1105         false
 1106       end
 1107     end
 1108 
 1109     # Public: Retrieves the {Extension} proxy object for the BlockProcessor registered
 1110     # to handle block content with the name.
 1111     #
 1112     # name - the String or Symbol (coersed to a Symbol) macro name
 1113     #
 1114     # Returns the [Extension] object stored in the registry that proxies the
 1115     # corresponding BlockProcessor or nil if a match is not found.
 1116     def find_block_extension name
 1117       @block_extensions[name.to_sym]
 1118     end
 1119 
 1120     # Public: Registers a {BlockMacroProcessor} with the extension registry to
 1121     # process a block macro with the specified name.
 1122     #
 1123     # The BlockMacroProcessor may be one of four types:
 1124     #
 1125     # * A BlockMacroProcessor subclass
 1126     # * An instance of a BlockMacroProcessor subclass
 1127     # * The String name of a BlockMacroProcessor subclass
 1128     # * A method block (i.e., Proc) that conforms to the BlockMacroProcessor contract
 1129     #
 1130     # Unless the BlockMacroProcessor is passed as the method block, it must be
 1131     # the first argument to this method. The second argument is the name
 1132     # (coersed to a Symbol) of the AsciiDoc block macro that this processor is
 1133     # registered to handle. If a block macro name is not passed as an argument,
 1134     # it gets read from the name property of the BlockMacroProcessor instance.
 1135     # If a name still cannot be determined, an error is raised.
 1136     #
 1137     # Examples
 1138     #
 1139     #   # as a BlockMacroProcessor subclass
 1140     #   block_macro GistBlockMacro
 1141     #
 1142     #   # as a BlockMacroProcessor subclass with an explicit macro name
 1143     #   block_macro GistBlockMacro, :gist
 1144     #
 1145     #   # as an instance of a BlockMacroProcessor subclass
 1146     #   block_macro GistBlockMacro.new
 1147     #
 1148     #   # as an instance of a BlockMacroProcessor subclass with an explicit macro name
 1149     #   block_macro GistBlockMacro.new, :gist
 1150     #
 1151     #   # as a name of a BlockMacroProcessor subclass
 1152     #   block_macro 'GistBlockMacro'
 1153     #
 1154     #   # as a name of a BlockMacroProcessor subclass with an explicit macro name
 1155     #   block_macro 'GistBlockMacro', :gist
 1156     #
 1157     #   # as a method block
 1158     #   block_macro do
 1159     #     named :gist
 1160     #     process do |parent, target, attrs|
 1161     #       ...
 1162     #     end
 1163     #   end
 1164     #
 1165     #   # as a method block with an explicit macro name
 1166     #   block_macro :gist do
 1167     #     process do |parent, target, attrs|
 1168     #       ...
 1169     #     end
 1170     #   end
 1171     #
 1172     # Returns an instance of the [Extension] proxy object that is stored in the
 1173     # registry and manages the instance of this BlockMacroProcessor.
 1174     def block_macro *args, &block
 1175       add_syntax_processor :block_macro, args, &block
 1176     end
 1177 
 1178     # Public: Checks whether any {BlockMacroProcessor} extensions have been registered.
 1179     #
 1180     # Returns a [Boolean] indicating whether any BlockMacroProcessor extensions are registered.
 1181     def block_macros?
 1182       !!@block_macro_extensions
 1183     end
 1184 
 1185     # Public: Checks whether any {BlockMacroProcessor} extensions are registered to
 1186     # handle the block macro with the specified name.
 1187     #
 1188     # name - the String or Symbol (coersed to a Symbol) macro name
 1189     #
 1190     # Returns the [Extension] proxy object for the BlockMacroProcessor that matches
 1191     # the macro name or false if no match is found.
 1192     #--
 1193     # TODO only allow blank target if format is :short
 1194     def registered_for_block_macro? name
 1195       (ext = @block_macro_extensions[name.to_sym]) ? ext : false
 1196     end
 1197 
 1198     # Public: Retrieves the {Extension} proxy object for the BlockMacroProcessor registered
 1199     # to handle a block macro with the specified name.
 1200     #
 1201     # name - the String or Symbol (coersed to a Symbol) macro name
 1202     #
 1203     # Returns the [Extension] object stored in the registry that proxies the
 1204     # cooresponding BlockMacroProcessor or nil if a match is not found.
 1205     def find_block_macro_extension name
 1206       @block_macro_extensions[name.to_sym]
 1207     end
 1208 
 1209     # Public: Registers a {InlineMacroProcessor} with the extension registry to
 1210     # process an inline macro with the specified name.
 1211     #
 1212     # The InlineMacroProcessor may be one of four types:
 1213     #
 1214     # * An InlineMacroProcessor subclass
 1215     # * An instance of an InlineMacroProcessor subclass
 1216     # * The String name of an InlineMacroProcessor subclass
 1217     # * A method block (i.e., Proc) that conforms to the InlineMacroProcessor contract
 1218     #
 1219     # Unless the InlineMacroProcessor is passed as the method block, it must be
 1220     # the first argument to this method. The second argument is the name
 1221     # (coersed to a Symbol) of the AsciiDoc block macro that this processor is
 1222     # registered to handle. If a block macro name is not passed as an argument,
 1223     # it gets read from the name property of the InlineMacroProcessor instance.
 1224     # If a name still cannot be determined, an error is raised.
 1225     #
 1226     # Examples
 1227     #
 1228     #   # as an InlineMacroProcessor subclass
 1229     #   inline_macro ChromeInlineMacro
 1230     #
 1231     #   # as an InlineMacroProcessor subclass with an explicit macro name
 1232     #   inline_macro ChromeInlineMacro, :chrome
 1233     #
 1234     #   # as an instance of an InlineMacroProcessor subclass
 1235     #   inline_macro ChromeInlineMacro.new
 1236     #
 1237     #   # as an instance of an InlineMacroProcessor subclass with an explicit macro name
 1238     #   inline_macro ChromeInlineMacro.new, :chrome
 1239     #
 1240     #   # as a name of an InlineMacroProcessor subclass
 1241     #   inline_macro 'ChromeInlineMacro'
 1242     #
 1243     #   # as a name of an InlineMacroProcessor subclass with an explicit macro name
 1244     #   inline_macro 'ChromeInlineMacro', :chrome
 1245     #
 1246     #   # as a method block
 1247     #   inline_macro do
 1248     #     named :chrome
 1249     #     process do |parent, target, attrs|
 1250     #       ...
 1251     #     end
 1252     #   end
 1253     #
 1254     #   # as a method block with an explicit macro name
 1255     #   inline_macro :chrome do
 1256     #     process do |parent, target, attrs|
 1257     #       ...
 1258     #     end
 1259     #   end
 1260     #
 1261     # Returns an instance of the [Extension] proxy object that is stored in the
 1262     # registry and manages the instance of this InlineMacroProcessor.
 1263     def inline_macro *args, &block
 1264       add_syntax_processor :inline_macro, args, &block
 1265     end
 1266 
 1267     # Public: Checks whether any {InlineMacroProcessor} extensions have been registered.
 1268     #
 1269     # Returns a [Boolean] indicating whether any IncludeMacroProcessor extensions are registered.
 1270     def inline_macros?
 1271       !!@inline_macro_extensions
 1272     end
 1273 
 1274     # Public: Checks whether any {InlineMacroProcessor} extensions are registered to
 1275     # handle the inline macro with the specified name.
 1276     #
 1277     # name - the String or Symbol (coersed to a Symbol) macro name
 1278     #
 1279     # Returns the [Extension] proxy object for the InlineMacroProcessor that matches
 1280     # the macro name or false if no match is found.
 1281     def registered_for_inline_macro? name
 1282       (ext = @inline_macro_extensions[name.to_sym]) ? ext : false
 1283     end
 1284 
 1285     # Public: Retrieves the {Extension} proxy object for the InlineMacroProcessor registered
 1286     # to handle an inline macro with the specified name.
 1287     #
 1288     # name - the String or Symbol (coersed to a Symbol) macro name
 1289     #
 1290     # Returns the [Extension] object stored in the registry that proxies the
 1291     # cooresponding InlineMacroProcessor or nil if a match is not found.
 1292     def find_inline_macro_extension name
 1293       @inline_macro_extensions[name.to_sym]
 1294     end
 1295 
 1296     # Public: Retrieves the {Extension} proxy objects for all
 1297     # InlineMacroProcessor instances in this registry.
 1298     #
 1299     # Returns an [Array] of Extension proxy objects.
 1300     def inline_macros
 1301       @inline_macro_extensions.values
 1302     end
 1303 
 1304     # Public: Inserts the document processor {Extension} instance as the first
 1305     # processor of its kind in the extension registry.
 1306     #
 1307     # Examples
 1308     #
 1309     #   prefer :include_processor do
 1310     #     process do |document, reader, target, attrs|
 1311     #       ...
 1312     #     end
 1313     #   end
 1314     #
 1315     # Returns the [Extension] stored in the registry that proxies the instance
 1316     # of this processor.
 1317     def prefer *args, &block
 1318       extension = ProcessorExtension === (arg0 = args.shift) ? arg0 : (send arg0, *args, &block)
 1319       extensions_store = instance_variable_get(%(@#{extension.kind}_extensions).to_sym)
 1320       extensions_store.unshift extensions_store.delete extension
 1321       extension
 1322     end
 1323 
 1324     private
 1325 
 1326     def add_document_processor kind, args, &block
 1327       kind_name = kind.to_s.tr '_', ' '
 1328       kind_class_symbol = kind_name.split.map {|it| it.capitalize }.join.to_sym
 1329       kind_class = Extensions.const_get kind_class_symbol, false
 1330       kind_java_class = (defined? ::AsciidoctorJ) ? (::AsciidoctorJ::Extensions.const_get kind_class_symbol, false) : nil
 1331       kind_store = instance_variable_get(%(@#{kind}_extensions).to_sym) || instance_variable_set(%(@#{kind}_extensions).to_sym, [])
 1332       # style 1: specified as block
 1333       extension = if block_given?
 1334         config = resolve_args args, 1
 1335         (processor = kind_class.new config).singleton_class.enable_dsl
 1336         if block.arity == 0
 1337           processor.instance_exec(&block)
 1338         else
 1339           yield processor
 1340         end
 1341         unless processor.process_block_given?
 1342           raise ::ArgumentError, %(No block specified to process #{kind_name} extension at #{block.source_location})
 1343         end
 1344         processor.freeze
 1345         ProcessorExtension.new kind, processor
 1346       else
 1347         processor, config = resolve_args args, 2
 1348         # style 2: specified as Class or String class name
 1349         if (processor_class = Helpers.resolve_class processor)
 1350           unless processor_class < kind_class || (kind_java_class && processor_class < kind_java_class)
 1351             raise ::ArgumentError, %(Invalid type for #{kind_name} extension: #{processor})
 1352           end
 1353           processor_instance = processor_class.new config
 1354           processor_instance.freeze
 1355           ProcessorExtension.new kind, processor_instance
 1356         # style 3: specified as instance
 1357         elsif kind_class === processor || (kind_java_class && kind_java_class === processor)
 1358           processor.update_config config
 1359           processor.freeze
 1360           ProcessorExtension.new kind, processor
 1361         else
 1362           raise ::ArgumentError, %(Invalid arguments specified for registering #{kind_name} extension: #{args})
 1363         end
 1364       end
 1365 
 1366       extension.config[:position] == :>> ? (kind_store.unshift extension) : (kind_store << extension)
 1367       extension
 1368     end
 1369 
 1370     def add_syntax_processor kind, args, &block
 1371       kind_name = kind.to_s.tr '_', ' '
 1372       kind_class_symbol = (kind_name.split.map {|it| it.capitalize } << 'Processor').join.to_sym
 1373       kind_class = Extensions.const_get kind_class_symbol, false
 1374       kind_java_class = (defined? ::AsciidoctorJ) ? (::AsciidoctorJ::Extensions.const_get kind_class_symbol, false) : nil
 1375       kind_store = instance_variable_get(%(@#{kind}_extensions).to_sym) || instance_variable_set(%(@#{kind}_extensions).to_sym, {})
 1376       # style 1: specified as block
 1377       if block_given?
 1378         name, config = resolve_args args, 2
 1379         (processor = kind_class.new (as_symbol name), config).singleton_class.enable_dsl
 1380         if block.arity == 0
 1381           processor.instance_exec(&block)
 1382         else
 1383           yield processor
 1384         end
 1385         unless (name = as_symbol processor.name)
 1386           raise ::ArgumentError, %(No name specified for #{kind_name} extension at #{block.source_location})
 1387         end
 1388         unless processor.process_block_given?
 1389           raise ::NoMethodError, %(No block specified to process #{kind_name} extension at #{block.source_location})
 1390         end
 1391         processor.freeze
 1392         kind_store[name] = ProcessorExtension.new kind, processor
 1393       else
 1394         processor, name, config = resolve_args args, 3
 1395         # style 2: specified as Class or String class name
 1396         if (processor_class = Helpers.resolve_class processor)
 1397           unless processor_class < kind_class || (kind_java_class && processor_class < kind_java_class)
 1398             raise ::ArgumentError, %(Class specified for #{kind_name} extension does not inherit from #{kind_class}: #{processor})
 1399           end
 1400           processor_instance = processor_class.new as_symbol(name), config
 1401           unless (name = as_symbol processor_instance.name)
 1402             raise ::ArgumentError, %(No name specified for #{kind_name} extension: #{processor})
 1403           end
 1404           processor_instance.freeze
 1405           kind_store[name] = ProcessorExtension.new kind, processor_instance
 1406         # style 3: specified as instance
 1407         elsif kind_class === processor || (kind_java_class && kind_java_class === processor)
 1408           processor.update_config config
 1409           # TODO need a test for this override!
 1410           unless (name = name ? (processor.name = as_symbol name) : (as_symbol processor.name))
 1411             raise ::ArgumentError, %(No name specified for #{kind_name} extension: #{processor})
 1412           end
 1413           processor.freeze
 1414           kind_store[name] = ProcessorExtension.new kind, processor
 1415         else
 1416           raise ::ArgumentError, %(Invalid arguments specified for registering #{kind_name} extension: #{args})
 1417         end
 1418       end
 1419     end
 1420 
 1421     def resolve_args args, expect
 1422       opts = ::Hash === args[-1] ? args.pop : {}
 1423       return opts if expect == 1
 1424       if (missing = expect - 1 - args.size) > 0
 1425         args += (::Array.new missing)
 1426       elsif missing < 0
 1427         args.pop(-missing)
 1428       end
 1429       args << opts
 1430       args
 1431     end
 1432 
 1433     def as_symbol name
 1434       name ? name.to_sym : nil
 1435     end
 1436   end
 1437 
 1438   class << self
 1439     def generate_name
 1440       %(extgrp#{next_auto_id})
 1441     end
 1442 
 1443     def next_auto_id
 1444       @auto_id ||= -1
 1445       @auto_id += 1
 1446     end
 1447 
 1448     def groups
 1449       @groups ||= {}
 1450     end
 1451 
 1452     def create name = nil, &block
 1453       if block_given?
 1454         Registry.new (name || generate_name) => block
 1455       else
 1456         Registry.new
 1457       end
 1458     end
 1459 
 1460     # Public: Registers an extension Group that subsequently registers a
 1461     # collection of extensions.
 1462     #
 1463     # Registers the extension Group specified under the given name. If a name is
 1464     # not given, one is calculated by appending the next value in a 0-based
 1465     # index to the string "extgrp". For instance, the first unnamed extension
 1466     # group to be registered is assigned the name "extgrp0" if a name is not
 1467     # specified.
 1468     #
 1469     # The names are not yet used, but are intended for selectively activating
 1470     # extensions in the future.
 1471     #
 1472     # If the extension group argument is a String or a Symbol, it gets resolved
 1473     # to a Class before being registered.
 1474     #
 1475     # name    - The name under which this extension group is registered (optional, default: nil)
 1476     # group   - A block (Proc), a Class, a String or Symbol name of a Class or
 1477     #           an Object instance of a Class.
 1478     #
 1479     # Examples
 1480     #
 1481     #   Asciidoctor::Extensions.register UmlExtensions
 1482     #
 1483     #   Asciidoctor::Extensions.register :uml, UmlExtensions
 1484     #
 1485     #   Asciidoctor::Extensions.register do
 1486     #     block_processor :plantuml, PlantUmlBlock
 1487     #   end
 1488     #
 1489     #   Asciidoctor::Extensions.register :uml do
 1490     #     block_processor :plantuml, PlantUmlBlock
 1491     #   end
 1492     #
 1493     # Returns the [Proc, Class or Object] instance, matching the type passed to this method.
 1494     def register *args, &block
 1495       argc = args.size
 1496       if block_given?
 1497         resolved_group = block
 1498       elsif (group = args.pop)
 1499         # QUESTION should we instantiate the group class here or defer until activation??
 1500         resolved_group = (Helpers.resolve_class group) || group
 1501       else
 1502         raise ::ArgumentError, %(Extension group to register not specified)
 1503       end
 1504       name = args.pop || generate_name
 1505       unless args.empty?
 1506         raise ::ArgumentError, %(Wrong number of arguments (#{argc} for 1..2))
 1507       end
 1508       groups[name.to_sym] = resolved_group
 1509     end
 1510 
 1511     # Public: Unregister all statically-registered extension groups.
 1512     #
 1513     # Returns nothing
 1514     def unregister_all
 1515       @groups = {}
 1516       nil
 1517     end
 1518 
 1519     # Public: Unregister statically-registered extension groups by name.
 1520     #
 1521     # names - one or more Symbol or String group names to unregister
 1522     #
 1523     # Returns nothing
 1524     def unregister *names
 1525       names.each {|group| @groups.delete group.to_sym }
 1526       nil
 1527     end
 1528   end
 1529 end
 1530 end