asciidoctor 1.5.6.2 → 1.5.7
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.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +330 -143
- data/README-fr.adoc +441 -0
- data/README-jp.adoc +418 -0
- data/README-zh_CN.adoc +430 -0
- data/README.adoc +454 -0
- data/Rakefile +57 -0
- data/asciidoctor.gemspec +7 -1
- data/data/locale/attributes-ar.adoc +22 -0
- data/data/locale/attributes-bg.adoc +22 -0
- data/data/locale/attributes-ca.adoc +22 -0
- data/data/locale/attributes-cs.adoc +22 -0
- data/data/locale/attributes-da.adoc +22 -0
- data/data/locale/attributes-de.adoc +22 -0
- data/data/locale/attributes-en.adoc +23 -0
- data/data/locale/attributes-es.adoc +22 -0
- data/data/locale/attributes-fa.adoc +22 -0
- data/data/locale/attributes-fi.adoc +22 -0
- data/data/locale/attributes-fr.adoc +22 -0
- data/data/locale/attributes-hu.adoc +22 -0
- data/data/locale/attributes-id.adoc +22 -0
- data/data/locale/attributes-it.adoc +22 -0
- data/data/locale/attributes-ja.adoc +22 -0
- data/data/locale/attributes-kr.adoc +22 -0
- data/data/locale/attributes-nb.adoc +22 -0
- data/data/locale/attributes-nl.adoc +22 -0
- data/data/locale/attributes-nn.adoc +22 -0
- data/data/locale/attributes-pl.adoc +22 -0
- data/data/locale/attributes-pt.adoc +22 -0
- data/data/locale/attributes-pt_BR.adoc +22 -0
- data/data/locale/attributes-ro.adoc +22 -0
- data/data/locale/attributes-ru.adoc +22 -0
- data/data/locale/attributes-sr.adoc +22 -0
- data/data/locale/attributes-sr_Latn.adoc +22 -0
- data/data/locale/attributes-tr.adoc +22 -0
- data/data/locale/attributes-uk.adoc +22 -0
- data/data/locale/attributes-zh_CN.adoc +22 -0
- data/data/locale/attributes-zh_TW.adoc +22 -0
- data/data/locale/attributes.adoc +8 -649
- data/data/stylesheets/asciidoctor-default.css +77 -72
- data/features/xref.feature +366 -7
- data/lib/asciidoctor.rb +107 -93
- data/lib/asciidoctor/abstract_block.rb +247 -239
- data/lib/asciidoctor/abstract_node.rb +56 -58
- data/lib/asciidoctor/block.rb +3 -3
- data/lib/asciidoctor/callouts.rb +1 -1
- data/lib/asciidoctor/cli/invoker.rb +36 -9
- data/lib/asciidoctor/cli/options.rb +63 -25
- data/lib/asciidoctor/converter.rb +23 -13
- data/lib/asciidoctor/converter/base.rb +4 -0
- data/lib/asciidoctor/converter/docbook45.rb +16 -9
- data/lib/asciidoctor/converter/docbook5.rb +115 -97
- data/lib/asciidoctor/converter/factory.rb +29 -31
- data/lib/asciidoctor/converter/html5.rb +229 -192
- data/lib/asciidoctor/converter/manpage.rb +72 -50
- data/lib/asciidoctor/converter/template.rb +12 -12
- data/lib/asciidoctor/core_ext.rb +5 -1
- data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +6 -0
- data/lib/asciidoctor/document.rb +168 -77
- data/lib/asciidoctor/extensions.rb +79 -47
- data/lib/asciidoctor/helpers.rb +33 -11
- data/lib/asciidoctor/inline.rb +3 -2
- data/lib/asciidoctor/list.rb +2 -1
- data/lib/asciidoctor/logging.rb +122 -0
- data/lib/asciidoctor/parser.rb +406 -382
- data/lib/asciidoctor/path_resolver.rb +169 -162
- data/lib/asciidoctor/reader.rb +166 -121
- data/lib/asciidoctor/section.rb +45 -28
- data/lib/asciidoctor/stylesheets.rb +13 -5
- data/lib/asciidoctor/substitutors.rb +328 -254
- data/lib/asciidoctor/table.rb +105 -48
- data/lib/asciidoctor/timings.rb +34 -6
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +41 -23
- data/man/asciidoctor.adoc +14 -8
- data/test/api_test.rb +1004 -0
- data/test/attributes_test.rb +241 -50
- data/test/blocks_test.rb +549 -124
- data/test/converter_test.rb +170 -78
- data/test/document_test.rb +208 -767
- data/test/extensions_test.rb +188 -53
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +1 -1
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +1 -1
- data/test/fixtures/file-with-missing-include.adoc +1 -0
- data/test/fixtures/include-file.jsx +8 -0
- data/test/fixtures/lists.adoc +96 -0
- data/test/fixtures/other-chapters.adoc +11 -0
- data/test/fixtures/outer-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +5 -1
- data/test/fixtures/subdir/index.adoc +3 -0
- data/test/fixtures/subdir/inner-include.adoc +3 -0
- data/test/fixtures/subdir/middle-include.adoc +5 -0
- data/test/fixtures/tagged-class-enclosed.rb +0 -1
- data/test/fixtures/unclosed-tag.adoc +3 -0
- data/test/fixtures/unexpected-end-tag.adoc +4 -0
- data/test/invoker_test.rb +101 -40
- data/test/links_test.rb +266 -72
- data/test/lists_test.rb +243 -45
- data/test/logger_test.rb +211 -0
- data/test/manpage_test.rb +124 -6
- data/test/options_test.rb +46 -1
- data/test/paragraphs_test.rb +23 -10
- data/test/parser_test.rb +30 -1
- data/test/paths_test.rb +115 -33
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +337 -81
- data/test/sections_test.rb +656 -72
- data/test/substitutions_test.rb +182 -57
- data/test/tables_test.rb +324 -57
- data/test/test_helper.rb +77 -32
- data/test/text_test.rb +7 -7
- metadata +67 -3
    
        data/lib/asciidoctor.rb
    CHANGED
    
    | @@ -13,6 +13,7 @@ if RUBY_ENGINE == 'opal' | |
