asciidoctor 2.0.0.rc.2 → 2.0.0.rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +31 -3
- data/README-de.adoc +2 -8
- data/README-fr.adoc +2 -8
- data/README-jp.adoc +2 -2
- data/README-zh_CN.adoc +2 -2
- data/README.adoc +3 -7
- data/data/locale/attributes-fr.adoc +1 -1
- data/data/stylesheets/asciidoctor-default.css +3 -2
- data/lib/asciidoctor.rb +13 -7
- data/lib/asciidoctor/abstract_block.rb +33 -27
- data/lib/asciidoctor/abstract_node.rb +6 -10
- data/lib/asciidoctor/block.rb +4 -4
- data/lib/asciidoctor/cli/options.rb +23 -24
- data/lib/asciidoctor/converter.rb +17 -16
- data/lib/asciidoctor/converter/docbook5.rb +102 -102
- data/lib/asciidoctor/converter/html5.rb +142 -84
- data/lib/asciidoctor/converter/manpage.rb +81 -81
- data/lib/asciidoctor/converter/template.rb +2 -2
- data/lib/asciidoctor/core_ext.rb +1 -0
- data/lib/asciidoctor/core_ext/hash/merge.rb +7 -0
- data/lib/asciidoctor/document.rb +28 -14
- data/lib/asciidoctor/extensions.rb +4 -3
- data/lib/asciidoctor/inline.rb +2 -9
- data/lib/asciidoctor/parser.rb +27 -17
- data/lib/asciidoctor/reader.rb +41 -26
- data/lib/asciidoctor/section.rb +1 -8
- data/lib/asciidoctor/substitutors.rb +125 -124
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +1 -1
- data/lib/asciidoctor/table.rb +1 -1
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +4 -4
- data/man/asciidoctor.adoc +1 -1
- metadata +3 -2
| @@ -58,7 +58,7 @@ class Converter::TemplateConverter < Converter::Base | |
| 58 58 | 
             
                @safe = opts[:safe]
         | 
| 59 59 | 
             
                @active_engines = {}
         | 
| 60 60 | 
             
                @engine = opts[:template_engine]
         | 
