asciidoctor 1.5.6.2 → 1.5.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of asciidoctor might be problematic. Click here for more details.

Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +330 -143
  3. data/README-fr.adoc +441 -0
  4. data/README-jp.adoc +418 -0
  5. data/README-zh_CN.adoc +430 -0
  6. data/README.adoc +454 -0
  7. data/Rakefile +57 -0
  8. data/asciidoctor.gemspec +7 -1
  9. data/data/locale/attributes-ar.adoc +22 -0
  10. data/data/locale/attributes-bg.adoc +22 -0
  11. data/data/locale/attributes-ca.adoc +22 -0
  12. data/data/locale/attributes-cs.adoc +22 -0
  13. data/data/locale/attributes-da.adoc +22 -0
  14. data/data/locale/attributes-de.adoc +22 -0
  15. data/data/locale/attributes-en.adoc +23 -0
  16. data/data/locale/attributes-es.adoc +22 -0
  17. data/data/locale/attributes-fa.adoc +22 -0
  18. data/data/locale/attributes-fi.adoc +22 -0
  19. data/data/locale/attributes-fr.adoc +22 -0
  20. data/data/locale/attributes-hu.adoc +22 -0
  21. data/data/locale/attributes-id.adoc +22 -0
  22. data/data/locale/attributes-it.adoc +22 -0
  23. data/data/locale/attributes-ja.adoc +22 -0
  24. data/data/locale/attributes-kr.adoc +22 -0
  25. data/data/locale/attributes-nb.adoc +22 -0
  26. data/data/locale/attributes-nl.adoc +22 -0
  27. data/data/locale/attributes-nn.adoc +22 -0
  28. data/data/locale/attributes-pl.adoc +22 -0
  29. data/data/locale/attributes-pt.adoc +22 -0
  30. data/data/locale/attributes-pt_BR.adoc +22 -0
  31. data/data/locale/attributes-ro.adoc +22 -0
  32. data/data/locale/attributes-ru.adoc +22 -0
  33. data/data/locale/attributes-sr.adoc +22 -0
  34. data/data/locale/attributes-sr_Latn.adoc +22 -0
  35. data/data/locale/attributes-tr.adoc +22 -0
  36. data/data/locale/attributes-uk.adoc +22 -0
  37. data/data/locale/attributes-zh_CN.adoc +22 -0
  38. data/data/locale/attributes-zh_TW.adoc +22 -0
  39. data/data/locale/attributes.adoc +8 -649
  40. data/data/stylesheets/asciidoctor-default.css +77 -72
  41. data/features/xref.feature +366 -7
  42. data/lib/asciidoctor.rb +107 -93
  43. data/lib/asciidoctor/abstract_block.rb +247 -239
  44. data/lib/asciidoctor/abstract_node.rb +56 -58
  45. data/lib/asciidoctor/block.rb +3 -3
  46. data/lib/asciidoctor/callouts.rb +1 -1
  47. data/lib/asciidoctor/cli/invoker.rb +36 -9
  48. data/lib/asciidoctor/cli/options.rb +63 -25
  49. data/lib/asciidoctor/converter.rb +23 -13
  50. data/lib/asciidoctor/converter/base.rb +4 -0
  51. data/lib/asciidoctor/converter/docbook45.rb +16 -9
  52. data/lib/asciidoctor/converter/docbook5.rb +115 -97
  53. data/lib/asciidoctor/converter/factory.rb +29 -31
  54. data/lib/asciidoctor/converter/html5.rb +229 -192
  55. data/lib/asciidoctor/converter/manpage.rb +72 -50
  56. data/lib/asciidoctor/converter/template.rb +12 -12
  57. data/lib/asciidoctor/core_ext.rb +5 -1
  58. data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +6 -0
  59. data/lib/asciidoctor/document.rb +168 -77
  60. data/lib/asciidoctor/extensions.rb +79 -47
  61. data/lib/asciidoctor/helpers.rb +33 -11
  62. data/lib/asciidoctor/inline.rb +3 -2
  63. data/lib/asciidoctor/list.rb +2 -1
  64. data/lib/asciidoctor/logging.rb +122 -0
  65. data/lib/asciidoctor/parser.rb +406 -382
  66. data/lib/asciidoctor/path_resolver.rb +169 -162
  67. data/lib/asciidoctor/reader.rb +166 -121
  68. data/lib/asciidoctor/section.rb +45 -28
  69. data/lib/asciidoctor/stylesheets.rb +13 -5
  70. data/lib/asciidoctor/substitutors.rb +328 -254
  71. data/lib/asciidoctor/table.rb +105 -48
  72. data/lib/asciidoctor/timings.rb +34 -6
  73. data/lib/asciidoctor/version.rb +1 -1
  74. data/man/asciidoctor.1 +41 -23
  75. data/man/asciidoctor.adoc +14 -8
  76. data/test/api_test.rb +1004 -0
  77. data/test/attributes_test.rb +241 -50
  78. data/test/blocks_test.rb +549 -124
  79. data/test/converter_test.rb +170 -78
  80. data/test/document_test.rb +208 -767
  81. data/test/extensions_test.rb +188 -53
  82. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +1 -1
  83. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +1 -1
  84. data/test/fixtures/file-with-missing-include.adoc +1 -0
  85. data/test/fixtures/include-file.jsx +8 -0
  86. data/test/fixtures/lists.adoc +96 -0
  87. data/test/fixtures/other-chapters.adoc +11 -0
  88. data/test/fixtures/outer-include.adoc +5 -0
  89. data/test/fixtures/sample.asciidoc +5 -1
  90. data/test/fixtures/subdir/index.adoc +3 -0
  91. data/test/fixtures/subdir/inner-include.adoc +3 -0
  92. data/test/fixtures/subdir/middle-include.adoc +5 -0
  93. data/test/fixtures/tagged-class-enclosed.rb +0 -1
  94. data/test/fixtures/unclosed-tag.adoc +3 -0
  95. data/test/fixtures/unexpected-end-tag.adoc +4 -0
  96. data/test/invoker_test.rb +101 -40
  97. data/test/links_test.rb +266 -72
  98. data/test/lists_test.rb +243 -45
  99. data/test/logger_test.rb +211 -0
  100. data/test/manpage_test.rb +124 -6
  101. data/test/options_test.rb +46 -1
  102. data/test/paragraphs_test.rb +23 -10
  103. data/test/parser_test.rb +30 -1
  104. data/test/paths_test.rb +115 -33
  105. data/test/preamble_test.rb +1 -1
  106. data/test/reader_test.rb +337 -81
  107. data/test/sections_test.rb +656 -72
  108. data/test/substitutions_test.rb +182 -57
  109. data/test/tables_test.rb +324 -57
  110. data/test/test_helper.rb +77 -32
  111. data/test/text_test.rb +7 -7
  112. metadata +67 -3