| 13 13 | 
             
              require 'asciidoctor/js'
         | 
| 14 14 | 
             
            else
         | 
| 15 15 | 
             
              autoload :Base64, 'base64'
         | 
| 16 | 
            +
              autoload :URI, 'uri'
         | 
| 16 17 | 
             
              autoload :OpenURI, 'open-uri'
         | 
| 17 18 | 
             
              autoload :StringScanner, 'strscan'
         | 
| 18 19 | 
             
            end
         | 
| @@ -20,6 +21,8 @@ end | |
| 20 21 | 
             
            # ideally we should use require_relative instead of modifying the LOAD_PATH
         | 
| 21 22 | 
             
            $:.unshift File.dirname __FILE__
         | 
| 22 23 |  | 
| 24 | 
            +
            require 'asciidoctor/logging'
         | 
| 25 | 
            +
             | 
| 23 26 | 
             
            # Public: Methods for parsing AsciiDoc input files and converting documents
         | 
| 24 27 | 
             
            # using eRuby templates.
         | 
| 25 28 | 
             
            #
         | 
| @@ -170,6 +173,11 @@ module Asciidoctor | |
| 170 173 | 
             
                # Compliance value: false
         | 
| 171 174 | 
             
                define :shorthand_property_syntax, true
         | 
| 172 175 |  | 
| 176 | 
            +
                # Asciidoctor will attempt to resolve the target of a cross reference by
         | 
| 177 | 
            +
                # matching its reference text (reftext or title) (e.g., <<Section Title>>)
         | 
| 178 | 
            +
                # Compliance value: false
         | 
| 179 | 
            +
                define :natural_xrefs, true
         | 
| 180 | 
            +
             | 
| 173 181 | 
             
                # Asciidoctor will start counting at the following number
         | 
| 174 182 | 
             
                # when creating a unique id when there is a conflict
         | 
| 175 183 | 
             
                # Compliance value: 2
         | 
| @@ -225,6 +233,9 @@ module Asciidoctor | |
| 225 233 | 
             
              # String for matching tab character
         | 
| 226 234 | 
             
              TAB = "\t"
         | 
| 227 235 |  | 
| 236 | 
            +
              # Maximum integer value for "boundless" operations; equal to MAX_SAFE_INTEGER in JavaScript
         | 
| 237 | 
            +
              MAX_INT = 9007199254740991
         | 
| 238 | 
            +
             | 
| 228 239 | 
             
              # The default document type
         | 
| 229 240 | 
             
              # Can influence markup generated by the converters
         | 
| 230 241 | 
             
              DEFAULT_DOCTYPE = 'article'
         | 
| @@ -259,10 +270,10 @@ module Asciidoctor | |
| 259 270 |  | 
| 260 271 | 
             
              # Set of file extensions recognized as AsciiDoc documents (stored as a truth hash)
         | 
| 261 272 | 
             
              ASCIIDOC_EXTENSIONS = {
         | 
| 262 | 
            -
                '.asciidoc' => true,
         | 
| 263 273 | 
             
                '.adoc' => true,
         | 
| 264 | 
            -
                '. | 
| 274 | 
            +
                '.asciidoc' => true,
         | 
| 265 275 | 
             
                '.asc' => true,
         | 
| 276 | 
            +
                '.ad' => true,
         | 
| 266 277 | 
             
                # TODO .txt should be deprecated
         | 
| 267 278 | 
             
                '.txt' => true
         | 
| 268 279 | 
             
              }
         | 