| 61 | 
            -
                @engine_options = {}.tap {|accum| DEFAULT_ENGINE_OPTIONS.each {|engine, engine_opts| accum[engine] = engine_opts. | 
| 61 | 
            +
                @engine_options = {}.tap {|accum| DEFAULT_ENGINE_OPTIONS.each {|engine, engine_opts| accum[engine] = engine_opts.merge } }
         | 
| 62 62 | 
             
                if opts[:htmlsyntax] == 'html' # if not set, assume xml since this converter is also used for DocBook (which doesn't specify htmlsyntax)
         | 
| 63 63 | 
             
                  @engine_options[:haml][:format] = :html5
         | 
| 64 64 | 
             
                  @engine_options[:slim][:format] = :html
         | 
| @@ -124,7 +124,7 @@ class Converter::TemplateConverter < Converter::Base | |
| 124 124 | 
             
              #
         | 
| 125 125 | 
             
              # Returns a [Hash] of Tilt template objects keyed by template name.
         | 
| 126 126 | 
             
              def templates
         | 
| 127 | 
            -
                @templates. | 
| 127 | 
            +
                @templates.merge
         | 
| 128 128 | 
             
              end
         | 
| 129 129 |  | 
| 130 130 | 
             
              # Public: Registers a Tilt template with this converter.
         | 
    
        data/lib/asciidoctor/core_ext.rb
    CHANGED
    
    | @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 | 
             
            require_relative 'core_ext/float/truncate'
         | 
| 3 | 
            +
            require_relative 'core_ext/hash/merge'
         | 
| 3 4 | 
             
            require_relative 'core_ext/match_data/names' if RUBY_ENGINE == 'opal'
         | 
| 4 5 | 
             
            require_relative 'core_ext/nil_or_empty'
         | 
| 5 6 | 
             
            require_relative 'core_ext/regexp/is_match'
         | 
| @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
            # NOTE remove once minimum required Ruby version is at least 2.6
         | 
| 3 | 
            +
            Hash.prepend(Module.new do
         | 
| 4 | 
            +
              def merge *args
         | 
| 5 | 
            +
                (len = args.length) < 1 ? super({}) : (len > 1 ? args.inject(self) {|acc, arg| acc.merge arg } : (super args[0]))
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
            end) if (Hash.instance_method :merge).arity == 1
         | 
    
        data/lib/asciidoctor/document.rb
    CHANGED
    
    | @@ -241,7 +241,7 @@ class Document < AbstractBlock | |
| 241 241 | 
             
              #
         | 
| 242 242 | 
             
              # data    - The AsciiDoc source data as a String or String Array. (default: nil)
         | 
| 243 243 | 
             
              # options - A Hash of options to control processing (e.g., safe mode value (:safe), backend (:backend),
         | 
| 244 | 
            -
              #            | 
| 244 | 
            +
              #           standalone enclosure (:standalone), custom attributes (:attributes)). (default: {})
         | 
| 245 245 | 
             
              #
         | 
| 246 246 | 
             
              # Duplication of the options Hash is handled in the enclosing API.
         | 
| 247 247 | 
             
              #
         | 
| @@ -257,10 +257,10 @@ class Document < AbstractBlock | |
| 257 257 | 
             
                  @parent_document = parent_doc
         | 
| 258 258 | 
             
                  options[:base_dir] ||= parent_doc.base_dir
         | 
| 259 259 | 
             
                  options[:catalog_assets] = true if parent_doc.options[:catalog_assets]
         | 
| 260 | 
            -
                  @catalog = parent_doc.catalog. | 
| 260 | 
            +
                  @catalog = parent_doc.catalog.merge footnotes: []
         | 
| 261 261 | 
             
                  # QUESTION should we support setting attribute in parent document from nested document?
         | 
| 262 262 | 
             
                  # NOTE we must dup or else all the assignments to the overrides clobbers the real attributes
         | 
| 263 | 
            -
                  @attribute_overrides = attr_overrides = parent_doc.attributes. | 
| 263 | 
            +
                  @attribute_overrides = attr_overrides = parent_doc.attributes.merge
         | 
| 264 264 | 
             
                  parent_doctype = attr_overrides.delete 'doctype'
         | 
| 265 265 | 
             
                  attr_overrides.delete 'compat-mode'
         | 
| 266 266 | 
             
                  attr_overrides.delete 'toc'
         | 
| @@ -284,7 +284,7 @@ class Document < AbstractBlock | |
| 284 284 | 
             
                    footnotes: [],
         | 
| 285 285 | 
             
                    links: [],
         | 
| 286 286 | 
             
                    images: [],
         | 
| 287 | 
            -
                    indexterms: [],
         | 
| 287 | 
            +
                    #indexterms: [],
         | 
| 288 288 | 
             
                    callouts: Callouts.new,
         | 
| 289 289 | 
             
                    includes: {},
         | 
| 290 290 | 
             
                  }
         | 
| @@ -328,6 +328,7 @@ class Document < AbstractBlock | |
| 328 328 | 
             
                  @path_resolver = PathResolver.new
         | 
| 329 329 | 
             
                  initialize_extensions = (defined? ::Asciidoctor::Extensions) ? true : nil
         | 
| 330 330 | 
             
                  @extensions = nil # initialize furthur down if initialize_extensions is true
         | 
| 331 | 
            +
                  options[:standalone] = options[:header_footer] if (options.key? :header_footer) && !(options.key? :standalone)
         | 
| 331 332 | 
             
                end
         | 
| 332 333 |  | 
| 333 334 | 
             
                @parsed = false
         | 
| @@ -335,20 +336,20 @@ class Document < AbstractBlock | |
| 335 336 | 
             
                @counters = {}
         | 
| 336 337 | 
             
                @attributes_modified = ::Set.new
         | 
| 337 338 | 
             
                @docinfo_processor_extensions = {}
         | 
| 338 | 
            -
                 | 
| 339 | 
            +
                standalone = options[:standalone]
         | 
| 339 340 | 
             
                (@options = options).freeze
         | 
| 340 341 |  | 
| 341 342 | 
             
                attrs = @attributes
         | 
| 342 343 | 
             
                #attrs['encoding'] = 'UTF-8'
         | 
| 343 344 | 
             
                attrs['sectids'] = ''
         | 
| 344 345 | 
             
                attrs['toc-placement'] = 'auto'
         | 
| 345 | 
            -
                if  | 
| 346 | 
            +
                if standalone
         | 
| 346 347 | 
             
                  attrs['copycss'] = ''
         | 
| 347 | 
            -
                  # sync embedded attribute with : | 
| 348 | 
            +
                  # sync embedded attribute with :standalone option value
         | 
| 348 349 | 
             
                  attr_overrides['embedded'] = nil
         | 
| 349 350 | 
             
                else
         | 
| 350 351 | 
             
                  attrs['notitle'] = ''
         | 
| 351 | 
            -
                  # sync embedded attribute with : | 
| 352 | 
            +
                  # sync embedded attribute with :standalone option value
         | 
| 352 353 | 
             
                  attr_overrides['embedded'] = ''
         | 
| 353 354 | 
             
                end
         | 
| 354 355 | 
             
                attrs['stylesheet'] = ''
         | 
| @@ -606,7 +607,8 @@ class Document < AbstractBlock | |
| 606 607 | 
             
                when :refs
         | 
| 607 608 | 
             
                  @catalog[:refs][value[0]] ||= (ref = value[1])
         | 
| 608 609 | 
             
                  ref
         | 
| 609 | 
            -
                when :footnotes, :indexterms
         | 
| 610 | 
            +
                #when :footnotes, :indexterms
         | 
| 611 | 
            +
                when :footnotes
         | 
| 610 612 | 
             
                  @catalog[type] << value
         | 
| 611 613 | 
             
                else
         | 
| 612 614 | 
             
                  @catalog[type] << (type == :images ? (ImageReference.new value[0], value[1]) : value) if @options[:catalog_assets]
         | 
| @@ -723,6 +725,10 @@ class Document < AbstractBlock | |
| 723 725 | 
             
              end
         | 
| 724 726 | 
             
              alias name doctitle
         | 
| 725 727 |  | 
| 728 | 
            +
              def xreftext xrefstyle = nil
         | 
| 729 | 
            +
                (val = reftext) && !val.empty? ? val : title
         | 
| 730 | 
            +
              end
         | 
| 731 | 
            +
             | 
| 726 732 | 
             
              # Public: Convenience method to retrieve the document attribute 'author'
         | 
| 727 733 | 
             
              #
         | 
| 728 734 | 
             
              # returns the full name of the author as a String
         | 
| @@ -928,7 +934,13 @@ class Document < AbstractBlock | |
| 928 934 | 
             
                    end
         | 
| 929 935 | 
             
                  end
         | 
| 930 936 | 
             
                else
         | 
| 931 | 
            -
                   | 
| 937 | 
            +
                  if opts.key? :standalone
         | 
| 938 | 
            +
                    transform = opts[:standalone] ? 'document' : 'embedded'
         | 
| 939 | 
            +
                  elsif opts.key? :header_footer
         | 
| 940 | 
            +
                    transform = opts[:header_footer] ? 'document' : 'embedded'
         | 
| 941 | 
            +
                  else
         | 
| 942 | 
            +
                    transform = @options[:standalone] ? 'document' : 'embedded'
         | 
| 943 | 
            +
                  end
         | 
| 932 944 | 
             
                  output = @converter.convert self, transform
         | 
| 933 945 | 
             
                end
         | 
| 934 946 |  | 
| @@ -1053,10 +1065,12 @@ class Document < AbstractBlock | |
| 1053 1065 |  | 
| 1054 1066 | 
             
                # TODO allow document to control whether extension docinfo is contributed
         | 
| 1055 1067 | 
             
                if @extensions && (docinfo_processors? location)
         | 
| 1056 | 
            -
                  (content  | 
| 1068 | 
            +
                  ((content || []).concat @docinfo_processor_extensions[location].map {|ext| ext.process_method[self] }.compact).join LF
         | 
| 1069 | 
            +
                elsif content
         | 
| 1070 | 
            +
                  content.join LF
         | 
| 1071 | 
            +
                else
         | 
| 1072 | 
            +
                  ''
         | 
| 1057 1073 | 
             
                end
         | 
| 1058 | 
            -
             | 
| 1059 | 
            -
                content ? (content.join LF) : ''
         | 
| 1060 1074 | 
             
              end
         | 
| 1061 1075 |  | 
| 1062 1076 | 
             
              def docinfo_processors?(location = :head)
         | 
| @@ -1235,7 +1249,7 @@ class Document < AbstractBlock | |
| 1235 1249 | 
             
                  end
         | 
| 1236 1250 | 
             
                end
         | 
| 1237 1251 |  | 
| 1238 | 
            -
                @header_attributes = attrs. | 
| 1252 | 
            +
                @header_attributes = attrs.merge
         | 
| 1239 1253 | 
             
              end
         | 
| 1240 1254 |  | 
| 1241 1255 | 
             
              # Internal: Assign the local and document datetime attributes, which includes localdate, localyear, localtime,
         | 
| @@ -201,7 +201,7 @@ module Extensions | |
| 201 201 | 
             
                end
         | 
| 202 202 |  | 
| 203 203 | 
             
                def create_inline parent, context, text, opts = {}
         | 
| 204 | 
            -
                  Inline.new parent, context, text, opts
         | 
| 204 | 
            +
                  Inline.new parent, context, text, context == :quoted ? ({ type: :unquoted }.merge opts) : opts
         | 
| 205 205 | 
             
                end
         | 
| 206 206 |  | 
| 207 207 | 
             
                # Public: Parses blocks in the content and attaches the block to the parent.
         | 
| @@ -211,7 +211,7 @@ module Extensions | |
| 211 211 | 
             
                # QUESTION is parse_content the right method name? should we wrap in open block automatically?
         | 
| 212 212 | 
             
                def parse_content parent, content, attributes = nil
         | 
| 213 213 | 
             
                  reader = Reader === content ? content : (Reader.new content)
         | 
| 214 | 
            -
                  while ((block = Parser.next_block reader, parent, (attributes ? attributes. | 
| 214 | 
            +
                  while ((block = Parser.next_block reader, parent, (attributes ? attributes.merge : {})) && parent << block) ||
         | 
| 215 215 | 
             
                      reader.has_more_lines?
         | 
| 216 216 | 
             
                  end
         | 
| 217 217 | 
             
                  parent
         | 
| @@ -225,7 +225,8 @@ module Extensions | |
| 225 225 | 
             
                  [:create_pass_block,    :create_block,  :pass],
         | 
| 226 226 | 
             
                  [:create_listing_block, :create_block,  :listing],
         | 
| 227 227 | 
             
                  [:create_literal_block, :create_block,  :literal],
         | 
| 228 | 
            -
                  [:create_anchor,        :create_inline, :anchor]
         | 
| 228 | 
            +
                  [:create_anchor,        :create_inline, :anchor],
         | 
| 229 | 
            +
                  [:create_inline_pass,   :create_inline, :quoted],
         | 
| 229 230 | 
             
                ].each do |method_name, delegate_method_name, context|
         | 
| 230 231 | 
             
                  define_method method_name do |*args|
         | 
| 231 232 | 
             
                    args.unshift args.shift, context
         | 
    
        data/lib/asciidoctor/inline.rb
    CHANGED
    
    | @@ -3,7 +3,7 @@ module Asciidoctor | |
| 3 3 | 
             
            # Public: Methods for managing inline elements in AsciiDoc block
         | 
| 4 4 | 
             
            class Inline < AbstractNode
         | 
| 5 5 | 
             
              # Public: Get the text of this inline element
         | 
| 6 | 
            -
               | 
| 6 | 
            +
              attr_accessor :text
         | 
| 7 7 |  | 
| 8 8 | 
             
              # Public: Get the type (qualifier) of this inline element
         | 
| 9 9 | 
             
              attr_reader :type
         | 
| @@ -12,19 +12,12 @@ class Inline < AbstractNode | |
| 12 12 | 
             
              attr_accessor :target
         | 
| 13 13 |  | 
| 14 14 | 
             
              def initialize(parent, context, text = nil, opts = {})
         | 
| 15 | 
            -
                super(parent, context)
         | 
| 15 | 
            +
                super(parent, context, opts)
         | 
| 16 16 | 
             
                @node_name = %(inline_#{context})
         | 
| 17 | 
            -
             | 
| 18 17 | 
             
                @text = text
         | 
| 19 | 
            -
             | 
| 20 18 | 
             
                @id = opts[:id]
         | 
| 21 19 | 
             
                @type = opts[:type]
         | 
| 22 20 | 
             
                @target = opts[:target]
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                # value of attributes option for inline nodes may be nil
         | 
| 25 | 
            -
                if (attrs = opts[:attributes])
         | 
| 26 | 
            -
                  @attributes = attrs.dup
         | 
| 27 | 
            -
                end
         | 
| 28 21 | 
             
              end
         | 
| 29 22 |  | 
| 30 23 | 
             
              def block?
         | 
    
        data/lib/asciidoctor/parser.rb
    CHANGED
    
    | @@ -346,7 +346,10 @@ class Parser | |
| 346 346 | 
             
                while reader.has_more_lines?
         | 
| 347 347 | 
             
                  parse_block_metadata_lines reader, document, attributes
         | 
| 348 348 | 
             
                  if (next_level = is_next_line_section?(reader, attributes))
         | 
| 349 | 
            -
                     | 
| 349 | 
            +
                    if document.attr? 'leveloffset'
         | 
| 350 | 
            +
                      next_level += (document.attr 'leveloffset').to_i
         | 
| 351 | 
            +
                      next_level = 0 if next_level < 0
         | 
| 352 | 
            +
                    end
         | 
| 350 353 | 
             
                    if next_level > current_level
         | 
| 351 354 | 
             
                      if expected_next_level
         | 
| 352 355 | 
             
                        unless next_level == expected_next_level || (expected_next_level_alt && next_level == expected_next_level_alt) || expected_next_level < 0
         | 
| @@ -445,7 +448,7 @@ class Parser | |
| 445 448 | 
             
                # of a section that need to get transfered to the next section
         | 
| 446 449 | 
             
                # see "trailing block attributes transfer to the following section" in
         | 
| 447 450 | 
             
                # test/attributes_test.rb for an example
         | 
| 448 | 
            -
                [section != parent ? section : nil, attributes. | 
| 451 | 
            +
                [section != parent ? section : nil, attributes.merge]
         | 
| 449 452 | 
             
              end
         | 
| 450 453 |  | 
| 451 454 | 
             
              # Public: Parse and return the next Block at the Reader's current location
         | 
| @@ -579,14 +582,14 @@ class Parser | |
| 579 582 | 
             
                          end
         | 
| 580 583 | 
             
                          # style doesn't have special meaning for media macros
         | 
| 581 584 | 
             
                          attributes.delete 'style' if attributes.key? 'style'
         | 
| 582 | 
            -
                          if  | 
| 583 | 
            -
                             | 
| 584 | 
            -
             | 
| 585 | 
            -
             | 
| 586 | 
            -
                            # otherwise, drop the line
         | 
| 587 | 
            -
                            else
         | 
| 585 | 
            +
                          if target.include? ATTR_REF_HEAD
         | 
| 586 | 
            +
                            if (expanded_target = block.sub_attributes target).empty? &&
         | 
| 587 | 
            +
                                (doc_attrs['attribute-missing'] || Compliance.attribute_missing) == 'drop-line' &&
         | 
| 588 | 
            +
                                (block.sub_attributes target + ' ', attribute_missing: 'drop-line', drop_line_severity: :ignore).empty?
         | 
| 588 589 | 
             
                              attributes.clear
         | 
| 589 590 | 
             
                              return
         | 
| 591 | 
            +
                            else
         | 
| 592 | 
            +
                              target = expanded_target
         | 
| 590 593 | 
             
                            end
         | 
| 591 594 | 
             
                          end
         | 
| 592 595 | 
             
                          if blk_ctx == :image
         | 
| @@ -616,12 +619,16 @@ class Parser | |
| 616 619 | 
             
                          if report_unknown_block_macro
         | 
| 617 620 | 
             
                            logger.debug message_with_context %(unknown name for block macro: #{$1}), source_location: reader.cursor_at_mark
         | 
| 618 621 | 
             
                          else
         | 
| 619 | 
            -
                            target = $2
         | 
| 620 622 | 
             
                            content = $3
         | 
| 621 | 
            -
                            if (target | 
| 622 | 
            -
                              ( | 
| 623 | 
            -
             | 
| 624 | 
            -
             | 
| 623 | 
            +
                            if (target = $2).include? ATTR_REF_HEAD
         | 
| 624 | 
            +
                              if (expanded_target = parent.sub_attributes target).empty? &&
         | 
| 625 | 
            +
                                  (doc_attrs['attribute-missing'] || Compliance.attribute_missing) == 'drop-line' &&
         | 
| 626 | 
            +
                                  (parent.sub_attributes target + ' ', attribute_missing: 'drop-line', drop_line_severity: :ignore).empty?
         | 
| 627 | 
            +
                                attributes.clear
         | 
| 628 | 
            +
                                return
         | 
| 629 | 
            +
                              else
         | 
| 630 | 
            +
                                target = expanded_target
         | 
| 631 | 
            +
                              end
         | 
| 625 632 | 
             
                            end
         | 
| 626 633 | 
             
                            if extension.config[:content_model] == :attributes
         | 
| 627 634 | 
             
                              document.parse_attributes content, extension.config[:pos_attrs] || [], sub_input: true, into: attributes if content
         | 
| @@ -903,7 +910,7 @@ class Parser | |
| 903 910 | 
             
                end
         | 
| 904 911 | 
             
                # FIXME remove the need for this update!
         | 
| 905 912 | 
             
                block.attributes.update(attributes) unless attributes.empty?
         | 
| 906 | 
            -
                block. | 
| 913 | 
            +
                block.commit_subs
         | 
| 907 914 |  | 
| 908 915 | 
             
                #if doc_attrs.key? :pending_attribute_entries
         | 
| 909 916 | 
             
                #  doc_attrs.delete(:pending_attribute_entries).each do |entry|
         | 
| @@ -1025,7 +1032,7 @@ class Parser | |
| 1025 1032 | 
             
                if (extension = options[:extension])
         | 
| 1026 1033 | 
             
                  # QUESTION do we want to delete the style?
         | 
| 1027 1034 | 
             
                  attributes.delete('style')
         | 
| 1028 | 
            -
                  if (block = extension.process_method[parent, block_reader || (Reader.new lines), attributes. | 
| 1035 | 
            +
                  if (block = extension.process_method[parent, block_reader || (Reader.new lines), attributes.merge])
         | 
| 1029 1036 | 
             
                    attributes.replace block.attributes
         | 
| 1030 1037 | 
             
                    # FIXME if the content model is set to compound, but we only have simple in this context, then
         | 
| 1031 1038 | 
             
                    # forcefully set the content_model to simple to prevent parsing blocks from children
         | 
| @@ -1749,7 +1756,10 @@ class Parser | |
| 1749 1756 | 
             
                else
         | 
| 1750 1757 | 
             
                  raise %(Unrecognized section at #{reader.cursor_at_prev_line})
         | 
| 1751 1758 | 
             
                end
         | 
| 1752 | 
            -
                 | 
| 1759 | 
            +
                if document.attr? 'leveloffset'
         | 
| 1760 | 
            +
                  sect_level += (document.attr 'leveloffset').to_i
         | 
| 1761 | 
            +
                  sect_level = 0 if sect_level < 0
         | 
| 1762 | 
            +
                end
         | 
| 1753 1763 | 
             
                [sect_id, sect_reftext, sect_title, sect_level, atx]
         | 
| 1754 1764 | 
             
              end
         | 
| 1755 1765 |  | 
| @@ -2448,7 +2458,7 @@ class Parser | |
| 2448 2458 | 
             
                    end
         | 
| 2449 2459 |  | 
| 2450 2460 | 
             
                    if m[1]
         | 
| 2451 | 
            -
                      1.upto(m[1].to_i) { specs << spec. | 
| 2461 | 
            +
                      1.upto(m[1].to_i) { specs << spec.merge }
         | 
| 2452 2462 | 
             
                    else
         | 
| 2453 2463 | 
             
                      specs << spec
         | 
| 2454 2464 | 
             
                    end
         | 
    
        data/lib/asciidoctor/reader.rb
    CHANGED
    
    | @@ -902,30 +902,28 @@ class PreprocessorReader < Reader | |
| 902 902 | 
             
                # attributes are case insensitive
         | 
| 903 903 | 
             
                target = target.downcase unless (no_target = target.empty?)
         | 
| 904 904 |  | 
| 905 | 
            -
                # must have a target before brackets if ifdef or ifndef
         | 
| 906 | 
            -
                # must not have text between brackets if endif
         | 
| 907 | 
            -
                # skip line if it doesn't meet this criteria
         | 
| 908 | 
            -
                # QUESTION should we warn for these bogus declarations?
         | 
| 909 | 
            -
                return false if (no_target && (keyword == 'ifdef' || keyword == 'ifndef')) || (text && keyword == 'endif')
         | 
| 910 | 
            -
             | 
| 911 905 | 
             
                if keyword == 'endif'
         | 
| 912 | 
            -
                  if  | 
| 913 | 
            -
                    logger.error message_with_context %( | 
| 906 | 
            +
                  if text
         | 
| 907 | 
            +
                    logger.error message_with_context %(malformed preprocessor directive - text not permitted: endif::#{target}[#{text}]), source_location: cursor
         | 
| 908 | 
            +
                  elsif @conditional_stack.empty?
         | 
| 909 | 
            +
                    logger.error message_with_context %(unmatched preprocessor directive: endif::#{target}[]), source_location: cursor
         | 
| 914 910 | 
             
                  elsif no_target || target == (pair = @conditional_stack[-1])[:target]
         | 
| 915 911 | 
             
                    @conditional_stack.pop
         | 
| 916 912 | 
             
                    @skipping = @conditional_stack.empty? ? false : @conditional_stack[-1][:skipping]
         | 
| 917 913 | 
             
                  else
         | 
| 918 | 
            -
                    logger.error message_with_context %(mismatched  | 
| 914 | 
            +
                    logger.error message_with_context %(mismatched preprocessor directive: endif::#{target}[], expected endif::#{pair[:target]}[]), source_location: cursor
         | 
| 919 915 | 
             
                  end
         | 
| 920 916 | 
             
                  return true
         | 
| 921 | 
            -
                 | 
| 922 | 
            -
             | 
| 923 | 
            -
                if @skipping
         | 
| 917 | 
            +
                elsif @skipping
         | 
| 924 918 | 
             
                  skip = false
         | 
| 925 919 | 
             
                else
         | 
| 926 920 | 
             
                  # QUESTION any way to wrap ifdef & ifndef logic up together?
         | 
| 927 921 | 
             
                  case keyword
         | 
| 928 922 | 
             
                  when 'ifdef'
         | 
| 923 | 
            +
                    if no_target
         | 
| 924 | 
            +
                      logger.error message_with_context %(malformed preprocessor directive - missing target: ifdef::[#{text}]), source_location: cursor
         | 
| 925 | 
            +
                      return true
         | 
| 926 | 
            +
                    end
         | 
| 929 927 | 
             
                    case delimiter
         | 
| 930 928 | 
             
                    when ','
         | 
| 931 929 | 
             
                      # skip if no attribute is defined
         | 
| @@ -938,6 +936,10 @@ class PreprocessorReader < Reader | |
| 938 936 | 
             
                      skip = !@document.attributes.key?(target)
         | 
| 939 937 | 
             
                    end
         | 
| 940 938 | 
             
                  when 'ifndef'
         | 
| 939 | 
            +
                    if no_target
         | 
| 940 | 
            +
                      logger.error message_with_context %(malformed preprocessor directive - missing target: ifndef::[#{text}]), source_location: cursor
         | 
| 941 | 
            +
                      return true
         | 
| 942 | 
            +
                    end
         | 
| 941 943 | 
             
                    case delimiter
         | 
| 942 944 | 
             
                    when ','
         | 
| 943 945 | 
             
                      # skip if any attribute is defined
         | 
| @@ -950,15 +952,19 @@ class PreprocessorReader < Reader | |
| 950 952 | 
             
                      skip = @document.attributes.key?(target)
         | 
| 951 953 | 
             
                    end
         | 
| 952 954 | 
             
                  when 'ifeval'
         | 
| 953 | 
            -
                     | 
| 954 | 
            -
             | 
| 955 | 
            -
             | 
| 956 | 
            -
             | 
| 957 | 
            -
             | 
| 958 | 
            -
             | 
| 959 | 
            -
             | 
| 960 | 
            -
             | 
| 961 | 
            -
             | 
| 955 | 
            +
                    if no_target
         | 
| 956 | 
            +
                      # the text in brackets must match a conditional expression
         | 
| 957 | 
            +
                      if text && EvalExpressionRx =~ text.strip
         | 
| 958 | 
            +
                        # regex enforces a restricted set of math-related operations (==, !=, <=, >=, <, >)
         | 
| 959 | 
            +
                        skip = ((resolve_expr_val $1).send $2, (resolve_expr_val $3)) ? false : true
         | 
| 960 | 
            +
                      else
         | 
| 961 | 
            +
                        logger.error message_with_context %(malformed preprocessor directive - #{text ? 'invalid expression' : 'missing expression'}: ifeval::[#{text}]), source_location: cursor
         | 
| 962 | 
            +
                        return true
         | 
| 963 | 
            +
                      end
         | 
| 964 | 
            +
                    else
         | 
| 965 | 
            +
                      logger.error message_with_context %(malformed preprocessor directive - target not permitted: ifeval::#{target}[#{text}]), source_location: cursor
         | 
| 966 | 
            +
                      return true
         | 
| 967 | 
            +
                    end
         | 
| 962 968 | 
             
                  end
         | 
| 963 969 | 
             
                end
         | 
| 964 970 |  | 
| @@ -1008,12 +1014,20 @@ class PreprocessorReader < Reader | |
| 1008 1014 | 
             
              def preprocess_include_directive target, attrlist
         | 
| 1009 1015 | 
             
                doc = @document
         | 
| 1010 1016 | 
             
                if ((expanded_target = target).include? ATTR_REF_HEAD) &&
         | 
| 1011 | 
            -
                    (expanded_target = doc.sub_attributes target, attribute_missing: 'drop-line').empty?
         | 
| 1012 | 
            -
                   | 
| 1013 | 
            -
             | 
| 1014 | 
            -
                     | 
| 1017 | 
            +
                    (expanded_target = doc.sub_attributes target, attribute_missing: ((attr_missing = doc.attributes['attribute-missing'] || Compliance.attribute_missing) == 'warn' ? 'drop-line' : attr_missing)).empty?
         | 
| 1018 | 
            +
                  if attr_missing == 'drop-line' && (doc.sub_attributes target + ' ', attribute_missing: 'drop-line', drop_line_severity: :ignore).empty?
         | 
| 1019 | 
            +
                    logger.info { message_with_context %(include dropped due to missing attribute: include::#{target}[#{attrlist}]), source_location: cursor }
         | 
| 1020 | 
            +
                    shift
         | 
| 1021 | 
            +
                    true
         | 
| 1022 | 
            +
                  elsif (doc.parse_attributes attrlist, [], sub_input: true)['optional-option']
         | 
| 1023 | 
            +
                    logger.info { message_with_context %(optional include dropped #{attr_missing == 'warn' && (doc.sub_attributes target + ' ', attribute_missing: 'drop-line', drop_line_severity: :ignore).empty? ? 'due to missing attribute' : 'because resolved target is blank'}: include::#{target}[#{attrlist}]), source_location: cursor }
         | 
| 1024 | 
            +
                    shift
         | 
| 1025 | 
            +
                    true
         | 
| 1026 | 
            +
                  else
         | 
| 1027 | 
            +
                    logger.warn message_with_context %(include dropped #{attr_missing == 'warn' && (doc.sub_attributes target + ' ', attribute_missing: 'drop-line', drop_line_severity: :ignore).empty? ? 'due to missing attribute' : 'because resolved target is blank'}: include::#{target}[#{attrlist}]), source_location: cursor
         | 
| 1028 | 
            +
                    # QUESTION should this line include target or expanded_target (or escaped target?)
         | 
| 1029 | 
            +
                    replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{attrlist}])
         | 
| 1015 1030 | 
             
                  end
         | 
| 1016 | 
            -
                  true
         | 
| 1017 1031 | 
             
                elsif include_processors? && (ext = @include_processor_extensions.find {|candidate| candidate.instance.handles? expanded_target })
         | 
| 1018 1032 | 
             
                  shift
         | 
| 1019 1033 | 
             
                  # FIXME parse attributes only if requested by extension
         | 
| @@ -1222,6 +1236,7 @@ class PreprocessorReader < Reader | |
| 1222 1236 | 
             
                  inc_path = doc.normalize_system_path target, @dir, nil, target_name: 'include file'
         | 
| 1223 1237 | 
             
                  unless ::File.file? inc_path
         | 
| 1224 1238 | 
             
                    if attributes['optional-option']
         | 
| 1239 | 
            +
                      logger.info { message_with_context %(optional include dropped because include file not found: #{inc_path}), source_location: cursor }
         | 
| 1225 1240 | 
             
                      shift
         | 
| 1226 1241 | 
             
                      return true
         | 
| 1227 1242 | 
             
                    else
         | 
    
        data/lib/asciidoctor/section.rb
    CHANGED
    
    | @@ -111,14 +111,7 @@ class Section < AbstractBlock | |
| 111 111 | 
             
              # Returns the section number as a String
         | 
| 112 112 | 
             
              def sectnum(delimiter = '.', append = nil)
         | 
| 113 113 | 
             
                append ||= (append == false ? '' : delimiter)
         | 
| 114 | 
            -
                 | 
| 115 | 
            -
                  %(#{@numeral}#{append})
         | 
| 116 | 
            -
                elsif @level > 1
         | 
| 117 | 
            -
                  Section === @parent ? %(#{@parent.sectnum(delimiter, delimiter)}#{@numeral}#{append}) : %(#{@numeral}#{append})
         | 
| 118 | 
            -
                else # @level == 0
         | 
| 119 | 
            -
                  # NOTE coerce @numeral to int just in case not set; can happen if section nesting is out of sequence
         | 
| 120 | 
            -
                  %(#{Helpers.int_to_roman @numeral.to_i}#{append})
         | 
| 121 | 
            -
                end
         | 
| 114 | 
            +
                @level > 1 && Section === @parent ? %(#{@parent.sectnum(delimiter, delimiter)}#{@numeral}#{append}) : %(#{@numeral}#{append})
         | 
| 122 115 | 
             
              end
         | 
| 123 116 |  | 
| 124 117 | 
             
              # (see AbstractBlock#xreftext)
         | 
| @@ -395,9 +395,9 @@ module Substitutors | |
| 395 395 | 
             
                  when :none
         | 
| 396 396 | 
             
                    replacement
         | 
| 397 397 | 
             
                  when :bounding
         | 
| 398 | 
            -
                     | 
| 398 | 
            +
                    m[1] + replacement + m[2]
         | 
| 399 399 | 
             
                  else # :leading
         | 
| 400 | 
            -
                     | 
| 400 | 
            +
                    m[1] + replacement
         | 
| 401 401 | 
             
                  end
         | 
| 402 402 | 
             
                end
         | 
| 403 403 | 
             
              end
         | 
| @@ -416,7 +416,7 @@ module Substitutors | |
| 416 416 | 
             
              # Returns the [String] text with the attribute references replaced with resolved values
         | 
| 417 417 | 
             
              def sub_attributes text, opts = {}
         | 
| 418 418 | 
             
                doc_attrs = @document.attributes
         | 
| 419 | 
            -
                drop = drop_line = drop_empty_line = attribute_undefined = attribute_missing = nil
         | 
| 419 | 
            +
                drop = drop_line = drop_line_severity = drop_empty_line = attribute_undefined = attribute_missing = nil
         | 
| 420 420 | 
             
                text = text.gsub AttributeReferenceRx do
         | 
| 421 421 | 
             
                  # escaped attribute, return unescaped
         | 
| 422 422 | 
             
                  if $1 == RS || $4 == RS
         | 
| @@ -426,7 +426,7 @@ module Substitutors | |
| 426 426 | 
             
                    when 'set'
         | 
| 427 427 | 
             
                      _, value = Parser.store_attribute args[0], args[1] || '', @document
         | 
| 428 428 | 
             
                      # NOTE since this is an assignment, only drop-line applies here (skip and drop imply the same result)
         | 
| 429 | 
            -
                      if value || (attribute_undefined ||= doc_attrs['attribute-undefined'] || Compliance.attribute_undefined) != 'drop-line'
         | 
| 429 | 
            +
                      if value || (attribute_undefined ||= (doc_attrs['attribute-undefined'] || Compliance.attribute_undefined)) != 'drop-line'
         | 
| 430 430 | 
             
                        drop = drop_empty_line = DEL
         | 
| 431 431 | 
             
                      else
         | 
| 432 432 | 
             
                        drop = drop_line = CAN
         | 
| @@ -442,11 +442,15 @@ module Substitutors | |
| 442 442 | 
             
                  elsif (value = INTRINSIC_ATTRIBUTES[key])
         | 
| 443 443 | 
             
                    value
         | 
| 444 444 | 
             
                  else
         | 
| 445 | 
            -
                    case (attribute_missing ||= opts[:attribute_missing] || doc_attrs['attribute-missing'] || Compliance.attribute_missing)
         | 
| 445 | 
            +
                    case (attribute_missing ||= (opts[:attribute_missing] || doc_attrs['attribute-missing'] || Compliance.attribute_missing))
         | 
| 446 446 | 
             
                    when 'drop'
         | 
| 447 447 | 
             
                      drop = drop_empty_line = DEL
         | 
| 448 448 | 
             
                    when 'drop-line'
         | 
| 449 | 
            -
                       | 
| 449 | 
            +
                      if (drop_line_severity ||= (opts[:drop_line_severity] || :info)) == :info
         | 
| 450 | 
            +
                        logger.info { %(dropping line containing reference to missing attribute: #{key}) }
         | 
| 451 | 
            +
                      #elsif drop_line_severity == :warn
         | 
| 452 | 
            +
                      #  logger.warn %(dropping line containing reference to missing attribute: #{key})
         | 
| 453 | 
            +
                      end
         | 
| 450 454 | 
             
                      drop = drop_line = CAN
         | 
| 451 455 | 
             
                    when 'warn'
         | 
| 452 456 | 
             
                      logger.warn %(skipping reference to missing attribute: #{key})
         | 
| @@ -460,7 +464,7 @@ module Substitutors | |
| 460 464 | 
             
                if drop
         | 
| 461 465 | 
             
                  # drop lines from text
         | 
| 462 466 | 
             
                  if drop_empty_line
         | 
| 463 | 
            -
                    lines = (text. | 
| 467 | 
            +
                    lines = (text.squeeze DEL).split LF, -1
         | 
| 464 468 | 
             
                    if drop_line
         | 
| 465 469 | 
             
                      (lines.reject {|line| line == DEL || line == CAN || (line.start_with? CAN) || (line.include? CAN) }.join LF).delete DEL
         | 
| 466 470 | 
             
                    else
         | 
| @@ -486,13 +490,54 @@ module Substitutors | |
| 486 490 | 
             
              def sub_macros(text)
         | 
| 487 491 | 
             
                #return text if text.nil_or_empty?
         | 
| 488 492 | 
             
                # some look ahead assertions to cut unnecessary regex calls
         | 
| 489 | 
            -
                 | 
| 490 | 
            -
                found_square_bracket = found[:square_bracket] = text.include? '['
         | 
| 493 | 
            +
                found_square_bracket = text.include? '['
         | 
| 491 494 | 
             
                found_colon = text.include? ':'
         | 
| 492 | 
            -
                found_macroish =  | 
| 495 | 
            +
                found_macroish = found_square_bracket && found_colon
         | 
| 493 496 | 
             
                found_macroish_short = found_macroish && (text.include? ':[')
         | 
| 494 497 | 
             
                doc_attrs = (doc = @document).attributes
         | 
| 495 498 |  | 
| 499 | 
            +
                # TODO allow position of substitution to be controlled (before or after other macros)
         | 
| 500 | 
            +
                # TODO this handling needs some cleanup
         | 
| 501 | 
            +
                if (extensions = doc.extensions) && extensions.inline_macros? # && found_macroish
         | 
| 502 | 
            +
                  extensions.inline_macros.each do |extension|
         | 
| 503 | 
            +
                    text = text.gsub extension.instance.regexp do
         | 
| 504 | 
            +
                      # honor the escape
         | 
| 505 | 
            +
                      next $&.slice 1, $&.length if $&.start_with? RS
         | 
| 506 | 
            +
                      extconf = extension.config
         | 
| 507 | 
            +
                      if $~.names.empty?
         | 
| 508 | 
            +
                        target, content = $1, $2
         | 
| 509 | 
            +
                      else
         | 
| 510 | 
            +
                        target, content = ($~[:target] rescue nil), ($~[:content] rescue nil)
         | 
| 511 | 
            +
                      end
         | 
| 512 | 
            +
                      attributes = (attributes = extconf[:default_attrs]) ? attributes.merge : {}
         | 
| 513 | 
            +
                      if content.nil_or_empty?
         | 
| 514 | 
            +
                        attributes['text'] = content if content && extconf[:content_model] != :attributes
         | 
| 515 | 
            +
                      else
         | 
| 516 | 
            +
                        content = unescape_bracketed_text content
         | 
| 517 | 
            +
                        # QUESTION should we store the unparsed attrlist in the attrlist key?
         | 
| 518 | 
            +
                        if extconf[:content_model] == :attributes
         | 
| 519 | 
            +
                          parse_attributes content, extconf[:pos_attrs] || [], into: attributes
         | 
| 520 | 
            +
                        else
         | 
| 521 | 
            +
                          attributes['text'] = content
         | 
| 522 | 
            +
                        end
         | 
| 523 | 
            +
                      end
         | 
| 524 | 
            +
                      # NOTE for convenience, map content (unparsed attrlist) to target when format is short
         | 
| 525 | 
            +
                      target ||= extconf[:format] == :short ? content : target
         | 
| 526 | 
            +
                      if (Inline === (replacement = extension.process_method[self, target, attributes]))
         | 
| 527 | 
            +
                        if (inline_subs = replacement.attributes.delete 'subs')
         | 
| 528 | 
            +
                          replacement.text = apply_subs replacement.text, (expand_subs inline_subs)
         | 
| 529 | 
            +
                        end
         | 
| 530 | 
            +
                        replacement.convert
         | 
| 531 | 
            +
                      elsif replacement
         | 
| 532 | 
            +
                        logger.info %(expected substitution value for custom inline macro to be of type Inline; got #{replacement.class}: #{$&})
         | 
| 533 | 
            +
                        replacement
         | 
| 534 | 
            +
                      else
         | 
| 535 | 
            +
                        ''
         | 
| 536 | 
            +
                      end
         | 
| 537 | 
            +
                    end
         | 
| 538 | 
            +
                  end
         | 
| 539 | 
            +
                end
         | 
| 540 | 
            +
             | 
| 496 541 | 
             
                if doc_attrs.key? 'experimental'
         | 
| 497 542 | 
             
                  if found_macroish_short && ((text.include? 'kbd:') || (text.include? 'btn:'))
         | 
| 498 543 | 
             
                    text = text.gsub InlineKbdBtnMacroRx do
         | 
| @@ -509,7 +554,7 @@ module Substitutors | |
| 509 554 | 
             
                          # NOTE handle special case where keys ends with delimiter (e.g., Ctrl++ or Ctrl,,)
         | 
| 510 555 | 
             
                          if keys.end_with? delim
         | 
| 511 556 | 
             
                            keys = (keys.chop.split delim, -1).map {|key| key.strip }
         | 
| 512 | 
            -
                            keys[-1]  | 
| 557 | 
            +
                            keys[-1] += delim
         | 
| 513 558 | 
             
                          else
         | 
| 514 559 | 
             
                            keys = keys.split(delim).map {|key| key.strip }
         | 
| 515 560 | 
             
                          end
         | 
| @@ -557,39 +602,6 @@ module Substitutors | |
| 557 602 | 
             
                  end
         | 
| 558 603 | 
             
                end
         | 
| 559 604 |  | 
| 560 | 
            -
                # FIXME this location is somewhat arbitrary, probably need to be able to control ordering
         | 
| 561 | 
            -
                # TODO this handling needs some cleanup
         | 
| 562 | 
            -
                if (extensions = doc.extensions) && extensions.inline_macros? # && found_macroish
         | 
| 563 | 
            -
                  extensions.inline_macros.each do |extension|
         | 
| 564 | 
            -
                    text = text.gsub extension.instance.regexp do
         | 
| 565 | 
            -
                      # honor the escape
         | 
| 566 | 
            -
                      next $&.slice 1, $&.length if $&.start_with? RS
         | 
| 567 | 
            -
                      extconf = extension.config
         | 
| 568 | 
            -
                      if $~.names.empty?
         | 
| 569 | 
            -
                        target, content = $1, $2
         | 
| 570 | 
            -
                      else
         | 
| 571 | 
            -
                        target, content = ($~[:target] rescue nil), ($~[:content] rescue nil)
         | 
| 572 | 
            -
                      end
         | 
| 573 | 
            -
                      attributes = (attributes = extconf[:default_attrs]) ? attributes.dup : {}
         | 
| 574 | 
            -
                      if content.nil_or_empty?
         | 
| 575 | 
            -
                        attributes['text'] = content if content && extconf[:content_model] != :attributes
         | 
| 576 | 
            -
                      else
         | 
| 577 | 
            -
                        content = unescape_bracketed_text content
         | 
| 578 | 
            -
                        # QUESTION should we store the unparsed attrlist in the attrlist key?
         | 
| 579 | 
            -
                        if extconf[:content_model] == :attributes
         | 
| 580 | 
            -
                          parse_attributes content, extconf[:pos_attrs] || [], into: attributes
         | 
| 581 | 
            -
                        else
         | 
| 582 | 
            -
                          attributes['text'] = content
         | 
| 583 | 
            -
                        end
         | 
| 584 | 
            -
                      end
         | 
| 585 | 
            -
                      # NOTE for convenience, map content (unparsed attrlist) to target when format is short
         | 
| 586 | 
            -
                      target ||= extconf[:format] == :short ? content : target
         | 
| 587 | 
            -
                      replacement = extension.process_method[self, target, attributes]
         | 
| 588 | 
            -
                      Inline === replacement ? replacement.convert : replacement
         | 
| 589 | 
            -
                    end
         | 
| 590 | 
            -
                  end
         | 
| 591 | 
            -
                end
         | 
| 592 | 
            -
             | 
| 593 605 | 
             
                if found_macroish && ((text.include? 'image:') || (text.include? 'icon:'))
         | 
| 594 606 | 
             
                  # image:filename.png[Alt Text]
         | 
| 595 607 | 
             
                  text = text.gsub InlineImageMacroRx do
         | 
| @@ -625,7 +637,7 @@ module Substitutors | |
| 625 637 |  | 
| 626 638 | 
             
                      # indexterm:[Tigers,Big cats]
         | 
| 627 639 | 
             
                      terms = split_simple_csv normalize_string $2, true
         | 
| 628 | 
            -
                      doc.register :indexterms, terms
         | 
| 640 | 
            +
                      #doc.register :indexterms, terms
         | 
| 629 641 | 
             
                      (Inline.new self, :indexterm, nil, attributes: { 'terms' => terms }).convert
         | 
| 630 642 | 
             
                    when 'indexterm2'
         | 
| 631 643 | 
             
                      # honor the escape
         | 
| @@ -633,7 +645,7 @@ module Substitutors | |
| 633 645 |  | 
| 634 646 | 
             
                      # indexterm2:[Tigers]
         | 
| 635 647 | 
             
                      term = normalize_string $2, true
         | 
| 636 | 
            -
                      doc.register :indexterms, [term]
         | 
| 648 | 
            +
                      #doc.register :indexterms, [term]
         | 
| 637 649 | 
             
                      (Inline.new self, :indexterm, term, type: :visible).convert
         | 
| 638 650 | 
             
                    else
         | 
| 639 651 | 
             
                      text = $3
         | 
| @@ -661,12 +673,12 @@ module Substitutors | |
| 661 673 | 
             
                      if visible
         | 
| 662 674 | 
             
                        # ((Tigers))
         | 
| 663 675 | 
             
                        term = normalize_string text
         | 
| 664 | 
            -
                        doc.register :indexterms, [term]
         | 
| 676 | 
            +
                        #doc.register :indexterms, [term]
         | 
| 665 677 | 
             
                        subbed_term = (Inline.new self, :indexterm, term, type: :visible).convert
         | 
| 666 678 | 
             
                      else
         | 
| 667 679 | 
             
                        # (((Tigers,Big cats)))
         | 
| 668 680 | 
             
                        terms = split_simple_csv(normalize_string text)
         | 
| 669 | 
            -
                        doc.register :indexterms, terms
         | 
| 681 | 
            +
                        #doc.register :indexterms, terms
         | 
| 670 682 | 
             
                        subbed_term = (Inline.new self, :indexterm, nil, attributes: { 'terms' => terms }).convert
         | 
| 671 683 | 
             
                      end
         | 
| 672 684 | 
             
                      before ? %(#{before}#{subbed_term}#{after}) : subbed_term
         | 
| @@ -771,15 +783,14 @@ module Substitutors | |
| 771 783 | 
             
                  end
         | 
| 772 784 | 
             
                end
         | 
| 773 785 |  | 
| 774 | 
            -
                if found_macroish && ((text.include? 'link:') || (text.include? ' | 
| 786 | 
            +
                if found_macroish && ((text.include? 'link:') || (text.include? 'ilto:'))
         | 
| 775 787 | 
             
                  # inline link macros, link:target[text]
         | 
| 776 788 | 
             
                  text = text.gsub InlineLinkMacroRx do
         | 
| 777 789 | 
             
                    # honor the escape
         | 
| 778 790 | 
             
                    if $&.start_with? RS
         | 
| 779 791 | 
             
                      next $&.slice 1, $&.length
         | 
| 780 792 | 
             
                    elsif (mailto = $1)
         | 
| 781 | 
            -
                      target =  | 
| 782 | 
            -
                      mailto_text = $2
         | 
| 793 | 
            +
                      target = 'mailto:' + (mailto_text = $2)
         | 
| 783 794 | 
             
                    else
         | 
| 784 795 | 
             
                      target = $2
         | 
| 785 796 | 
             
                    end
         | 
| @@ -854,7 +865,7 @@ module Substitutors | |
| 854 865 | 
             
                    # honor the escape
         | 
| 855 866 | 
             
                    next $1 == RS ? ($&.slice 1, $&.length) : $& if $1
         | 
| 856 867 |  | 
| 857 | 
            -
                    target =  | 
| 868 | 
            +
                    target = 'mailto:' + $&
         | 
| 858 869 | 
             
                    # QUESTION should this be registered as an e-mail address?
         | 
| 859 870 | 
             
                    doc.register(:links, target)
         | 
| 860 871 |  | 
| @@ -862,65 +873,11 @@ module Substitutors | |
| 862 873 | 
             
                  end
         | 
| 863 874 | 
             
                end
         | 
| 864 875 |  | 
| 865 | 
            -
                if  | 
| 866 | 
            -
                  text = text.gsub InlineFootnoteMacroRx do
         | 
| 867 | 
            -
                    # honor the escape
         | 
| 868 | 
            -
                    next $&.slice 1, $&.length if $&.start_with? RS
         | 
| 869 | 
            -
             | 
| 870 | 
            -
                    # footnoteref
         | 
| 871 | 
            -
                    if $1
         | 
| 872 | 
            -
                      if $3
         | 
| 873 | 
            -
                        id, text = $3.split ',', 2
         | 
| 874 | 
            -
                        logger.warn %(found deprecated footnoteref macro: #{$&}; use footnote macro with target instead) unless doc.compat_mode
         | 
| 875 | 
            -
                      else
         | 
| 876 | 
            -
                        next $&
         | 
| 877 | 
            -
                      end
         | 
| 878 | 
            -
                    # footnote
         | 
| 879 | 
            -
                    else
         | 
| 880 | 
            -
                      id = $2
         | 
| 881 | 
            -
                      text = $3
         | 
| 882 | 
            -
                    end
         | 
| 883 | 
            -
             | 
| 884 | 
            -
                    if id
         | 
| 885 | 
            -
                      if text
         | 
| 886 | 
            -
                        # REVIEW it's a dirty job, but somebody's gotta do it
         | 
| 887 | 
            -
                        text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)))
         | 
| 888 | 
            -
                        index = doc.counter('footnote-number')
         | 
| 889 | 
            -
                        doc.register(:footnotes, Document::Footnote.new(index, id, text))
         | 
| 890 | 
            -
                        type, target = :ref, nil
         | 
| 891 | 
            -
                      else
         | 
| 892 | 
            -
                        if (footnote = doc.footnotes.find {|candidate| candidate.id == id })
         | 
| 893 | 
            -
                          index, text = footnote.index, footnote.text
         | 
| 894 | 
            -
                        else
         | 
| 895 | 
            -
                          logger.warn %(invalid footnote reference: #{id})
         | 
| 896 | 
            -
                          index, text = nil, id
         | 
| 897 | 
            -
                        end
         | 
| 898 | 
            -
                        type, target, id = :xref, id, nil
         | 
| 899 | 
            -
                      end
         | 
| 900 | 
            -
                    elsif text
         | 
| 901 | 
            -
                      # REVIEW it's a dirty job, but somebody's gotta do it
         | 
| 902 | 
            -
                      text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)))
         | 
| 903 | 
            -
                      index = doc.counter('footnote-number')
         | 
| 904 | 
            -
                      doc.register(:footnotes, Document::Footnote.new(index, id, text))
         | 
| 905 | 
            -
                      type = target = nil
         | 
| 906 | 
            -
                    else
         | 
| 907 | 
            -
                      next $&
         | 
| 908 | 
            -
                    end
         | 
| 909 | 
            -
                    Inline.new(self, :footnote, text, attributes: { 'index' => index }, id: id, target: target, type: type).convert
         | 
| 910 | 
            -
                  end
         | 
| 911 | 
            -
                end
         | 
| 912 | 
            -
             | 
| 913 | 
            -
                sub_inline_xrefs(sub_inline_anchors(text, found), found)
         | 
| 914 | 
            -
              end
         | 
| 915 | 
            -
             | 
| 916 | 
            -
              # Internal: Substitute normal and bibliographic anchors
         | 
| 917 | 
            -
              def sub_inline_anchors(text, found = nil)
         | 
| 918 | 
            -
                if @context == :list_item && @parent.style == 'bibliography'
         | 
| 876 | 
            +
                if found_square_bracket && @context == :list_item && @parent.style == 'bibliography'
         | 
| 919 877 | 
             
                  text = text.sub(InlineBiblioAnchorRx) { (Inline.new self, :anchor, $2, type: :bibref, id: $1).convert }
         | 
| 920 878 | 
             
                end
         | 
| 921 879 |  | 
| 922 | 
            -
                if ( | 
| 923 | 
            -
                    ((!found || found[:macroish]) && text.include?('or:'))
         | 
| 880 | 
            +
                if (found_square_bracket && text.include?('[[')) || (found_macroish && text.include?('or:'))
         | 
| 924 881 | 
             
                  text = text.gsub InlineAnchorRx do
         | 
| 925 882 | 
             
                    # honor the escape
         | 
| 926 883 | 
             
                    next $&.slice 1, $&.length if $1
         | 
| @@ -938,17 +895,13 @@ module Substitutors | |
| 938 895 | 
             
                  end
         | 
| 939 896 | 
             
                end
         | 
| 940 897 |  | 
| 941 | 
            -
                text
         | 
| 942 | 
            -
             | 
| 943 | 
            -
             | 
| 944 | 
            -
              # Internal: Substitute cross reference links
         | 
| 945 | 
            -
              def sub_inline_xrefs(content, found = nil)
         | 
| 946 | 
            -
                if ((found ? found[:macroish] : (content.include? '[')) && (content.include? 'xref:')) || ((content.include? '&') && (content.include? 'lt;&'))
         | 
| 947 | 
            -
                  content = content.gsub InlineXrefMacroRx do
         | 
| 898 | 
            +
                #if (text.include? ';&l') || (found_macroish && (text.include? 'xref:'))
         | 
| 899 | 
            +
                if ((text.include? '&') && (text.include? ';&l')) || (found_macroish && (text.include? 'xref:'))
         | 
| 900 | 
            +
                  text = text.gsub InlineXrefMacroRx do
         | 
| 948 901 | 
             
                    # honor the escape
         | 
| 949 902 | 
             
                    next $&.slice 1, $&.length if $&.start_with? RS
         | 
| 950 903 |  | 
| 951 | 
            -
                    attrs | 
| 904 | 
            +
                    attrs = {}
         | 
| 952 905 | 
             
                    if (refid = $1)
         | 
| 953 906 | 
             
                      refid, text = refid.split ',', 2
         | 
| 954 907 | 
             
                      text = text.lstrip if text
         | 
| @@ -994,14 +947,14 @@ module Substitutors | |
| 994 947 | 
             
                    # handles: #id
         | 
| 995 948 | 
             
                    if target
         | 
| 996 949 | 
             
                      refid = fragment
         | 
| 997 | 
            -
                      logger. | 
| 950 | 
            +
                      logger.info %(possible invalid reference: #{refid}) if logger.info? && !doc.catalog[:refs][refid]
         | 
| 998 951 | 
             
                    elsif path
         | 
| 999 952 | 
             
                      # handles: path#, path#id, path.adoc#, path.adoc#id, or path.adoc (xref macro only)
         | 
| 1000 953 | 
             
                      # the referenced path is the current document, or its contents have been included in the current document
         | 
| 1001 954 | 
             
                      if src2src && (doc.attributes['docname'] == path || doc.catalog[:includes][path])
         | 
| 1002 955 | 
             
                        if fragment
         | 
| 1003 956 | 
             
                          refid, path, target = fragment, nil, %(##{fragment})
         | 
| 1004 | 
            -
                          logger. | 
| 957 | 
            +
                          logger.info %(possible invalid reference: #{refid}) if logger.info? && !doc.catalog[:refs][refid]
         | 
| 1005 958 | 
             
                        else
         | 
| 1006 959 | 
             
                          refid, path, target = nil, nil, '#'
         | 
| 1007 960 | 
             
                        end
         | 
| @@ -1016,7 +969,7 @@ module Substitutors | |
| 1016 969 | 
             
                    # handles: id (in compat mode or when natural xrefs are disabled)
         | 
| 1017 970 | 
             
                    elsif doc.compat_mode || !Compliance.natural_xrefs
         | 
| 1018 971 | 
             
                      refid, target = fragment, %(##{fragment})
         | 
| 1019 | 
            -
                      logger. | 
| 972 | 
            +
                      logger.info %(possible invalid reference: #{refid}) if logger.info? && doc.catalog[:refs][refid]
         | 
| 1020 973 | 
             
                    # handles: id
         | 
| 1021 974 | 
             
                    elsif doc.catalog[:refs][fragment]
         | 
| 1022 975 | 
             
                      refid, target = fragment, %(##{fragment})
         | 
| @@ -1026,14 +979,62 @@ module Substitutors | |
| 1026 979 | 
             
                      fragment, target = refid, %(##{refid})
         | 
| 1027 980 | 
             
                    else
         | 
| 1028 981 | 
             
                      refid, target = fragment, %(##{fragment})
         | 
| 1029 | 
            -
                      logger. | 
| 982 | 
            +
                      logger.info %(possible invalid reference: #{refid}) if logger.info?
         | 
| 1030 983 | 
             
                    end
         | 
| 1031 | 
            -
                    attrs['path'] | 
| 984 | 
            +
                    attrs['path'] = path
         | 
| 985 | 
            +
                    attrs['fragment'] = fragment
         | 
| 986 | 
            +
                    attrs['refid'] = refid
         | 
| 1032 987 | 
             
                    Inline.new(self, :anchor, text, type: :xref, target: target, attributes: attrs).convert
         | 
| 1033 988 | 
             
                  end
         | 
| 1034 989 | 
             
                end
         | 
| 1035 990 |  | 
| 1036 | 
            -
                 | 
| 991 | 
            +
                if found_macroish && (text.include? 'tnote')
         | 
| 992 | 
            +
                  text = text.gsub InlineFootnoteMacroRx do
         | 
| 993 | 
            +
                    # honor the escape
         | 
| 994 | 
            +
                    next $&.slice 1, $&.length if $&.start_with? RS
         | 
| 995 | 
            +
             | 
| 996 | 
            +
                    # footnoteref
         | 
| 997 | 
            +
                    if $1
         | 
| 998 | 
            +
                      if $3
         | 
| 999 | 
            +
                        id, text = $3.split ',', 2
         | 
| 1000 | 
            +
                        logger.warn %(found deprecated footnoteref macro: #{$&}; use footnote macro with target instead) unless doc.compat_mode
         | 
| 1001 | 
            +
                      else
         | 
| 1002 | 
            +
                        next $&
         | 
| 1003 | 
            +
                      end
         | 
| 1004 | 
            +
                    # footnote
         | 
| 1005 | 
            +
                    else
         | 
| 1006 | 
            +
                      id = $2
         | 
| 1007 | 
            +
                      text = $3
         | 
| 1008 | 
            +
                    end
         | 
| 1009 | 
            +
             | 
| 1010 | 
            +
                    if id
         | 
| 1011 | 
            +
                      if text
         | 
| 1012 | 
            +
                        text = restore_passthroughs(normalize_string text, true)
         | 
| 1013 | 
            +
                        index = doc.counter('footnote-number')
         | 
| 1014 | 
            +
                        doc.register(:footnotes, Document::Footnote.new(index, id, text))
         | 
| 1015 | 
            +
                        type, target = :ref, nil
         | 
| 1016 | 
            +
                      else
         | 
| 1017 | 
            +
                        if (footnote = doc.footnotes.find {|candidate| candidate.id == id })
         | 
| 1018 | 
            +
                          index, text = footnote.index, footnote.text
         | 
| 1019 | 
            +
                        else
         | 
| 1020 | 
            +
                          logger.warn %(invalid footnote reference: #{id})
         | 
| 1021 | 
            +
                          index, text = nil, id
         | 
| 1022 | 
            +
                        end
         | 
| 1023 | 
            +
                        type, target, id = :xref, id, nil
         | 
| 1024 | 
            +
                      end
         | 
| 1025 | 
            +
                    elsif text
         | 
| 1026 | 
            +
                      text = restore_passthroughs(normalize_string text, true)
         | 
| 1027 | 
            +
                      index = doc.counter('footnote-number')
         | 
| 1028 | 
            +
                      doc.register(:footnotes, Document::Footnote.new(index, id, text))
         | 
| 1029 | 
            +
                      type = target = nil
         | 
| 1030 | 
            +
                    else
         | 
| 1031 | 
            +
                      next $&
         | 
| 1032 | 
            +
                    end
         | 
| 1033 | 
            +
                    Inline.new(self, :footnote, text, attributes: { 'index' => index }, id: id, target: target, type: type).convert
         | 
| 1034 | 
            +
                  end
         | 
| 1035 | 
            +
                end
         | 
| 1036 | 
            +
             | 
| 1037 | 
            +
                text
         | 
| 1037 1038 | 
             
              end
         | 
| 1038 1039 |  | 
| 1039 1040 | 
             
              # Public: Substitute callout source references
         | 
| @@ -1473,7 +1474,7 @@ module Substitutors | |
| 1473 1474 | 
             
              # Internal: Inserts text into a formatted text enclosure; used by xreftext
         | 
| 1474 1475 | 
             
              alias sub_placeholder sprintf unless RUBY_ENGINE == 'opal'
         | 
| 1475 1476 |  | 
| 1476 | 
            -
              # Internal:  | 
| 1477 | 
            +
              # Internal: Commit the requested substitutions to this block.
         | 
| 1477 1478 | 
             
              #
         | 
| 1478 1479 | 
             
              # Looks for an attribute named "subs". If present, resolves substitutions
         | 
| 1479 1480 | 
             
              # from the value of that attribute and assigns them to the subs property on
         | 
| @@ -1482,7 +1483,7 @@ module Substitutors | |
| 1482 1483 | 
             
              # the content model of the block.
         | 
| 1483 1484 | 
             
              #
         | 
| 1484 1485 | 
             
              # Returns The Array of resolved substitutions now assigned to this block
         | 
| 1485 | 
            -
              def  | 
| 1486 | 
            +
              def commit_subs
         | 
| 1486 1487 | 
             
                unless (default_subs = @default_subs)
         | 
| 1487 1488 | 
             
                  case @content_model
         | 
| 1488 1489 | 
             
                  when :simple
         |