asciidoctor 2.0.6 → 2.0.11

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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +159 -6
  3. data/LICENSE +2 -1
  4. data/README-de.adoc +5 -5
  5. data/README-fr.adoc +4 -4
  6. data/README-jp.adoc +248 -183
  7. data/README-zh_CN.adoc +6 -6
  8. data/README.adoc +17 -11
  9. data/asciidoctor.gemspec +8 -8
  10. data/data/locale/attributes-ar.adoc +4 -3
  11. data/data/locale/attributes-bg.adoc +4 -3
  12. data/data/locale/attributes-ca.adoc +6 -5
  13. data/data/locale/attributes-cs.adoc +4 -3
  14. data/data/locale/attributes-da.adoc +6 -5
  15. data/data/locale/attributes-de.adoc +4 -4
  16. data/data/locale/attributes-en.adoc +4 -4
  17. data/data/locale/attributes-es.adoc +6 -5
  18. data/data/locale/attributes-fa.adoc +4 -3
  19. data/data/locale/attributes-fi.adoc +4 -3
  20. data/data/locale/attributes-fr.adoc +6 -5
  21. data/data/locale/attributes-hu.adoc +4 -3
  22. data/data/locale/attributes-id.adoc +4 -3
  23. data/data/locale/attributes-it.adoc +4 -3
  24. data/data/locale/attributes-ja.adoc +4 -3
  25. data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
  26. data/data/locale/attributes-nb.adoc +4 -3
  27. data/data/locale/attributes-nl.adoc +4 -3
  28. data/data/locale/attributes-nn.adoc +4 -3
  29. data/data/locale/attributes-pl.adoc +8 -7
  30. data/data/locale/attributes-pt.adoc +6 -5
  31. data/data/locale/attributes-pt_BR.adoc +6 -5
  32. data/data/locale/attributes-ro.adoc +4 -3
  33. data/data/locale/attributes-ru.adoc +6 -5
  34. data/data/locale/attributes-sr.adoc +4 -4
  35. data/data/locale/attributes-sr_Latn.adoc +4 -4
  36. data/data/locale/attributes-sv.adoc +4 -4
  37. data/data/locale/attributes-tr.adoc +4 -3
  38. data/data/locale/attributes-uk.adoc +6 -5
  39. data/data/locale/attributes-zh_CN.adoc +4 -3
  40. data/data/locale/attributes-zh_TW.adoc +4 -3
  41. data/data/stylesheets/asciidoctor-default.css +29 -26
  42. data/lib/asciidoctor.rb +94 -1098
  43. data/lib/asciidoctor/abstract_block.rb +19 -11
  44. data/lib/asciidoctor/abstract_node.rb +21 -15
  45. data/lib/asciidoctor/attribute_list.rb +59 -67
  46. data/lib/asciidoctor/cli/invoker.rb +2 -0
  47. data/lib/asciidoctor/cli/options.rb +8 -8
  48. data/lib/asciidoctor/convert.rb +198 -0
  49. data/lib/asciidoctor/converter.rb +14 -13
  50. data/lib/asciidoctor/converter/docbook5.rb +9 -25
  51. data/lib/asciidoctor/converter/html5.rb +65 -42
  52. data/lib/asciidoctor/converter/manpage.rb +13 -12
  53. data/lib/asciidoctor/converter/template.rb +6 -3
  54. data/lib/asciidoctor/document.rb +40 -48
  55. data/lib/asciidoctor/extensions.rb +3 -3
  56. data/lib/asciidoctor/helpers.rb +38 -39
  57. data/lib/asciidoctor/inline.rb +1 -1
  58. data/lib/asciidoctor/load.rb +117 -0
  59. data/lib/asciidoctor/parser.rb +29 -25
  60. data/lib/asciidoctor/path_resolver.rb +35 -25
  61. data/lib/asciidoctor/reader.rb +14 -7
  62. data/lib/asciidoctor/rx.rb +722 -0
  63. data/lib/asciidoctor/substitutors.rb +62 -40
  64. data/lib/asciidoctor/syntax_highlighter.rb +22 -8
  65. data/lib/asciidoctor/syntax_highlighter/coderay.rb +1 -1
  66. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +12 -4
  67. data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
  68. data/lib/asciidoctor/syntax_highlighter/pygments.rb +2 -3
  69. data/lib/asciidoctor/syntax_highlighter/rouge.rb +18 -11
  70. data/lib/asciidoctor/table.rb +49 -20
  71. data/lib/asciidoctor/version.rb +1 -1
  72. data/man/asciidoctor.1 +17 -17
  73. data/man/asciidoctor.adoc +15 -14
  74. metadata +12 -9
@@ -148,7 +148,7 @@ module Asciidoctor
148
148
  # Compliance value: 'drop-line'
149
149
  define :attribute_missing, 'skip'
150
150
 
151
- # AsciiDoc drops lines that contain an attribute unassignemnt.
151
+ # AsciiDoc drops lines that contain an attribute unassignment.
152
152
  # This behavior may need to be tuned depending on the circumstances.
153
153
  # Compliance value: 'drop-line'
154
154
  define :attribute_undefined, 'drop-line'
@@ -230,7 +230,7 @@ module Asciidoctor
230
230
 
231
231
  # Pointers to the preferred version for a given backend.
232
232
  BACKEND_ALIASES = {
233
- 'html' => 'html5',
233
+ 'html' => 'html5',
234
234
  'docbook' => 'docbook5'
235
235
  }
236
236
 
@@ -270,14 +270,14 @@ module Asciidoctor
270
270
 
271
271
  ADMONITION_STYLES = ['NOTE', 'TIP', 'IMPORTANT', 'WARNING', 'CAUTION'].to_set
272
272
 
273
- ADMONITION_STYLE_HEADS = ['N', 'T', 'I', 'W', 'C'].to_set
273
+ ADMONITION_STYLE_HEADS = ::Set.new.tap {|accum| ADMONITION_STYLES.each {|s| accum << s.chr } }
274
274
 
275
275
  PARAGRAPH_STYLES = ['comment', 'example', 'literal', 'listing', 'normal', 'open', 'pass', 'quote', 'sidebar', 'source', 'verse', 'abstract', 'partintro'].to_set
276
276
 
277
277
  VERBATIM_STYLES = ['literal', 'listing', 'source', 'verse'].to_set
278
278
 
279
279
  DELIMITED_BLOCKS = {
280
- '--' => [:open, ['comment', 'example', 'literal', 'listing', 'pass', 'quote', 'sidebar', 'source', 'verse', 'admonition', 'abstract', 'partintro'].to_set],
280
+ '--' => [:open, ['comment', 'example', 'literal', 'listing', 'pass', 'quote', 'sidebar', 'source', 'verse', 'admonition', 'abstract', 'partintro'].to_set],
281
281
  '----' => [:listing, ['literal', 'source'].to_set],
282
282
  '....' => [:literal, ['listing', 'source'].to_set],
283
283
  '====' => [:example, ['admonition'].to_set],
@@ -289,24 +289,24 @@ module Asciidoctor
289
289
  ':===' => [:table, ::Set.new],
290
290
  '!===' => [:table, ::Set.new],
291
291
  '////' => [:comment, ::Set.new],
292
- '```' => [:fenced_code, ::Set.new]
292
+ '```' => [:fenced_code, ::Set.new]
293
293
  }
294
294
 
295
295
  DELIMITED_BLOCK_HEADS = {}.tap {|accum| DELIMITED_BLOCKS.each_key {|k| accum[k.slice 0, 2] = true } }
296
296
  DELIMITED_BLOCK_TAILS = {}.tap {|accum| DELIMITED_BLOCKS.each_key {|k| accum[k] = k[k.length - 1] if k.length == 4 } }
297
297
 
298
298
  # NOTE the 'figure' key as a string is historical and used by image blocks