@@ -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
- '.ad' => true,
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|(end))::(\S+)\[\]$/
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 = /^:(!?\w.*?):(?:[ \t]+(.*))?$/
528
+ AttributeEntryRx = /^:(!?#{CG_WORD}[^:]*):(?:[ \t]+(.*))?$/
519
529
 
520
530
  # Matches invalid characters in an attribute name.
521
- InvalidAttributeNameCharsRx = /[^\w\-]/
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 = /(\\)?\{(\w+[-\w]*|(set|counter2?):.+?)(\\)?\}/
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 id.
663
+ # Matches invalid ID characters in a section title.
654
664
  #
655
- # NOTE uppercase chars are not included since the expression is used on a lowercased string
656
- InvalidSectionIdCharsRx = /&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^#{CC_WORD}]+?/
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]*.*?(?::{2,4}|;;)(?:$|[ \t])|<?\d+>[ \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
- # foo:::
716
- # foo::::
717
- # foo;;
716
+ # bar:::
717
+ # baz::::
718
+ # blah;;
718
719
  #
719
- # # the term can be followed by a description on the same line...
720
+ # # the term may be followed by a description on the same line...
720
721
  #
721
- # foo:: That which precedes 'bar' (see also, <<bar>>)
722
+ # foo:: The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).
722
723
  #
723
- # # ...or on a separate line (optionally indented)
724
+ # # ...or on a separate line, which may optionally be indented
724
725
  #
725
726
  # foo::
726
- # That which precedes 'bar' (see also, <<bar>>)
727
+ # The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).
727
728
  #
728
- # # the term or description may be an attribute reference
729
+ # # attribute references may be used in both the term and the description
729
730
  #
730
- # {foo_term}:: {foo_def}
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
- # (?:.*?[^:])? - a non-capturing group which grabs longest sequence of characters that doesn't end w/ colon
740
- '::' => %r(^(?!//)[ \t]*((?:.*[^:])?)(::)(?:[ \t]+(.*))?$),
741
- ':::' => %r(^(?!//)[ \t]*((?:.*[^:])?)(:::)(?:[ \t]+(.*))?$),
742
- '::::' => %r(^(?!//)[ \t]*((?:.*[^:])?)(::::)(?:[ \t]+(.*))?$),
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+%?)?([a-z])?$/
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
- EmailInlineRx = %r(([\\>:/])?#{CG_WORD}[#{CC_WORD}.%+-]*@#{CG_ALNUM}[#{CC_ALNUM}.-]*\.#{CG_ALPHA}{2,4}\b)
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
- # footnoteref:[id,text]
867
- # footnoteref:[id]
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 = /\\?(footnote(?:ref)?):\[(#{CC_ALL}*?[^\\])\]/m
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
- LinkInlineRx = %r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*[^\s.,\[\]<])(?:\[(|#{CC_ALL}*?[^\\])\])?)m
921
+ InlineLinkRx = %r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];])(\\?(?: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
- MenuInlineRx = /\\?"([#{CC_WORD}&][^"]*?[ \n]+&gt;[ \n]+[^"]*)"/
963
+ InlineMenuRx = /\\?"([#{CC_WORD}&][^"]*?[ \n]+&gt;[ \n]+[^"]*)"/
959
964
 
960
- # Matches an inline passthrough value, which may span multiple lines.
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
- PassInlineRx = {
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, information about the file is stored in attributes on the
1261
- # Document object.
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, information about the file is stored in
1385
- # attributes on the Document.
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 :in_place option is true, and the input is a File, the output is
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]) : (::File.expand_path ::Dir.pwd)
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
- unless ::File.directory? outdir
1488
- if mkdirs
1489
- Helpers.mkdir_p outdir
1490
- else
1491
- # NOTE we intentionally refer to the directory as it was passed to the API
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
- Helpers.mkdir_p stylesoutdir if mkdirs
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