asciidoctor 2.0.7 → 2.0.8

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.
@@ -38,12 +38,13 @@ class AbstractBlock < AbstractNode
38
38
  @blocks = []
39
39
  @subs = []
40
40
  @id = @title = @caption = @numeral = @style = @default_subs = @source_location = nil
41
- case context
42
- when :document, :section
41
+ if context == :document || context == :section
43
42
  @level = @next_section_index = 0
44
43
  @next_section_ordinal = 1
44
+ elsif AbstractBlock === parent
45
+ @level = parent.level
45
46
  else
46
- @level = AbstractBlock === parent ? parent.level : nil
47
+ @level = nil
47
48
  end
48
49
  end
49
50
 
@@ -310,14 +310,10 @@ class AbstractNode
310
310
  # Returns A String reference or data URI for the target image
311
311
  def image_uri(target_image, asset_dir_key = 'imagesdir')
312
312
  if (doc = @document).safe < SafeMode::SECURE && (doc.attr? 'data-uri')
313
- if ((Helpers.uriish? target_image) && (target_image = Helpers.encode_uri target_image)) ||
313
+ if ((Helpers.uriish? target_image) && (target_image = Helpers.encode_spaces_in_uri target_image)) ||
314
314
  (asset_dir_key && (images_base = doc.attr asset_dir_key) && (Helpers.uriish? images_base) &&
315
315
  (target_image = normalize_web_path target_image, images_base, false))
316
- if doc.attr? 'allow-uri-read'
317
- generate_data_uri_from_uri target_image, (doc.attr? 'cache-uri')
318
- else
319
- target_image
320
- end
316
+ (doc.attr? 'allow-uri-read') ? (generate_data_uri_from_uri target_image, (doc.attr? 'cache-uri')) : target_image
321
317
  else
322
318
  generate_data_uri target_image, asset_dir_key
323
319
  end
@@ -474,7 +470,7 @@ class AbstractNode
474
470
  # Returns the resolved [String] path
475
471
  def normalize_web_path(target, start = nil, preserve_uri_target = true)
476
472
  if preserve_uri_target && (Helpers.uriish? target)
477
- Helpers.encode_uri target
473
+ Helpers.encode_spaces_in_uri target
478
474
  else
479
475
  @document.path_resolver.web_path target, start
480
476
  end
@@ -148,7 +148,7 @@ module Converter
148
148
  #
149
149
  # Returns nothing.
150
150
  def register_for *backends
151
- Converter.register self, *backends
151
+ Converter.register self, *(backends.map {|backend| backend.to_s })
152
152
  end
153
153
  end
154
154
 
@@ -25,8 +25,8 @@ class Converter::DocBook5Converter < Converter::Base
25
25
  MANPAGE_SECTION_TAGS = { 'section' => 'refsection', 'synopsis' => 'refsynopsisdiv' }
26
26
  TABLE_PI_NAMES = ['dbhtml', 'dbfo', 'dblatex']
27
27
 