299
- CAPTION_ATTR_NAMES = { example: 'example-caption', 'figure' => 'figure-caption', listing: 'listing-caption', table: 'table-caption' }
299
+ CAPTION_ATTRIBUTE_NAMES = { example: 'example-caption', 'figure' => 'figure-caption', listing: 'listing-caption', table: 'table-caption' }
300
300
 
301
301
  LAYOUT_BREAK_CHARS = {
302
302
  '\'' => :thematic_break,
303
- '<' => :page_break
303
+ '<' => :page_break
304
304
  }
305
305
 
306
306
  MARKDOWN_THEMATIC_BREAK_CHARS = {
307
- '-' => :thematic_break,
308
- '*' => :thematic_break,
309
- '_' => :thematic_break
307
+ '-' => :thematic_break,
308
+ '*' => :thematic_break,
309
+ '_' => :thematic_break
310
310
  }
311
311
 
312
312
  HYBRID_LAYOUT_BREAK_CHARS = LAYOUT_BREAK_CHARS.merge MARKDOWN_THEMATIC_BREAK_CHARS
@@ -319,8 +319,8 @@ module Asciidoctor
319
319
  ORDERED_LIST_STYLES = [:arabic, :loweralpha, :lowerroman, :upperalpha, :upperroman] #, :lowergreek]
320
320
 
