asciidoctor 2.0.7 → 2.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +169 -7
  3. data/LICENSE +2 -1
  4. data/README-de.adoc +5 -15
  5. data/README-fr.adoc +4 -14
  6. data/README-jp.adoc +234 -186
  7. data/README-zh_CN.adoc +7 -17
  8. data/README.adoc +18 -18
  9. data/asciidoctor.gemspec +4 -4
  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 +33 -30
  42. data/lib/asciidoctor.rb +89 -791
  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 +3 -3
  48. data/lib/asciidoctor/convert.rb +167 -162
  49. data/lib/asciidoctor/converter.rb +14 -13
  50. data/lib/asciidoctor/converter/docbook5.rb +10 -26
  51. data/lib/asciidoctor/converter/html5.rb +62 -43
  52. data/lib/asciidoctor/converter/manpage.rb +13 -12
  53. data/lib/asciidoctor/converter/template.rb +6 -3
  54. data/lib/asciidoctor/document.rb +25 -41
  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 +101 -101
  59. data/lib/asciidoctor/parser.rb +30 -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 +61 -39
  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 +15 -7
  70. data/lib/asciidoctor/table.rb +52 -23
  71. data/lib/asciidoctor/version.rb +1 -1
  72. data/man/asciidoctor.1 +6 -6
  73. data/man/asciidoctor.adoc +4 -3
  74. metadata +10 -9
@@ -433,8 +433,10 @@ class Parser
433
433
  # is treated like an untitled section
434
434
  elsif preamble # implies parent == document
435
435
  if preamble.blocks?
436
+ if book || document.blocks[1] || !Compliance.unwrap_standalone_preamble
437
+ preamble.source_location = preamble.blocks[0].source_location if document.sourcemap
436
438
  # unwrap standalone preamble (i.e., document has no sections) except for books, if permissible
437
- unless book || document.blocks[1] || !Compliance.unwrap_standalone_preamble
439
+ else
438
440
  document.blocks.shift
439
441
  while (child_block = preamble.blocks.shift)
440
442
  document << child_block
@@ -596,7 +598,8 @@ class Parser
596
598
  end
597
599
  end
598
600
  if blk_ctx == :image
599
- document.register :images, [target, (attributes['imagesdir'] = doc_attrs['imagesdir'])]
601
+ document.register :images, target
602
+ attributes['imagesdir'] = doc_attrs['imagesdir']
600
603
  # NOTE style is the value of the first positional attribute in the block attribute line