28
- CopyrightRx = /^(.+?)(?: ((?:\d{4}\-)?\d{4}))?$/
29
- ImageMacroRx = /^image::?(.+?)\[(.*?)\]$/
28
+ CopyrightRx = /^(#{CC_ANY}+?)(?: ((?:\d{4}\-)?\d{4}))?$/
29
+ ImageMacroRx = /^image::?(\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
30
30
 
31
31
  def initialize backend, opts = {}
32
32
  @backend = backend
@@ -35,20 +35,8 @@ class Converter::DocBook5Converter < Converter::Base
35
35
 
36
36
  def convert_document node
37
37
  result = ['<?xml version="1.0" encoding="UTF-8"?>']
38
- if node.attr? 'toc'
39
- if node.attr? 'toclevels'
40
- result << %(<?asciidoc-toc maxdepth="#{node.attr 'toclevels'}"?>)
41
- else
42
- result << '<?asciidoc-toc?>'
43
- end
44
- end
45
- if node.attr? 'sectnums'
46
- if node.attr? 'sectnumlevels'
47
- result << %(<?asciidoc-numbered maxdepth="#{node.attr 'sectnumlevels'}"?>)
48
- else
49
- result << '<?asciidoc-numbered?>'
50
- end
51
- end
38
+ result << ((node.attr? 'toclevels') ? %(<?asciidoc-toc maxdepth="#{node.attr 'toclevels'}"?>) : '<?asciidoc-toc?>') if node.attr? 'toc'
39
+ result << ((node.attr? 'sectnumlevels') ? %(<?asciidoc-numbered maxdepth="#{node.attr 'sectnumlevels'}"?>) : '<?asciidoc-numbered?>') if node.attr? 'sectnums'
52
40
  lang_attribute = (node.attr? 'nolang') ? '' : %( xml:lang="#{node.attr 'lang', 'en'}")
53
41
  if (root_tag_name = node.doctype) == 'manpage'
54
42
  root_tag_name = 'refentry'
@@ -634,7 +622,7 @@ class Converter::DocBook5Converter < Converter::Base
634
622
  if (reftext.include? '<') && ((reftext = reftext.gsub XmlSanitizeRx, '').include? ' ')
635
623
  reftext = (reftext.squeeze ' ').strip
636
624
  end
637
- reftext = (reftext.gsub '"', '&quot;') if reftext.include? '"'
625
+ reftext = reftext.gsub '"', '&quot;' if reftext.include? '"'
638
626
  %(#{attrs} xreflabel="#{reftext}")
639
627
  else
640
628
  attrs
@@ -742,7 +730,7 @@ class Converter::DocBook5Converter < Converter::Base
742
730
  if (cover_image.include? ':') && ImageMacroRx =~ cover_image
743
731
  attrlist = $2
744
732
  cover_image = doc.image_uri $1
745
- unless attrlist.empty?
733
+ if attrlist
746
734
  attrs = (AttributeList.new attrlist).parse ['alt', 'width', 'height']
747
735
  if attrs.key? 'scaledwidth'
748
736
  # NOTE scalefit="1" is the default in this case
@@ -23,9 +23,15 @@ class Converter::Html5Converter < Converter::Base
23
23
 
24
24
  DropAnchorRx = /<(?:a[^>+]+|\/a)>/
25
25
  StemBreakRx = / *\\\n(?:\\?\n)*|\n\n+/
26
- SvgPreambleRx = /\A.*?(?=<svg\b)/m
27
- SvgStartTagRx = /\A<svg[^>]*>/
28
- DimensionAttributeRx = /\s(?:width|height|style)=(["']).*?\1/
26
+ if RUBY_ENGINE == 'opal'
27
+ # NOTE In JavaScript, ^ matches the start of the string when the m flag is not set
28
+ SvgPreambleRx = /^#{CC_ALL}*?(?=<svg\b)/
29
+ SvgStartTagRx = /^<svg[^>]*>/
30
+ else
31
+ SvgPreambleRx = /\A.*?(?=<svg\b)/m
32
+ SvgStartTagRx = /\A<svg[^>]*>/
33
+ end
34
+ DimensionAttributeRx = /\s(?:width|height|style)=(["'])#{CC_ANY}*?\1/
29
35
 
30
36
  def initialize backend, opts = {}
31
37
  @backend = backend
@@ -17,7 +17,7 @@ class Converter::ManPageConverter < Converter::Base
17
17
 
18
18
  LiteralBackslashRx = /(?:\A|[^#{ESC}])\\/
19
19
  LeadingPeriodRx = /^\./
20
- EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) ".*?" ".*?" )( |[^\s]*)(.*?)(?: *#{ESC}\\c)?$/
20
+ EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) "#{CC_ANY}*?" "#{CC_ANY}*?" )( |[^\s]*)(#{CC_ANY}*?)(?: *#{ESC}\\c)?$/
21
21
  MockBoundaryRx = /<\/?BOUNDARY>/
22
22
  EmDashCharRefRx = /&#8212;(?:&#8203;)?/
23
23
  EllipsisCharRefRx = /&#8230;(?:&#8203;)?/
@@ -34,8 +34,8 @@ class Converter::TemplateConverter < Converter::Base
34
34
  }
35
35
 
36
36
  begin
37
- require 'concurrent/hash' unless defined? ::Concurrent::Hash
38
- @caches = { scans: ::Concurrent::Hash.new, templates: ::Concurrent::Hash.new }
37
+ require 'concurrent/map' unless defined? ::Concurrent::Map
38
+ @caches = { scans: ::Concurrent::Map.new, templates: ::Concurrent::Map.new }
39
39
  rescue ::LoadError
40
40
  @caches = { scans: {}, templates: {} }
41
41
  end
@@ -71,7 +71,7 @@ class Converter::TemplateConverter < Converter::Base
71
71
  end
72
72
  case opts[:template_cache]
73
73
  when true
74
- logger.warn 'optional gem \'concurrent-ruby\' is not available. This gem is recommended when using the default template cache.' unless defined? ::Concurrent::Hash
74
+ logger.warn 'optional gem \'concurrent-ruby\' is not available. This gem is recommended when using the default template cache.' unless defined? ::Concurrent::Map
75
75
  @caches = self.class.caches
76
76
  when ::Hash
77
77
  @caches = opts[:template_cache]
@@ -622,7 +622,7 @@ module Extensions
622
622
 
623
623
  def resolve_regexp name, format
624
624
  raise ::ArgumentError, %(invalid name for inline macro: #{name}) unless MacroNameRx.match? name
625
- @@rx_cache[[name, format]] ||= /\\?#{name}:#{format == :short ? '(){0}' : '(\S+?)'}\[(|.*?[^\\])\]/
625
+ @@rx_cache[[name, format]] ||= /\\?#{name}:#{format == :short ? '(){0}' : '(\S+?)'}\[(|#{CC_ANY}*?[^\\])\]/
626
626
  end
627
627
  end
628
628
 
@@ -2,7 +2,9 @@
2
2
  module Asciidoctor
3
3
  # Internal: Except where noted, a module that contains internal helper functions.
4
4
  module Helpers
5
- # Internal: Require the specified library using Kernel#require.
5
+ module_function
6
+
7
+ # Public: Require the specified library using Kernel#require.
6
8
  #
7
9
  # Attempts to load the library specified in the first argument using the
8
10
  # Kernel#require. Rescues the LoadError if the library is not available and
@@ -21,7 +23,7 @@ module Helpers
21
23
  # Otherwise, if on_failure is :abort, Kernel#raise is called with an appropriate message.
22
24
  # Otherwise, if on_failure is :warn, Kernel#warn is called with an appropriate message and nil returned.
23
25
  # Otherwise, nil is returned.
24
- def self.require_library name, gem_name = true, on_failure = :abort
26
+ def require_library name, gem_name = true, on_failure = :abort
25
27
  require name
26
28
  rescue ::LoadError
27
29
  include Logging unless include? Logging
@@ -57,7 +59,7 @@ module Helpers
57
59
  # data - the source data Array to prepare (no nil entries allowed)
58
60
  #
59
61
  # returns a String Array of prepared lines
60
- def self.prepare_source_array data
62
+ def prepare_source_array data
61
63
  return [] if data.empty?
62
64
  if (leading_2_bytes = (leading_bytes = (first = data[0]).unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
63
65
  data[0] = first.byteslice 2, first.bytesize
@@ -87,7 +89,7 @@ module Helpers
87
89
  # data - the source data String to prepare
88
90
  #
89
91
  # returns a String Array of prepared lines
90
- def self.prepare_source_string data
92
+ def prepare_source_string data
91
93
  return [] if data.nil_or_empty?
92
94
  if (leading_2_bytes = (leading_bytes = data.unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
93
95
  data = (data.byteslice 2, data.bytesize).encode UTF_8, ::Encoding::UTF_16LE
@@ -110,29 +112,17 @@ module Helpers
110
112
  # str - the String to check
111
113
  #
112
114
  # returns true if the String is a URI, false if it is not
113
- def self.uriish? str
115
+ def uriish? str
114
116
  (str.include? ':') && (UriSniffRx.match? str)
115
117
  end
116
118
 
117
- # Internal: Efficiently retrieves the URI prefix of the specified String
118
- #
119
- # Uses the Asciidoctor::UriSniffRx regex to match the URI prefix in the
120
- # specified String (e.g., http://), if present.
121
- #
122
- # str - the String to check
123
- #
124
- # returns the string URI prefix if the string is a URI, otherwise nil
125
- def self.uri_prefix str
126
- (str.include? ':') && UriSniffRx =~ str ? $& : nil
127
- end
128
-
129
119
  # Internal: Encode a URI component String for safe inclusion in a URI.
130
120
  #
131
121
  # str - the URI component String to encode
132
122
  #
133
123
  # Returns the String with all reserved URI characters encoded (e.g., /, &, =, space, etc).
134
124
  if RUBY_ENGINE == 'opal'
135
- def self.encode_uri_component str
125
+ def encode_uri_component str
136
126
  # patch necessary to adhere with RFC-3986 (and thus CGI.escape)
137
127
  # see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Description
138
128
  %x(
@@ -143,17 +133,17 @@ module Helpers
143
133
  end
144
134
  else
145
135
  CGI = ::CGI
146
- def self.encode_uri_component str
136
+ def encode_uri_component str
147
137
  CGI.escape str
148
138
  end
149
139
  end
150
140
 
151
- # Internal: Encode a URI String (namely the path portion).
141
+ # Internal: Apply URI path encoding to spaces in the specified string (i.e., convert spaces to %20).
152
142
  #
153
143
  # str - the String to encode
154
144
  #
155
- # Returns the String with all spaces replaced with %20.
156
- def self.encode_uri str
145
+ # Returns the specified String with all spaces replaced with %20.
146
+ def encode_spaces_in_uri str
157
147
  (str.include? ' ') ? (str.gsub ' ', '%20') : str
158
148
  end
159
149
 
@@ -167,7 +157,7 @@ module Helpers
167
157
  # # => "part1/chapter1"
168
158
  #
169
159
  # Returns the String filename with the file extension removed
170
- def self.rootname filename
160
+ def rootname filename
171
161
  if (last_dot_idx = filename.rindex '.')
172
162
  (filename.index '/', last_dot_idx) ? filename : (filename.slice 0, last_dot_idx)
173
163
  else
@@ -190,7 +180,7 @@ module Helpers
190
180
  # # => "tiger"
191
181
  #
192
182
  # Returns the String filename with leading directories removed and, if specified, the extension removed
193
- def self.basename filename, drop_ext = nil
183
+ def basename filename, drop_ext = nil
194
184
  if drop_ext
195
185
  ::File.basename filename, (drop_ext == true ? (extname filename) : drop_ext)
196
186
  else
@@ -203,7 +193,7 @@ module Helpers
203
193
  # path - The path String to check; expects a posix path
204
194
  #
205
195
  # Returns true if the path has a file extension, false otherwise
206
- def self.extname? path
196
+ def extname? path
207
197
  (last_dot_idx = path.rindex '.') && !(path.index '/', last_dot_idx)
208
198
  end
209
199
 
@@ -217,7 +207,7 @@ module Helpers
217
207
  #
218
208
  # Returns the String file extension (with the leading dot included) or the fallback value if the path has no file extension.
219
209
  if ::File::ALT_SEPARATOR
220
- def self.extname path, fallback = ''
210
+ def extname path, fallback = ''
221
211
  if (last_dot_idx = path.rindex '.')
222
212
  (path.index '/', last_dot_idx) || (path.index ::File::ALT_SEPARATOR, last_dot_idx) ? fallback : (path.slice last_dot_idx, path.length)
223
213
  else
@@ -225,7 +215,7 @@ module Helpers
225
215
  end
226
216
  end
227
217
  else
228
- def self.extname path, fallback = ''
218
+ def extname path, fallback = ''
229
219
  if (last_dot_idx = path.rindex '.')
230
220
  (path.index '/', last_dot_idx) ? fallback : (path.slice last_dot_idx, path.length)
231
221
  else
@@ -235,7 +225,7 @@ module Helpers
235
225
  end
236
226
 
237
227
  # Internal: Make a directory, ensuring all parent directories exist.
238
- def self.mkdir_p dir
228
+ def mkdir_p dir
239
229
  unless ::File.directory? dir
240
230
  unless (parent_dir = ::File.dirname dir) == '.'
241
231
  mkdir_p parent_dir
@@ -252,13 +242,14 @@ module Helpers
252
242
  'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, 'XC' => 90,
253
243
  'L' => 50, 'XL' => 40, 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1
254
244
  }
245
+ private_constant :ROMAN_NUMERALS
255
246
 
256
247
  # Internal: Converts an integer to a Roman numeral.
257
248
  #
258
249
  # val - the [Integer] value to convert
259
250
  #
260
251
  # Returns the [String] roman numeral for this integer
261
- def self.int_to_roman val
252
+ def int_to_roman val
262
253
  ROMAN_NUMERALS.map do |l, i|
263
254
  repeat, val = val.divmod i
264
255
  l * repeat
@@ -272,7 +263,7 @@ module Helpers
272
263
  # current - the value to increment as a String or Integer
273
264
  #
274
265
  # returns the next value in the sequence according to the current value's type
275
- def self.nextval current
266
+ def nextval current
276
267
  if ::Integer === current
277
268
  current + 1
278
269
  else
@@ -291,14 +282,14 @@ module Helpers
291
282
  #
292
283
  # Returns a Class if the specified object is a Class (but not a Module) or
293
284
  # a String that resolves to a Class; otherwise, nil
294
- def self.resolve_class object
285
+ def resolve_class object
295
286
  ::Class === object ? object : (::String === object ? (class_for_name object) : nil)
296
287
  end
297
288
 
298
289
  # Internal: Resolves a Class object (not a Module) for the qualified name.
299
290
  #
300
291
  # Returns Class
301
- def self.class_for_name qualified_name
292
+ def class_for_name qualified_name
302
293
  raise unless ::Class === (resolved = ::Object.const_get qualified_name, false)
303
294
  resolved
304
295
  rescue
@@ -476,25 +476,15 @@ class PathResolver
476
476
  def web_path target, start = nil
477
477
  target = posixify target
478
478
  start = posixify start
479
- uri_prefix = nil
480
479
 
481
480
  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
481
+ target, uri_prefix = extract_uri_prefix %(#{start}#{(start.end_with? SLASH) ? '' : SLASH}#{target})
486
482
  end
487
483
 
488
484
  # use this logic instead if we want to normalize target if it contains a URI
489
485
  #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
486
+ # target, uri_prefix = extract_uri_prefix target if preserve_uri_target
487
+ # target, uri_prefix = extract_uri_prefix %(#{start}#{SLASH}#{target}) unless uri_prefix || start.nil_or_empty?
498
488
  #end
499
489
 
500
490
  target_segments, target_root = partition_path target, true
@@ -521,5 +511,23 @@ class PathResolver
521
511
 
522
512
  uri_prefix ? %(#{uri_prefix}#{resolved_path}) : resolved_path
523
513
  end
514
+
515
+ private
516
+
517
+ # Internal: Efficiently extracts the URI prefix from the specified String if the String is a URI
518
+ #
519
+ # Uses the Asciidoctor::UriSniffRx regex to match the URI prefix in the specified String (e.g., http://). If present,
520
+ # the prefix is removed.
521
+ #
522
+ # str - the String to check
523
+ #
524
+ # returns a tuple containing the specified string without the URI prefix, if present, and the extracted URI prefix.
525
+ def extract_uri_prefix str
526
+ if (str.include? ':') && UriSniffRx =~ str
527
+ [(str.slice $&.length, str.length), $&]
528
+ else
529
+ str
530
+ end
531
+ end
524
532
  end
525
533
  end
@@ -0,0 +1,720 @@
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}*?[^\\]))\]/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
+ #
518
+ # FIXME revisit! the main issue is we need different rules for implicit vs explicit
519
+ InlineLinkRx = %r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*([^\s.,\[\]<]))(?:\[(|#{CC_ALL}*?[^\\])\])?)m
520
+
521
+ # Match a link or e-mail inline macro.
522
+ #
523
+ # Examples
524
+ #
525
+ # link:path[label]
526
+ # mailto:doc.writer@example.com[]
527
+ #
528
+ # NOTE be as non-greedy as possible by not allowing space or left square bracket in target
529
+ InlineLinkMacroRx = /\\?(?:link|(mailto)):(|[^:\s\[][^\s\[]*)\[(|#{CC_ALL}*?[^\\])\]/m
530
+
531
+ # Matches the name of a macro.
532
+ #
533
+ MacroNameRx = /^#{CG_WORD}[#{CC_WORD}-]*$/
534
+
535
+ # Matches a stem (and alternatives, asciimath and latexmath) inline macro, which may span multiple lines.
536
+ #
537
+ # Examples
538
+ #
539
+ # stem:[x != 0]
540
+ # asciimath:[x != 0]
541
+ # latexmath:[\sqrt{4} = 2]
542
+ #
543
+ InlineStemMacroRx = /\\?(stem|(?:latex|ascii)math):([a-z]+(?:,[a-z-]+)*)?\[(#{CC_ALL}*?[^\\])\]/m
544
+
545
+ # Matches a menu inline macro.
546
+ #
547
+ # Examples
548
+ #
549
+ # menu:File[Save As...]
550
+ # menu:View[Page Style > No Style]
551
+ # menu:View[Page Style, No Style]
552
+ #
553
+ InlineMenuMacroRx = /\\?menu:(#{CG_WORD}|[#{CC_WORD}&][^\n\[]*[^\s\[])\[ *(#{CC_ALL}*?[^\\])?\]/m
554
+
555
+ # Matches an implicit menu inline macro.
556
+ #
557
+ # Examples
558
+ #
559
+ # "File > New..."
560
+ #
561
+ InlineMenuRx = /\\?"([#{CC_WORD}&][^"]*?[ \n]+&gt;[ \n]+[^"]*)"/
562
+
563
+ # Matches an inline passthrough, which may span multiple lines.
564
+ #
565
+ # Examples
566
+ #
567
+ # +text+
568
+ # `text` (compat)
569
+ #
570
+ # NOTE we always capture the attributes so we know when to use compatible (i.e., legacy) behavior
571
+ InlinePassRx = {
572
+ false => ['+', '`', /(^|[^#{CC_WORD};:])(?:\[([^\]]+)\])?(\\?(\+|`)(\S|\S#{CC_ALL}*?\S)\4)(?!#{CG_WORD})/m],
573
+ true => ['`', nil, /(^|[^`#{CC_WORD}])(?:\[([^\]]+)\])?(\\?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\4)(?![`#{CC_WORD}])/m]
574
+ }
575
+
576
+ # Matches an inline plus passthrough spanning multiple lines, but only when it occurs directly
577
+ # inside constrained monospaced formatting in non-compat mode.
578
+ #
579
+ # Examples
580
+ #
581
+ # +text+
582
+ #
583
+ SinglePlusInlinePassRx = /^(\\)?\+(\S|\S#{CC_ALL}*?\S)\+$/m
584
+
585
+ # Matches several variants of the passthrough inline macro, which may span multiple lines.
586
+ #
587
+ # Examples
588
+ #
589
+ # +++text+++
590
+ # $$text$$
591
+ # pass:quotes[text]
592
+ #
593
+ # NOTE we have to support an empty pass:[] for compatibility with AsciiDoc Python
594
+ InlinePassMacroRx = /(?:(?:(\\?)\[([^\]]+)\])?(\\{0,2})(\+\+\+?|\$\$)(#{CC_ALL}*?)\4|(\\?)pass:([a-z]+(?:,[a-z-]+)*)?\[(|#{CC_ALL}*?[^\\])\])/m
595
+
596
+ # Matches an xref (i.e., cross-reference) inline macro, which may span multiple lines.
597
+ #
598
+ # Examples
599
+ #
600
+ # <<id,reftext>>
601
+ # xref:id[reftext]
602
+ #
603
+ # NOTE special characters have already been escaped, hence the entity references
604
+ # NOTE { is included in start characters to support target that begins with attribute reference in title content
605
+ InlineXrefMacroRx = %r(\\?(?:&lt;&lt;([#{CC_WORD}#/.:{]#{CC_ALL}*?)&gt;&gt;|xref:([#{CC_WORD}#/.:{]#{CC_ALL}*?)\[(?:\]|(#{CC_ALL}*?[^\\])\])))m
606
+
607
+ ## Layout
608
+
609
+ # Matches a trailing + preceded by at least one space character,
610
+ # which forces a hard line break (<br> tag in HTML output).
611
+ #
612
+ # NOTE AsciiDoc Python allows + to be preceded by TAB; Asciidoctor does not
613
+ #
614
+ # Examples
615
+ #
616
+ # Humpty Dumpty sat on a wall, +
617
+ # Humpty Dumpty had a great fall.
618
+ #
619
+ if RUBY_ENGINE == 'opal'
620
+ # NOTE In JavaScript, ^ and $ only match the start and end of line if the multiline flag is present
621
+ HardLineBreakRx = /^(#{CC_ANY}*) \+$/m
622
+ else
623
+ # NOTE In Ruby, ^ and $ always match start and end of line
624
+ HardLineBreakRx = /^(.*) \+$/
625
+ end
626
+
627
+ # Matches a Markdown horizontal rule.
628
+ #
629
+ # Examples
630
+ #
631
+ # --- or - - -
632
+ # *** or * * *
633
+ # ___ or _ _ _
634
+ #
635
+ MarkdownThematicBreakRx = /^ {0,3}([-*_])( *)\1\2\1$/
636
+
637
+ # Matches an AsciiDoc or Markdown horizontal rule or AsciiDoc page break.
638
+ #
639
+ # Examples
640
+ #
641
+ # ''' (horizontal rule)
642
+ # <<< (page break)
643
+ # --- or - - - (horizontal rule, Markdown)
644
+ # *** or * * * (horizontal rule, Markdown)
645
+ # ___ or _ _ _ (horizontal rule, Markdown)
646
+ #
647
+ ExtLayoutBreakRx = /^(?:'{3,}|<{3,}|([-*_])( *)\1\2\1)$/
648
+
649
+ ## General
650
+
651
+ # Matches consecutive blank lines.
652
+ #
653
+ # Examples
654
+ #
655
+ # one
656
+ #
657
+ # two
658
+ #
659
+ BlankLineRx = /\n{2,}/
660
+
661
+ # Matches a comma or semi-colon delimiter.
662
+ #
663
+ # Examples
664
+ #
665
+ # one,two
666
+ # three;four
667
+ #
668
+ #DataDelimiterRx = /[,;]/
669
+
670
+ # Matches whitespace (space, tab, newline) escaped by a backslash.
671
+ #
672
+ # Examples
673
+ #
674
+ # three\ blind\ mice
675
+ #
676
+ EscapedSpaceRx = /\\([ \t\n])/
677
+
678
+ # Detects if text is a possible candidate for the replacements substitution.
679
+ #
680
+ ReplaceableTextRx = /[&']|--|\.\.\.|\([CRT]M?\)/
681
+
682
+ # Matches a whitespace delimiter, a sequence of spaces, tabs, and/or newlines.
683
+ # Matches the parsing rules of %w strings in Ruby.
684
+ #
685
+ # Examples
686
+ #
687
+ # one two three four
688
+ # five six
689
+ #
690
+ # TODO change to /(?<!\\)[ \t\n]+/ once lookbehind assertions are implemented in all modern browsers
691
+ SpaceDelimiterRx = /([^\\])[ \t\n]+/
692
+
693
+ # Matches a + or - modifier in a subs list
694
+ #
695
+ SubModifierSniffRx = /[+-]/
696
+
697
+ # Matches one or more consecutive digits at the end of a line.
698
+ #
699
+ # Examples
700
+ #
701
+ # docbook5
702
+ # html5
703
+ #
704
+ TrailingDigitsRx = /\d+$/
705
+
706
+ # Detects strings that resemble URIs.
707
+ #
708
+ # Examples
709
+ # http://domain
710
+ # https://domain
711
+ # file:///path
712
+ # data:info
713
+ #
714
+ # not c:/sample.adoc or c:\sample.adoc
715
+ #
716
+ UriSniffRx = %r(^#{CG_ALPHA}[#{CC_ALNUM}.+-]+:/{0,2})
717
+
718
+ # Detects XML tags
719
+ XmlSanitizeRx = /<[^>]+>/
720
+ end