321
321
  ORDERED_LIST_KEYWORDS = {
322
- #'arabic' => '1',
323
- #'decimal' => '1',
322
+ #'arabic' => '1',
323
+ #'decimal' => '1',
324
324
  'loweralpha' => 'a',
325
325
  'lowerroman' => 'i',
326
326
  #'lowergreek' => 'a',
@@ -357,789 +357,86 @@ module Asciidoctor
357
357
 
358
358
  FONT_AWESOME_VERSION = '4.7.0'
359
359
 
360
- HIGHLIGHT_JS_VERSION = '9.15.6'
361
-
362
- MATHJAX_VERSION = '2.7.5'
360
+ HIGHLIGHT_JS_VERSION = '9.18.3'
361
+
362
+ MATHJAX_VERSION = '2.7.9'
363
+
364
+ DEFAULT_ATTRIBUTES = {
365
+ 'appendix-caption' => 'Appendix',
366
+ 'appendix-refsig' => 'Appendix',
367
+ 'caution-caption' => 'Caution',
368
+ 'chapter-refsig' => 'Chapter',
369
+ #'encoding' => 'UTF-8',
370
+ 'example-caption' => 'Example',
371
+ 'figure-caption' => 'Figure',
372
+ 'important-caption' => 'Important',
373
+ 'last-update-label' => 'Last updated',
374
+ #'listing-caption' => 'Listing',
375
+ 'note-caption' => 'Note',
376
+ 'part-refsig' => 'Part',
377
+ #'preface-title' => 'Preface',
378
+ 'prewrap' => '',
379
+ 'sectids' => '',
380
+ 'section-refsig' => 'Section',
381
+ 'table-caption' => 'Table',
382
+ 'tip-caption' => 'Tip',
383
+ 'toc-placement' => 'auto',
384
+ 'toc-title' => 'Table of Contents',
385
+ 'untitled-label' => 'Untitled',
386
+ 'version-label' => 'Version',
387
+ 'warning-caption' => 'Warning',
388
+ }
363
389
 
364
- # attributes which be changed within the content of the document (but not
365
- # header) because it has semantic meaning; ex. sectnums
390
+ # attributes which be changed throughout the flow of the document (e.g., sectnums)
366
391
  FLEXIBLE_ATTRIBUTES = ['sectnums']
367
392
 
368
- # A collection of regular expressions used by the parser.
369
- #
370
- # NOTE The following pattern, which appears frequently, captures the
371
- # contents between square brackets, ignoring escaped closing brackets
372
- # (closing brackets prefixed with a backslash '\' character)
373
- #
374
- # Pattern: \[(|#{CC_ALL}*?[^\\])\]
375
- # Matches: [enclosed text] and [enclosed [text\]], not [enclosed text \\] or [\\] (as these require a trailing space)
376
- #
377
- # NOTE \w only matches ASCII word characters, whereas [[:word:]] or \p{Word} matches any character in the Unicode word category.
378
- #(pseudo)module Rx
379
-
380
- ## Regular expression character classes (to ensure regexp compatibility between Ruby and JavaScript)
381
- ## CC stands for "character class", CG stands for "character class group"
382
-
383
- unless RUBY_ENGINE == 'opal'
384
- # CC_ALL is any character, including newlines (must be accompanied by multiline regexp flag)
385
- CC_ALL = '.'
386
- # CC_ANY is any character except newlines
387
- CC_ANY = '.'
388
- CC_EOL = '$'
389
- CC_ALPHA = CG_ALPHA = '\p{Alpha}'
390
- CC_ALNUM = CG_ALNUM = '\p{Alnum}'
391
- CG_BLANK = '\p{Blank}'
392
- CC_WORD = CG_WORD = '\p{Word}'
393
- end
394
-
395
- ## Document header
396
-
397
- # Matches the author info line immediately following the document title.
398
- #
399
- # Examples
400
- #
401
- # Doc Writer <doc@example.com>
402
- # Mary_Sue Brontë
403
- #
404
- AuthorInfoLineRx = /^(#{CG_WORD}[#{CC_WORD}\-'.]*)(?: +(#{CG_WORD}[#{CC_WORD}\-'.]*))?(?: +(#{CG_WORD}[#{CC_WORD}\-'.]*))?(?: +<([^>]+)>)?$/
405
-
406
- # Matches the delimiter that separates multiple authors.
407
- #
408
- # Examples
409
- #
410
- # Doc Writer; Junior Writer
411
- #
412
- AuthorDelimiterRx = /;(?: |$)/
413
-
414
- # Matches the revision info line, which appears immediately following
415
- # the author info line beneath the document title.
416
- #
417
- # Examples
418
- #
419
- # v1.0
420
- # 2013-01-01
421
- # v1.0, 2013-01-01: Ring in the new year release
422
- # 1.0, Jan 01, 2013
423
- #
424
- RevisionInfoLineRx = /^(?:[^\d{]*(#{CC_ANY}*?),)? *(?!:)(#{CC_ANY}*?)(?: *(?!^),?: *(#{CC_ANY}*))?$/
425
-
426
- # Matches the title and volnum in the manpage doctype.
427
- #
428
- # Examples
429
- #
430
- # = asciidoctor(1)
431
- # = asciidoctor ( 1 )
432
- #
433
- ManpageTitleVolnumRx = /^(#{CC_ANY}+?) *\( *(#{CC_ANY}+?) *\)$/
434
-
435
- # Matches the name and purpose in the manpage doctype.
436
- #
437
- # Examples
438
- #
439
- # asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
440
- #
441
- ManpageNamePurposeRx = /^(#{CC_ANY}+?) +- +(#{CC_ANY}+)$/
442
-
443
- ## Preprocessor directives
444
-
445
- # Matches a conditional preprocessor directive (e.g., ifdef, ifndef, ifeval and endif).
446
- #
447
- # Examples
448
- #
449
- # ifdef::basebackend-html[]
450
- # ifndef::theme[]
451
- # ifeval::["{asciidoctor-version}" >= "0.1.0"]
452
- # ifdef::asciidoctor[Asciidoctor!]
453
- # endif::theme[]
454
- # endif::basebackend-html[]
455
- # endif::[]
456
- #
457
- ConditionalDirectiveRx = /^(\\)?(ifdef|ifndef|ifeval|endif)::(\S*?(?:([,+])\S*?)?)\[(#{CC_ANY}+)?\]$/
458
-
459
- # Matches a restricted (read as safe) eval expression.
460
- #
461
- # Examples
462
- #
463
- # "{asciidoctor-version}" >= "0.1.0"
464
- #
465
- EvalExpressionRx = /^(#{CC_ANY}+?) *([=!><]=|[><]) *(#{CC_ANY}+)$/
466
-
467
- # Matches an include preprocessor directive.
468
- #
469
- # Examples
470
- #
471
- # include::chapter1.ad[]
472
- # include::example.txt[lines=1;2;5..10]
473
- #
474
- IncludeDirectiveRx = /^(\\)?include::([^\[][^\[]*)\[(#{CC_ANY}+)?\]$/
475
-
476
- # Matches a trailing tag directive in an include file.
477
- #
478
- # Examples
479
- #
480
- # // tag::try-catch[]
481
- # try {
482
- # someMethod();
483
- # catch (Exception e) {
484
- # log(e);
485
- # }
486
- # // end::try-catch[]
487
- # NOTE m flag is required for Asciidoctor.js
488
- TagDirectiveRx = /\b(?:tag|(e)nd)::(\S+?)\[\](?=$|[ \r])/m
489
-
490
- ## Attribute entries and references
491
-
492
- # Matches a document attribute entry.
493
- #
494
- # Examples
495
- #
496
- # :foo: bar
497
- # :First Name: Dan
498
- # :sectnums!:
499
- # :!toc:
500
- # :long-entry: Attribute value lines ending in ' \' \
501
- # are joined together as a single value, \
502
- # collapsing the line breaks and indentation to \
503
- # a single space.
504
- #
505
- AttributeEntryRx = /^:(!?#{CG_WORD}[^:]*):(?:[ \t]+(#{CC_ANY}*))?$/
506
-
507
- # Matches invalid characters in an attribute name.
508
- InvalidAttributeNameCharsRx = /[^-#{CC_WORD}]/
509
-
510
- # Matches a pass inline macro that surrounds the value of an attribute
511
- # entry once it has been parsed.
512
- #
513
- # Examples
514
- #
515
- # pass:[text]
516
- # pass:a[{a} {b} {c}]
517
- #
518
- if RUBY_ENGINE == 'opal'
519
- # NOTE In JavaScript, ^ and $ match the boundaries of the string when the m flag is not set
520
- AttributeEntryPassMacroRx = /^pass:([a-z]+(?:,[a-z-]+)*)?\[(#{CC_ALL}*)\]$/
521
- else
522
- AttributeEntryPassMacroRx = /\Apass:([a-z]+(?:,[a-z-]+)*)?\[(.*)\]\Z/m
523
- end
524
-
525
- # Matches an inline attribute reference.
526
- #
527
- # Examples
528
- #
529
- # {foobar} or {app_name} or {product-version}
530
- # {counter:sequence-name:1}
531
- # {set:foo:bar}
532
- # {set:name!}
533
- #
534
- AttributeReferenceRx = /(\\)?\{(#{CG_WORD}[-#{CC_WORD}]*|(set|counter2?):#{CC_ANY}+?)(\\)?\}/
535
-
536
- ## Paragraphs and delimited blocks
537
-
538
- # Matches an anchor (i.e., id + optional reference text) on a line above a block.
539
- #
540
- # Examples
541
- #
542
- # [[idname]]
543
- # [[idname,Reference Text]]
544
- #
545
- BlockAnchorRx = /^\[\[(?:|([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(#{CC_ANY}+))?)\]\]$/
546
-
547
- # Matches an attribute list above a block element.
548
- #
549
- # Examples
550
- #
551
- # # strictly positional
552
- # [quote, Adam Smith, Wealth of Nations]
553
- #
554
- # # name/value pairs
555
- # [NOTE, caption="Good to know"]
556
- #
557
- # # as attribute reference
558
- # [{lead}]
559
- #
560
- BlockAttributeListRx = /^\[(|[#{CC_WORD}.#%{,"']#{CC_ANY}*)\]$/
561
-
562
- # A combined pattern that matches either a block anchor or a block attribute list.
563
- #
564
- # TODO this one gets hit a lot, should be optimized as much as possible
565
- BlockAttributeLineRx = /^\[(?:|[#{CC_WORD}.#%{,"']#{CC_ANY}*|\[(?:|[#{CC_ALPHA}_:][#{CC_WORD}:.-]*(?:, *#{CC_ANY}+)?)\])\]$/
566
-
567
- # Matches a title above a block.
568
- #
569
- # Examples
570
- #
571
- # .Title goes here
572
- #
573
- BlockTitleRx = /^\.(\.?[^ \t.]#{CC_ANY}*)$/
574
-
575
- # Matches an admonition label at the start of a paragraph.
576
- #
577
- # Examples
578
- #
579
- # NOTE: Just a little note.
580
- # TIP: Don't forget!
581
- #
582
- AdmonitionParagraphRx = /^(#{ADMONITION_STYLES.to_a.join '|'}):[ \t]+/
583
-
584
- # Matches a literal paragraph, which is a line of text preceded by at least one space.
585
- #
586
- # Examples
587
- #
588
- # <SPACE>Foo
589
- # <TAB>Foo
590
- LiteralParagraphRx = /^([ \t]+#{CC_ANY}*)$/
591
-
592
- # Matches a comment block.
593
- #
594
- # Examples
595
- #
596
- # ////
597
- # This is a block comment.
598
- # It can span one or more lines.
599
- # ////
600
- #CommentBlockRx = %r(^/{4,}$)
601
-
602
- # Matches a comment line.
603
- #
604
- # Examples
605
- #
606
- # // note to author
607
- #
608
- #CommentLineRx = %r(^//(?=[^/]|$))
609
-
610
- ## Section titles
611
-
612
- # Matches an Atx (single-line) section title.
613
- #
614
- # Examples
615
- #
616
- # == Foo
617
- # // ^ a level 1 (h2) section title
618
- #
619
- # == Foo ==
620
- # // ^ also a level 1 (h2) section title
621
- #
622
- AtxSectionTitleRx = /^(=={0,5})[ \t]+(#{CC_ANY}+?)(?:[ \t]+\1)?$/
623
-
624
- # Matches an extended Atx section title that includes support for the Markdown variant.
625
- ExtAtxSectionTitleRx = /^(=={0,5}|#\#{0,5})[ \t]+(#{CC_ANY}+?)(?:[ \t]+\1)?$/
626
-
627
- # Matches the title only (first line) of an Setext (two-line) section title.
628
- # The title cannot begin with a dot and must have at least one alphanumeric character.
629
- SetextSectionTitleRx = /^((?!\.)#{CC_ANY}*?#{CG_ALNUM}#{CC_ANY}*)$/
630
-
631
- # Matches an anchor (i.e., id + optional reference text) inside a section title.
632
- #
633
- # Examples
634
- #
635
- # Section Title [[idname]]
636
- # Section Title [[idname,Reference Text]]
637
- #
638
- InlineSectionAnchorRx = / (\\)?\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(#{CC_ANY}+))?\]\]$/
639
-
640
- # Matches invalid ID characters in a section title.
641
- #
642
- # NOTE uppercase chars not included since expression is only run on a lowercase string
643
- InvalidSectionIdCharsRx = /<[^>]+>|&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^ #{CC_WORD}\-.]+?/
644
-
645
- # Matches an explicit section level style like sect1
646
- #
647
- SectionLevelStyleRx = /^sect\d$/
648
-
649
- ## Lists
650
-
651
- # Detects the start of any list item.
652
- #
653
- # NOTE we only have to check as far as the blank character because we know it means non-whitespace follows.
654
- # IMPORTANT if this regexp does not agree with the regexp for each list type, the parser will hang.
655
- AnyListRx = %r(^(?:[ \t]*(?:-|\*\**|\.\.*|\u2022|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|(?!//[^/])[ \t]*[^ \t]#{CC_ANY}*?(?::::{0,2}|;;)(?:$|[ \t])|<?\d+>[ \t]))
656
-
657
- # Matches an unordered list item (one level for hyphens, up to 5 levels for asterisks).
658
- #
659
- # Examples
660
- #
661
- # * Foo
662
- # - Foo
663
- #
664
- # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
665
- UnorderedListRx = /^[ \t]*(-|\*\**|\u2022)[ \t]+(#{CC_ANY}*)$/
666
-
667
- # Matches an ordered list item (explicit numbering or up to 5 consecutive dots).
668
- #
669
- # Examples
670
- #
671
- # . Foo
672
- # .. Foo
673
- # 1. Foo (arabic, default)
674
- # a. Foo (loweralpha)
675
- # A. Foo (upperalpha)
676
- # i. Foo (lowerroman)
677
- # I. Foo (upperroman)
678
- #
679
- # NOTE leading space match is not always necessary, but is used for list reader
680
- # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
681
- OrderedListRx = /^[ \t]*(\.\.*|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]+(#{CC_ANY}*)$/
682
-
683
- # Matches the ordinals for each type of ordered list.
684
- OrderedListMarkerRxMap = {
685
- arabic: /\d+\./,
686
- loweralpha: /[a-z]\./,
687
- lowerroman: /[ivx]+\)/,
688
- upperalpha: /[A-Z]\./,
689
- upperroman: /[IVX]+\)/,
690
- #lowergreek: /[a-z]\]/,
691
- }
692
-
693
- # Matches a description list entry.
694
- #
695
- # Examples
696
- #
697
- # foo::
698
- # bar:::
699
- # baz::::
700
- # blah;;
701
- #
702
- # # the term may be followed by a description on the same line...
703
- #
704
- # foo:: The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).
705
- #
706
- # # ...or on a separate line, which may optionally be indented
707
- #
708
- # foo::
709
- # The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).
710
- #
711
- # # attribute references may be used in both the term and the description
712
- #
713
- # {foo-term}:: {foo-desc}
714
- #
715
- # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
716
- # NOTE must skip line comment when looking for next list item inside list
717
- DescriptionListRx = %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?)(:::{0,2}|;;)(?:$|[ \t]+(#{CC_ANY}*)$))
718
-
719
- # Matches a sibling description list item (excluding the delimiter specified by the key).
720
- # NOTE must skip line comment when looking for sibling list item
721
- DescriptionListSiblingRx = {
722
- '::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(::)(?:$|[ \t]+(#{CC_ANY}*)$)),
723
- ':::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(:::)(?:$|[ \t]+(#{CC_ANY}*)$)),
724
- '::::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(::::)(?:$|[ \t]+(#{CC_ANY}*)$)),
725
- ';;' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?)(;;)(?:$|[ \t]+(#{CC_ANY}*)$))
726
- }
727
-
728
- # Matches a callout list item.
729
- #
730
- # Examples
731
- #
732
- # <1> Explanation
733
- #
734
- # or
735
- #
736
- # <.> Explanation with automatic number
737
- #
738
- # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
739
- CalloutListRx = /^<(\d+|\.)>[ \t]+(#{CC_ANY}*)$/
740
-
741
- # Matches a callout reference inside literal text.
742
- #
743
- # Examples
744
- # <1> (optionally prefixed by //, #, -- or ;; line comment chars)
745
- # <1> <2> (multiple callouts on one line)
746
- # <!--1--> (for XML-based languages)
747
- # <.> (auto-numbered)
748
- #
749
- # NOTE extract regexps are applied line-by-line, so we can use $ as end-of-line char
750
- CalloutExtractRx = %r(((?://|#|--|;;) ?)?(\\)?<!?(|--)(\d+|\.)\3>(?=(?: ?\\?<!?\3(?:\d+|\.)\3>)*$))
751
- CalloutExtractRxt = '(\\\\)?<()(\\d+|\\.)>(?=(?: ?\\\\?<(?:\\d+|\\.)>)*$)'
752
- CalloutExtractRxMap = ::Hash.new {|h, k| h[k] = /(#{k.empty? ? '' : "#{::Regexp.escape k} ?"})?#{CalloutExtractRxt}/ }
753
- # NOTE special characters have not been replaced when scanning
754
- CalloutScanRx = /\\?<!?(|--)(\d+|\.)\1>(?=(?: ?\\?<!?\1(?:\d+|\.)\1>)*#{CC_EOL})/
755
- # NOTE special characters have already been replaced when converting to an SGML format
756
- CalloutSourceRx = %r(((?://|#|--|;;) ?)?(\\)?&lt;!?(|--)(\d+|\.)\3&gt;(?=(?: ?\\?&lt;!?\3(?:\d+|\.)\3&gt;)*#{CC_EOL}))
757
- CalloutSourceRxt = "(\\\\)?&lt;()(\\d+|\\.)&gt;(?=(?: ?\\\\?&lt;(?:\\d+|\\.)&gt;)*#{CC_EOL})"
758
- CalloutSourceRxMap = ::Hash.new {|h, k| h[k] = /(#{k.empty? ? '' : "#{::Regexp.escape k} ?"})?#{CalloutSourceRxt}/ }
759
-
760
- # A Hash of regexps for lists used for dynamic access.
761
- ListRxMap = {
762
- ulist: UnorderedListRx,
763
- olist: OrderedListRx,
764
- dlist: DescriptionListRx,
765
- colist: CalloutListRx,
766
- }
767
-
768
- ## Tables
769
-
770
- # Parses the column spec (i.e., colspec) for a table.
771
- #
772
- # Examples
773
- #
774
- # 1*h,2*,^3e
775
- #
776
- ColumnSpecRx = /^(?:(\d+)\*)?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?(\d+%?|~)?([a-z])?$/
777
-
778
- # Parses the start and end of a cell spec (i.e., cellspec) for a table.
779
- #
780
- # Examples
781
- #
782
- # 2.3+<.>m
783
- #
784
- # FIXME use step-wise scan (or treetop) rather than this mega-regexp
785
- CellSpecStartRx = /^[ \t]*(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
786
- CellSpecEndRx = /[ \t]+(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
787
-
788
- # Block macros
789
-
790
- # Matches the custom block macro pattern.
791
- #
792
- # Examples
793
- #
794
- # gist::123456[]
795
- #
796
- #--
797
- # NOTE we've relaxed the match for target to accomodate the short format (e.g., name::[attrlist])
798
- CustomBlockMacroRx = /^(#{CG_WORD}[-#{CC_WORD}]*)::(|\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
799
-
800
- # Matches an image, video or audio block macro.
801
- #
802
- # Examples
803
- #
804
- # image::filename.png[Caption]
805
- # video::http://youtube.com/12345[Cats vs Dogs]
806
- #
807
- BlockMediaMacroRx = /^(image|video|audio)::(\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
808
-
809
- # Matches the TOC block macro.
810
- #
811
- # Examples
812
- #
813
- # toc::[]
814
- # toc::[levels=2]
815
- #
816
- BlockTocMacroRx = /^toc::\[(#{CC_ANY}+)?\]$/
817
-
818
- ## Inline macros
819
-
820
- # Matches an anchor (i.e., id + optional reference text) in the flow of text.
821
- #
822
- # Examples
823
- #
824
- # [[idname]]
825
- # [[idname,Reference Text]]
826
- # anchor:idname[]
827
- # anchor:idname[Reference Text]
828
- #
829
- InlineAnchorRx = /(\\)?(?:\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(#{CC_ANY}+?))?\]\]|anchor:([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)\[(?:\]|(#{CC_ANY}*?[^\\])\]))/
830
-
831
- # Scans for a non-escaped anchor (i.e., id + optional reference text) in the flow of text.
832
- InlineAnchorScanRx = /(?:^|[^\\\[])\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(#{CC_ANY}+?))?\]\]|(?:^|[^\\])anchor:([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)\[(?:\]|(#{CC_ANY}*?[^\\])\])/
833
-
834
- # Scans for a leading, non-escaped anchor (i.e., id + optional reference text).
835
- LeadingInlineAnchorRx = /^\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(#{CC_ANY}+?))?\]\]/
836
-
837
- # Matches a bibliography anchor at the start of the list item text (in a bibliography list).
838
- #
839
- # Examples
840
- #
841
- # [[[Fowler_1997]]] Fowler M. ...
842
- #
843
- InlineBiblioAnchorRx = /^\[\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(#{CC_ANY}+?))?\]\]\]/
844
-
845
- # Matches an inline e-mail address.
846
- #
847
- # doc.writer@example.com
848
- #
849
- InlineEmailRx = %r(([\\>:/])?#{CG_WORD}(?:&amp;|[#{CC_WORD}.%+-])*@#{CG_ALNUM}[#{CC_ALNUM}_.-]*\.[a-zA-Z]{2,5}\b)
850
-
851
- # Matches an inline footnote macro, which is allowed to span multiple lines.
852
- #
853
- # Examples
854
- # footnote:[text] (not referenceable)
855
- # footnote:id[text] (referenceable)
856
- # footnote:id[] (reference)
857
- # footnoteref:[id,text] (legacy)
858
- # footnoteref:[id] (legacy)
859
- #
860
- InlineFootnoteMacroRx = /\\?footnote(?:(ref):|:([\w-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\]/m
861
-
862
- # Matches an image or icon inline macro.
863
- #
864
- # Examples
865
- #
866
- # image:filename.png[Alt Text]
867
- # image:http://example.com/images/filename.png[Alt Text]
868
- # image:filename.png[More [Alt\] Text] (alt text becomes "More [Alt] Text")
869
- # icon:github[large]
870
- #
871
- # NOTE be as non-greedy as possible by not allowing newline or left square bracket in target
872
- InlineImageMacroRx = /\\?i(?:mage|con):([^:\s\[](?:[^\n\[]*[^\s\[])?)\[(|#{CC_ALL}*?[^\\])\]/m
873
-
874
- # Matches an indexterm inline macro, which may span multiple lines.
875
- #
876
- # Examples
877
- #
878
- # indexterm:[Tigers,Big cats]
879
- # (((Tigers,Big cats)))
880
- # indexterm2:[Tigers]
881
- # ((Tigers))
882
- #
883
- InlineIndextermMacroRx = /\\?(?:(indexterm2?):\[(#{CC_ALL}*?[^\\])\]|\(\((#{CC_ALL}+?)\)\)(?!\)))/m
884
-
885
- # Matches either the kbd or btn inline macro.
886
- #
887
- # Examples
888
- #
889
- # kbd:[F3]
890
- # kbd:[Ctrl+Shift+T]
891
- # kbd:[Ctrl+\]]
892
- # kbd:[Ctrl,T]
893
- # btn:[Save]
894
- #
895
- InlineKbdBtnMacroRx = /(\\)?(kbd|btn):\[(#{CC_ALL}*?[^\\])\]/m
896
-
897
- # Matches an implicit link and some of the link inline macro.
898
- #
899
- # Examples
900
- #
901
- # https://github.com
902
- # https://github.com[GitHub]
903
- # <https://github.com>
904
- # link:https://github.com[]
905
- #
906
- # FIXME revisit! the main issue is we need different rules for implicit vs explicit
907
- InlineLinkRx = %r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*([^\s.,\[\]<]))(?:\[(|#{CC_ALL}*?[^\\])\])?)m
908
-
909
- # Match a link or e-mail inline macro.
910
- #
911
- # Examples
912
- #
913
- # link:path[label]
914
- # mailto:doc.writer@example.com[]
915
- #
916
- # NOTE be as non-greedy as possible by not allowing space or left square bracket in target
917
- InlineLinkMacroRx = /\\?(?:link|(mailto)):(|[^:\s\[][^\s\[]*)\[(|#{CC_ALL}*?[^\\])\]/m
918
-
919
- # Matches the name of a macro.
920
- #
921
- MacroNameRx = /^#{CG_WORD}[-#{CC_WORD}]*$/
922
-
923
- # Matches a stem (and alternatives, asciimath and latexmath) inline macro, which may span multiple lines.
924
- #
925
- # Examples
926
- #
927
- # stem:[x != 0]
928
- # asciimath:[x != 0]
929
- # latexmath:[\sqrt{4} = 2]
930
- #
931
- InlineStemMacroRx = /\\?(stem|(?:latex|ascii)math):([a-z]+(?:,[a-z-]+)*)?\[(#{CC_ALL}*?[^\\])\]/m
932
-
933
- # Matches a menu inline macro.
934
- #
935
- # Examples
936
- #
937
- # menu:File[Save As...]
938
- # menu:View[Page Style > No Style]
939
- # menu:View[Page Style, No Style]
940
- #
941
- InlineMenuMacroRx = /\\?menu:(#{CG_WORD}|[#{CC_WORD}&][^\n\[]*[^\s\[])\[ *(#{CC_ALL}*?[^\\])?\]/m
942
-
943
- # Matches an implicit menu inline macro.
944
- #
945
- # Examples
946
- #
947
- # "File > New..."
948
- #
949
- InlineMenuRx = /\\?"([#{CC_WORD}&][^"]*?[ \n]+&gt;[ \n]+[^"]*)"/
950
-
951
- # Matches an inline passthrough, which may span multiple lines.
952
- #
953
- # Examples
954
- #
955
- # +text+
956
- # `text` (compat)
957
- #
958
- # NOTE we always capture the attributes so we know when to use compatible (i.e., legacy) behavior
959
- InlinePassRx = {
960
- false => ['+', '`', /(^|[^#{CC_WORD};:])(?:\[([^\]]+)\])?(\\?(\+|`)(\S|\S#{CC_ALL}*?\S)\4)(?!#{CG_WORD})/m],
961
- true => ['`', nil, /(^|[^`#{CC_WORD}])(?:\[([^\]]+)\])?(\\?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\4)(?![`#{CC_WORD}])/m]
962
- }
963
-
964
- # Matches an inline plus passthrough spanning multiple lines, but only when it occurs directly
965
- # inside constrained monospaced formatting in non-compat mode.
966
- #
967
- # Examples
968
- #
969
- # +text+
970
- #
971
- SinglePlusInlinePassRx = /^(\\)?\+(\S|\S#{CC_ALL}*?\S)\+$/m
972
-
973
- # Matches several variants of the passthrough inline macro, which may span multiple lines.
974
- #
975
- # Examples
976
- #
977
- # +++text+++
978
- # $$text$$
979
- # pass:quotes[text]
980
- #
981
- # NOTE we have to support an empty pass:[] for compatibility with AsciiDoc Python
982
- InlinePassMacroRx = /(?:(?:(\\?)\[([^\]]+)\])?(\\{0,2})(\+\+\+?|\$\$)(#{CC_ALL}*?)\4|(\\?)pass:([a-z]+(?:,[a-z-]+)*)?\[(|#{CC_ALL}*?[^\\])\])/m
983
-
984
- # Matches an xref (i.e., cross-reference) inline macro, which may span multiple lines.
985
- #
986
- # Examples
987
- #
988
- # <<id,reftext>>
989
- # xref:id[reftext]
990
- #
991
- # NOTE special characters have already been escaped, hence the entity references
992
- # NOTE { is included in start characters to support target that begins with attribute reference in title content
993
- InlineXrefMacroRx = %r(\\?(?:&lt;&lt;([#{CC_WORD}#/.:{]#{CC_ALL}*?)&gt;&gt;|xref:([#{CC_WORD}#/.:{]#{CC_ALL}*?)\[(?:\]|(#{CC_ALL}*?[^\\])\])))m
994
-
995
- ## Layout
996
-
997
- # Matches a trailing + preceded by at least one space character,
998
- # which forces a hard line break (<br> tag in HTML output).
999
- #
1000
- # NOTE AsciiDoc Python allows + to be preceded by TAB; Asciidoctor does not
1001
- #
1002
- # Examples
1003
- #
1004
- # Humpty Dumpty sat on a wall, +
1005
- # Humpty Dumpty had a great fall.
1006
- #
1007
- if RUBY_ENGINE == 'opal'
1008
- # NOTE In JavaScript, ^ and $ only match the start and end of line if the multiline flag is present
1009
- HardLineBreakRx = /^(#{CC_ANY}*) \+$/m
1010
- else
1011
- # NOTE In Ruby, ^ and $ always match start and end of line
1012
- HardLineBreakRx = /^(.*) \+$/
1013
- end
1014
-
1015
- # Matches a Markdown horizontal rule.
1016
- #
1017
- # Examples
1018
- #
1019
- # --- or - - -
1020
- # *** or * * *
1021
- # ___ or _ _ _
1022
- #
1023
- MarkdownThematicBreakRx = /^ {0,3}([-*_])( *)\1\2\1$/
1024
-
1025
- # Matches an AsciiDoc or Markdown horizontal rule or AsciiDoc page break.
1026
- #
1027
- # Examples
1028
- #
1029
- # ''' (horizontal rule)
1030
- # <<< (page break)
1031
- # --- or - - - (horizontal rule, Markdown)
1032
- # *** or * * * (horizontal rule, Markdown)
1033
- # ___ or _ _ _ (horizontal rule, Markdown)
1034
- #
1035
- ExtLayoutBreakRx = /^(?:'{3,}|<{3,}|([-*_])( *)\1\2\1)$/
1036
-
1037
- ## General
1038
-
1039
- # Matches consecutive blank lines.
1040
- #
1041
- # Examples
1042
- #
1043
- # one
1044
- #
1045
- # two
1046
- #
1047
- BlankLineRx = /\n{2,}/
1048
-
1049
- # Matches a comma or semi-colon delimiter.
1050
- #
1051
- # Examples
1052
- #
1053
- # one,two
1054
- # three;four
1055
- #
1056
- #DataDelimiterRx = /[,;]/
1057
-
1058
- # Matches whitespace (space, tab, newline) escaped by a backslash.
1059
- #
1060
- # Examples
1061
- #
1062
- # three\ blind\ mice
1063
- #
1064
- EscapedSpaceRx = /\\([ \t\n])/
1065
-
1066
- # Detects if text is a possible candidate for the replacements substitution.
1067
- #
1068
- ReplaceableTextRx = /[&']|--|\.\.\.|\([CRT]M?\)/
1069
-
1070
- # Matches a whitespace delimiter, a sequence of spaces, tabs, and/or newlines.
1071
- # Matches the parsing rules of %w strings in Ruby.
1072
- #
1073
- # Examples
1074
- #
1075
- # one two three four
1076
- # five six
1077
- #
1078
- # TODO change to /(?<!\\)[ \t\n]+/ once lookbehind assertions are implemented in all modern browsers
1079
- SpaceDelimiterRx = /([^\\])[ \t\n]+/
1080
-
1081
- # Matches a + or - modifier in a subs list
1082
- #
1083
- SubModifierSniffRx = /[+-]/
1084
-
1085
- # Matches one or more consecutive digits at the end of a line.
1086
- #
1087
- # Examples
1088
- #
1089
- # docbook5
1090
- # html5
1091
- #
1092
- TrailingDigitsRx = /\d+$/
1093
-
1094
- # Detects strings that resemble URIs.
1095
- #
1096
- # Examples
1097
- # http://domain
1098
- # https://domain
1099
- # file:///path
1100
- # data:info
1101
- #
1102
- # not c:/sample.adoc or c:\sample.adoc
1103
- #
1104
- UriSniffRx = %r(^#{CG_ALPHA}[#{CC_ALNUM}.+-]+:/{0,2})
1105
-
1106
- # Detects XML tags
1107
- XmlSanitizeRx = /<[^>]+>/
1108
- #end
1109
-
1110
393
  INTRINSIC_ATTRIBUTES = {
1111
- 'startsb' => '[',
1112
- 'endsb' => ']',
1113
- 'vbar' => '|',
1114
- 'caret' => '^',
1115
- 'asterisk' => '*',
1116
- 'tilde' => '~',
1117
- 'plus' => '&#43;',
1118
- 'backslash' => '\\',
1119
- 'backtick' => '`',
1120
- 'blank' => '',
1121
- 'empty' => '',
1122
- 'sp' => ' ',
394
+ 'startsb' => '[',
395
+ 'endsb' => ']',
396
+ 'vbar' => '|',
397
+ 'caret' => '^',
398
+ 'asterisk' => '*',
399
+ 'tilde' => '~',
400
+ 'plus' => '&#43;',
401
+ 'backslash' => '\\',
402
+ 'backtick' => '`',
403
+ 'blank' => '',
404
+ 'empty' => '',
405
+ 'sp' => ' ',
1123
406
  'two-colons' => '::',
1124
407
  'two-semicolons' => ';;',
1125
- 'nbsp' => '&#160;',
1126
- 'deg' => '&#176;',
1127
- 'zwsp' => '&#8203;',
1128
- 'quot' => '&#34;',
1129
- 'apos' => '&#39;',
1130
- 'lsquo' => '&#8216;',
1131
- 'rsquo' => '&#8217;',
1132
- 'ldquo' => '&#8220;',
1133
- 'rdquo' => '&#8221;',
1134
- 'wj' => '&#8288;',
1135
- 'brvbar' => '&#166;',
1136
- 'pp' => '&#43;&#43;',
1137
- 'cpp' => 'C&#43;&#43;',
1138
- 'amp' => '&',
1139
- 'lt' => '<',
1140
- 'gt' => '>'
408
+ 'nbsp' => '&#160;',
409
+ 'deg' => '&#176;',
410
+ 'zwsp' => '&#8203;',
411
+ 'quot' => '&#34;',
412
+ 'apos' => '&#39;',
413
+ 'lsquo' => '&#8216;',
414
+ 'rsquo' => '&#8217;',
415
+ 'ldquo' => '&#8220;',
416
+ 'rdquo' => '&#8221;',
417
+ 'wj' => '&#8288;',
418
+ 'brvbar' => '&#166;',
419
+ 'pp' => '&#43;&#43;',
420
+ 'cpp' => 'C&#43;&#43;',
421
+ 'amp' => '&',
422
+ 'lt' => '<',
423
+ 'gt' => '>'
1141
424
  }
1142
425
 
426
+ # Regular expression character classes (to ensure regexp compatibility between Ruby and JavaScript)
427
+ # CC stands for "character class", CG stands for "character class group"
428
+ unless RUBY_ENGINE == 'opal'
429
+ # CC_ALL is any character, including newlines (must be accompanied by multiline regexp flag)
430
+ CC_ALL = '.'
431
+ # CC_ANY is any character except newlines
432
+ CC_ANY = '.'
433
+ CC_EOL = '$'
434
+ CC_ALPHA = CG_ALPHA = '\p{Alpha}'
435
+ CC_ALNUM = CG_ALNUM = '\p{Alnum}'
436
+ CG_BLANK = '\p{Blank}'
437
+ CC_WORD = CG_WORD = '\p{Word}'
438
+ end
439
+
1143
440
  QUOTE_SUBS = {}.tap do |accum|
1144
441
  # unconstrained quotes:: can appear anywhere
1145
442
  # constrained quotes:: must be bordered by non-word characters
@@ -1198,8 +495,8 @@ module Asciidoctor
1198
495
  # (TM)
1199
496
  [/\\?\(TM\)/, '&#8482;', :none],
1200
497
  # foo -- bar (where either space character can be a newline)
1201
- # NOTE this necessarily drops the newline if it appears at end of line
1202
- [/(^|\n| |\\)--( |\n|$)/, '&#8201;&#8212;&#8201;', :none],
498
+ # NOTE this necessarily drops the newline if replacement appears at end of line
499
+ [/(?: |\n|^|\\)--(?: |\n|$)/, '&#8201;&#8212;&#8201;', :none],
1203
500
  # foo--bar
1204
501
  [/(#{CG_WORD})\\?--(?=#{CG_WORD})/, '&#8212;&#8203;', :leading],
1205
502
  # ellipsis
@@ -1220,310 +517,6 @@ module Asciidoctor
1220
517
  [/\\?(&)amp;((?:[a-zA-Z][a-zA-Z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-fA-F][\da-fA-F][\da-fA-F]{0,3});)/, '', :bounding]
1221
518
  ]
1222
519
 
1223
- class << self
1224
-
1225
- # Public: Parse the AsciiDoc source input into a {Document}
1226
- #
1227
- # Accepts input as an IO (or StringIO), String or String Array object. If the
1228
- # input is a File, the object is expected to be opened for reading and is not
1229
- # closed afterwards by this method. Information about the file (filename,
1230
- # directory name, etc) gets assigned to attributes on the Document object.
1231
- #
1232
- # input - the AsciiDoc source as a IO, String or Array.
1233
- # options - a String, Array or Hash of options to control processing (default: {})
1234
- # String and Array values are converted into a Hash.
1235
- # See {Document#initialize} for details about these options.
1236
- #
1237
- # Returns the Document
1238
- def load input, options = {}
1239
- options = options.merge
1240
-
1241
- if (timings = options[:timings])
1242
- timings.start :read
1243
- end
1244
-
1245
- if (logger = options[:logger]) && logger != LoggerManager.logger
1246
- LoggerManager.logger = logger
1247
- end
1248
-
1249
- if !(attrs = options[:attributes])
1250
- attrs = {}
1251
- elsif ::Hash === attrs
1252
- attrs = attrs.merge
1253
- elsif (defined? ::Java::JavaUtil::Map) && ::Java::JavaUtil::Map === attrs
1254
- attrs = attrs.dup
1255
- elsif ::Array === attrs
1256
- attrs = {}.tap do |accum|
1257
- attrs.each do |entry|
1258
- k, _, v = entry.partition '='
1259
- accum[k] = v
1260
- end
1261
- end
1262
- elsif ::String === attrs
1263
- # condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null
1264
- attrs = {}.tap do |accum|
1265
- attrs.gsub(SpaceDelimiterRx, '\1' + NULL).gsub(EscapedSpaceRx, '\1').split(NULL).each do |entry|
1266
- k, _, v = entry.partition '='
1267
- accum[k] = v
1268
- end
1269
- end
1270
- elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
1271
- # coerce attrs to a real Hash
1272
- attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } }
1273
- else
1274
- raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '})
1275
- end
1276
-
1277
- if ::File === input
1278
- options[:input_mtime] = input.mtime
1279
- # NOTE defer setting infile and indir until we get a better sense of their purpose
1280
- # TODO cli checks if input path can be read and is file, but might want to add check to API too
1281
- attrs['docfile'] = input_path = ::File.absolute_path input.path
1282
- attrs['docdir'] = ::File.dirname input_path
1283
- attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = Helpers.extname input_path)
1284
- source = input.read
1285
- elsif input.respond_to? :read
1286
- # NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either
1287
- # just fail the rewind operation silently to handle all cases
1288
- input.rewind rescue nil
1289
- source = input.read
1290
- elsif ::String === input
1291
- source = input
1292
- elsif ::Array === input
1293
- source = input.drop 0
1294
- elsif input
1295
- raise ::ArgumentError, %(unsupported input type: #{input.class})
1296
- end
1297
-
1298
- if timings
1299
- timings.record :read
1300
- timings.start :parse
1301
- end
1302
-
1303
- options[:attributes] = attrs
1304
- doc = options[:parse] == false ? (Document.new source, options) : (Document.new source, options).parse
1305
-
1306
- timings.record :parse if timings
1307
- doc
1308
- rescue => ex
1309
- begin
1310
- context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
1311
- if ex.respond_to? :exception
1312
- # The original message must be explicitly preserved when wrapping a Ruby exception
1313
- wrapped_ex = ex.exception %(#{context} - #{ex.message})
1314
- # JRuby automatically sets backtrace; MRI did not until 2.6
1315
- wrapped_ex.set_backtrace ex.backtrace
1316
- else
1317
- # Likely a Java exception class
1318
- wrapped_ex = ex.class.new context, ex
1319
- wrapped_ex.stack_trace = ex.stack_trace
1320
- end
1321
- rescue
1322
- wrapped_ex = ex
1323
- end
1324
- raise wrapped_ex
1325
- end
1326
-
1327
- # Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
1328
- #
1329
- # input - the String AsciiDoc source filename
1330
- # options - a String, Array or Hash of options to control processing (default: {})
1331
- # String and Array values are converted into a Hash.
1332
- # See Asciidoctor::Document#initialize for details about options.
1333
- #
1334
- # Returns the Asciidoctor::Document
1335
- def load_file filename, options = {}
1336
- ::File.open(filename, FILE_READ_MODE) {|file| self.load file, options }
1337
- end
1338
-
1339
- # Public: Parse the AsciiDoc source input into an Asciidoctor::Document and
1340
- # convert it to the specified backend format.
1341
- #
1342
- # Accepts input as an IO (or StringIO), String or String Array object. If the
1343
- # input is a File, the object is expected to be opened for reading and is not
1344
- # closed afterwards by this method. Information about the file (filename,
1345
- # directory name, etc) gets assigned to attributes on the Document object.
1346
- #
1347
- # If the :to_file option is true, and the input is a File, the output is
1348
- # written to a file adjacent to the input file, having an extension that
1349
- # corresponds to the backend format. Otherwise, if the :to_file option is
1350
- # specified, the file is written to that file. If :to_file is not an absolute
1351
- # path, it is resolved relative to :to_dir, if given, otherwise the
1352
- # Document#base_dir. If the target directory does not exist, it will not be
1353
- # created unless the :mkdirs option is set to true. If the file cannot be
1354
- # written because the target directory does not exist, or because it falls
1355
- # outside of the Document#base_dir in safe mode, an IOError is raised.
1356
- #
1357
- # If the output is going to be written to a file, the header and footer are
1358
- # included unless specified otherwise (writing to a file implies creating a
1359
- # standalone document). Otherwise, the header and footer are not included by
1360
- # default and the converted result is returned.
1361
- #
1362
- # input - the String AsciiDoc source filename
1363
- # options - a String, Array or Hash of options to control processing (default: {})
1364
- # String and Array values are converted into a Hash.
1365
- # See Asciidoctor::Document#initialize for details about options.
1366
- #
1367
- # Returns the Document object if the converted String is written to a
1368
- # file, otherwise the converted String
1369
- def convert input, options = {}
1370
- (options = options.merge).delete :parse
1371
- to_dir = options.delete :to_dir
1372
- mkdirs = options.delete :mkdirs
1373
-
1374
- case (to_file = options.delete :to_file)
1375
- when true, nil
1376
- unless (write_to_target = to_dir)
1377
- sibling_path = ::File.absolute_path input.path if ::File === input
1378
- end
1379
- to_file = nil
1380
- when false
1381
- to_file = nil
1382
- when '/dev/null'
1383
- return self.load input, options
1384
- else
1385
- options[:to_file] = write_to_target = to_file unless (stream_output = to_file.respond_to? :write)
1386
- end
1387
-
1388
- unless options.key? :standalone
1389
- if sibling_path || write_to_target
1390
- options[:standalone] = true
1391
- elsif options.key? :header_footer
1392
- options[:standalone] = options[:header_footer]
1393
- end
1394
- end
1395
-
1396
- # NOTE outfile may be controlled by document attributes, so resolve outfile after loading
1397
- if sibling_path
1398
- options[:to_dir] = outdir = ::File.dirname sibling_path
1399
- elsif write_to_target
1400
- if to_dir
1401
- if to_file
1402
- options[:to_dir] = ::File.dirname ::File.expand_path ::File.join to_dir, to_file
1403
- else
1404
- options[:to_dir] = ::File.expand_path to_dir
1405
- end
1406
- elsif to_file
1407
- options[:to_dir] = ::File.dirname ::File.expand_path to_file
1408
- end
1409
- end
1410
-
1411
- # NOTE :to_dir is always set when outputting to a file
1412
- # NOTE :to_file option only passed if assigned an explicit path
1413
- doc = self.load input, options
1414
-
1415
- if sibling_path # write to file in same directory
1416
- outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix})
1417
- raise ::IOError, %(input file and output file cannot be the same: #{outfile}) if outfile == sibling_path
1418
- elsif write_to_target # write to explicit file or directory
1419
- working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) : ::Dir.pwd
1420
- # QUESTION should the jail be the working_dir or doc.base_dir???
1421
- jail = doc.safe >= SafeMode::SAFE ? working_dir : nil
1422
- if to_dir
1423
- outdir = doc.normalize_system_path(to_dir, working_dir, jail, target_name: 'to_dir', recover: false)
1424
- if to_file
1425
- outfile = doc.normalize_system_path(to_file, outdir, nil, target_name: 'to_dir', recover: false)
1426
- # reestablish outdir as the final target directory (in the case to_file had directory segments)
1427
- outdir = ::File.dirname outfile
1428
- else
1429
- outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix})
1430
- end
1431
- elsif to_file
1432
- outfile = doc.normalize_system_path(to_file, working_dir, jail, target_name: 'to_dir', recover: false)
1433
- # establish outdir as the final target directory (in the case to_file had directory segments)
1434
- outdir = ::File.dirname outfile
1435
- end
1436
-
1437
- if ::File === input && outfile == (::File.absolute_path input.path)
1438
- raise ::IOError, %(input file and output file cannot be the same: #{outfile})
1439
- end
1440
-
1441
- if mkdirs
1442
- Helpers.mkdir_p outdir
1443
- else
1444
- # NOTE we intentionally refer to the directory as it was passed to the API
1445
- raise ::IOError, %(target directory does not exist: #{to_dir} (hint: set :mkdirs option)) unless ::File.directory? outdir
1446
- end
1447
- else # write to stream
1448
- outfile = to_file
1449
- outdir = nil
1450
- end
1451
-
1452
- if outfile && !stream_output
1453
- output = doc.convert 'outfile' => outfile, 'outdir' => outdir
1454
- else
1455
- output = doc.convert
1456
- end
1457
-
1458
- if outfile
1459
- doc.write output, outfile
1460
-
1461
- # NOTE document cannot control this behavior if safe >= SafeMode::SERVER
1462
- # NOTE skip if stylesdir is a URI
1463
- if !stream_output && doc.safe < SafeMode::SECURE && (doc.attr? 'linkcss') && (doc.attr? 'copycss') &&
1464
- (doc.basebackend? 'html') && !((stylesdir = (doc.attr 'stylesdir')) && (Helpers.uriish? stylesdir))
1465
- if (stylesheet = doc.attr 'stylesheet')
1466
- if DEFAULT_STYLESHEET_KEYS.include? stylesheet
1467
- copy_asciidoctor_stylesheet = true
1468
- elsif !(Helpers.uriish? stylesheet)
1469
- copy_user_stylesheet = true
1470
- end
1471
- end
1472
- copy_syntax_hl_stylesheet = (syntax_hl = doc.syntax_highlighter) && (syntax_hl.write_stylesheet? doc)
1473
- if copy_asciidoctor_stylesheet || copy_user_stylesheet || copy_syntax_hl_stylesheet
1474
- stylesoutdir = doc.normalize_system_path(stylesdir, outdir, doc.safe >= SafeMode::SAFE ? outdir : nil)
1475
- if mkdirs
1476
- Helpers.mkdir_p stylesoutdir
1477
- else
1478
- raise ::IOError, %(target stylesheet directory does not exist: #{stylesoutdir} (hint: set :mkdirs option)) unless ::File.directory? stylesoutdir
1479
- end
1480
-
1481
- if copy_asciidoctor_stylesheet
1482
- Stylesheets.instance.write_primary_stylesheet stylesoutdir
1483
- # FIXME should Stylesheets also handle the user stylesheet?
1484
- elsif copy_user_stylesheet
1485
- if (stylesheet_src = doc.attr 'copycss').empty?
1486
- stylesheet_src = doc.normalize_system_path stylesheet
1487
- else
1488
- # NOTE in this case, copycss is a source location (but cannot be a URI)
1489
- stylesheet_src = doc.normalize_system_path stylesheet_src
1490
- end
1491
- stylesheet_dest = doc.normalize_system_path stylesheet, stylesoutdir, (doc.safe >= SafeMode::SAFE ? outdir : nil)
1492
- # NOTE don't warn if src can't be read and dest already exists (see #2323)
1493
- if stylesheet_src != stylesheet_dest && (stylesheet_data = doc.read_asset stylesheet_src,
1494
- warn_on_failure: !(::File.file? stylesheet_dest), label: 'stylesheet')
1495
- ::File.write stylesheet_dest, stylesheet_data, mode: FILE_WRITE_MODE
1496
- end
1497
- end
1498
- syntax_hl.write_stylesheet doc, stylesoutdir if copy_syntax_hl_stylesheet
1499
- end
1500
- end
1501
- doc
1502
- else
1503
- output
1504
- end
1505
- end
1506
-
1507
- # Deprecated: Use {Asciidoctor.convert} instead.
1508
- alias render convert
1509
-
1510
- # Public: Parse the contents of the AsciiDoc source file into an
1511
- # Asciidoctor::Document and convert it to the specified backend format.
1512
- #
1513
- # input - the String AsciiDoc source filename
1514
- # options - a String, Array or Hash of options to control processing (default: {})
1515
- # String and Array values are converted into a Hash.
1516
- # See Asciidoctor::Document#initialize for details about options.
1517
- #
1518
- # Returns the Document object if the converted String is written to a
1519
- # file, otherwise the converted String
1520
- def convert_file filename, options = {}
1521
- ::File.open(filename, FILE_READ_MODE) {|file| self.convert file, options }
1522
- end
1523
-
1524
- # Deprecated: Use {Asciidoctor.convert_file} instead.
1525
- alias render_file convert_file
1526
-
1527
520
  # Internal: Automatically load the Asciidoctor::Extensions module.
1528
521
  #
1529
522
  # Requires the Asciidoctor::Extensions module if the name is :Extensions.
@@ -1534,7 +527,7 @@ module Asciidoctor
1534
527
  # defined prior to it being loaded.
1535
528
  #
1536
529
  # Returns the resolved constant, if resolved, otherwise nothing.
1537
- def const_missing name
530
+ def self.const_missing name
1538
531
  if name == :Extensions
1539
532
  require_relative 'asciidoctor/extensions'
1540
533
  Extensions
@@ -1543,11 +536,9 @@ module Asciidoctor
1543
536
  end
1544
537
  end unless RUBY_ENGINE == 'opal'
1545
538
 
1546
- end
1547
-
1548
539
  unless RUBY_ENGINE == 'opal'
1549
- autoload :SyntaxHighlighter, %(#{LIB_DIR}/asciidoctor/syntax_highlighter)
1550
- autoload :Timings, %(#{LIB_DIR}/asciidoctor/timings)
540
+ autoload :SyntaxHighlighter, %(#{__dir__}/asciidoctor/syntax_highlighter)
541
+ autoload :Timings, %(#{__dir__}/asciidoctor/timings)
1551
542
  end
1552
543
  end
1553
544
 
@@ -1557,6 +548,7 @@ require_relative 'asciidoctor/core_ext'
1557
548
  # modules and helpers
1558
549
  require_relative 'asciidoctor/helpers'
1559
550
  require_relative 'asciidoctor/logging'
551
+ require_relative 'asciidoctor/rx'
1560
552
  require_relative 'asciidoctor/substitutors'
1561
553
  require_relative 'asciidoctor/version'
1562
554
 
@@ -1580,6 +572,10 @@ require_relative 'asciidoctor/stylesheets'
1580
572
  require_relative 'asciidoctor/table'
1581
573
  require_relative 'asciidoctor/writer'
1582
574
 
575
+ # main API entry points
576
+ require_relative 'asciidoctor/load'
577
+ require_relative 'asciidoctor/convert'
578
+
1583
579
  if RUBY_ENGINE == 'opal'
1584
580
  require_relative 'asciidoctor/syntax_highlighter'
1585
581
  require_relative 'asciidoctor/timings'