601
604
  attributes['alt'] ||= style || (attributes['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
602
605
  unless (scaledwidth = attributes.delete 'scaledwidth').nil_or_empty?
@@ -857,6 +860,7 @@ class Parser
857
860
  when :literal
858
861
  block = build_block(block_context, :verbatim, terminator, parent, reader, attributes)
859
862
  when :example
863
+ attributes['caption'] = '' if attributes['collapsible-option']
860
864
  block = build_block(block_context, :compound, terminator, parent, reader, attributes)
861
865
  when :quote, :verse
862
866
  AttributeList.rekey(attributes, [nil, 'attribution', 'citetitle'])
@@ -898,7 +902,7 @@ class Parser
898
902
  # FIXME title and caption should be assigned when block is constructed (though we need to handle all cases)
899
903
  if attributes['title']
900
904
  block.title = block_title = attributes.delete 'title'
901
- if (caption_attr_name = CAPTION_ATTR_NAMES[block.context]) && document.attributes[caption_attr_name]
905
+ if (caption_attr_name = CAPTION_ATTRIBUTE_NAMES[block.context]) && document.attributes[caption_attr_name]
902
906
  block.assign_caption (attributes.delete 'caption')
903
907
  end
904
908
  end
@@ -1147,14 +1151,16 @@ class Parser
1147
1151
  def self.catalog_inline_anchors text, block, document, reader
1148
1152
  text.scan InlineAnchorScanRx do
1149
1153
  if (id = $1)
1150
- if (reftext = $2)
1151
- next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
1152
- end
1154
+ next if (reftext = $2) && (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
1153
1155
  else
1154
1156
  id = $3
1155
1157
  if (reftext = $4)
1156
- reftext = reftext.gsub '\]', ']' if reftext.include? ']'
1157
- next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
1158
+ if reftext.include? ']'
1159
+ reftext = reftext.gsub '\]', ']'
1160
+ reftext = document.sub_attributes reftext if reftext.include? ATTR_REF_HEAD
1161
+ elsif (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
1162
+ next
1163
+ end
1158
1164
  end
1159
1165
  end
1160
1166
  unless document.register :refs, [id, (Inline.new block, :anchor, reftext, type: :ref, id: id)]
@@ -1532,7 +1538,7 @@ class Parser
1532
1538
 
1533
1539
  reader.unshift_line this_line if this_line
1534
1540
 
1535
- buffer.delete_at detached_continuation if detached_continuation
1541
+ buffer[detached_continuation] = '' if detached_continuation
1536
1542
 
1537
1543
  until buffer.empty?
1538
1544
  # strip trailing blank lines to prevent empty blocks
@@ -2118,6 +2124,8 @@ class Parser
2118
2124
  name = 'sectnums'
2119
2125
  elsif name == 'hardbreaks'
2120
2126
  name = 'hardbreaks-option'
2127
+ elsif name == 'showtitle'
2128
+ store_attribute 'notitle', (value ? nil : ''), doc, attrs
2121
2129
  end
2122
2130
 
2123
2131
  if doc
@@ -2272,9 +2280,15 @@ class Parser
2272
2280
  end
2273
2281
 
2274
2282
  skipped = table_reader.skip_blank_lines || 0
2283
+ if attributes['header-option']
2284
+ table.has_header_option = true
2285
+ elsif skipped == 0 && !attributes['noheader-option']
2286
+ # NOTE: assume table has header until we know otherwise; if it doesn't (nil), cells in first row get reprocessed
2287
+ table.has_header_option = :implicit
2288
+ implicit_header = true
2289
+ end
2275
2290
  parser_ctx = Table::ParserContext.new table_reader, table, attributes
2276
2291
  format, loop_idx, implicit_header_boundary = parser_ctx.format, -1, nil
2277
- implicit_header = true unless skipped > 0 || attributes['header-option'] || attributes['noheader-option']
2278
2292
 
2279
2293
  while (line = table_reader.read_line)
2280
2294
  if (beyond_first = (loop_idx += 1) > 0) && line.empty?
@@ -2294,7 +2308,7 @@ class Parser
2294
2308
  implicit_header_boundary = nil if implicit_header_boundary
2295
2309
  # otherwise, the cell continues from previous line
2296
2310
  elsif implicit_header_boundary && implicit_header_boundary == loop_idx
2297
- implicit_header, implicit_header_boundary = false, nil
2311
+ table.has_header_option = implicit_header = implicit_header_boundary = nil
2298
2312
  end
2299
2313
  end
2300
2314
  end
@@ -2306,7 +2320,7 @@ class Parser
2306
2320
  if table_reader.has_more_lines? && table_reader.peek_line.empty?
2307
2321
  implicit_header_boundary = 1
2308
2322
  else
2309
- implicit_header = false
2323
+ table.has_header_option = implicit_header = nil
2310
2324
  end
2311
2325
  end
2312
2326
  end
@@ -2357,7 +2371,7 @@ class Parser
2357
2371
  case format
2358
2372
  when 'csv'
2359
2373
  if parser_ctx.buffer_has_unclosed_quotes?
2360
- implicit_header, implicit_header_boundary = false, nil if implicit_header_boundary && loop_idx == 0
2374
+ table.has_header_option = implicit_header = implicit_header_boundary = nil if implicit_header_boundary && loop_idx == 0
2361
2375
  parser_ctx.keep_cell_open
2362
2376
  else
2363
2377
  parser_ctx.close_cell true
@@ -2379,15 +2393,8 @@ class Parser
2379
2393
  end
2380
2394
  end
2381
2395
 
2382
- unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
2383
- table.assign_column_widths
2384
- end
2385
-
2386
- if implicit_header
2387
- table.has_header_option = true
2388
- attributes['header-option'] = ''
2389
- end
2390
-
2396
+ table.assign_column_widths unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
2397
+ table.has_header_option = true if implicit_header
2391
2398
  table.partition_header_footer attributes
2392
2399
 
2393
2400
  table
@@ -2578,9 +2585,7 @@ class Parser
2578
2585
  attributes['role'] = (existing_role = attributes['role']).nil_or_empty? ? (parsed_attrs[:role].join ' ') : %(#{existing_role} #{parsed_attrs[:role].join ' '})
2579
2586
  end
2580
2587
 
2581
- if parsed_attrs.key? :option
2582
- (opts = parsed_attrs[:option]).each {|opt| attributes[%(#{opt}-option)] = '' }
2583
- end
2588
+ parsed_attrs[:option].each {|opt| attributes[%(#{opt}-option)] = '' } if parsed_attrs.key? :option
2584
2589
 
2585
2590
  parsed_style
2586
2591
  else
@@ -331,11 +331,12 @@ class PathResolver
331
331
 
332
332
  # Public: Securely resolve a system path
333
333
  #
334
- # Resolve a system path from the target relative to the start path, jail path, or working
335
- # directory (specified in the constructor), in that order. If a jail path is specified, enforce
336
- # that the resolved path descends from the jail path. If a jail path is not provided, the resolved
337
- # path may be any location on the system. If the resolved path is absolute, use it as is (unless
338
- # it breaches the jail path). Expand all parent and self references in the resolved path.
334
+ # Resolves the target to an absolute path on the current filesystem. The target is assumed to be
335
+ # relative to the start path, jail path, or working directory (specified in the constructor), in
336
+ # that order. If a jail path is specified, the resolved path is forced to descend from the jail
337
+ # path. If a jail path is not provided, the resolved path may be any location on the system. If
338
+ # the target is an absolute path, use it as is (unless it breaches the jail path). Expands all
339
+ # parent and self references in the resolved path.
339
340
  #
340
341
  # target - the String target path
341
342
  # start - the String start path from which to resolve a relative target; falls back to jail, if
@@ -347,8 +348,9 @@ class PathResolver
347
348
  # automatically recover when an illegal path is encountered
348
349
  # * :target_name is used in messages to refer to the path being resolved
349
350
  #
350
- # returns a String path relative to the start path, if specified, and confined to the jail path,
351
- # if specified. The path is posixified and all parent and self references in the path are expanded.
351
+ # Returns an absolute String path relative to the start path, if specified, and confined to the
352
+ # jail path, if specified. The path is posixified and all parent and self references in the path
353
+ # are expanded.
352
354
  def system_path target, start = nil, jail = nil, opts = {}
353
355
  if jail
354
356
  raise ::SecurityError, %(Jail is not an absolute path: #{jail}) unless root? jail
@@ -362,7 +364,7 @@ class PathResolver
362
364
  if jail && !(descends_from? target_path, jail)
363
365
  if opts.fetch :recover, true
364
366
  logger.warn %(#{opts[:target_name] || 'path'} is outside of jail; recovering automatically)
365
- target_segments, _ = partition_path target_path
367
+ target_segments, = partition_path target_path
366
368
  jail_segments, jail_root = partition_path jail
367
369
  return join_path jail_segments + target_segments, jail_root
368
370
  else
@@ -371,7 +373,7 @@ class PathResolver
371
373
  end
372
374
  return target_path
373
375
  else
374
- target_segments, _ = partition_path target
376
+ target_segments, = partition_path target
375
377
  end
376
378
  else
377
379
  target_segments = []
@@ -387,7 +389,7 @@ class PathResolver
387
389
  return expand_path start
388
390
  end
389
391
  else
390
- target_segments, _ = partition_path start
392
+ target_segments, = partition_path start
391
393
  start = jail || @working_dir
392
394
  end
393
395
  elsif start.nil_or_empty?
@@ -419,7 +421,7 @@ class PathResolver
419
421
  if (resolved_segments = start_segments + target_segments).include? DOT_DOT
420
422
  unresolved_segments, resolved_segments = resolved_segments, []
421
423
  if jail
422
- jail_segments, _ = partition_path jail unless jail_segments
424
+ jail_segments, = partition_path jail unless jail_segments
423
425
  warned = false
424
426
  unresolved_segments.each do |segment|
425
427
  if segment == DOT_DOT
@@ -450,7 +452,7 @@ class PathResolver
450
452
  target_path
451
453
  elsif opts.fetch :recover, true
452
454
  logger.warn %(#{opts[:target_name] || 'path'} is outside of jail; recovering automatically)
453
- jail_segments, _ = partition_path jail unless jail_segments
455
+ jail_segments, = partition_path jail unless jail_segments
454
456
  join_path jail_segments + target_segments, jail_root
455
457
  else
456
458
  raise ::SecurityError, %(#{opts[:target_name] || 'path'} #{target} is outside of jail: #{jail} (disallowed in safe mode))
@@ -476,25 +478,15 @@ class PathResolver
476
478
  def web_path target, start = nil
477
479
  target = posixify target
478
480
  start = posixify start
479
- uri_prefix = nil
480
481
 
481
482
  unless start.nil_or_empty? || (web_root? target)
482
- target = (start.end_with? SLASH) ? %(#{start}#{target}) : %(#{start}#{SLASH}#{target})
483
- if (uri_prefix = Helpers.uri_prefix target)
484
- target = target[uri_prefix.length..-1]
485
- end
483
+ target, uri_prefix = extract_uri_prefix %(#{start}#{(start.end_with? SLASH) ? '' : SLASH}#{target})
486
484
  end
487
485
 
488
486
  # use this logic instead if we want to normalize target if it contains a URI
489
487
  #unless web_root? target
490
- # if preserve_uri_target && (uri_prefix = Helpers.uri_prefix target)
491
- # target = target[uri_prefix.length..-1]
492
- # elsif !start.nil_or_empty?
493
- # target = %(#{start}#{SLASH}#{target})
494
- # if (uri_prefix = Helpers.uri_prefix target)
495
- # target = target[uri_prefix.length..-1]
496
- # end
497
- # end
488
+ # target, uri_prefix = extract_uri_prefix target if preserve_uri_target
489
+ # target, uri_prefix = extract_uri_prefix %(#{start}#{SLASH}#{target}) unless uri_prefix || start.nil_or_empty?
498
490
  #end
499
491
 
500
492
  target_segments, target_root = partition_path target, true
@@ -521,5 +513,23 @@ class PathResolver
521
513
 
522
514
  uri_prefix ? %(#{uri_prefix}#{resolved_path}) : resolved_path
523
515
  end
516
+
517
+ private
518
+
519
+ # Internal: Efficiently extracts the URI prefix from the specified String if the String is a URI
520
+ #
521
+ # Uses the Asciidoctor::UriSniffRx regex to match the URI prefix in the specified String (e.g., http://). If present,
522
+ # the prefix is removed.
523
+ #
524
+ # str - the String to check
525
+ #
526
+ # returns a tuple containing the specified string without the URI prefix, if present, and the extracted URI prefix.
527
+ def extract_uri_prefix str
528
+ if (str.include? ':') && UriSniffRx =~ str
529
+ [(str.slice $&.length, str.length), $&]
530
+ else
531
+ str
532
+ end
533
+ end
524
534
  end
525
535
  end
@@ -570,17 +570,18 @@ class Reader
570
570
  #
571
571
  # data - A String Array or String of source data to be normalized.
572
572
  # opts - A Hash of options to control how lines are prepared.
573
- # :normalize - Enables line normalization, which coerces the encoding to UTF-8 and removes trailing whitespace
574
- # (optional, default: false).
573
+ # :normalize - Enables line normalization, which coerces the encoding to UTF-8 and removes trailing whitespace;
574
+ # :rstrip removes all trailing whitespace; :chomp removes trailing newline only (optional, not set).
575
575
  #
576
576
  # Returns A String Array of source lines. If the source data is an Array, this method returns a copy.
577
577
  def prepare_lines data, opts = {}
578
- if opts[:normalize]
579
- ::Array === data ? (Helpers.prepare_source_array data) : (Helpers.prepare_source_string data)
578
+ if (normalize = opts[:normalize])
579
+ trim_end = normalize == :chomp ? false : true
580
+ ::Array === data ? (Helpers.prepare_source_array data, trim_end) : (Helpers.prepare_source_string data, trim_end)
580
581
  elsif ::Array === data
581
582
  data.drop 0
582
583
  elsif data
583
- data.split LF, -1
584
+ data.chomp.split LF, -1
584
585
  else
585
586
  []
586
587
  end
@@ -717,7 +718,7 @@ class PreprocessorReader < Reader
717
718
  end
718
719
 
719
720
  # effectively fill the buffer
720
- if (@lines = prepare_lines data, normalize: true, condense: false, indent: attributes['indent']).empty?
721
+ if (@lines = prepare_lines data, normalize: @process_lines || :chomp, condense: @process_lines, indent: attributes['indent']).empty?
721
722
  pop_include
722
723
  else
723
724
  # FIXME we eventually want to handle leveloffset without affecting the lines
@@ -1060,7 +1061,13 @@ class PreprocessorReader < Reader
1060
1061
  return inc_path
1061
1062
  end
1062
1063
 
1064
+ if (enc = parsed_attrs['encoding']) && (::Encoding.find enc rescue nil)
1065
+ (read_mode_params = read_mode.split ':')[1] = enc
1066
+ read_mode = read_mode_params.join ':'
1067
+ end unless RUBY_ENGINE_OPAL
1068
+
1063
1069
  inc_linenos = inc_tags = nil
1070
+ # NOTE attrlist is nil if missing from include directive
1064
1071
  if attrlist
1065
1072
  if parsed_attrs.key? 'lines'
1066
1073
  inc_linenos = []
@@ -1148,7 +1155,7 @@ class PreprocessorReader < Reader
1148
1155
  active_tag, select = tag_stack.empty? ? [nil, base_select] : tag_stack[-1]
1149
1156
  elsif inc_tags.key? this_tag
1150
1157
  include_cursor = create_include_cursor inc_path, expanded_target, inc_lineno
1151
- if (idx = tag_stack.rindex {|key, _| key == this_tag })
1158
+ if (idx = tag_stack.rindex {|key,| key == this_tag })
1152
1159
  idx == 0 ? tag_stack.shift : (tag_stack.delete_at idx)
1153
1160
  logger.warn message_with_context %(mismatched end tag (expected '#{active_tag}' but found '#{this_tag}') at line #{inc_lineno} of include #{target_type}: #{inc_path}), source_location: cursor, include_location: include_cursor
1154
1161
  else
@@ -0,0 +1,722 @@
1
+ module Asciidoctor
2
+ # A collection of regular expression constants used by the parser. (For speed, these are not defined in the Rx module,
3
+ # but rather directly in the Asciidoctor module).
4
+ #
5
+ # NOTE The following pattern, which appears frequently, captures the contents between square brackets, ignoring
6
+ # escaped closing brackets (closing brackets prefixed with a backslash '\' character)
7
+ #
8
+ # Pattern: \[(|#{CC_ALL}*?[^\\])\]
9
+ # Matches: [enclosed text] and [enclosed [text\]], not [enclosed text \\] or [\\] (as these require a trailing space)
10
+ module Rx; end
11
+
12
+ ## Document header
13
+
14
+ # Matches the author info line immediately following the document title.
15
+ #
16
+ # Examples
17
+ #
18
+ # Doc Writer <doc@example.com>
19
+ # Mary_Sue Brontë
20
+ #
21
+ AuthorInfoLineRx = /^(#{CG_WORD}[#{CC_WORD}\-'.]*)(?: +(#{CG_WORD}[#{CC_WORD}\-'.]*))?(?: +(#{CG_WORD}[#{CC_WORD}\-'.]*))?(?: +<([^>]+)>)?$/
22
+
23
+ # Matches the delimiter that separates multiple authors.
24
+ #
25
+ # Examples
26
+ #
27
+ # Doc Writer; Junior Writer
28
+ #
29
+ AuthorDelimiterRx = /;(?: |$)/
30
+
31
+ # Matches the revision info line, which appears immediately following
32
+ # the author info line beneath the document title.
33
+ #
34
+ # Examples
35
+ #
36
+ # v1.0
37
+ # 2013-01-01
38
+ # v1.0, 2013-01-01: Ring in the new year release
39
+ # 1.0, Jan 01, 2013
40
+ #
41
+ RevisionInfoLineRx = /^(?:[^\d{]*(#{CC_ANY}*?),)? *(?!:)(#{CC_ANY}*?)(?: *(?!^),?: *(#{CC_ANY}*))?$/
42
+
43
+ # Matches the title and volnum in the manpage doctype.
44
+ #
45
+ # Examples
46
+ #
47
+ # = asciidoctor(1)
48
+ # = asciidoctor ( 1 )
49
+ #
50
+ ManpageTitleVolnumRx = /^(#{CC_ANY}+?) *\( *(#{CC_ANY}+?) *\)$/
51
+
52
+ # Matches the name and purpose in the manpage doctype.
53
+ #
54
+ # Examples
55
+ #
56
+ # asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
57
+ #
58
+ ManpageNamePurposeRx = /^(#{CC_ANY}+?) +- +(#{CC_ANY}+)$/
59
+
60
+ ## Preprocessor directives
61
+
62
+ # Matches a conditional preprocessor directive (e.g., ifdef, ifndef, ifeval and endif).
63
+ #
64
+ # Examples
65
+ #
66
+ # ifdef::basebackend-html[]
67
+ # ifndef::theme[]
68
+ # ifeval::["{asciidoctor-version}" >= "0.1.0"]
69
+ # ifdef::asciidoctor[Asciidoctor!]
70
+ # endif::theme[]
71
+ # endif::basebackend-html[]
72
+ # endif::[]
73
+ #
74
+ ConditionalDirectiveRx = /^(\\)?(ifdef|ifndef|ifeval|endif)::(\S*?(?:([,+])\S*?)?)\[(#{CC_ANY}+)?\]$/
75
+
76
+ # Matches a restricted (read as safe) eval expression.
77
+ #
78
+ # Examples
79
+ #
80
+ # "{asciidoctor-version}" >= "0.1.0"
81
+ #
82
+ EvalExpressionRx = /^(#{CC_ANY}+?) *([=!><]=|[><]) *(#{CC_ANY}+)$/
83
+
84
+ # Matches an include preprocessor directive.
85
+ #
86
+ # Examples
87
+ #
88
+ # include::chapter1.ad[]
89
+ # include::example.txt[lines=1;2;5..10]
90
+ #
91
+ IncludeDirectiveRx = /^(\\)?include::([^\[][^\[]*)\[(#{CC_ANY}+)?\]$/
92
+
93
+ # Matches a trailing tag directive in an include file.
94
+ #
95
+ # Examples
96
+ #
97
+ # // tag::try-catch[]
98
+ # try {
99
+ # someMethod();
100
+ # catch (Exception e) {
101
+ # log(e);
102
+ # }
103
+ # // end::try-catch[]
104
+ # NOTE m flag is required for Asciidoctor.js
105
+ TagDirectiveRx = /\b(?:tag|(e)nd)::(\S+?)\[\](?=$|[ \r])/m
106
+
107
+ ## Attribute entries and references
108
+
109
+ # Matches a document attribute entry.
110
+ #
111
+ # Examples
112
+ #
113
+ # :foo: bar
114
+ # :First Name: Dan
115
+ # :sectnums!:
116
+ # :!toc:
117
+ # :long-entry: Attribute value lines ending in ' \' \
118
+ # are joined together as a single value, \
119
+ # collapsing the line breaks and indentation to \
120
+ # a single space.
121
+ #
122
+ AttributeEntryRx = /^:(!?#{CG_WORD}[^:]*):(?:[ \t]+(#{CC_ANY}*))?$/
123
+
124
+ # Matches invalid characters in an attribute name.
125
+ InvalidAttributeNameCharsRx = /[^#{CC_WORD}-]/
126
+
127
+ # Matches a pass inline macro that surrounds the value of an attribute
128
+ # entry once it has been parsed.
129
+ #
130
+ # Examples
131
+ #
132
+ # pass:[text]
133
+ # pass:a[{a} {b} {c}]
134
+ #
135
+ if RUBY_ENGINE == 'opal'
136
+ # NOTE In JavaScript, ^ and $ match the boundaries of the string when the m flag is not set
137
+ AttributeEntryPassMacroRx = /^pass:([a-z]+(?:,[a-z-]+)*)?\[(#{CC_ALL}*)\]$/
138
+ else
139
+ AttributeEntryPassMacroRx = /\Apass:([a-z]+(?:,[a-z-]+)*)?\[(.*)\]\Z/m
140
+ end
141
+
142
+ # Matches an inline attribute reference.
143
+ #
144
+ # Examples
145
+ #
146
+ # {foobar} or {app_name} or {product-version}
147
+ # {counter:sequence-name:1}
148
+ # {set:foo:bar}
149
+ # {set:name!}
150
+ #
151
+ AttributeReferenceRx = /(\\)?\{(#{CG_WORD}[#{CC_WORD}-]*|(set|counter2?):#{CC_ANY}+?)(\\)?\}/
152
+
153
+ ## Paragraphs and delimited blocks
154
+
155
+ # Matches an anchor (i.e., id + optional reference text) on a line above a block.
156
+ #
157
+ # Examples
158
+ #
159
+ # [[idname]]
160
+ # [[idname,Reference Text]]
161
+ #
162
+ BlockAnchorRx = /^\[\[(?:|([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+))?)\]\]$/
163
+
164
+ # Matches an attribute list above a block element.
165
+ #
166
+ # Examples
167
+ #
168
+ # # strictly positional
169
+ # [quote, Adam Smith, Wealth of Nations]
170
+ #
171
+ # # name/value pairs
172
+ # [NOTE, caption="Good to know"]
173
+ #
174
+ # # as attribute reference
175
+ # [{lead}]
176
+ #
177
+ BlockAttributeListRx = /^\[(|[#{CC_WORD}.#%{,"']#{CC_ANY}*)\]$/
178
+
179
+ # A combined pattern that matches either a block anchor or a block attribute list.
180
+ #
181
+ # TODO this one gets hit a lot, should be optimized as much as possible
182
+ BlockAttributeLineRx = /^\[(?:|[#{CC_WORD}.#%{,"']#{CC_ANY}*|\[(?:|[#{CC_ALPHA}_:][#{CC_WORD}\-:.]*(?:, *#{CC_ANY}+)?)\])\]$/
183
+
184
+ # Matches a title above a block.
185
+ #
186
+ # Examples
187
+ #
188
+ # .Title goes here
189
+ #
190
+ BlockTitleRx = /^\.(\.?[^ \t.]#{CC_ANY}*)$/
191
+
192
+ # Matches an admonition label at the start of a paragraph.
193
+ #
194
+ # Examples
195
+ #
196
+ # NOTE: Just a little note.
197
+ # TIP: Don't forget!
198
+ #
199
+ AdmonitionParagraphRx = /^(#{ADMONITION_STYLES.to_a.join '|'}):[ \t]+/
200
+
201
+ # Matches a literal paragraph, which is a line of text preceded by at least one space.
202
+ #
203
+ # Examples
204
+ #
205
+ # <SPACE>Foo
206
+ # <TAB>Foo
207
+ LiteralParagraphRx = /^([ \t]+#{CC_ANY}*)$/
208
+
209
+ # Matches a comment block.
210
+ #
211
+ # Examples
212
+ #
213
+ # ////
214
+ # This is a block comment.
215
+ # It can span one or more lines.
216
+ # ////
217
+ #CommentBlockRx = %r(^/{4,}$)
218
+
219
+ # Matches a comment line.
220
+ #
221
+ # Examples
222
+ #
223
+ # // note to author
224
+ #
225
+ #CommentLineRx = %r(^//(?=[^/]|$))
226
+
227
+ ## Section titles
228
+
229
+ # Matches an Atx (single-line) section title.
230
+ #
231
+ # Examples
232
+ #
233
+ # == Foo
234
+ # // ^ a level 1 (h2) section title
235
+ #
236
+ # == Foo ==
237
+ # // ^ also a level 1 (h2) section title
238
+ #
239
+ AtxSectionTitleRx = /^(=={0,5})[ \t]+(#{CC_ANY}+?)(?:[ \t]+\1)?$/
240
+
241
+ # Matches an extended Atx section title that includes support for the Markdown variant.
242
+ ExtAtxSectionTitleRx = /^(=={0,5}|#\#{0,5})[ \t]+(#{CC_ANY}+?)(?:[ \t]+\1)?$/
243
+
244
+ # Matches the title only (first line) of an Setext (two-line) section title.
245
+ # The title cannot begin with a dot and must have at least one alphanumeric character.
246
+ SetextSectionTitleRx = /^((?!\.)#{CC_ANY}*?#{CG_ALNUM}#{CC_ANY}*)$/
247
+
248
+ # Matches an anchor (i.e., id + optional reference text) inside a section title.
249
+ #
250
+ # Examples
251
+ #
252
+ # Section Title [[idname]]
253
+ # Section Title [[idname,Reference Text]]
254
+ #
255
+ InlineSectionAnchorRx = / (\\)?\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+))?\]\]$/
256
+
257
+ # Matches invalid ID characters in a section title.
258
+ #
259
+ # NOTE uppercase chars not included since expression is only run on a lowercase string
260
+ InvalidSectionIdCharsRx = /<[^>]+>|&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^ #{CC_WORD}\-.]+?/
261
+
262
+ # Matches an explicit section level style like sect1
263
+ #
264
+ SectionLevelStyleRx = /^sect\d$/
265
+
266
+ ## Lists
267
+
268
+ # Detects the start of any list item.
269
+ #
270
+ # NOTE we only have to check as far as the blank character because we know it means non-whitespace follows.
271
+ # IMPORTANT if this regexp does not agree with the regexp for each list type, the parser will hang.
272
+ AnyListRx = %r(^(?:[ \t]*(?:-|\*\**|\.\.*|\u2022|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|(?!//[^/])[ \t]*[^ \t]#{CC_ANY}*?(?::::{0,2}|;;)(?:$|[ \t])|<(?:\d+|\.)>[ \t]))
273
+
274
+ # Matches an unordered list item (one level for hyphens, up to 5 levels for asterisks).
275
+ #
276
+ # Examples
277
+ #
278
+ # * Foo
279
+ # - Foo
280
+ #
281
+ # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
282
+ UnorderedListRx = /^[ \t]*(-|\*\**|\u2022)[ \t]+(#{CC_ANY}*)$/
283
+
284
+ # Matches an ordered list item (explicit numbering or up to 5 consecutive dots).
285
+ #
286
+ # Examples
287
+ #
288
+ # . Foo
289
+ # .. Foo
290
+ # 1. Foo (arabic, default)
291
+ # a. Foo (loweralpha)
292
+ # A. Foo (upperalpha)
293
+ # i. Foo (lowerroman)
294
+ # I. Foo (upperroman)
295
+ #
296
+ # NOTE leading space match is not always necessary, but is used for list reader
297
+ # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
298
+ OrderedListRx = /^[ \t]*(\.\.*|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]+(#{CC_ANY}*)$/
299
+
300
+ # Matches the ordinals for each type of ordered list.
301
+ OrderedListMarkerRxMap = {
302
+ arabic: /\d+\./,
303
+ loweralpha: /[a-z]\./,
304
+ lowerroman: /[ivx]+\)/,
305
+ upperalpha: /[A-Z]\./,
306
+ upperroman: /[IVX]+\)/,
307
+ #lowergreek: /[a-z]\]/,
308
+ }
309
+
310
+ # Matches a description list entry.
311
+ #
312
+ # Examples
313
+ #
314
+ # foo::
315
+ # bar:::
316
+ # baz::::
317
+ # blah;;
318
+ #
319
+ # # the term may be followed by a description on the same line...
320
+ #
321
+ # foo:: The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).
322
+ #
323
+ # # ...or on a separate line, which may optionally be indented
324
+ #
325
+ # foo::
326
+ # The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).
327
+ #
328
+ # # attribute references may be used in both the term and the description
329
+ #
330
+ # {foo-term}:: {foo-desc}
331
+ #
332
+ # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
333
+ # NOTE must skip line comment when looking for next list item inside list
334
+ DescriptionListRx = %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?)(:::{0,2}|;;)(?:$|[ \t]+(#{CC_ANY}*)$))
335
+
336
+ # Matches a sibling description list item (excluding the delimiter specified by the key).
337
+ # NOTE must skip line comment when looking for sibling list item
338
+ DescriptionListSiblingRx = {
339
+ '::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(::)(?:$|[ \t]+(#{CC_ANY}*)$)),
340
+ ':::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(:::)(?:$|[ \t]+(#{CC_ANY}*)$)),
341
+ '::::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(::::)(?:$|[ \t]+(#{CC_ANY}*)$)),
342
+ ';;' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?)(;;)(?:$|[ \t]+(#{CC_ANY}*)$))
343
+ }
344
+
345
+ # Matches a callout list item.
346
+ #
347
+ # Examples
348
+ #
349
+ # <1> Explanation
350
+ #
351
+ # or
352
+ #
353
+ # <.> Explanation with automatic number
354
+ #
355
+ # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
356
+ CalloutListRx = /^<(\d+|\.)>[ \t]+(#{CC_ANY}*)$/
357
+
358
+ # Matches a callout reference inside literal text.
359
+ #
360
+ # Examples
361
+ # <1> (optionally prefixed by //, #, -- or ;; line comment chars)
362
+ # <1> <2> (multiple callouts on one line)
363
+ # <!--1--> (for XML-based languages)
364
+ # <.> (auto-numbered)
365
+ #
366
+ # NOTE extract regexps are applied line-by-line, so we can use $ as end-of-line char
367
+ CalloutExtractRx = %r(((?://|#|--|;;) ?)?(\\)?<!?(|--)(\d+|\.)\3>(?=(?: ?\\?<!?\3(?:\d+|\.)\3>)*$))
368
+ CalloutExtractRxt = '(\\\\)?<()(\\d+|\\.)>(?=(?: ?\\\\?<(?:\\d+|\\.)>)*$)'
369
+ CalloutExtractRxMap = ::Hash.new {|h, k| h[k] = /(#{k.empty? ? '' : "#{::Regexp.escape k} ?"})?#{CalloutExtractRxt}/ }
370
+ # NOTE special characters have not been replaced when scanning
371
+ CalloutScanRx = /\\?<!?(|--)(\d+|\.)\1>(?=(?: ?\\?<!?\1(?:\d+|\.)\1>)*#{CC_EOL})/
372
+ # NOTE special characters have already been replaced when converting to an SGML format
373
+ CalloutSourceRx = %r(((?://|#|--|;;) ?)?(\\)?&lt;!?(|--)(\d+|\.)\3&gt;(?=(?: ?\\?&lt;!?\3(?:\d+|\.)\3&gt;)*#{CC_EOL}))
374
+ CalloutSourceRxt = "(\\\\)?&lt;()(\\d+|\\.)&gt;(?=(?: ?\\\\?&lt;(?:\\d+|\\.)&gt;)*#{CC_EOL})"
375
+ CalloutSourceRxMap = ::Hash.new {|h, k| h[k] = /(#{k.empty? ? '' : "#{::Regexp.escape k} ?"})?#{CalloutSourceRxt}/ }
376
+
377
+ # A Hash of regexps for lists used for dynamic access.
378
+ ListRxMap = { ulist: UnorderedListRx, olist: OrderedListRx, dlist: DescriptionListRx, colist: CalloutListRx }
379
+
380
+ ## Tables
381
+
382
+ # Parses the column spec (i.e., colspec) for a table.
383
+ #
384
+ # Examples
385
+ #
386
+ # 1*h,2*,^3e
387
+ #
388
+ ColumnSpecRx = /^(?:(\d+)\*)?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?(\d+%?|~)?([a-z])?$/
389
+
390
+ # Parses the start and end of a cell spec (i.e., cellspec) for a table.
391
+ #
392
+ # Examples
393
+ #
394
+ # 2.3+<.>m
395
+ #
396
+ # FIXME use step-wise scan (or treetop) rather than this mega-regexp
397
+ CellSpecStartRx = /^[ \t]*(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
398
+ CellSpecEndRx = /[ \t]+(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
399
+
400
+ # Block macros
401
+
402
+ # Matches the custom block macro pattern.
403
+ #
404
+ # Examples
405
+ #
406
+ # gist::123456[]
407
+ #
408
+ #--
409
+ # NOTE we've relaxed the match for target to accomodate the short format (e.g., name::[attrlist])
410
+ CustomBlockMacroRx = /^(#{CG_WORD}[#{CC_WORD}-]*)::(|\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
411
+
412
+ # Matches an image, video or audio block macro.
413
+ #
414
+ # Examples
415
+ #
416
+ # image::filename.png[Caption]
417
+ # video::http://youtube.com/12345[Cats vs Dogs]
418
+ #
419
+ BlockMediaMacroRx = /^(image|video|audio)::(\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
420
+
421
+ # Matches the TOC block macro.
422
+ #
423
+ # Examples
424
+ #
425
+ # toc::[]
426
+ # toc::[levels=2]
427
+ #
428
+ BlockTocMacroRx = /^toc::\[(#{CC_ANY}+)?\]$/
429
+
430
+ ## Inline macros
431
+
432
+ # Matches an anchor (i.e., id + optional reference text) in the flow of text.
433
+ #
434
+ # Examples
435
+ #
436
+ # [[idname]]
437
+ # [[idname,Reference Text]]
438
+ # anchor:idname[]
439
+ # anchor:idname[Reference Text]
440
+ #
441
+ InlineAnchorRx = /(\\)?(?:\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+?))?\]\]|anchor:([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)\[(?:\]|(#{CC_ANY}*?[^\\])\]))/
442
+
443
+ # Scans for a non-escaped anchor (i.e., id + optional reference text) in the flow of text.
444
+ InlineAnchorScanRx = /(?:^|[^\\\[])\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+?))?\]\]|(?:^|[^\\])anchor:([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)\[(?:\]|(#{CC_ANY}*?[^\\])\])/
445
+
446
+ # Scans for a leading, non-escaped anchor (i.e., id + optional reference text).
447
+ LeadingInlineAnchorRx = /^\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+?))?\]\]/
448
+
449
+ # Matches a bibliography anchor at the start of the list item text (in a bibliography list).
450
+ #
451
+ # Examples
452
+ #
453
+ # [[[Fowler_1997]]] Fowler M. ...
454
+ #
455
+ InlineBiblioAnchorRx = /^\[\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+?))?\]\]\]/
456
+
457
+ # Matches an inline e-mail address.
458
+ #
459
+ # doc.writer@example.com
460
+ #
461
+ InlineEmailRx = %r(([\\>:/])?#{CG_WORD}(?:&amp;|[#{CC_WORD}\-.%+])*@#{CG_ALNUM}[#{CC_ALNUM}_\-.]*\.[a-zA-Z]{2,5}\b)
462
+
463
+ # Matches an inline footnote macro, which is allowed to span multiple lines.
464
+ #
465
+ # Examples
466
+ # footnote:[text] (not referenceable)
467
+ # footnote:id[text] (referenceable)
468
+ # footnote:id[] (reference)
469
+ # footnoteref:[id,text] (legacy)
470
+ # footnoteref:[id] (legacy)
471
+ #
472
+ InlineFootnoteMacroRx = /\\?footnote(?:(ref):|:([#{CC_WORD}-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\](?!<\/a>)/m
473
+
474
+ # Matches an image or icon inline macro.
475
+ #
476
+ # Examples
477
+ #
478
+ # image:filename.png[Alt Text]
479
+ # image:http://example.com/images/filename.png[Alt Text]
480
+ # image:filename.png[More [Alt\] Text] (alt text becomes "More [Alt] Text")
481
+ # icon:github[large]
482
+ #
483
+ # NOTE be as non-greedy as possible by not allowing newline or left square bracket in target
484
+ InlineImageMacroRx = /\\?i(?:mage|con):([^:\s\[](?:[^\n\[]*[^\s\[])?)\[(|#{CC_ALL}*?[^\\])\]/m
485
+
486
+ # Matches an indexterm inline macro, which may span multiple lines.
487
+ #
488
+ # Examples
489
+ #
490
+ # indexterm:[Tigers,Big cats]
491
+ # (((Tigers,Big cats)))
492
+ # indexterm2:[Tigers]
493
+ # ((Tigers))
494
+ #
495
+ InlineIndextermMacroRx = /\\?(?:(indexterm2?):\[(#{CC_ALL}*?[^\\])\]|\(\((#{CC_ALL}+?)\)\)(?!\)))/m
496
+
497
+ # Matches either the kbd or btn inline macro.
498
+ #
499
+ # Examples
500
+ #
501
+ # kbd:[F3]
502
+ # kbd:[Ctrl+Shift+T]
503
+ # kbd:[Ctrl+\]]
504
+ # kbd:[Ctrl,T]
505
+ # btn:[Save]
506
+ #
507
+ InlineKbdBtnMacroRx = /(\\)?(kbd|btn):\[(#{CC_ALL}*?[^\\])\]/m
508
+
509
+ # Matches an implicit link and some of the link inline macro.
510
+ #
511
+ # Examples
512
+ #
513
+ # https://github.com
514
+ # https://github.com[GitHub]
515
+ # <https://github.com>
516
+ # link:https://github.com[]
517
+ # "https://github.com[]"
518
+ #
519
+ # FIXME revisit! the main issue is we need different rules for implicit vs explicit
520
+ InlineLinkRx = %r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];"'])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*([^\s.,\[\]<]))(?:\[(|#{CC_ALL}*?[^\\])\])?)m
521
+
522
+ # Match a link or e-mail inline macro.
523
+ #
524
+ # Examples
525
+ #
526
+ # link:path[label]
527
+ # mailto:doc.writer@example.com[]
528
+ #
529
+ # NOTE be as non-greedy as possible by not allowing space or left square bracket in target
530
+ InlineLinkMacroRx = /\\?(?:link|(mailto)):(|[^:\s\[][^\s\[]*)\[(|#{CC_ALL}*?[^\\])\]/m
531
+
532
+ # Matches the name of a macro.
533
+ #
534
+ MacroNameRx = /^#{CG_WORD}[#{CC_WORD}-]*$/
535
+
536
+ # Matches a stem (and alternatives, asciimath and latexmath) inline macro, which may span multiple lines.
537
+ #
538
+ # Examples
539
+ #
540
+ # stem:[x != 0]
541
+ # asciimath:[x != 0]
542
+ # latexmath:[\sqrt{4} = 2]
543
+ #
544
+ InlineStemMacroRx = /\\?(stem|(?:latex|ascii)math):([a-z]+(?:,[a-z-]+)*)?\[(#{CC_ALL}*?[^\\])\]/m
545
+
546
+ # Matches a menu inline macro.
547
+ #
548
+ # Examples
549
+ #
550
+ # menu:File[Save As...]
551
+ # menu:Edit[]
552
+ # menu:View[Page Style > No Style]
553
+ # menu:View[Page Style, No Style]
554
+ #
555
+ InlineMenuMacroRx = /\\?menu:(#{CG_WORD}|[#{CC_WORD}&][^\n\[]*[^\s\[])\[ *(?:|(#{CC_ALL}*?[^\\]))\]/m
556
+
557
+ # Matches an implicit menu inline macro.
558
+ #
559
+ # Examples
560
+ #
561
+ # "File > New..."
562
+ #
563
+ InlineMenuRx = /\\?"([#{CC_WORD}&][^"]*?[ \n]+&gt;[ \n]+[^"]*)"/
564
+
565
+ # Matches an inline passthrough, which may span multiple lines.
566
+ #
567
+ # Examples
568
+ #
569
+ # +text+
570
+ # `text` (compat)
571
+ #
572
+ # NOTE we always capture the attributes so we know when to use compatible (i.e., legacy) behavior
573
+ InlinePassRx = {
574
+ false => ['+', '`', /(^|[^#{CC_WORD};:])(?:\[([^\]]+)\])?(\\?(\+|`)(\S|\S#{CC_ALL}*?\S)\4)(?!#{CG_WORD})/m],
575
+ true => ['`', nil, /(^|[^`#{CC_WORD}])(?:\[([^\]]+)\])?(\\?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\4)(?![`#{CC_WORD}])/m]
576
+ }
577
+
578
+ # Matches an inline plus passthrough spanning multiple lines, but only when it occurs directly
579
+ # inside constrained monospaced formatting in non-compat mode.
580
+ #
581
+ # Examples
582
+ #
583
+ # +text+
584
+ #
585
+ SinglePlusInlinePassRx = /^(\\)?\+(\S|\S#{CC_ALL}*?\S)\+$/m
586
+
587
+ # Matches several variants of the passthrough inline macro, which may span multiple lines.
588
+ #
589
+ # Examples
590
+ #
591
+ # +++text+++
592
+ # $$text$$
593
+ # pass:quotes[text]
594
+ #
595
+ # NOTE we have to support an empty pass:[] for compatibility with AsciiDoc Python
596
+ InlinePassMacroRx = /(?:(?:(\\?)\[([^\]]+)\])?(\\{0,2})(\+\+\+?|\$\$)(#{CC_ALL}*?)\4|(\\?)pass:([a-z]+(?:,[a-z-]+)*)?\[(|#{CC_ALL}*?[^\\])\])/m
597
+
598
+ # Matches an xref (i.e., cross-reference) inline macro, which may span multiple lines.
599
+ #
600
+ # Examples
601
+ #
602
+ # <<id,reftext>>
603
+ # xref:id[reftext]
604
+ #
605
+ # NOTE special characters have already been escaped, hence the entity references
606
+ # NOTE { is included in start characters to support target that begins with attribute reference in title content
607
+ InlineXrefMacroRx = %r(\\?(?:&lt;&lt;([#{CC_WORD}#/.:{]#{CC_ALL}*?)&gt;&gt;|xref:([#{CC_WORD}#/.:{]#{CC_ALL}*?)\[(?:\]|(#{CC_ALL}*?[^\\])\])))m
608
+
609
+ ## Layout
610
+
611
+ # Matches a trailing + preceded by at least one space character,
612
+ # which forces a hard line break (<br> tag in HTML output).
613
+ #
614
+ # NOTE AsciiDoc Python allows + to be preceded by TAB; Asciidoctor does not
615
+ #
616
+ # Examples
617
+ #
618
+ # Humpty Dumpty sat on a wall, +
619
+ # Humpty Dumpty had a great fall.
620
+ #
621
+ if RUBY_ENGINE == 'opal'
622
+ # NOTE In JavaScript, ^ and $ only match the start and end of line if the multiline flag is present
623
+ HardLineBreakRx = /^(#{CC_ANY}*) \+$/m
624
+ else
625
+ # NOTE In Ruby, ^ and $ always match start and end of line
626
+ HardLineBreakRx = /^(.*) \+$/
627
+ end
628
+
629
+ # Matches a Markdown horizontal rule.
630
+ #
631
+ # Examples
632
+ #
633
+ # --- or - - -
634
+ # *** or * * *
635
+ # ___ or _ _ _
636
+ #
637
+ MarkdownThematicBreakRx = /^ {0,3}([-*_])( *)\1\2\1$/
638
+
639
+ # Matches an AsciiDoc or Markdown horizontal rule or AsciiDoc page break.
640
+ #
641
+ # Examples
642
+ #
643
+ # ''' (horizontal rule)
644
+ # <<< (page break)
645
+ # --- or - - - (horizontal rule, Markdown)
646
+ # *** or * * * (horizontal rule, Markdown)
647
+ # ___ or _ _ _ (horizontal rule, Markdown)
648
+ #
649
+ ExtLayoutBreakRx = /^(?:'{3,}|<{3,}|([-*_])( *)\1\2\1)$/
650
+
651
+ ## General
652
+
653
+ # Matches consecutive blank lines.
654
+ #
655
+ # Examples
656
+ #
657
+ # one
658
+ #
659
+ # two
660
+ #
661
+ BlankLineRx = /\n{2,}/
662
+
663
+ # Matches a comma or semi-colon delimiter.
664
+ #
665
+ # Examples
666
+ #
667
+ # one,two
668
+ # three;four
669
+ #
670
+ #DataDelimiterRx = /[,;]/
671
+
672
+ # Matches whitespace (space, tab, newline) escaped by a backslash.
673
+ #
674
+ # Examples
675
+ #
676
+ # three\ blind\ mice
677
+ #
678
+ EscapedSpaceRx = /\\([ \t\n])/
679
+
680
+ # Detects if text is a possible candidate for the replacements substitution.
681
+ #
682
+ ReplaceableTextRx = /[&']|--|\.\.\.|\([CRT]M?\)/
683
+
684
+ # Matches a whitespace delimiter, a sequence of spaces, tabs, and/or newlines.
685
+ # Matches the parsing rules of %w strings in Ruby.
686
+ #
687
+ # Examples
688
+ #
689
+ # one two three four
690
+ # five six
691
+ #
692
+ # TODO change to /(?<!\\)[ \t\n]+/ once lookbehind assertions are implemented in all modern browsers
693
+ SpaceDelimiterRx = /([^\\])[ \t\n]+/
694
+
695
+ # Matches a + or - modifier in a subs list
696
+ #
697
+ SubModifierSniffRx = /[+-]/
698
+
699
+ # Matches one or more consecutive digits at the end of a line.
700
+ #
701
+ # Examples
702
+ #
703
+ # docbook5
704
+ # html5
705
+ #
706
+ TrailingDigitsRx = /\d+$/
707
+
708
+ # Detects strings that resemble URIs.
709
+ #
710
+ # Examples
711
+ # http://domain
712
+ # https://domain
713
+ # file:///path
714
+ # data:info
715
+ #
716
+ # not c:/sample.adoc or c:\sample.adoc
717
+ #
718
+ UriSniffRx = %r(^#{CG_ALPHA}[#{CC_ALNUM}.+-]+:/{0,2})
719
+
720
+ # Detects XML tags
721
+ XmlSanitizeRx = /<[^>]+>/
722
+ end