asciidoctor 2.0.7 → 2.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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