| @@ -281,7 +292,7 @@ module Asciidoctor | |
| 281 292 |  | 
| 282 293 | 
             
              CALLOUT_LIST_HEADS = ['<', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'].to_set
         | 
| 283 294 |  | 
| 284 | 
            -
              PARAGRAPH_STYLES = ['comment', 'example', 'literal', 'listing', 'normal', 'pass', 'quote', 'sidebar', 'source', 'verse', 'abstract', 'partintro'].to_set
         | 
| 295 | 
            +
              PARAGRAPH_STYLES = ['comment', 'example', 'literal', 'listing', 'normal', 'open', 'pass', 'quote', 'sidebar', 'source', 'verse', 'abstract', 'partintro'].to_set
         | 
| 285 296 |  | 
| 286 297 | 
             
              VERBATIM_STYLES = ['literal', 'listing', 'source', 'verse'].to_set
         | 
| 287 298 |  | 
| @@ -345,6 +356,8 @@ module Asciidoctor | |
| 345 356 |  | 
| 346 357 | 
             
              LINE_CONTINUATION_LEGACY = ' +'
         | 
| 347 358 |  | 
| 359 | 
            +
              MATHJAX_VERSION = '2.7.4'
         | 
| 360 | 
            +
             | 
| 348 361 | 
             
              BLOCK_MATH_DELIMITERS = {
         | 
| 349 362 | 
             
                :asciimath => ['\$', '\$'],
         | 
| 350 363 | 
             
                :latexmath => ['\[', '\]'],
         | 
| @@ -355,21 +368,18 @@ module Asciidoctor | |
| 355 368 | 
             
                :latexmath => ['\(', '\)'],
         | 
| 356 369 | 
             
              }
         | 
| 357 370 |  | 
| 371 | 
            +
              (STEM_TYPE_ALIASES = {
         | 
| 372 | 
            +
                'latexmath' => 'latexmath',
         | 
| 373 | 
            +
                'latex' => 'latexmath',
         | 
| 374 | 
            +
                'tex' => 'latexmath'
         | 
| 375 | 
            +
              }).default = 'asciimath'
         | 
| 376 | 
            +
             | 
| 377 | 
            +
              FONT_AWESOME_VERSION = '4.7.0'
         | 
| 378 | 
            +
             | 
| 358 379 | 
             
              # attributes which be changed within the content of the document (but not
         | 
| 359 380 | 
             
              # header) because it has semantic meaning; ex. sectnums
         | 
| 360 381 | 
             
              FLEXIBLE_ATTRIBUTES = ['sectnums']
         | 
| 361 382 |  | 
| 362 | 
            -
              # map of file extension to comment affixes for languages that only support circumfix comments
         | 
| 363 | 
            -
              CIRCUMFIX_COMMENTS = {
         | 
| 364 | 
            -
                ['/*', '*/'] => ['.css'],
         | 
| 365 | 
            -
                ['(*', '*)'] => ['.ml', '.mli', '.nb'],
         | 
| 366 | 
            -
                ['<!--', '-->'] => ['.html', '.xhtml', '.xml', '.xsl'],
         | 
| 367 | 
            -
                ['<%--', '--%>'] => ['.asp', '.jsp']
         | 
| 368 | 
            -
              }.inject({}) {|accum, (affixes, exts)|
         | 
| 369 | 
            -
                exts.each {|ext| accum[ext] = { :prefix => affixes[0], :suffix => affixes[-1] } }
         | 
| 370 | 
            -
                accum
         | 
| 371 | 
            -
              }
         | 
| 372 | 
            -
             | 
| 373 383 | 
             
              # A collection of regular expressions used by the parser.
         | 
| 374 384 | 
             
              #
         | 
| 375 385 | 
             
              # NOTE: The following pattern, which appears frequently, captures the
         | 
| @@ -485,7 +495,7 @@ module Asciidoctor | |
| 485 495 | 
             
                #   include::chapter1.ad[]
         | 
| 486 496 | 
             
                #   include::example.txt[lines=1;2;5..10]
         | 
| 487 497 | 
             
                #
         | 
| 488 | 
            -
                IncludeDirectiveRx = /^(\\)?include::([^\[][^\[]*)\[( | 
| 498 | 
            +
                IncludeDirectiveRx = /^(\\)?include::([^\[][^\[]*)\[(.+)?\]$/
         | 
| 489 499 |  | 
| 490 500 | 
             
                # Matches a trailing tag directive in an include file.
         | 
| 491 501 | 
             
                #
         | 
| @@ -498,7 +508,7 @@ module Asciidoctor | |
| 498 508 | 
             
                #     log(e);
         | 
| 499 509 | 
             
                #   }
         | 
| 500 510 | 
             
                #   // end::try-catch[]
         | 
| 501 | 
            -
                TagDirectiveRx = /\b(?:tag|( | 
| 511 | 
            +
                TagDirectiveRx = /\b(?:tag|(e)nd)::(\S+?)\[\][\n \r]/
         | 
| 502 512 |  | 
| 503 513 | 
             
                ## Attribute entries and references
         | 
| 504 514 |  | 
| @@ -515,10 +525,10 @@ module Asciidoctor | |
| 515 525 | 
             
                #                collapsing the line breaks and indentation to \
         | 
| 516 526 | 
             
                #                a single space.
         | 
| 517 527 | 
             
                #
         | 
| 518 | 
            -
                AttributeEntryRx = /^:( | 
| 528 | 
            +
                AttributeEntryRx = /^:(!?#{CG_WORD}[^:]*):(?:[ \t]+(.*))?$/
         | 
| 519 529 |  | 
| 520 530 | 
             
                # Matches invalid characters in an attribute name.
         | 
| 521 | 
            -
                InvalidAttributeNameCharsRx = /[ | 
| 531 | 
            +
                InvalidAttributeNameCharsRx = /[^#{CC_WORD}-]/
         | 
| 522 532 |  | 
| 523 533 | 
             
                # Matches a pass inline macro that surrounds the value of an attribute
         | 
| 524 534 | 
             
                # entry once it has been parsed.
         | 
| @@ -544,7 +554,7 @@ module Asciidoctor | |
| 544 554 | 
             
                #   {set:foo:bar}
         | 
| 545 555 | 
             
                #   {set:name!}
         | 
| 546 556 | 
             
                #
         | 
| 547 | 
            -
                AttributeReferenceRx = /(\\)?\{( | 
| 557 | 
            +
                AttributeReferenceRx = /(\\)?\{(#{CG_WORD}+[-#{CC_WORD}]*|(set|counter2?):.+?)(\\)?\}/
         | 
| 548 558 |  | 
| 549 559 | 
             
                ## Paragraphs and delimited blocks
         | 
| 550 560 |  | 
| @@ -583,7 +593,7 @@ module Asciidoctor | |
| 583 593 | 
             
                #
         | 
| 584 594 | 
             
                #   .Title goes here
         | 
| 585 595 | 
             
                #
         | 
| 586 | 
            -
                BlockTitleRx = /^\.([^ \t.].*)$/
         | 
| 596 | 
            +
                BlockTitleRx = /^\.(\.?[^ \t.].*)$/
         | 
| 587 597 |  | 
| 588 598 | 
             
                # Matches an admonition label at the start of a paragraph.
         | 
| 589 599 | 
             
                #
         | 
| @@ -650,26 +660,17 @@ module Asciidoctor | |
| 650 660 | 
             
                #
         | 
| 651 661 | 
             
                InlineSectionAnchorRx = / (\\)?\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+))?\]\]$/
         | 
| 652 662 |  | 
| 653 | 
            -
                # Matches invalid characters in a section  | 
| 663 | 
            +
                # Matches invalid ID characters in a section title.
         | 
| 654 664 | 
             
                #
         | 
| 655 | 
            -
                # NOTE uppercase chars  | 
| 656 | 
            -
                InvalidSectionIdCharsRx =  | 
| 657 | 
            -
             | 
| 658 | 
            -
                # Matches the block style used to designate a discrete (aka free-floating) heading.
         | 
| 659 | 
            -
                #
         | 
| 660 | 
            -
                # Examples
         | 
| 661 | 
            -
                #
         | 
| 662 | 
            -
                #   [discrete]
         | 
| 663 | 
            -
                #   = Discrete Heading
         | 
| 664 | 
            -
                #
         | 
| 665 | 
            -
                DiscreteHeadingStyleRx = /^(?:discrete|float)\b/
         | 
| 665 | 
            +
                # NOTE uppercase chars not included since expression is only run on a lowercase string
         | 
| 666 | 
            +
                InvalidSectionIdCharsRx = /<[^>]+>|&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^ #{CC_WORD}\-.]+?/
         | 
| 666 667 |  | 
| 667 668 | 
             
                ## Lists
         | 
| 668 669 |  | 
| 669 670 | 
             
                # Detects the start of any list item.
         | 
| 670 671 | 
             
                #
         | 
| 671 672 | 
             
                # NOTE we only have to check as far as the blank character because we know it means non-whitespace follows.
         | 
| 672 | 
            -
                AnyListRx = /^(?:[ \t]*(?:-|\*\*{0,4}|\.\.{0,4}|\u2022\u2022{0,4}|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|[ \t]*.*?( | 
| 673 | 
            +
                AnyListRx = /^(?:[ \t]*(?:-|\*\*{0,4}|\.\.{0,4}|\u2022\u2022{0,4}|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|[ \t]*.*?(?::::{0,2}|;;)(?:$|[ \t])|<?\d+>[ \t])/
         | 
| 673 674 |  | 
| 674 675 | 
             
                # Matches an unordered list item (one level for hyphens, up to 5 levels for asterisks).
         | 
| 675 676 | 
             
                #
         | 
| @@ -712,35 +713,34 @@ module Asciidoctor | |
| 712 713 | 
             
                # Examples
         | 
| 713 714 | 
             
                #
         | 
| 714 715 | 
             
                #   foo::
         | 
| 715 | 
            -
                #    | 
| 716 | 
            -
                #    | 
| 717 | 
            -
                #    | 
| 716 | 
            +
                #   bar:::
         | 
| 717 | 
            +
                #   baz::::
         | 
| 718 | 
            +
                #   blah;;
         | 
| 718 719 | 
             
                #
         | 
| 719 | 
            -
                #   # the term  | 
| 720 | 
            +
                #   # the term may be followed by a description on the same line...
         | 
| 720 721 | 
             
                #
         | 
| 721 | 
            -
                #   foo::  | 
| 722 | 
            +
                #   foo:: The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).
         | 
| 722 723 | 
             
                #
         | 
| 723 | 
            -
                #   # ...or on a separate line  | 
| 724 | 
            +
                #   # ...or on a separate line, which may optionally be indented
         | 
| 724 725 | 
             
                #
         | 
| 725 726 | 
             
                #   foo::
         | 
| 726 | 
            -
                #      | 
| 727 | 
            +
                #     The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).
         | 
| 727 728 | 
             
                #
         | 
| 728 | 
            -
                #   #  | 
| 729 | 
            +
                #   # attribute references may be used in both the term and the description
         | 
| 729 730 | 
             
                #
         | 
| 730 | 
            -
                #   { | 
| 731 | 
            +
                #   {foo-term}:: {foo-desc}
         | 
| 731 732 | 
             
                #
         | 
| 733 | 
            +
                # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
         | 
| 732 734 | 
             
                # NOTE negative match for comment line is intentional since that isn't handled when looking for next list item
         | 
| 733 735 | 
             
                # TODO check for line comment when scanning lines instead of in regex
         | 
| 734 | 
            -
                 | 
| 735 | 
            -
                DescriptionListRx = %r(^(?!//)[ \t]*(.*?)(:{2,4}|;;)(?:[ \t]+(.*))?$)
         | 
| 736 | 
            +
                DescriptionListRx = %r(^(?!//)[ \t]*(.*?)(:::{0,2}|;;)(?:$|[ \t]+(.*)$))
         | 
| 736 737 |  | 
| 737 738 | 
             
                # Matches a sibling description list item (which does not include the type in the key).
         | 
| 738 739 | 
             
                DescriptionListSiblingRx = {
         | 
| 739 | 
            -
                   | 
| 740 | 
            -
                  ' | 
| 741 | 
            -
                  ' | 
| 742 | 
            -
                  ' | 
| 743 | 
            -
                  ';;' => %r(^(?!//)[ \t]*(.*)(;;)(?:[ \t]+(.*))?$)
         | 
| 740 | 
            +
                  '::' => %r(^(?!//)[ \t]*(.*[^:]|)(::)(?:$|[ \t]+(.*)$)),
         | 
| 741 | 
            +
                  ':::' => %r(^(?!//)[ \t]*(.*[^:]|)(:::)(?:$|[ \t]+(.*)$)),
         | 
| 742 | 
            +
                  '::::' => %r(^(?!//)[ \t]*(.*[^:]|)(::::)(?:$|[ \t]+(.*)$)),
         | 
| 743 | 
            +
                  ';;' => %r(^(?!//)[ \t]*(.*?)(;;)(?:$|[ \t]+(.*)$))
         | 
| 744 744 | 
             
                }
         | 
| 745 745 |  | 
| 746 746 | 
             
                # Matches a callout list item.
         | 
| @@ -787,7 +787,7 @@ module Asciidoctor | |
| 787 787 | 
             
                #
         | 
| 788 788 | 
             
                #   1*h,2*,^3e
         | 
| 789 789 | 
             
                #
         | 
| 790 | 
            -
                ColumnSpecRx = /^(?:(\d+)\*)?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?(\d | 
| 790 | 
            +
                ColumnSpecRx = /^(?:(\d+)\*)?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?(\d+%?|~)?([a-z])?$/
         | 
| 791 791 |  | 
| 792 792 | 
             
                # Parses the start and end of a cell spec (i.e., cellspec) for a table.
         | 
| 793 793 | 
             
                #
         | 
| @@ -809,7 +809,7 @@ module Asciidoctor | |
| 809 809 | 
             
                #
         | 
| 810 810 | 
             
                #--
         | 
| 811 811 | 
             
                # NOTE we've relaxed the match for target to accomodate the short format (e.g., name::[attrlist])
         | 
| 812 | 
            -
                CustomBlockMacroRx = /^(#{CG_WORD}+)::(|\S|\S.*?\S)\[( | 
| 812 | 
            +
                CustomBlockMacroRx = /^(#{CG_WORD}+)::(|\S|\S.*?\S)\[(.+)?\]$/
         | 
| 813 813 |  | 
| 814 814 | 
             
                # Matches an image, video or audio block macro.
         | 
| 815 815 | 
             
                #
         | 
| @@ -818,7 +818,7 @@ module Asciidoctor | |
| 818 818 | 
             
                #   image::filename.png[Caption]
         | 
| 819 819 | 
             
                #   video::http://youtube.com/12345[Cats vs Dogs]
         | 
| 820 820 | 
             
                #
         | 
| 821 | 
            -
                BlockMediaMacroRx = /^(image|video|audio)::(\S|\S.*?\S)\[( | 
| 821 | 
            +
                BlockMediaMacroRx = /^(image|video|audio)::(\S|\S.*?\S)\[(.+)?\]$/
         | 
| 822 822 |  | 
| 823 823 | 
             
                # Matches the TOC block macro.
         | 
| 824 824 | 
             
                #
         | 
| @@ -827,7 +827,7 @@ module Asciidoctor | |
| 827 827 | 
             
                #   toc::[]
         | 
| 828 828 | 
             
                #   toc::[levels=2]
         | 
| 829 829 | 
             
                #
         | 
| 830 | 
            -
                BlockTocMacroRx = /^toc::\[( | 
| 830 | 
            +
                BlockTocMacroRx = /^toc::\[(.+)?\]$/
         | 
| 831 831 |  | 
| 832 832 | 
             
                ## Inline macros
         | 
| 833 833 |  | 
| @@ -845,6 +845,9 @@ module Asciidoctor | |
| 845 845 | 
             
                # Scans for a non-escaped anchor (i.e., id + optional reference text) in the flow of text.
         | 
| 846 846 | 
             
                InlineAnchorScanRx = /(?:^|[^\\\[])\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]|(?:^|[^\\])anchor:([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)\[(?:\]|(.*?[^\\])\])/
         | 
| 847 847 |  | 
| 848 | 
            +
                # Scans for a leading, non-escaped anchor (i.e., id + optional reference text).
         | 
| 849 | 
            +
                LeadingInlineAnchorRx = /^\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]/
         | 
| 850 | 
            +
             | 
| 848 851 | 
             
                # Matches a bibliography anchor at the start of the list item text (in a bibliography list).
         | 
| 849 852 | 
             
                #
         | 
| 850 853 | 
             
                # Examples
         | 
| @@ -857,16 +860,18 @@ module Asciidoctor | |
| 857 860 | 
             
                #
         | 
| 858 861 | 
             
                #   doc.writer@example.com
         | 
| 859 862 | 
             
                #
         | 
| 860 | 
            -
                 | 
| 863 | 
            +
                InlineEmailRx = %r(([\\>:/])?#{CG_WORD}[#{CC_WORD}.%+-]*@#{CG_ALNUM}[#{CC_ALNUM}.-]*\.#{CG_ALPHA}{2,4}\b)
         | 
| 861 864 |  | 
| 862 865 | 
             
                # Matches an inline footnote macro, which is allowed to span multiple lines.
         | 
| 863 866 | 
             
                #
         | 
| 864 867 | 
             
                # Examples
         | 
| 865 | 
            -
                #   footnote:[text]
         | 
| 866 | 
            -
                #    | 
| 867 | 
            -
                #    | 
| 868 | 
            +
                #   footnote:[text] (not referenceable)
         | 
| 869 | 
            +
                #   footnote:id[text] (referenceable)
         | 
| 870 | 
            +
                #   footnote:id[] (reference)
         | 
| 871 | 
            +
                #   footnoteref:[id,text] (legacy)
         | 
| 872 | 
            +
                #   footnoteref:[id] (legacy)
         | 
| 868 873 | 
             
                #
         | 
| 869 | 
            -
                InlineFootnoteMacroRx = /\\? | 
| 874 | 
            +
                InlineFootnoteMacroRx = /\\?footnote(?:(ref):|:([\w-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\]/m
         | 
| 870 875 |  | 
| 871 876 | 
             
                # Matches an image or icon inline macro.
         | 
| 872 877 | 
             
                #
         | 
| @@ -913,7 +918,7 @@ module Asciidoctor | |
| 913 918 | 
             
                #   link:https://github.com[]
         | 
| 914 919 | 
             
                #
         | 
| 915 920 | 
             
                # FIXME revisit! the main issue is we need different rules for implicit vs explicit
         | 
| 916 | 
            -
                 | 
| 921 | 
            +
                InlineLinkRx = %r((^|link:|#{CG_BLANK}|<|[>\(\)\[\];])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*[^\s.,\[\]<])(?:\[(|#{CC_ALL}*?[^\\])\])?)m
         | 
| 917 922 |  | 
| 918 923 | 
             
                # Match a link or e-mail inline macro.
         | 
| 919 924 | 
             
                #
         | 
| @@ -955,9 +960,9 @@ module Asciidoctor | |
| 955 960 | 
             
                #
         | 
| 956 961 | 
             
                #   "File > New..."
         | 
| 957 962 | 
             
                #
         | 
| 958 | 
            -
                 | 
| 963 | 
            +
                InlineMenuRx = /\\?"([#{CC_WORD}&][^"]*?[ \n]+>[ \n]+[^"]*)"/
         | 
| 959 964 |  | 
| 960 | 
            -
                # Matches an inline passthrough | 
| 965 | 
            +
                # Matches an inline passthrough, which may span multiple lines.
         | 
| 961 966 | 
             
                #
         | 
| 962 967 | 
             
                # Examples
         | 
| 963 968 | 
             
                #
         | 
| @@ -965,11 +970,20 @@ module Asciidoctor | |
| 965 970 | 
             
                #   `text` (compat)
         | 
| 966 971 | 
             
                #
         | 
| 967 972 | 
             
                # NOTE we always capture the attributes so we know when to use compatible (i.e., legacy) behavior
         | 
| 968 | 
            -
                 | 
| 973 | 
            +
                InlinePassRx = {
         | 
| 969 974 | 
             
                  false => ['+', '`', /(^|[^#{CC_WORD};:])(?:\[([^\]]+)\])?(\\?(\+|`)(\S|\S#{CC_ALL}*?\S)\4)(?!#{CG_WORD})/m],
         | 
| 970 975 | 
             
                  true  => ['`', nil, /(^|[^`#{CC_WORD}])(?:\[([^\]]+)\])?(\\?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\4)(?![`#{CC_WORD}])/m]
         | 
| 971 976 | 
             
                }
         | 
| 972 977 |  | 
| 978 | 
            +
                # Matches an inline plus passthrough spanning multiple lines, but only when it occurs directly
         | 
| 979 | 
            +
                # inside constrained monospaced formatting in non-compat mode.
         | 
| 980 | 
            +
                #
         | 
| 981 | 
            +
                # Examples
         | 
| 982 | 
            +
                #
         | 
| 983 | 
            +
                #   +text+
         | 
| 984 | 
            +
                #
         | 
| 985 | 
            +
                SinglePlusInlinePassRx = /^(\\)?\+(\S|\S#{CC_ALL}*?\S)\+$/m
         | 
| 986 | 
            +
             | 
| 973 987 | 
             
                # Matches several variants of the passthrough inline macro, which may span multiple lines.
         | 
| 974 988 | 
             
                #
         | 
| 975 989 | 
             
                # Examples
         | 
| @@ -1257,8 +1271,9 @@ module Asciidoctor | |
| 1257 1271 | 
             
              # Public: Parse the AsciiDoc source input into a {Document}
         | 
| 1258 1272 | 
             
              #
         | 
| 1259 1273 | 
             
              # Accepts input as an IO (or StringIO), String or String Array object. If the
         | 
| 1260 | 
            -
              # input is a File,  | 
| 1261 | 
            -
              #  | 
| 1274 | 
            +
              # input is a File, the object is expected to be opened for reading and is not
         | 
| 1275 | 
            +
              # closed afterwards by this method. Information about the file (filename,
         | 
| 1276 | 
            +
              # directory name, etc) gets assigned to attributes on the Document object.
         | 
| 1262 1277 | 
             
              #
         | 
| 1263 1278 | 
             
              # input   - the AsciiDoc source as a IO, String or Array.
         | 
| 1264 1279 | 
             
              # options - a String, Array or Hash of options to control processing (default: {})
         | 
| @@ -1268,10 +1283,15 @@ module Asciidoctor | |
| 1268 1283 | 
             
              # Returns the Document
         | 
| 1269 1284 | 
             
              def load input, options = {}
         | 
| 1270 1285 | 
             
                options = options.dup
         | 
| 1286 | 
            +
             | 
| 1271 1287 | 
             
                if (timings = options[:timings])
         | 
| 1272 1288 | 
             
                  timings.start :read
         | 
| 1273 1289 | 
             
                end
         | 
| 1274 1290 |  | 
| 1291 | 
            +
                if (logger = options[:logger]) && logger != LoggerManager.logger
         | 
| 1292 | 
            +
                  LoggerManager.logger = logger
         | 
| 1293 | 
            +
                end
         | 
| 1294 | 
            +
             | 
| 1275 1295 | 
             
                if !(attrs = options[:attributes])
         | 
| 1276 1296 | 
             
                  attrs = {}
         | 
| 1277 1297 | 
             
                elsif ::Hash === attrs || (::RUBY_ENGINE_JRUBY && ::Java::JavaUtil::Map === attrs)
         | 
| @@ -1363,10 +1383,6 @@ module Asciidoctor | |
| 1363 1383 |  | 
| 1364 1384 | 
             
              # Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
         | 
| 1365 1385 | 
             
              #
         | 
| 1366 | 
            -
              # Accepts input as an IO, String or String Array object. If the
         | 
| 1367 | 
            -
              # input is a File, information about the file is stored in
         | 
| 1368 | 
            -
              # attributes on the Document.
         | 
| 1369 | 
            -
              #
         | 
| 1370 1386 | 
             
              # input   - the String AsciiDoc source filename
         | 
| 1371 1387 | 
             
              # options - a String, Array or Hash of options to control processing (default: {})
         | 
| 1372 1388 | 
             
              #           String and Array values are converted into a Hash.
         | 
| @@ -1374,17 +1390,18 @@ module Asciidoctor | |
| 1374 1390 | 
             
              #
         | 
| 1375 1391 | 
             
              # Returns the Asciidoctor::Document
         | 
| 1376 1392 | 
             
              def load_file filename, options = {}
         | 
| 1377 | 
            -
                ::File.open(filename) {|file| self.load file, options }
         | 
| 1393 | 
            +
                ::File.open(filename, 'rb') {|file| self.load file, options }
         | 
| 1378 1394 | 
             
              end
         | 
| 1379 1395 |  | 
| 1380 1396 | 
             
              # Public: Parse the AsciiDoc source input into an Asciidoctor::Document and
         | 
| 1381 1397 | 
             
              # convert it to the specified backend format.
         | 
| 1382 1398 | 
             
              #
         | 
| 1383 | 
            -
              # Accepts input as an IO, String or String Array object. If the
         | 
| 1384 | 
            -
              # input is a File,  | 
| 1385 | 
            -
              #  | 
| 1399 | 
            +
              # Accepts input as an IO (or StringIO), String or String Array object. If the
         | 
| 1400 | 
            +
              # input is a File, the object is expected to be opened for reading and is not
         | 
| 1401 | 
            +
              # closed afterwards by this method. Information about the file (filename,
         | 
| 1402 | 
            +
              # directory name, etc) gets assigned to attributes on the Document object.
         | 
| 1386 1403 | 
             
              #
         | 
| 1387 | 
            -
              # If the : | 
| 1404 | 
            +
              # If the :to_file option is true, and the input is a File, the output is
         | 
| 1388 1405 | 
             
              # written to a file adjacent to the input file, having an extension that
         | 
| 1389 1406 | 
             
              # corresponds to the backend format. Otherwise, if the :to_file option is
         | 
| 1390 1407 | 
             
              # specified, the file is written to that file. If :to_file is not an absolute
         | 
| @@ -1412,7 +1429,6 @@ module Asciidoctor | |
| 1412 1429 | 
             
                to_file = options.delete(:to_file)
         | 
| 1413 1430 | 
             
                to_dir = options.delete(:to_dir)
         | 
| 1414 1431 | 
             
                mkdirs = options.delete(:mkdirs) || false
         | 
| 1415 | 
            -
                timings = options[:timings]
         | 
| 1416 1432 |  | 
| 1417 1433 | 
             
                case to_file
         | 
| 1418 1434 | 
             
                when true, nil
         | 
| @@ -1429,7 +1445,7 @@ module Asciidoctor | |
| 1429 1445 | 
             
                  return self.load input, options
         | 
| 1430 1446 | 
             
                else
         | 
| 1431 1447 | 
             
                  write_to_same_dir = false
         | 
| 1432 | 
            -
                  write_to_target = (stream_output = to_file.respond_to? :write) ? false : to_file
         | 
| 1448 | 
            +
                  write_to_target = (stream_output = to_file.respond_to? :write) ? false : (options[:to_file] = to_file)
         | 
| 1433 1449 | 
             
                end
         | 
| 1434 1450 |  | 
| 1435 1451 | 
             
                unless options.key? :header_footer
         | 
| @@ -1443,17 +1459,17 @@ module Asciidoctor | |
| 1443 1459 | 
             
                elsif write_to_target
         | 
| 1444 1460 | 
             
                  if to_dir
         | 
| 1445 1461 | 
             
                    if to_file
         | 
| 1446 | 
            -
                      options[:to_dir] = ::File.expand_path ::File.join to_dir, to_file | 
| 1462 | 
            +
                      options[:to_dir] = ::File.dirname ::File.expand_path ::File.join to_dir, to_file
         | 
| 1447 1463 | 
             
                    else
         | 
| 1448 1464 | 
             
                      options[:to_dir] = ::File.expand_path to_dir
         | 
| 1449 1465 | 
             
                    end
         | 
| 1450 1466 | 
             
                  elsif to_file
         | 
| 1451 | 
            -
                    options[:to_dir] = ::File.expand_path to_file | 
| 1467 | 
            +
                    options[:to_dir] = ::File.dirname ::File.expand_path to_file
         | 
| 1452 1468 | 
             
                  end
         | 
| 1453 | 
            -
                else
         | 
| 1454 | 
            -
                  options[:to_dir] = nil
         | 
| 1455 1469 | 
             
                end
         | 
| 1456 1470 |  | 
| 1471 | 
            +
                # NOTE :to_dir is always set when outputting to a file
         | 
| 1472 | 
            +
                # NOTE :to_file option only passed if assigned an explicit path
         | 
| 1457 1473 | 
             
                doc = self.load input, options
         | 
| 1458 1474 |  | 
| 1459 1475 | 
             
                if write_to_same_dir # write to file in same directory
         | 
| @@ -1462,7 +1478,7 @@ module Asciidoctor | |
| 1462 1478 | 
             
                    raise ::IOError, %(input file and output file cannot be the same: #{outfile})
         | 
| 1463 1479 | 
             
                  end
         | 
| 1464 1480 | 
             
                elsif write_to_target # write to explicit file or directory
         | 
| 1465 | 
            -
                  working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) :  | 
| 1481 | 
            +
                  working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) : ::Dir.pwd
         | 
| 1466 1482 | 
             
                  # QUESTION should the jail be the working_dir or doc.base_dir???
         | 
| 1467 1483 | 
             
                  jail = doc.safe >= SafeMode::SAFE ? working_dir : nil
         | 
| 1468 1484 | 
             
                  if to_dir
         | 
| @@ -1484,28 +1500,22 @@ module Asciidoctor | |
| 1484 1500 | 
             
                    raise ::IOError, %(input file and output file cannot be the same: #{outfile})
         | 
| 1485 1501 | 
             
                  end
         | 
| 1486 1502 |  | 
| 1487 | 
            -
                   | 
| 1488 | 
            -
                     | 
| 1489 | 
            -
             | 
| 1490 | 
            -
                     | 
| 1491 | 
            -
             | 
| 1492 | 
            -
                      raise ::IOError, %(target directory does not exist: #{to_dir})
         | 
| 1493 | 
            -
                    end
         | 
| 1503 | 
            +
                  if mkdirs
         | 
| 1504 | 
            +
                    Helpers.mkdir_p outdir
         | 
| 1505 | 
            +
                  else
         | 
| 1506 | 
            +
                    # NOTE we intentionally refer to the directory as it was passed to the API
         | 
| 1507 | 
            +
                    raise ::IOError, %(target directory does not exist: #{to_dir} (hint: set mkdirs option)) unless ::File.directory? outdir
         | 
| 1494 1508 | 
             
                  end
         | 
| 1495 1509 | 
             
                else # write to stream
         | 
| 1496 1510 | 
             
                  outfile = to_file
         | 
| 1497 1511 | 
             
                  outdir = nil
         | 
| 1498 1512 | 
             
                end
         | 
| 1499 1513 |  | 
| 1500 | 
            -
                timings.start :convert if timings
         | 
| 1501 1514 | 
             
                opts = outfile && !stream_output ? { 'outfile' => outfile, 'outdir' => outdir } : {}
         | 
| 1502 1515 | 
             
                output = doc.convert opts
         | 
| 1503 | 
            -
                timings.record :convert if timings
         | 
| 1504 1516 |  | 
| 1505 1517 | 
             
                if outfile
         | 
| 1506 | 
            -
                  timings.start :write if timings
         | 
| 1507 1518 | 
             
                  doc.write output, outfile
         | 
| 1508 | 
            -
                  timings.record :write if timings
         | 
| 1509 1519 |  | 
| 1510 1520 | 
             
                  # NOTE document cannot control this behavior if safe >= SafeMode::SERVER
         | 
| 1511 1521 | 
             
                  # NOTE skip if stylesdir is a URI
         | 
| @@ -1525,7 +1535,11 @@ module Asciidoctor | |
| 1525 1535 | 
             
                    copy_pygments_stylesheet = (doc.attr? 'source-highlighter', 'pygments') && (doc.attr 'pygments-css', 'class') == 'class'
         | 
| 1526 1536 | 
             
                    if copy_asciidoctor_stylesheet || copy_user_stylesheet || copy_coderay_stylesheet || copy_pygments_stylesheet
         | 
| 1527 1537 | 
             
                      stylesoutdir = doc.normalize_system_path(stylesdir, outdir, doc.safe >= SafeMode::SAFE ? outdir : nil)
         | 
| 1528 | 
            -
                       | 
| 1538 | 
            +
                      if mkdirs
         | 
| 1539 | 
            +
                        Helpers.mkdir_p stylesoutdir
         | 
| 1540 | 
            +
                      else
         | 
| 1541 | 
            +
                        raise ::IOError, %(target stylesheet directory does not exist: #{stylesoutdir} (hint: set mkdirs option)) unless ::File.directory? stylesoutdir
         | 
| 1542 | 
            +
                      end
         | 
| 1529 1543 |  | 
| 1530 1544 | 
             
                      if copy_asciidoctor_stylesheet
         | 
| 1531 1545 | 
             
                        Stylesheets.instance.write_primary_stylesheet stylesoutdir
         | 
| @@ -1572,7 +1586,7 @@ module Asciidoctor | |
| 1572 1586 | 
             
              # Returns the Document object if the converted String is written to a
         | 
| 1573 1587 | 
             
              # file, otherwise the converted String
         | 
| 1574 1588 | 
             
              def convert_file filename, options = {}
         | 
| 1575 | 
            -
                ::File.open(filename) {|file| self.convert file, options }
         | 
| 1589 | 
            +
                ::File.open(filename, 'rb') {|file| self.convert file, options }
         | 
| 1576 1590 | 
             
              end
         | 
| 1577 1591 |  | 
| 1578 1592 | 
             
              # Alias render_file to convert_file to maintain backwards compatibility
         |