asciidoctor 1.5.8 → 2.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +162 -17
  3. data/LICENSE +1 -1
  4. data/README-de.adoc +12 -13
  5. data/README-fr.adoc +11 -12
  6. data/README-jp.adoc +11 -12
  7. data/README-zh_CN.adoc +12 -13
  8. data/README.adoc +6 -7
  9. data/asciidoctor.gemspec +19 -24
  10. data/bin/asciidoctor +5 -4
  11. data/data/reference/syntax.adoc +283 -0
  12. data/data/stylesheets/asciidoctor-default.css +56 -52
  13. data/data/stylesheets/coderay-asciidoctor.css +7 -9
  14. data/lib/asciidoctor.rb +171 -232
  15. data/lib/asciidoctor/abstract_block.rb +96 -105
  16. data/lib/asciidoctor/abstract_node.rb +118 -139
  17. data/lib/asciidoctor/attribute_list.rb +10 -14
  18. data/lib/asciidoctor/block.rb +20 -19
  19. data/lib/asciidoctor/callouts.rb +4 -2
  20. data/lib/asciidoctor/cli.rb +3 -2
  21. data/lib/asciidoctor/cli/invoker.rb +14 -21
  22. data/lib/asciidoctor/cli/options.rb +64 -54
  23. data/lib/asciidoctor/converter.rb +357 -185
  24. data/lib/asciidoctor/converter/composite.rb +40 -48
  25. data/lib/asciidoctor/converter/docbook5.rb +604 -640
  26. data/lib/asciidoctor/converter/html5.rb +949 -963
  27. data/lib/asciidoctor/converter/manpage.rb +569 -548
  28. data/lib/asciidoctor/converter/template.rb +231 -272
  29. data/lib/asciidoctor/core_ext.rb +5 -18
  30. data/lib/asciidoctor/core_ext/float/truncate.rb +19 -0
  31. data/lib/asciidoctor/core_ext/match_data/names.rb +7 -0
  32. data/lib/asciidoctor/core_ext/nil_or_empty.rb +1 -0
  33. data/lib/asciidoctor/core_ext/regexp/is_match.rb +4 -2
  34. data/lib/asciidoctor/document.rb +399 -377
  35. data/lib/asciidoctor/extensions.rb +72 -140
  36. data/lib/asciidoctor/helpers.rb +122 -83
  37. data/lib/asciidoctor/inline.rb +5 -1
  38. data/lib/asciidoctor/list.rb +13 -11
  39. data/lib/asciidoctor/logging.rb +17 -16
  40. data/lib/asciidoctor/parser.rb +390 -423
  41. data/lib/asciidoctor/path_resolver.rb +10 -5
  42. data/lib/asciidoctor/reader.rb +286 -263
  43. data/lib/asciidoctor/rouge_ext.rb +39 -0
  44. data/lib/asciidoctor/section.rb +9 -8
  45. data/lib/asciidoctor/stylesheets.rb +19 -37
  46. data/lib/asciidoctor/substitutors.rb +364 -509
  47. data/lib/asciidoctor/syntax_highlighter.rb +238 -0
  48. data/lib/asciidoctor/syntax_highlighter/coderay.rb +87 -0
  49. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +26 -0
  50. data/lib/asciidoctor/syntax_highlighter/html_pipeline.rb +10 -0
  51. data/lib/asciidoctor/syntax_highlighter/prettify.rb +27 -0
  52. data/lib/asciidoctor/syntax_highlighter/pygments.rb +149 -0
  53. data/lib/asciidoctor/syntax_highlighter/rouge.rb +129 -0
  54. data/lib/asciidoctor/table.rb +73 -66
  55. data/lib/asciidoctor/timings.rb +4 -2
  56. data/lib/asciidoctor/version.rb +2 -1
  57. data/lib/asciidoctor/writer.rb +30 -0
  58. data/man/asciidoctor.1 +19 -15
  59. data/man/asciidoctor.adoc +14 -12
  60. metadata +69 -216
  61. data/CONTRIBUTING.adoc +0 -185
  62. data/Gemfile +0 -60
  63. data/Rakefile +0 -129
  64. data/bin/asciidoctor-safe +0 -15
  65. data/features/open_block.feature +0 -92
  66. data/features/pass_block.feature +0 -66
  67. data/features/step_definitions.rb +0 -49
  68. data/features/text_formatting.feature +0 -57
  69. data/features/xref.feature +0 -1039
  70. data/lib/asciidoctor/converter/base.rb +0 -59
  71. data/lib/asciidoctor/converter/docbook45.rb +0 -93
  72. data/lib/asciidoctor/converter/factory.rb +0 -226
  73. data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +0 -6
  74. data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +0 -5
  75. data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +0 -4
  76. data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +0 -6
  77. data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +0 -5
  78. data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +0 -6
  79. data/lib/asciidoctor/core_ext/1.8.7/string/limit_bytesize.rb +0 -29
  80. data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +0 -6
  81. data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +0 -6
  82. data/lib/asciidoctor/core_ext/string/limit_bytesize.rb +0 -10
  83. data/test/api_test.rb +0 -1240
  84. data/test/attribute_list_test.rb +0 -242
  85. data/test/attributes_test.rb +0 -1623
  86. data/test/blocks_test.rb +0 -3870
  87. data/test/converter_test.rb +0 -470
  88. data/test/document_test.rb +0 -1853
  89. data/test/extensions_test.rb +0 -1560
  90. data/test/fixtures/asciidoc_index.txt +0 -521
  91. data/test/fixtures/basic-docinfo-footer.html +0 -6
  92. data/test/fixtures/basic-docinfo-footer.xml +0 -8
  93. data/test/fixtures/basic-docinfo.html +0 -1
  94. data/test/fixtures/basic-docinfo.xml +0 -4
  95. data/test/fixtures/basic.asciidoc +0 -5
  96. data/test/fixtures/chapter-a.adoc +0 -3
  97. data/test/fixtures/child-include.adoc +0 -5
  98. data/test/fixtures/circle.svg +0 -9
  99. data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +0 -6
  100. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +0 -6
  101. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +0 -1
  102. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +0 -3
  103. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +0 -5
  104. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +0 -6
  105. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +0 -3
  106. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +0 -5
  107. data/test/fixtures/custom-docinfodir/basic-docinfo.html +0 -1
  108. data/test/fixtures/custom-docinfodir/docinfo.html +0 -1
  109. data/test/fixtures/docinfo-footer.html +0 -1
  110. data/test/fixtures/docinfo-footer.xml +0 -9
  111. data/test/fixtures/docinfo.html +0 -1
  112. data/test/fixtures/docinfo.xml +0 -3
  113. data/test/fixtures/doctime-localtime.adoc +0 -2
  114. data/test/fixtures/dot.gif +0 -0
  115. data/test/fixtures/encoding.asciidoc +0 -13
  116. data/test/fixtures/file-with-missing-include.adoc +0 -1
  117. data/test/fixtures/grandchild-include.adoc +0 -3
  118. data/test/fixtures/hello-asciidoctor.pdf +0 -69
  119. data/test/fixtures/include-file.asciidoc +0 -24
  120. data/test/fixtures/include-file.jsx +0 -8
  121. data/test/fixtures/include-file.ml +0 -3
  122. data/test/fixtures/include-file.xml +0 -5
  123. data/test/fixtures/lists.adoc +0 -96
  124. data/test/fixtures/master.adoc +0 -5
  125. data/test/fixtures/mismatched-end-tag.adoc +0 -7
  126. data/test/fixtures/other-chapters.adoc +0 -11
  127. data/test/fixtures/outer-include.adoc +0 -5
  128. data/test/fixtures/parent-include-restricted.adoc +0 -5
  129. data/test/fixtures/parent-include.adoc +0 -5
  130. data/test/fixtures/sample.asciidoc +0 -30
  131. data/test/fixtures/section-a.adoc +0 -4
  132. data/test/fixtures/stylesheets/custom.css +0 -3
  133. data/test/fixtures/subdir/index.adoc +0 -3
  134. data/test/fixtures/subdir/inner-include.adoc +0 -3
  135. data/test/fixtures/subdir/middle-include.adoc +0 -5
  136. data/test/fixtures/subs-docinfo.html +0 -2
  137. data/test/fixtures/subs.adoc +0 -6
  138. data/test/fixtures/tagged-class-enclosed.rb +0 -25
  139. data/test/fixtures/tagged-class.rb +0 -23
  140. data/test/fixtures/tip.gif +0 -0
  141. data/test/fixtures/unclosed-tag.adoc +0 -3
  142. data/test/fixtures/unexpected-end-tag.adoc +0 -4
  143. data/test/invoker_test.rb +0 -745
  144. data/test/links_test.rb +0 -855
  145. data/test/lists_test.rb +0 -5151
  146. data/test/logger_test.rb +0 -211
  147. data/test/manpage_test.rb +0 -660
  148. data/test/options_test.rb +0 -262
  149. data/test/paragraphs_test.rb +0 -562
  150. data/test/parser_test.rb +0 -742
  151. data/test/paths_test.rb +0 -395
  152. data/test/preamble_test.rb +0 -173
  153. data/test/reader_test.rb +0 -2161
  154. data/test/sections_test.rb +0 -3575
  155. data/test/substitutions_test.rb +0 -2066
  156. data/test/tables_test.rb +0 -2036
  157. data/test/test_helper.rb +0 -447
  158. data/test/text_test.rb +0 -309
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ require 'rouge' unless defined? Rouge.version
3
+
4
+ module Asciidoctor; module RougeExt; module Formatters
5
+ class HTMLTable < ::Rouge::Formatter
6
+ def initialize delegate, opts
7
+ @delegate = delegate
8
+ @start_line = opts[:start_line] || 1
9
+ end
10
+
11
+ def stream tokens
12
+ formatted_code = @delegate.format tokens
13
+ formatted_code += LF unless formatted_code.end_with? LF, HangingEndSpanTagCs
14
+ last_lineno = (first_lineno = @start_line) + (formatted_code.count LF) - 1 # assume number of newlines is constant
15
+ lineno_format = %(%#{(::Math.log10 last_lineno).floor + 1}i)
16
+ formatted_linenos = ((first_lineno..last_lineno).map {|lineno| sprintf lineno_format, lineno } << '').join LF
17
+ yield %(<table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno">#{formatted_linenos}</pre></td><td class="code"><pre>#{formatted_code}</pre></td></tr></tbody></table>)
18
+ end
19
+ end
20
+
21
+ class HTMLLineHighlighter < ::Rouge::Formatter
22
+ def initialize delegate, opts
23
+ @delegate = delegate
24
+ @lines = opts[:lines] || []
25
+ end
26
+
27
+ def stream tokens
28
+ lineno = 0
29
+ token_lines tokens do |tokens_in_line|
30
+ yield (@lines.include? lineno += 1) ? %(<span class="hll">#{@delegate.format tokens_in_line}#{LF}</span>) : %(#{@delegate.format tokens_in_line}#{LF})
31
+ end
32
+ end
33
+ end
34
+
35
+ LF = ?\n
36
+ HangingEndSpanTagCs = %(#{LF}</span>)
37
+
38
+ private_constant :HangingEndSpanTagCs, :LF
39
+ end; end; end
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
  module Asciidoctor
3
3
  # Public: Methods for managing sections of AsciiDoc content in a document.
4
4
  # The section responds as an Array of content blocks by delegating
@@ -130,9 +130,9 @@ class Section < AbstractBlock
130
130
  case xrefstyle
131
131
  when 'full'
132
132
  if (type = @sectname) == 'chapter' || type == 'appendix'
133
- quoted_title = sprintf sub_quotes('_%s_'), title
133
+ quoted_title = sub_placeholder (sub_quotes '_%s_'), title
134
134
  else
135
- quoted_title = sprintf sub_quotes(@document.compat_mode ? %q(``%s'') : '"`%s`"'), title
135
+ quoted_title = sub_placeholder (sub_quotes @document.compat_mode ? %q(``%s'') : '"`%s`"'), title
136
136
  end
137
137
  if (signifier = @document.attributes[%(#{type}-refsig)])
138
138
  %(#{signifier} #{sectnum '.', ','} #{quoted_title})
@@ -146,10 +146,10 @@ class Section < AbstractBlock
146
146
  sectnum '.', ''
147
147
  end
148
148
  else # 'basic'
149
- (type = @sectname) == 'chapter' || type == 'appendix' ? (sprintf sub_quotes('_%s_'), title) : title
149
+ (type = @sectname) == 'chapter' || type == 'appendix' ? (sub_placeholder (sub_quotes '_%s_'), title) : title
150
150
  end
151
151
  else # apply basic styling
152
- (type = @sectname) == 'chapter' || type == 'appendix' ? (sprintf sub_quotes('_%s_'), title) : title
152
+ (type = @sectname) == 'chapter' || type == 'appendix' ? (sub_placeholder (sub_quotes '_%s_'), title) : title
153
153
  end
154
154
  else
155
155
  title
@@ -215,9 +215,10 @@ class Section < AbstractBlock
215
215
  # ensure id doesn't begin with idseparator if idprefix is empty (assuming idseparator is not empty)
216
216
  gen_id = gen_id.slice 1, gen_id.length if pre.empty? && (gen_id.start_with? sep)
217
217
  end
218
- if document.catalog[:ids].key? gen_id
219
- ids, cnt = document.catalog[:ids], Compliance.unique_id_start_index
220
- cnt += 1 while ids.key?(candidate_id = %(#{gen_id}#{sep}#{cnt}))
218
+ if document.catalog[:refs].key? gen_id
219
+ ids = document.catalog[:refs]
220
+ cnt = Compliance.unique_id_start_index
221
+ cnt += 1 while ids[candidate_id = %(#{gen_id}#{sep}#{cnt})]
221
222
  candidate_id
222
223
  else
223
224
  gen_id
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
  module Asciidoctor
3
3
  # A utility class for working with the built-in stylesheets.
4
4
  #--
@@ -6,9 +6,7 @@ module Asciidoctor
6
6
  # QUESTION create method for user stylesheet?
7
7
  class Stylesheets
8
8
  DEFAULT_STYLESHEET_NAME = 'asciidoctor.css'
9
- DEFAULT_PYGMENTS_STYLE = 'default'
10
- STYLESHEETS_DATA_PATH = ::File.join DATA_PATH, 'stylesheets'
11
- PygmentsBgColorRx = /^\.pygments +\{ *background: *([^;]+);/
9
+ STYLESHEETS_DIR = ::File.join DATA_DIR, 'stylesheets'
12
10
 
13
11
  @__instance__ = new
14
12
 
@@ -24,9 +22,12 @@ class Stylesheets
24
22
  #
25
23
  # returns the [String] Asciidoctor stylesheet data
26
24
  def primary_stylesheet_data
27
- @primary_stylesheet_data ||= ::IO.read(::File.join(STYLESHEETS_DATA_PATH, 'asciidoctor-default.css')).rstrip
25
+ @primary_stylesheet_data ||= (::File.read (::File.join STYLESHEETS_DIR, 'asciidoctor-default.css'), mode: FILE_READ_MODE).rstrip
28
26
  end
29
27
 
28
+ # Deprecated: Generate code to embed the primary stylesheet
29
+ #
30
+ # Returns the [String] primary stylesheet data wrapped in a <style> tag
30
31
  def embed_primary_stylesheet
31
32
  %(<style>
32
33
  #{primary_stylesheet_data}
@@ -34,24 +35,23 @@ class Stylesheets
34
35
  end
35
36
 
36
37
  def write_primary_stylesheet target_dir = '.'
37
- ::IO.write(::File.join(target_dir, primary_stylesheet_name), primary_stylesheet_data)
38
+ ::File.write (::File.join target_dir, primary_stylesheet_name), primary_stylesheet_data, mode: FILE_WRITE_MODE
38
39
  end
39
40
 
40
41
  def coderay_stylesheet_name
41
- 'coderay-asciidoctor.css'
42
+ (SyntaxHighlighter.for 'coderay').stylesheet_basename
42
43
  end
43
44
 
44
45
  # Public: Read the contents of the default CodeRay stylesheet
45
46
  #
46
47
  # returns the [String] CodeRay stylesheet data
47
48
  def coderay_stylesheet_data
48
- # NOTE use the following lines to load a built-in theme instead
49
- # unless load_coderay.nil?
50
- # ::CodeRay::Encoders[:html]::CSS.new(:default).stylesheet
51
- # end
52
- @coderay_stylesheet_data ||= ::IO.read(::File.join(STYLESHEETS_DATA_PATH, 'coderay-asciidoctor.css')).rstrip
49
+ (SyntaxHighlighter.for 'coderay').read_stylesheet
53
50
  end
54
51
 
52
+ # Deprecated: Generate code to embed the CodeRay stylesheet
53
+ #
54
+ # Returns the [String] CodeRay stylesheet data wrapped in a <style> tag
55
55
  def embed_coderay_stylesheet
56
56
  %(<style>
57
57
  #{coderay_stylesheet_data}
@@ -59,33 +59,23 @@ class Stylesheets
59
59
  end
60
60
 
61
61
  def write_coderay_stylesheet target_dir = '.'
62
- ::IO.write(::File.join(target_dir, coderay_stylesheet_name), coderay_stylesheet_data)
62
+ ::File.write (::File.join target_dir, coderay_stylesheet_name), coderay_stylesheet_data, mode: FILE_WRITE_MODE
63
63
  end
64
64
 
65
65
  def pygments_stylesheet_name style = nil
66
- %(pygments-#{style || DEFAULT_PYGMENTS_STYLE}.css)
67
- end
68
-
69
- def pygments_background style = nil
70
- if load_pygments && PygmentsBgColorRx =~ (::Pygments.css '.pygments', :style => style || DEFAULT_PYGMENTS_STYLE)
71
- $1
72
- end
66
+ (SyntaxHighlighter.for 'pygments').stylesheet_basename style
73
67
  end
74
68
 
75
69
  # Public: Generate the Pygments stylesheet with the specified style.
76
70
  #
77
71
  # returns the [String] Pygments stylesheet data
78
72
  def pygments_stylesheet_data style = nil
79
- if load_pygments
80
- style ||= DEFAULT_PYGMENTS_STYLE
81
- (@pygments_stylesheet_data ||= {})[style] ||=
82
- ((::Pygments.css '.listingblock .pygments', :classprefix => 'tok-', :style => style) || '/* Failed to load Pygments CSS. */').
83
- sub('.listingblock .pygments {', '.listingblock .pygments, .listingblock .pygments code {')
84
- else
85
- '/* Pygments CSS disabled. Pygments is not available. */'
86
- end
73
+ (SyntaxHighlighter.for 'pygments').read_stylesheet style
87
74
  end
88
75
 
76
+ # Deprecated: Generate code to embed the Pygments stylesheet
77
+ #
78
+ # Returns the [String] Pygments stylesheet data for the specified style wrapped in a <style> tag
89
79
  def embed_pygments_stylesheet style = nil
90
80
  %(<style>
91
81
  #{pygments_stylesheet_data style}
@@ -93,15 +83,7 @@ class Stylesheets
93
83
  end
94
84
 
95
85
  def write_pygments_stylesheet target_dir = '.', style = nil
96
- ::IO.write(::File.join(target_dir, pygments_stylesheet_name(style)), pygments_stylesheet_data(style))
97
- end
98
-
99
- #def load_coderay
100
- # (defined? ::CodeRay) ? true : !(Helpers.require_library 'coderay', true, :ignore).nil?
101
- #end
102
-
103
- def load_pygments
104
- (defined? ::Pygments) ? true : !(Helpers.require_library 'pygments', 'pygments.rb', :ignore).nil?
86
+ ::File.write (::File.join target_dir, (pygments_stylesheet_name style)), (pygments_stylesheet_data style), mode: FILE_WRITE_MODE
105
87
  end
106
88
  end
107
89
  end
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
  module Asciidoctor
3
3
  # Public: Methods to perform substitutions on lines of AsciiDoc text. This module
4
4
  # is intented to be mixed-in to Section and Block to provide operations for performing
@@ -12,55 +12,45 @@ module Substitutors
12
12
 
13
13
  (BASIC_SUBS = [:specialcharacters]).freeze
14
14
  (HEADER_SUBS = [:specialcharacters, :attributes]).freeze
15
+ (NO_SUBS = []).freeze
15
16
  (NORMAL_SUBS = [:specialcharacters, :quotes, :attributes, :replacements, :macros, :post_replacements]).freeze
16
- (NONE_SUBS = []).freeze
17
- (TITLE_SUBS = [:specialcharacters, :quotes, :replacements, :macros, :attributes, :post_replacements]).freeze
18
17
  (REFTEXT_SUBS = [:specialcharacters, :quotes, :replacements]).freeze
19
18
  (VERBATIM_SUBS = [:specialcharacters, :callouts]).freeze
20
19
 
21
20
  SUB_GROUPS = {
22
- :none => NONE_SUBS,
23
- :normal => NORMAL_SUBS,
24
- :verbatim => VERBATIM_SUBS,
25
- :specialchars => BASIC_SUBS
21
+ none: NO_SUBS,
22
+ normal: NORMAL_SUBS,
23
+ verbatim: VERBATIM_SUBS,
24
+ specialchars: BASIC_SUBS,
26
25
  }
27
26
 
28
27
  SUB_HINTS = {
29
- :a => :attributes,
30
- :m => :macros,
31
- :n => :normal,
32
- :p => :post_replacements,
33
- :q => :quotes,
34
- :r => :replacements,
35
- :c => :specialcharacters,
36
- :v => :verbatim
28
+ a: :attributes,
29
+ m: :macros,
30
+ n: :normal,
31
+ p: :post_replacements,
32
+ q: :quotes,
33
+ r: :replacements,
34
+ c: :specialcharacters,
35
+ v: :verbatim,
37
36
  }
38
37
 
39
38
  SUB_OPTIONS = {
40
- :block => SUB_GROUPS.keys + NORMAL_SUBS + [:callouts],
41
- :inline => SUB_GROUPS.keys + NORMAL_SUBS
39
+ block: SUB_GROUPS.keys + NORMAL_SUBS + [:callouts],
40
+ inline: SUB_GROUPS.keys + NORMAL_SUBS,
42
41
  }
43
42
 
44
- SUB_HIGHLIGHT = ['coderay', 'pygments']
43
+ CAN = ?\u0018
44
+ DEL = ?\u007f
45
45
 
46
- if ::RUBY_MIN_VERSION_1_9
47
- CAN = %(\u0018)
48
- DEL = %(\u007f)
46
+ # Delimiters and matchers for the passthrough placeholder
47
+ # See http://www.aivosto.com/vbtips/control-characters.html#listabout for characters to use
49
48
 
50
- # Delimiters and matchers for the passthrough placeholder
51
- # See http://www.aivosto.com/vbtips/control-characters.html#listabout for characters to use
49
+ # SPA, start of guarded protected area (\u0096)
50
+ PASS_START = ?\u0096
52
51
 
53
- # SPA, start of guarded protected area (\u0096)
54
- PASS_START = %(\u0096)
55
-
56
- # EPA, end of guarded protected area (\u0097)
57
- PASS_END = %(\u0097)
58
- else
59
- CAN = 24.chr
60
- DEL = 127.chr
61
- PASS_START = 150.chr
62
- PASS_END = 151.chr
63
- end
52
+ # EPA, end of guarded protected area (\u0097)
53
+ PASS_END = ?\u0097
64
54
 
65
55
  # match passthrough slot
66
56
  PassSlotRx = /#{PASS_START}(\d+)#{PASS_END}/
@@ -76,14 +66,6 @@ module Substitutors
76
66
 
77
67
  PLUS = '+'
78
68
 
79
- PygmentsWrapperDivRx = %r(<div class="pyhl">(.*)</div>)m
80
- # NOTE handles all permutations of <pre> wrapper
81
- # NOTE trailing whitespace appears when pygments-linenums-mode=table; <pre> has style attribute when pygments-css=inline
82
- PygmentsWrapperPreRx = %r(<pre\b[^>]*?>(.*?)</pre>\s*)m
83
-
84
- # Internal: A String Array of passthough (unprocessed) text captured from this block
85
- attr_reader :passthroughs
86
-
87
69
  # Public: Apply the specified substitutions to the text.
88
70
  #
89
71
  # text - The String or String Array of text to process; must not be nil.
@@ -93,14 +75,18 @@ module Substitutors
93
75
  def apply_subs text, subs = NORMAL_SUBS
94
76
  return text if text.empty? || !subs
95
77
 
96
- if (multiline = ::Array === text)
97
- #text = text.size > 1 ? (text.join LF) : text[0]
78
+ if (is_multiline = ::Array === text)
98
79
  text = text[1] ? (text.join LF) : text[0]
99
80
  end
100
81
 
101
- if (has_passthroughs = subs.include? :macros)
102
- text = extract_passthroughs text
103
- has_passthroughs = false if @passthroughs.empty?
82
+ if subs.include? :macros
83
+ text, passthrus = extract_passthroughs text
84
+ if passthrus.empty?
85
+ passthrus = nil
86
+ else
87
+ # NOTE placeholders can move around, so we can only clear in the outermost substitution call
88
+ @passthroughs_locked ||= (reset_passthrus = true)
89
+ end
104
90
  end
105
91
 
106
92
  subs.each do |type|
@@ -125,9 +111,16 @@ module Substitutors
125
111
  logger.warn %(unknown substitution type #{type})
126
112
  end
127
113
  end
128
- text = restore_passthroughs text if has_passthroughs
129
114
 
130
- multiline ? (text.split LF, -1) : text
115
+ if passthrus
116
+ text = restore_passthroughs text
117
+ if reset_passthrus
118
+ passthrus.clear
119
+ @passthroughs_locked = nil
120
+ end
121
+ end
122
+
123
+ is_multiline ? (text.split LF, -1) : text
131
124
  end
132
125
 
133
126
  # Public: Apply normal substitutions.
@@ -138,7 +131,7 @@ module Substitutors
138
131
  #
139
132
  # Returns the String with normal substitutions applied.
140
133
  def apply_normal_subs text
141
- apply_subs text
134
+ apply_subs text, NORMAL_SUBS
142
135
  end
143
136
 
144
137
  # Public: Apply substitutions for titles.
@@ -146,9 +139,7 @@ module Substitutors
146
139
  # title - The String title to process
147
140
  #
148
141
  # returns - A String with title substitutions performed
149
- def apply_title_subs(title)
150
- apply_subs title, TITLE_SUBS
151
- end
142
+ alias apply_title_subs apply_subs
152
143
 
153
144
  # Public: Apply substitutions for reftext.
154
145
  #
@@ -172,33 +163,30 @@ module Substitutors
172
163
  #
173
164
  # text - The String from which to extract passthrough fragements
174
165
  #
175
- # returns - The text with the passthrough region substituted with placeholders
166
+ # returns - A tuple of the String text with passthrough regions substituted with placeholders and the passthroughs Hash
176
167
  def extract_passthroughs(text)
177
168
  compat_mode = @document.compat_mode
178
- passes = @passthroughs
179
- text = text.gsub(InlinePassMacroRx) {
180
- # alias match for Ruby 1.8.7 compat
181
- m = $~
182
- preceding = nil
169
+ passthrus = @passthroughs
170
+ text = text.gsub InlinePassMacroRx do
171
+ preceding = ''
183
172
 
184
- if (boundary = m[4]) # $$, ++, or +++
173
+ if (boundary = $4) # $$, ++, or +++
185
174
  # skip ++ in compat mode, handled as normal quoted text
186
175
  if compat_mode && boundary == '++'
187
- next m[2] ?
188
- %(#{m[1]}[#{m[2]}]#{m[3]}++#{extract_passthroughs m[5]}++) :
189
- %(#{m[1]}#{m[3]}++#{extract_passthroughs m[5]}++)
176
+ content, _ = extract_passthroughs $5
177
+ next $2 ? %(#{$1}[#{$2}]#{$3}++#{content}++) : %(#{$1}#{$3}++#{content}++)
190
178
  end
191
179
 
192
- attributes = m[2]
193
- escape_count = m[3].length
194
- content = m[5]
180
+ attributes = $2
181
+ escape_count = $3.length
182
+ content = $5
195
183
  old_behavior = false
196
184
 
197
185
  if attributes
198
186
  if escape_count > 0
199
187
  # NOTE we don't look for nested unconstrained pass macros
200
- next %(#{m[1]}[#{attributes}]#{RS * (escape_count - 1)}#{boundary}#{m[5]}#{boundary})
201
- elsif m[1] == RS
188
+ next %(#{$1}[#{attributes}]#{RS * (escape_count - 1)}#{boundary}#{$5}#{boundary})
189
+ elsif $1 == RS
202
190
  preceding = %([#{attributes}])
203
191
  attributes = nil
204
192
  else
@@ -210,41 +198,36 @@ module Substitutors
210
198
  end
211
199
  elsif escape_count > 0
212
200
  # NOTE we don't look for nested unconstrained pass macros
213
- next %(#{RS * (escape_count - 1)}#{boundary}#{m[5]}#{boundary})
201
+ next %(#{RS * (escape_count - 1)}#{boundary}#{$5}#{boundary})
214
202
  end
215
203
  subs = (boundary == '+++' ? [] : BASIC_SUBS)
216
204
 
217
- pass_key = passes.size
218
205
  if attributes
219
206
  if old_behavior
220
- passes[pass_key] = {:text => content, :subs => NORMAL_SUBS, :type => :monospaced, :attributes => attributes}
207
+ passthrus[passthru_key = passthrus.size] = { text: content, subs: NORMAL_SUBS, type: :monospaced, attributes: attributes }
221
208
  else
222
- passes[pass_key] = {:text => content, :subs => subs, :type => :unquoted, :attributes => attributes}
209
+ passthrus[passthru_key = passthrus.size] = { text: content, subs: subs, type: :unquoted, attributes: attributes }
223
210
  end
224
211
  else
225
- passes[pass_key] = {:text => content, :subs => subs}
212
+ passthrus[passthru_key = passthrus.size] = { text: content, subs: subs }
226
213
  end
227
214
  else # pass:[]
228
- if m[6] == RS
229
- # NOTE we don't look for nested pass:[] macros
230
- next m[0].slice 1, m[0].length
231
- end
232
-
233
- passes[pass_key = passes.size] = {:text => (unescape_brackets m[8]), :subs => (m[7] ? (resolve_pass_subs m[7]) : nil)}
215
+ # NOTE we don't look for nested pass:[] macros
216
+ # honor the escape
217
+ next $&.slice 1, $&.length if $6 == RS
218
+ passthrus[passthru_key = passthrus.size] = { text: (unescape_brackets $8), subs: ($7 ? (resolve_pass_subs $7) : nil) }
234
219
  end
235
220
 
236
- %(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
237
- } if (text.include? '++') || (text.include? '$$') || (text.include? 'ss:')
221
+ %(#{preceding}#{PASS_START}#{passthru_key}#{PASS_END})
222
+ end if (text.include? '++') || (text.include? '$$') || (text.include? 'ss:')
238
223
 
239
224
  pass_inline_char1, pass_inline_char2, pass_inline_rx = InlinePassRx[compat_mode]
240
- text = text.gsub(pass_inline_rx) {
241
- # alias match for Ruby 1.8.7 compat
242
- m = $~
243
- preceding = m[1]
244
- attributes = m[2]
245
- escape_mark = RS if (quoted_text = m[3]).start_with? RS
246
- format_mark = m[4]
247
- content = m[5]
225
+ text = text.gsub pass_inline_rx do
226
+ preceding = $1
227
+ attributes = $2
228
+ escape_mark = RS if (quoted_text = $3).start_with? RS
229
+ format_mark = $4
230
+ content = $5
248
231
 
249
232
  if compat_mode
250
233
  old_behavior = true
@@ -256,11 +239,8 @@ module Substitutors
256
239
 
257
240
  if attributes
258
241
  if format_mark == '`' && !old_behavior
259
- # extract nested single-plus passthrough; otherwise return unprocessed
260
- next (extract_inner_passthrough content, %(#{preceding}[#{attributes}]#{escape_mark}), attributes)
261
- end
262
-
263
- if escape_mark
242
+ next extract_inner_passthrough content, %(#{preceding}[#{attributes}]#{escape_mark}), attributes
243
+ elsif escape_mark
264
244
  # honor the escape of the formatting mark
265
245
  next %(#{preceding}[#{attributes}]#{quoted_text.slice 1, quoted_text.length})
266
246
  elsif preceding == RS
@@ -271,60 +251,55 @@ module Substitutors
271
251
  attributes = parse_quoted_text_attributes attributes
272
252
  end
273
253
  elsif format_mark == '`' && !old_behavior
274
- # extract nested single-plus passthrough; otherwise return unprocessed
275
- next (extract_inner_passthrough content, %(#{preceding}#{escape_mark}))
254
+ next extract_inner_passthrough content, %(#{preceding}#{escape_mark})
276
255
  elsif escape_mark
277
256
  # honor the escape of the formatting mark
278
257
  next %(#{preceding}#{quoted_text.slice 1, quoted_text.length})
279
258
  end
280
259
 
281
- pass_key = passes.size
282
260
  if compat_mode
283
- passes[pass_key] = {:text => content, :subs => BASIC_SUBS, :attributes => attributes, :type => :monospaced}
261
+ passthrus[passthru_key = passthrus.size] = { text: content, subs: BASIC_SUBS, attributes: attributes, type: :monospaced }
284
262
  elsif attributes
285
263
  if old_behavior
286
264
  subs = (format_mark == '`' ? BASIC_SUBS : NORMAL_SUBS)
287
- passes[pass_key] = {:text => content, :subs => subs, :attributes => attributes, :type => :monospaced}
265
+ passthrus[passthru_key = passthrus.size] = { text: content, subs: subs, attributes: attributes, type: :monospaced }
288
266
  else
289
- passes[pass_key] = {:text => content, :subs => BASIC_SUBS, :attributes => attributes, :type => :unquoted}
267
+ passthrus[passthru_key = passthrus.size] = { text: content, subs: BASIC_SUBS, attributes: attributes, type: :unquoted }
290
268
  end
291
269
  else
292
- passes[pass_key] = {:text => content, :subs => BASIC_SUBS}
270
+ passthrus[passthru_key = passthrus.size] = { text: content, subs: BASIC_SUBS }
293
271
  end
294
272
 
295
- %(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
296
- } if (text.include? pass_inline_char1) || (pass_inline_char2 && (text.include? pass_inline_char2))
273
+ %(#{preceding}#{PASS_START}#{passthru_key}#{PASS_END})
274
+ end if (text.include? pass_inline_char1) || (pass_inline_char2 && (text.include? pass_inline_char2))
297
275
 
298
276
  # NOTE we need to do the stem in a subsequent step to allow it to be escaped by the former
299
- text = text.gsub(InlineStemMacroRx) {
300
- # alias match for Ruby 1.8.7 compat
301
- m = $~
277
+ text = text.gsub InlineStemMacroRx do
302
278
  # honor the escape
303
- if $&.start_with? RS
304
- next m[0].slice 1, m[0].length
305
- end
279
+ next $&.slice 1, $&.length if $&.start_with? RS
306
280
 
307
- if (type = m[1].to_sym) == :stem
281
+ if (type = $1.to_sym) == :stem
308
282
  type = STEM_TYPE_ALIASES[@document.attributes['stem']].to_sym
309
283
  end
310
- content = unescape_brackets m[3]
311
- subs = m[2] ? (resolve_pass_subs m[2]) : ((@document.basebackend? 'html') ? BASIC_SUBS : nil)
312
- passes[pass_key = passes.size] = {:text => content, :subs => subs, :type => type}
313
- %(#{PASS_START}#{pass_key}#{PASS_END})
314
- } if (text.include? ':') && ((text.include? 'stem:') || (text.include? 'math:'))
284
+ content = unescape_brackets $3
285
+ subs = $2 ? (resolve_pass_subs $2) : ((@document.basebackend? 'html') ? BASIC_SUBS : nil)
286
+ passthrus[passthru_key = passthrus.size] = { text: content, subs: subs, type: type }
287
+ %(#{PASS_START}#{passthru_key}#{PASS_END})
288
+ end if (text.include? ':') && ((text.include? 'stem:') || (text.include? 'math:'))
315
289
 
316
- text
290
+ [text, passthrus]
317
291
  end
318
292
 
293
+ # Internal: Extract nested single-plus passthrough; otherwise return unprocessed
319
294
  def extract_inner_passthrough text, pre, attributes = nil
320
295
  if (text.end_with? '+') && (text.start_with? '+', '\+') && SinglePlusInlinePassRx =~ text
321
296
  if $1
322
297
  %(#{pre}`+#{$2}+`)
323
298
  else
324
- @passthroughs[pass_key = @passthroughs.size] = attributes ?
325
- { :text => $2, :subs => BASIC_SUBS, :attributes => attributes, :type => :unquoted } :
326
- { :text => $2, :subs => BASIC_SUBS }
327
- %(#{pre}`#{PASS_START}#{pass_key}#{PASS_END}`)
299
+ @passthroughs[passthru_key = @passthroughs.size] = attributes ?
300
+ { text: $2, subs: BASIC_SUBS, attributes: attributes, type: :unquoted } :
301
+ { text: $2, subs: BASIC_SUBS }
302
+ %(#{pre}`#{PASS_START}#{passthru_key}#{PASS_END}`)
328
303
  end
329
304
  else
330
305
  %(#{pre}`#{text}`)
@@ -334,99 +309,71 @@ module Substitutors
334
309
  # Internal: Restore the passthrough text by reinserting into the placeholder positions
335
310
  #
336
311
  # text - The String text into which to restore the passthrough text
337
- # outer - A Boolean indicating whether we are in the outer call (default: true)
338
312
  #
339
313
  # returns The String text with the passthrough text restored
340
- def restore_passthroughs text, outer = true
341
- passes = @passthroughs
342
- # passthroughs may have been eagerly restored (e.g., footnotes)
343
- #if outer && (passes.empty? || !text.include?(PASS_START))
344
- # return text
345
- #end
346
-
347
- text.gsub(PassSlotRx) {
348
- # NOTE we can't remove entry from map because placeholder may have been duplicated by other substitutions
349
- pass = passes[$1.to_i]
350
- subbed_text = apply_subs(pass[:text], pass[:subs])
351
- if (type = pass[:type])
352
- subbed_text = Inline.new(self, :quoted, subbed_text, :type => type, :attributes => pass[:attributes]).convert
353
- end
354
- subbed_text.include?(PASS_START) ? restore_passthroughs(subbed_text, false) : subbed_text
355
- }
356
- ensure
357
- # free memory if in outer call...we don't need these anymore
358
- passes.clear if outer
359
- end
360
-
361
- if RUBY_ENGINE == 'opal'
362
- def sub_quotes text
363
- if QuotedTextSniffRx[compat = @document.compat_mode].match? text
364
- QUOTE_SUBS[compat].each do |type, scope, pattern|
365
- text = text.gsub(pattern) { convert_quoted_text $~, type, scope }
314
+ def restore_passthroughs text
315
+ passthrus = @passthroughs
316
+ text.gsub PassSlotRx do
317
+ if (pass = passthrus[$1.to_i])
318
+ subbed_text = apply_subs(pass[:text], pass[:subs])
319
+ if (type = pass[:type])
320
+ subbed_text = Inline.new(self, :quoted, subbed_text, type: type, attributes: pass[:attributes]).convert
366
321
  end
322
+ subbed_text.include?(PASS_START) ? restore_passthroughs(subbed_text) : subbed_text
323
+ else
324
+ logger.error %(unresolved passthrough detected: #{text})
325
+ '??pass??'
367
326
  end
368
- text
369
327
  end
328
+ end
370
329
 
371
- def sub_replacements text
372
- if ReplaceableTextRx.match? text
373
- REPLACEMENTS.each do |pattern, replacement, restore|
374
- text = text.gsub(pattern) { do_replacement $~, replacement, restore }
375
- end
376
- end
377
- text
378
- end
379
- else
380
- # Public: Substitute quoted text (includes emphasis, strong, monospaced, etc)
381
- #
382
- # text - The String text to process
383
- #
384
- # returns The converted String text
385
- def sub_quotes text
386
- if QuotedTextSniffRx[compat = @document.compat_mode].match? text
387
- # NOTE interpolation is faster than String#dup
388
- text = %(#{text})
389
- QUOTE_SUBS[compat].each do |type, scope, pattern|
390
- # NOTE using gsub! here as an MRI Ruby optimization
391
- text.gsub!(pattern) { convert_quoted_text $~, type, scope }
392
- end
330
+ # Public: Substitute quoted text (includes emphasis, strong, monospaced, etc.)
331
+ #
332
+ # text - The String text to process
333
+ #
334
+ # returns The converted [String] text
335
+ def sub_quotes text
336
+ if QuotedTextSniffRx[compat = @document.compat_mode].match? text
337
+ QUOTE_SUBS[compat].each do |type, scope, pattern|
338
+ text = text.gsub(pattern) { convert_quoted_text $~, type, scope }
393
339
  end
394
- text
395
340
  end
341
+ text
342
+ end
396
343
 
397
- # Public: Substitute replacement characters (e.g., copyright, trademark, etc)
398
- #
399
- # text - The String text to process
400
- #
401
- # returns The String text with the replacement characters substituted
402
- def sub_replacements text
403
- if ReplaceableTextRx.match? text
404
- # NOTE interpolation is faster than String#dup
405
- text = %(#{text})
406
- REPLACEMENTS.each do |pattern, replacement, restore|
407
- # NOTE Using gsub! as optimization
408
- text.gsub!(pattern) { do_replacement $~, replacement, restore }
409
- end
344
+ # Public: Substitute replacement characters (e.g., copyright, trademark, etc.)
345
+ #
346
+ # text - The String text to process
347
+ #
348
+ # returns The [String] text with the replacement characters substituted
349
+ def sub_replacements text
350
+ if ReplaceableTextRx.match? text
351
+ REPLACEMENTS.each do |pattern, replacement, restore|
352
+ text = text.gsub(pattern) { do_replacement $~, replacement, restore }
410
353
  end
411
- text
412
354
  end
355
+ text
413
356
  end
414
357
 
415
358
  # Public: Substitute special characters (i.e., encode XML)
416
359
  #
417
- # The special characters <, &, and > get replaced with &lt;,
418
- # &amp;, and &gt;, respectively.
360
+ # The special characters <, &, and > get replaced with &lt;, &amp;, and &gt;, respectively.
419
361
  #
420
362
  # text - The String text to process.
421
363
  #
422
364
  # returns The String text with special characters replaced.
423
- if ::RUBY_MIN_VERSION_1_9
365
+ if RUBY_ENGINE == 'opal'
424
366
  def sub_specialchars text
425
- (text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub SpecialCharsRx, SpecialCharsTr) : text
367
+ (text.include? ?>) || (text.include? ?&) || (text.include? ?<) ? (text.gsub SpecialCharsRx, SpecialCharsTr) : text
426
368
  end
427
369
  else
370
+ CGI = ::CGI
428
371
  def sub_specialchars text
429
- (text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub(SpecialCharsRx) { SpecialCharsTr[$&] }) : text
372
+ if (text.include? ?>) || (text.include? ?&) || (text.include? ?<)
373
+ (text.include? ?') || (text.include? ?") ? (text.gsub SpecialCharsRx, SpecialCharsTr) : (CGI.escape_html text)
374
+ else
375
+ text
376
+ end
430
377
  end
431
378
  end
432
379
  alias sub_specialcharacters sub_specialchars
@@ -535,7 +482,7 @@ module Substitutors
535
482
  #return text if text.nil_or_empty?
536
483
  # some look ahead assertions to cut unnecessary regex calls
537
484
  found = {}
538
- found_square_bracket = found[:square_bracket] = (text.include? '[')
485
+ found_square_bracket = found[:square_bracket] = text.include? '['
539
486
  found_colon = text.include? ':'
540
487
  found_macroish = found[:macroish] = found_square_bracket && found_colon
541
488
  found_macroish_short = found_macroish && (text.include? ':[')
@@ -543,7 +490,7 @@ module Substitutors
543
490
 
544
491
  if doc_attrs.key? 'experimental'
545
492
  if found_macroish_short && ((text.include? 'kbd:') || (text.include? 'btn:'))
546
- text = text.gsub(InlineKbdBtnMacroRx) {
493
+ text = text.gsub InlineKbdBtnMacroRx do
547
494
  # honor the escape
548
495
  if $1
549
496
  $&.slice 1, $&.length
@@ -564,25 +511,20 @@ module Substitutors
564
511
  else
565
512
  keys = [keys]
566
513
  end
567
- (Inline.new self, :kbd, nil, :attributes => { 'keys' => keys }).convert
514
+ (Inline.new self, :kbd, nil, attributes: { 'keys' => keys }).convert
568
515
  else # $2 == 'btn'
569
516
  (Inline.new self, :button, (unescape_bracketed_text $3)).convert
570
517
  end
571
- }
518
+ end
572
519
  end
573
520
 
574
521
  if found_macroish && (text.include? 'menu:')
575
- text = text.gsub(InlineMenuMacroRx) {
576
- # alias match for Ruby 1.8.7 compat
577
- m = $~
522
+ text = text.gsub InlineMenuMacroRx do
578
523
  # honor the escape
579
- if $&.start_with? RS
580
- next m[0].slice 1, m[0].length
581
- end
582
-
583
- menu, items = m[1], m[2]
524
+ next $&.slice 1, $&.length if $&.start_with? RS
584
525
 
585
- if items
526
+ menu = $1
527
+ if (items = $2)
586
528
  items = items.gsub ESC_R_SB, R_SB if items.include? R_SB
587
529
  if (delim = items.include?('&gt;') ? '&gt;' : (items.include?(',') ? ',' : nil))
588
530
  submenus = items.split(delim).map {|it| it.strip }
@@ -594,25 +536,19 @@ module Substitutors
594
536
  submenus, menuitem = [], nil
595
537
  end
596
538
 
597
- Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).convert
598
- }
539
+ Inline.new(self, :menu, nil, attributes: { 'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem }).convert
540
+ end
599
541
  end
600
542
 
601
543
  if (text.include? '"') && (text.include? '&gt;')
602
- text = text.gsub(InlineMenuRx) {
603
- # alias match for Ruby 1.8.7 compat
604
- m = $~
544
+ text = text.gsub InlineMenuRx do
605
545
  # honor the escape
606
- if $&.start_with? RS
607
- next m[0].slice 1, m[0].length
608
- end
609
-
610
- input = m[1]
546
+ next $&.slice 1, $&.length if $&.start_with? RS
611
547
 
612
- menu, *submenus = input.split('&gt;').map {|it| it.strip }
548
+ menu, *submenus = $1.split('&gt;').map {|it| it.strip }
613
549
  menuitem = submenus.pop
614
- Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).convert
615
- }
550
+ Inline.new(self, :menu, nil, attributes: { 'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem }).convert
551
+ end
616
552
  end
617
553
  end
618
554
 
@@ -620,61 +556,55 @@ module Substitutors
620
556
  # TODO this handling needs some cleanup
621
557
  if (extensions = doc.extensions) && extensions.inline_macros? # && found_macroish
622
558
  extensions.inline_macros.each do |extension|
623
- text = text.gsub(extension.instance.regexp) {
624
- # alias match for Ruby 1.8.7 compat
625
- m = $~
559
+ text = text.gsub extension.instance.regexp do
626
560
  # honor the escape
627
- if $&.start_with? RS
628
- next m[0].slice 1, m[0].length
629
- end
630
-
631
- if (m.names rescue []).empty?
632
- target, content, extconf = m[1], m[2], extension.config
561
+ next $&.slice 1, $&.length if $&.start_with? RS
562
+ extconf = extension.config
563
+ if $~.names.empty?
564
+ target, content = $1, $2
633
565
  else
634
- target, content, extconf = (m[:target] rescue nil), (m[:content] rescue nil), extension.config
566
+ target, content = ($~[:target] rescue nil), ($~[:content] rescue nil)
635
567
  end
636
568
  attributes = (attributes = extconf[:default_attrs]) ? attributes.dup : {}
637
569
  if content.nil_or_empty?
638
570
  attributes['text'] = content if content && extconf[:content_model] != :attributes
639
571
  else
640
572
  content = unescape_bracketed_text content
573
+ # QUESTION should we store the unparsed attrlist in the attrlist key?
641
574
  if extconf[:content_model] == :attributes
642
- # QUESTION should we store the text in the _text key?
643
- # NOTE bracked text has already been escaped
644
- parse_attributes content, extconf[:pos_attrs] || [], :into => attributes
575
+ parse_attributes content, extconf[:pos_attrs] || [], into: attributes
645
576
  else
646
577
  attributes['text'] = content
647
578
  end
648
579
  end
649
- # NOTE use content if target is not set (short form only); deprecated - remove in 1.6.0
650
- replacement = extension.process_method[self, target || content, attributes]
580
+ # NOTE for convenience, map content (unparsed attrlist) to target when format is short
581
+ target ||= extconf[:format] == :short ? content : target
582
+ replacement = extension.process_method[self, target, attributes]
651
583
  Inline === replacement ? replacement.convert : replacement
652
- }
584
+ end
653
585
  end
654
586
  end
655
587
 
656
588
  if found_macroish && ((text.include? 'image:') || (text.include? 'icon:'))
657
589
  # image:filename.png[Alt Text]
658
- text = text.gsub(InlineImageMacroRx) {
659
- # alias match for Ruby 1.8.7 compat
660
- m = $~
590
+ text = text.gsub InlineImageMacroRx do
661
591
  # honor the escape
662
- if (captured = $&).start_with? RS
663
- next captured.slice 1, captured.length
664
- elsif captured.start_with? 'icon:'
592
+ if $&.start_with? RS
593
+ next $&.slice 1, $&.length
594
+ elsif $&.start_with? 'icon:'
665
595
  type, posattrs = 'icon', ['size']
666
596
  else
667
597
  type, posattrs = 'image', ['alt', 'width', 'height']
668
598
  end
669
- if (target = m[1]).include? ATTR_REF_HEAD
599
+ if (target = $1).include? ATTR_REF_HEAD
670
600
  # TODO remove this special case once titles use normal substitution order
671
601
  target = sub_attributes target
672
602
  end
673
- attrs = parse_attributes m[2], posattrs, :unescape_input => true
603
+ attrs = parse_attributes $2, posattrs, unescape_input: true
674
604
  doc.register :images, [target, (attrs['imagesdir'] = doc_attrs['imagesdir'])] unless type == 'icon'
675
605
  attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
676
- Inline.new(self, :image, nil, :type => type, :target => target, :attributes => attrs).convert
677
- }
606
+ Inline.new(self, :image, nil, type: type, target: target, attributes: attrs).convert
607
+ end
678
608
  end
679
609
 
680
610
  if ((text.include? '((') && (text.include? '))')) || (found_macroish_short && (text.include? 'dexterm'))
@@ -682,39 +612,34 @@ module Substitutors
682
612
  # indexterm:[Tigers,Big cats]
683
613
  # ((Tigers))
684
614
  # indexterm2:[Tigers]
685
- text = text.gsub(InlineIndextermMacroRx) {
686
- captured = $&
615
+ text = text.gsub InlineIndextermMacroRx do
687
616
  case $1
688
617
  when 'indexterm'
689
- text = $2
690
618
  # honor the escape
691
- if captured.start_with? RS
692
- next captured.slice 1, captured.length
693
- end
619
+ next $&.slice 1, $&.length if $&.start_with? RS
620
+
694
621
  # indexterm:[Tigers,Big cats]
695
- terms = split_simple_csv normalize_string text, true
622
+ terms = split_simple_csv normalize_string $2, true
696
623
  doc.register :indexterms, terms
697
- (Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
624
+ (Inline.new self, :indexterm, nil, attributes: { 'terms' => terms }).convert
698
625
  when 'indexterm2'
699
- text = $2
700
626
  # honor the escape
701
- if captured.start_with? RS
702
- next captured.slice 1, captured.length
703
- end
627
+ next $&.slice 1, $&.length if $&.start_with? RS
628
+
704
629
  # indexterm2:[Tigers]
705
- term = normalize_string text, true
630
+ term = normalize_string $2, true
706
631
  doc.register :indexterms, [term]
707
- (Inline.new self, :indexterm, term, :type => :visible).convert
632
+ (Inline.new self, :indexterm, term, type: :visible).convert
708
633
  else
709
634
  text = $3
710
635
  # honor the escape
711
- if captured.start_with? RS
636
+ if $&.start_with? RS
712
637
  # escape concealed index term, but process nested flow index term
713
638
  if (text.start_with? '(') && (text.end_with? ')')
714
639
  text = text.slice 1, text.length - 2
715
640
  visible, before, after = true, '(', ')'
716
641
  else
717
- next captured.slice 1, captured.length
642
+ next $&.slice 1, $&.length
718
643
  end
719
644
  else
720
645
  visible = true
@@ -725,43 +650,41 @@ module Substitutors
725
650
  text, before, after = (text.slice 1, text.length), '(', ''
726
651
  end
727
652
  elsif text.end_with? ')'
728
- text, before, after = (text.slice 0, text.length - 1), '', ')'
653
+ text, before, after = text.chop, '', ')'
729
654
  end
730
655
  end
731
656
  if visible
732
657
  # ((Tigers))
733
658
  term = normalize_string text
734
659
  doc.register :indexterms, [term]
735
- subbed_term = (Inline.new self, :indexterm, term, :type => :visible).convert
660
+ subbed_term = (Inline.new self, :indexterm, term, type: :visible).convert
736
661
  else
737
662
  # (((Tigers,Big cats)))
738
663
  terms = split_simple_csv(normalize_string text)
739
664
  doc.register :indexterms, terms
740
- subbed_term = (Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
665
+ subbed_term = (Inline.new self, :indexterm, nil, attributes: { 'terms' => terms }).convert
741
666
  end
742
667
  before ? %(#{before}#{subbed_term}#{after}) : subbed_term
743
668
  end
744
- }
669
+ end
745
670
  end
746
671
 
747
672
  if found_colon && (text.include? '://')
748
673
  # inline urls, target[text] (optionally prefixed with link: and optionally surrounded by <>)
749
- text = text.gsub(InlineLinkRx) {
750
- # alias match for Ruby 1.8.7 compat
751
- m = $~
674
+ text = text.gsub InlineLinkRx do
675
+ target = $2
752
676
  # honor the escape
753
- if (target = $2).start_with? RS
754
- next %(#{m[1]}#{target.slice 1, target.length}#{m[3]})
755
- end
677
+ next %(#{$1}#{target.slice 1, target.length}#{$3}) if target.start_with? RS
678
+
756
679
  # NOTE if text is non-nil, then we've matched a formal macro (i.e., trailing square brackets)
757
- prefix, text, suffix = m[1], (macro = m[3]) || '', ''
680
+ captured, prefix, text, suffix = $&, $1, (macro = $3) || '', ''
758
681
  if prefix == 'link:'
759
682
  if macro
760
683
  prefix = ''
761
684
  else
762
685
  # invalid macro syntax (link: prefix w/o trailing square brackets)
763
686
  # we probably shouldn't even get here...our regex is doing too much
764
- next m[0]
687
+ next captured
765
688
  end
766
689
  end
767
690
  unless macro || UriTerminatorRx !~ target
@@ -794,10 +717,10 @@ module Substitutors
794
717
  end
795
718
  end
796
719
  # NOTE handle case when remaining target is a URI scheme (e.g., http://)
797
- return m[0] if target.end_with? '://'
720
+ return captured if target.end_with? '://'
798
721
  end
799
722
 
800
- attrs, link_opts = nil, { :type => :link }
723
+ attrs, link_opts = nil, { type: :link }
801
724
  unless text.empty?
802
725
  text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
803
726
  if !doc.compat_mode && (text.include? '=')
@@ -810,7 +733,7 @@ module Substitutors
810
733
  #unless attrs && (attrs.key? 'title')
811
734
  # if text.include? '|'
812
735
  # attrs ||= {}
813
- # text, attrs['title'] = text.split '|', 2
736
+ # text, _, attrs['title'] = text.partition '|'
814
737
  # end
815
738
  #end
816
739
 
@@ -837,21 +760,23 @@ module Substitutors
837
760
  doc.register :links, (link_opts[:target] = target)
838
761
  link_opts[:attributes] = attrs if attrs
839
762
  %(#{prefix}#{Inline.new(self, :anchor, text, link_opts).convert}#{suffix})
840
- }
763
+ end
841
764
  end
842
765
 
843
766
  if found_macroish && ((text.include? 'link:') || (text.include? 'mailto:'))
844
767
  # inline link macros, link:target[text]
845
- text = text.gsub(InlineLinkMacroRx) {
846
- # alias match for Ruby 1.8.7 compat
847
- m = $~
768
+ text = text.gsub InlineLinkMacroRx do
848
769
  # honor the escape
849
770
  if $&.start_with? RS
850
- next m[0].slice 1, m[0].length
771
+ next $&.slice 1, $&.length
772
+ elsif (mailto = $1)
773
+ target = %(mailto:#{$2})
774
+ mailto_text = $2
775
+ else
776
+ target = $2
851
777
  end
852
- target = (mailto = m[1]) ? %(mailto:#{m[2]}) : m[2]
853
- attrs, link_opts = nil, { :type => :link }
854
- unless (text = m[3]).empty?
778
+ attrs, link_opts = nil, { type: :link }
779
+ unless (text = $3).empty?
855
780
  text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
856
781
  if mailto
857
782
  if !doc.compat_mode && (text.include? ',')
@@ -859,9 +784,9 @@ module Substitutors
859
784
  link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
860
785
  if attrs.key? 2
861
786
  if attrs.key? 3
862
- target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]}&amp;body=#{Helpers.uri_encode attrs[3]})
787
+ target = %(#{target}?subject=#{Helpers.encode_uri_component attrs[2]}&amp;body=#{Helpers.encode_uri_component attrs[3]})
863
788
  else
864
- target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]})
789
+ target = %(#{target}?subject=#{Helpers.encode_uri_component attrs[2]})
865
790
  end
866
791
  end
867
792
  end
@@ -875,7 +800,7 @@ module Substitutors
875
800
  #unless attrs && (attrs.key? 'title')
876
801
  # if text.include? '|'
877
802
  # attrs ||= {}
878
- # text, attrs['title'] = text.split '|', 2
803
+ # text, _, attrs['title'] = text.partition '|'
879
804
  # end
880
805
  #end
881
806
 
@@ -892,7 +817,7 @@ module Substitutors
892
817
  if text.empty?
893
818
  # mailto is a special case, already processed
894
819
  if mailto
895
- text = m[2]
820
+ text = mailto_text
896
821
  else
897
822
  if doc_attrs.key? 'hide-uri-scheme'
898
823
  if (text = target.sub UriSniffRx, '').empty?
@@ -913,41 +838,45 @@ module Substitutors
913
838
  doc.register :links, (link_opts[:target] = target)
914
839
  link_opts[:attributes] = attrs if attrs
915
840
  Inline.new(self, :anchor, text, link_opts).convert
916
- }
841
+ end
917
842
  end
918
843
 
919
844
  if text.include? '@'
920
- text = text.gsub(InlineEmailRx) {
921
- address, tip = $&, $1
922
- if tip
923
- next (tip == RS ? (address.slice 1, address.length) : address)
924
- end
845
+ text = text.gsub InlineEmailRx do
846
+ # honor the escape
847
+ next $1 == RS ? ($&.slice 1, $&.length) : $& if $1
925
848
 
926
- target = %(mailto:#{address})
849
+ target = %(mailto:#{$&})
927
850
  # QUESTION should this be registered as an e-mail address?
928
851
  doc.register(:links, target)
929
852
 
930
- Inline.new(self, :anchor, address, :type => :link, :target => target).convert
931
- }
853
+ Inline.new(self, :anchor, $&, type: :link, target: target).convert
854
+ end
932
855
  end
933
856
 
934
857
  if found_macroish && (text.include? 'tnote')
935
- text = text.gsub(InlineFootnoteMacroRx) {
936
- # alias match for Ruby 1.8.7 compat
937
- m = $~
858
+ text = text.gsub InlineFootnoteMacroRx do
938
859
  # honor the escape
939
- if $&.start_with? RS
940
- next m[0].slice 1, m[0].length
941
- end
942
- if m[1] # footnoteref (legacy)
943
- id, text = (m[3] || '').split(',', 2)
860
+ next $&.slice 1, $&.length if $&.start_with? RS
861
+
862
+ # footnoteref
863
+ if $1
864
+ if $3
865
+ id, text = $3.split ',', 2
866
+ logger.warn %(found deprecated footnoteref macro: #{$&}; use footnote macro with target instead) unless doc.compat_mode
867
+ else
868
+ next $&
869
+ end
870
+ # footnote
944
871
  else
945
- id, text = m[2], m[3]
872
+ id = $2
873
+ text = $3
946
874
  end
875
+
947
876
  if id
948
877
  if text
949
878
  # REVIEW it's a dirty job, but somebody's gotta do it
950
- text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
879
+ text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)))
951
880
  index = doc.counter('footnote-number')
952
881
  doc.register(:footnotes, Document::Footnote.new(index, id, text))
953
882
  type, target = :ref, nil
@@ -962,15 +891,15 @@ module Substitutors
962
891
  end
963
892
  elsif text
964
893
  # REVIEW it's a dirty job, but somebody's gotta do it
965
- text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
894
+ text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)))
966
895
  index = doc.counter('footnote-number')
967
896
  doc.register(:footnotes, Document::Footnote.new(index, id, text))
968
897
  type = target = nil
969
898
  else
970
- next m[0]
899
+ next $&
971
900
  end
972
- Inline.new(self, :footnote, text, :attributes => {'index' => index}, :id => id, :target => target, :type => type).convert
973
- }
901
+ Inline.new(self, :footnote, text, attributes: { 'index' => index }, id: id, target: target, type: type).convert
902
+ end
974
903
  end
975
904
 
976
905
  sub_inline_xrefs(sub_inline_anchors(text, found), found)
@@ -979,17 +908,15 @@ module Substitutors
979
908
  # Internal: Substitute normal and bibliographic anchors
980
909
  def sub_inline_anchors(text, found = nil)
981
910
  if @context == :list_item && @parent.style == 'bibliography'
982
- text = text.sub(InlineBiblioAnchorRx) {
983
- # NOTE target property on :bibref is deprecated
984
- Inline.new(self, :anchor, %([#{$2 || $1}]), :type => :bibref, :id => $1, :target => $1).convert
985
- }
911
+ text = text.sub(InlineBiblioAnchorRx) { (Inline.new self, :anchor, $2, type: :bibref, id: $1).convert }
986
912
  end
987
913
 
988
914
  if ((!found || found[:square_bracket]) && text.include?('[[')) ||
989
915
  ((!found || found[:macroish]) && text.include?('or:'))
990
- text = text.gsub(InlineAnchorRx) {
916
+ text = text.gsub InlineAnchorRx do
991
917
  # honor the escape
992
918
  next $&.slice 1, $&.length if $1
919
+
993
920
  # NOTE reftext is only relevant for DocBook output; used as value of xreflabel attribute
994
921
  if (id = $2)
995
922
  reftext = $3
@@ -999,9 +926,8 @@ module Substitutors
999
926
  reftext = reftext.gsub ESC_R_SB, R_SB
1000
927
  end
1001
928
  end
1002
- # NOTE target property on :ref is deprecated
1003
- Inline.new(self, :anchor, reftext, :type => :ref, :id => id, :target => id).convert
1004
- }
929
+ Inline.new(self, :anchor, reftext, type: :ref, id: id).convert
930
+ end
1005
931
  end
1006
932
 
1007
933
  text
@@ -1010,21 +936,18 @@ module Substitutors
1010
936
  # Internal: Substitute cross reference links
1011
937
  def sub_inline_xrefs(content, found = nil)
1012
938
  if ((found ? found[:macroish] : (content.include? '[')) && (content.include? 'xref:')) || ((content.include? '&') && (content.include? 'lt;&'))
1013
- content = content.gsub(InlineXrefMacroRx) {
1014
- # alias match for Ruby 1.8.7 compat
1015
- m = $~
939
+ content = content.gsub InlineXrefMacroRx do
1016
940
  # honor the escape
1017
- if $&.start_with? RS
1018
- next m[0].slice 1, m[0].length
1019
- end
941
+ next $&.slice 1, $&.length if $&.start_with? RS
942
+
1020
943
  attrs, doc = {}, @document
1021
- if (refid = m[1])
944
+ if (refid = $1)
1022
945
  refid, text = refid.split ',', 2
1023
946
  text = text.lstrip if text
1024
947
  else
1025
948
  macro = true
1026
- refid = m[2]
1027
- if (text = m[3])
949
+ refid = $2
950
+ if (text = $3)
1028
951
  text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
1029
952
  # NOTE if an equal sign (=) is present, parse text as attributes
1030
953
  text = ((AttributeList.new text, self).parse_into attrs)[1] if !doc.compat_mode && (text.include? '=')
@@ -1035,21 +958,27 @@ module Substitutors
1035
958
  fragment = refid
1036
959
  elsif (hash_idx = refid.index '#')
1037
960
  if hash_idx > 0
1038
- if (fragment_len = refid.length - hash_idx - 1) > 0
961
+ if (fragment_len = refid.length - 1 - hash_idx) > 0
1039
962
  path, fragment = (refid.slice 0, hash_idx), (refid.slice hash_idx + 1, fragment_len)
1040
963
  else
1041
- path = refid.slice 0, hash_idx
964
+ path = refid.chop
1042
965
  end
1043
- if (ext = ::File.extname path).empty?
966
+ if macro
967
+ src2src = (path = path.slice 0, path.length - 5) if path.end_with? '.adoc'
968
+ elsif (last_dot_idx = path.rindex '.') && ASCIIDOC_EXTENSIONS[path.slice last_dot_idx, path.length]
969
+ src2src = (path = path.slice 0, last_dot_idx)
970
+ else
1044
971
  src2src = path
1045
- elsif ASCIIDOC_EXTENSIONS[ext]
1046
- src2src = (path = path.slice 0, path.length - ext.length)
1047
972
  end
1048
973
  else
1049
974
  target, fragment = refid, (refid.slice 1, refid.length)
1050
975
  end
1051
- elsif macro && (refid.end_with? '.adoc')
1052
- src2src = (path = refid.slice 0, refid.length - 5)
976
+ elsif macro && (refid.include? '.')
977
+ if refid.end_with? '.adoc'
978
+ src2src = (path = refid.slice 0, refid.length - 5)
979
+ else
980
+ path = refid
981
+ end
1053
982
  else
1054
983
  fragment = refid
1055
984
  end
@@ -1057,14 +986,14 @@ module Substitutors
1057
986
  # handles: #id
1058
987
  if target
1059
988
  refid = fragment
1060
- logger.warn %(invalid reference: #{refid}) if $VERBOSE && !(doc.catalog[:ids].key? refid)
989
+ logger.debug %(possible invalid reference: #{refid}) if logger.debug? && !doc.catalog[:refs][refid]
1061
990
  elsif path
1062
991
  # handles: path#, path#id, path.adoc#, path.adoc#id, or path.adoc (xref macro only)
1063
992
  # the referenced path is the current document, or its contents have been included in the current document
1064
993
  if src2src && (doc.attributes['docname'] == path || doc.catalog[:includes][path])
1065
994
  if fragment
1066
995
  refid, path, target = fragment, nil, %(##{fragment})
1067
- logger.warn %(invalid reference: #{refid}) if $VERBOSE && !(doc.catalog[:ids].key? refid)
996
+ logger.debug %(possible invalid reference: #{refid}) if logger.debug? && !doc.catalog[:refs][refid]
1068
997
  else
1069
998
  refid, path, target = nil, nil, '#'
1070
999
  end
@@ -1079,21 +1008,21 @@ module Substitutors
1079
1008
  # handles: id (in compat mode or when natural xrefs are disabled)
1080
1009
  elsif doc.compat_mode || !Compliance.natural_xrefs
1081
1010
  refid, target = fragment, %(##{fragment})
1082
- logger.warn %(invalid reference: #{refid}) if $VERBOSE && !(doc.catalog[:ids].key? refid)
1011
+ logger.debug %(possible invalid reference: #{refid}) if logger.debug? && doc.catalog[:refs][refid]
1083
1012
  # handles: id
1084
- elsif doc.catalog[:ids].key? fragment
1013
+ elsif doc.catalog[:refs][fragment]
1085
1014
  refid, target = fragment, %(##{fragment})
1086
1015
  # handles: Node Title or Reference Text
1087
1016
  # do reverse lookup on fragment if not a known ID and resembles reftext (contains a space or uppercase char)
1088
- elsif (refid = doc.catalog[:ids].key fragment) && ((fragment.include? ' ') || fragment.downcase != fragment)
1017
+ elsif (refid = doc.resolve_id fragment) && ((fragment.include? ' ') || fragment.downcase != fragment)
1089
1018
  fragment, target = refid, %(##{refid})
1090
1019
  else
1091
1020
  refid, target = fragment, %(##{fragment})
1092
- logger.warn %(invalid reference: #{refid}) if $VERBOSE
1021
+ logger.debug %(possible invalid reference: #{refid}) if logger.debug?
1093
1022
  end
1094
1023
  attrs['path'], attrs['fragment'], attrs['refid'] = path, fragment, refid
1095
- Inline.new(self, :anchor, text, :type => :xref, :target => target, :attributes => attrs).convert
1096
- }
1024
+ Inline.new(self, :anchor, text, type: :xref, target: target, attributes: attrs).convert
1025
+ end
1097
1026
  end
1098
1027
 
1099
1028
  content
@@ -1107,15 +1036,15 @@ module Substitutors
1107
1036
  def sub_callouts(text)
1108
1037
  callout_rx = (attr? 'line-comment') ? CalloutSourceRxMap[attr 'line-comment'] : CalloutSourceRx
1109
1038
  autonum = 0
1110
- text.gsub(callout_rx) {
1039
+ text.gsub callout_rx do
1111
1040
  # honor the escape
1112
1041
  if $2
1113
1042
  # use sub since it might be behind a line comment
1114
1043
  $&.sub(RS, '')
1115
1044
  else
1116
- Inline.new(self, :callout, $4 == '.' ? (autonum += 1).to_s : $4, :id => @document.callouts.read_next_id, :attributes => { 'guard' => $1 }).convert
1045
+ Inline.new(self, :callout, $4 == '.' ? (autonum += 1).to_s : $4, id: @document.callouts.read_next_id, attributes: { 'guard' => $1 }).convert
1117
1046
  end
1118
- }
1047
+ end
1119
1048
  end
1120
1049
 
1121
1050
  # Public: Substitute post replacements
@@ -1124,15 +1053,16 @@ module Substitutors
1124
1053
  #
1125
1054
  # Returns the converted String text
1126
1055
  def sub_post_replacements(text)
1127
- if (@document.attributes.key? 'hardbreaks') || (@attributes.key? 'hardbreaks-option')
1056
+ #if attr? 'hardbreaks-option', nil, true
1057
+ if @attributes['hardbreaks-option'] || @document.attributes['hardbreaks-option']
1128
1058
  lines = text.split LF, -1
1129
1059
  return text if lines.size < 2
1130
1060
  last = lines.pop
1131
- (lines.map {|line|
1132
- Inline.new(self, :break, (line.end_with? HARD_LINE_BREAK) ? (line.slice 0, line.length - 2) : line, :type => :line).convert
1133
- } << last).join LF
1061
+ (lines.map do |line|
1062
+ Inline.new(self, :break, (line.end_with? HARD_LINE_BREAK) ? (line.slice 0, line.length - 2) : line, type: :line).convert
1063
+ end << last).join LF
1134
1064
  elsif (text.include? PLUS) && (text.include? HARD_LINE_BREAK)
1135
- text.gsub(HardLineBreakRx) { Inline.new(self, :break, $1, :type => :line).convert }
1065
+ text.gsub(HardLineBreakRx) { Inline.new(self, :break, $1, type: :line).convert }
1136
1066
  else
1137
1067
  text
1138
1068
  end
@@ -1156,20 +1086,20 @@ module Substitutors
1156
1086
 
1157
1087
  if scope == :constrained
1158
1088
  if unescaped_attrs
1159
- %(#{unescaped_attrs}#{Inline.new(self, :quoted, match[3], :type => type).convert})
1089
+ %(#{unescaped_attrs}#{Inline.new(self, :quoted, match[3], type: type).convert})
1160
1090
  else
1161
1091
  if (attrlist = match[2])
1162
1092
  id = (attributes = parse_quoted_text_attributes attrlist).delete 'id'
1163
1093
  type = :unquoted if type == :mark
1164
1094
  end
1165
- %(#{match[1]}#{Inline.new(self, :quoted, match[3], :type => type, :id => id, :attributes => attributes).convert})
1095
+ %(#{match[1]}#{Inline.new(self, :quoted, match[3], type: type, id: id, attributes: attributes).convert})
1166
1096
  end
1167
1097
  else
1168
1098
  if (attrlist = match[1])
1169
1099
  id = (attributes = parse_quoted_text_attributes attrlist).delete 'id'
1170
1100
  type = :unquoted if type == :mark
1171
1101
  end
1172
- Inline.new(self, :quoted, match[2], :type => type, :id => id, :attributes => attributes).convert
1102
+ Inline.new(self, :quoted, match[2], type: type, id: id, attributes: attributes).convert
1173
1103
  end
1174
1104
  end
1175
1105
 
@@ -1180,20 +1110,18 @@ module Substitutors
1180
1110
  #
1181
1111
  # Returns a Hash of attributes (role and id only)
1182
1112
  def parse_quoted_text_attributes str
1113
+ return {} if (str = str.rstrip).empty?
1183
1114
  # NOTE attributes are typically resolved after quoted text, so substitute eagerly
1184
1115
  str = sub_attributes str if str.include? ATTR_REF_HEAD
1185
- # for compliance, only consider first positional attribute
1116
+ # for compliance, only consider first positional attribute (very unlikely)
1186
1117
  str = str.slice 0, (str.index ',') if str.include? ','
1187
1118
 
1188
- if (str = str.strip).empty?
1189
- {}
1190
- elsif (str.start_with? '.', '#') && Compliance.shorthand_property_syntax
1191
- segments = str.split('#', 2)
1119
+ if (str.start_with? '.', '#') && Compliance.shorthand_property_syntax
1120
+ segments = str.split '#', 2
1192
1121
 
1193
1122
  if segments.size > 1
1194
1123
  id, *more_roles = segments[1].split('.')
1195
1124
  else
1196
- id = nil
1197
1125
  more_roles = []
1198
1126
  end
1199
1127
 
@@ -1211,7 +1139,7 @@ module Substitutors
1211
1139
  attrs['role'] = roles.join ' ' unless roles.empty?
1212
1140
  attrs
1213
1141
  else
1214
- {'role' => str}
1142
+ { 'role' => str }
1215
1143
  end
1216
1144
  end
1217
1145
 
@@ -1231,8 +1159,8 @@ module Substitutors
1231
1159
  # Returns an empty Hash if attrlist is nil or empty, otherwise a Hash of parsed attributes.
1232
1160
  def parse_attributes attrlist, posattrs = [], opts = {}
1233
1161
  return {} unless attrlist && !attrlist.empty?
1234
- attrlist = @document.sub_attributes attrlist if opts[:sub_input] && (attrlist.include? ATTR_REF_HEAD)
1235
1162
  attrlist = unescape_bracketed_text attrlist if opts[:unescape_input]
1163
+ attrlist = @document.sub_attributes attrlist if opts[:sub_input] && (attrlist.include? ATTR_REF_HEAD)
1236
1164
  # substitutions are only performed on attribute values if block is not nil
1237
1165
  block = self if opts[:sub_result]
1238
1166
  if (into = opts[:into])
@@ -1268,7 +1196,7 @@ module Substitutors
1268
1196
  end
1269
1197
  end
1270
1198
 
1271
- # Internal: Strip bounding whitespace, fold endlines and unescape closing
1199
+ # Internal: Strip bounding whitespace, fold newlines and unescape closing
1272
1200
  # square brackets from text extracted from brackets
1273
1201
  def unescape_bracketed_text text
1274
1202
  if (text = text.strip.tr LF, ' ').include? R_SB
@@ -1277,7 +1205,7 @@ module Substitutors
1277
1205
  text
1278
1206
  end
1279
1207
 
1280
- # Internal: Strip bounding whitespace and fold endlines
1208
+ # Internal: Strip bounding whitespace and fold newlines
1281
1209
  def normalize_string str, unescape_brackets = false
1282
1210
  unless str.empty?
1283
1211
  str = str.strip.tr LF, ' '
@@ -1299,33 +1227,30 @@ module Substitutors
1299
1227
  # for double-quoted values (in which commas are ignored)
1300
1228
  def split_simple_csv str
1301
1229
  if str.empty?
1302
- values = []
1230
+ []
1303
1231
  elsif str.include? '"'
1304
1232
  values = []
1305
- current = []
1233
+ accum = ''
1306
1234
  quote_open = false
1307
1235
  str.each_char do |c|
1308
1236
  case c
1309
1237
  when ','
1310
1238
  if quote_open
1311
- current << c
1239
+ accum = accum + c
1312
1240
  else
1313
- values << current.join.strip
1314
- current = []
1241
+ values << accum.strip
1242
+ accum = ''
1315
1243
  end
1316
1244
  when '"'
1317
1245
  quote_open = !quote_open
1318
1246
  else
1319
- current << c
1247
+ accum = accum + c
1320
1248
  end
1321
1249
  end
1322
-
1323
- values << current.join.strip
1250
+ values << accum.strip
1324
1251
  else
1325
- values = str.split(',').map {|it| it.strip }
1252
+ str.split(',').map {|it| it.strip }
1326
1253
  end
1327
-
1328
- values
1329
1254
  end
1330
1255
 
1331
1256
  # Internal: Resolve the list of comma-delimited subs against the possible options.
@@ -1403,160 +1328,92 @@ module Substitutors
1403
1328
  resolve_subs subs, :inline, nil, 'passthrough macro'
1404
1329
  end
1405
1330
 
1406
- # Public: Highlight the source code if a source highlighter is defined
1407
- # on the document, otherwise return the text unprocessed
1331
+ # Public: Highlight (i.e., colorize) the source code during conversion using a syntax highlighter, if activated by the
1332
+ # source-highlighter document attribute. Otherwise return the text with verbatim substitutions applied.
1408
1333
  #
1409
- # Callout marks are stripped from the source prior to passing it to the
1410
- # highlighter, then later restored in converted form, so they are not
1411
- # incorrectly processed by the source highlighter.
1334
+ # If the process_callouts argument is true, this method will extract the callout marks from the source before passing
1335
+ # it to the syntax highlighter, then subsequently restore those callout marks to the highlighted source so the callout
1336
+ # marks don't confuse the syntax highlighter.
1412
1337
  #
1413
- # source - the source code String to highlight
1414
- # process_callouts - a Boolean flag indicating whether callout marks should be substituted
1338
+ # source - the source code String to syntax highlight
1339
+ # process_callouts - a Boolean flag indicating whether callout marks should be located and substituted
1415
1340
  #
1416
- # returns the highlighted source code, if a source highlighter is defined
1417
- # on the document, otherwise the source with verbatim substituions applied
1418
- def highlight_source source, process_callouts, highlighter = nil
1419
- case (highlighter ||= @document.attributes['source-highlighter'])
1420
- when 'coderay'
1421
- unless (highlighter_loaded = defined? ::CodeRay) ||
1422
- (defined? @@coderay_unavailable) || @document.attributes['coderay-unavailable']
1423
- if (Helpers.require_library 'coderay', true, :warn).nil?
1424
- # prevent further attempts to load CodeRay in this process
1425
- @@coderay_unavailable = true
1426
- else
1427
- highlighter_loaded = true
1428
- end
1429
- end
1430
- when 'pygments'
1431
- unless (highlighter_loaded = defined? ::Pygments) ||
1432
- (defined? @@pygments_unavailable) || @document.attributes['pygments-unavailable']
1433
- if (Helpers.require_library 'pygments', 'pygments.rb', :warn).nil?
1434
- # prevent further attempts to load Pygments in this process
1435
- @@pygments_unavailable = true
1436
- else
1437
- highlighter_loaded = true
1438
- end
1439
- end
1440
- else
1441
- # unknown highlighting library (something is misconfigured if we arrive here)
1442
- highlighter_loaded = false
1443
- end
1444
-
1445
- return sub_source source, process_callouts unless highlighter_loaded
1341
+ # Returns the highlighted source code, if a syntax highlighter is defined on the document, otherwise the source with
1342
+ # verbatim substituions applied
1343
+ def highlight_source source, process_callouts
1344
+ # NOTE the call to highlight? is a defensive check since, normally, we wouldn't arrive here unless it returns true
1345
+ return sub_source source, process_callouts unless (syntax_hl = @document.syntax_highlighter) && syntax_hl.highlight?
1446
1346
 
1447
- lineno = 0
1448
- callout_on_last = false
1449
1347
  if process_callouts
1450
1348
  callout_marks = {}
1451
- last = -1
1349
+ lineno = 0
1350
+ last_lineno = nil
1452
1351
  callout_rx = (attr? 'line-comment') ? CalloutExtractRxMap[attr 'line-comment'] : CalloutExtractRx
1453
1352
  # extract callout marks, indexed by line number
1454
- source = source.split(LF, -1).map {|line|
1455
- lineno = lineno + 1
1456
- line.gsub(callout_rx) {
1353
+ source = (source.split LF, -1).map do |line|
1354
+ lineno += 1
1355
+ line.gsub callout_rx do
1457
1356
  # honor the escape
1458
1357
  if $2
1459
1358
  # use sub since it might be behind a line comment
1460
- $&.sub(RS, '')
1359
+ $&.sub RS, ''
1461
1360
  else
1462
1361
  (callout_marks[lineno] ||= []) << [$1, $4]
1463
- last = lineno
1362
+ last_lineno = lineno
1464
1363
  nil
1465
1364
  end
1466
- }
1467
- }.join LF
1468
- callout_on_last = (last == lineno)
1469
- callout_marks = nil if callout_marks.empty?
1470
- else
1471
- callout_marks = nil
1472
- end
1473
-
1474
- linenums_mode = nil
1475
- highlight_lines = nil
1476
-
1477
- case highlighter
1478
- when 'coderay'
1479
- if (linenums_mode = (attr? 'linenums', nil, false) ? (@document.attributes['coderay-linenums-mode'] || :table).to_sym : nil)
1480
- start = 1 if (start = (attr 'start', nil, 1).to_i) < 1
1481
- if attr? 'highlight', nil, false
1482
- highlight_lines = resolve_lines_to_highlight source, (attr 'highlight', nil, false)
1483
- end
1484
- end
1485
- result = ::CodeRay::Duo[attr('language', :text, false).to_sym, :html, {
1486
- :css => (@document.attributes['coderay-css'] || :class).to_sym,
1487
- :line_numbers => linenums_mode,
1488
- :line_number_start => start,
1489
- :line_number_anchors => false,
1490
- :highlight_lines => highlight_lines,
1491
- :bold_every => false
1492
- }].highlight source
1493
- when 'pygments'
1494
- lexer = ::Pygments::Lexer.find_by_alias(attr 'language', 'text', false) || ::Pygments::Lexer.find_by_mimetype('text/plain')
1495
- opts = { :cssclass => 'pyhl', :classprefix => 'tok-', :nobackground => true, :stripnl => false }
1496
- opts[:startinline] = !(option? 'mixed') if lexer.name == 'PHP'
1497
- unless (@document.attributes['pygments-css'] || 'class') == 'class'
1498
- opts[:noclasses] = true
1499
- opts[:style] = (@document.attributes['pygments-style'] || Stylesheets::DEFAULT_PYGMENTS_STYLE)
1500
- end
1501
- if attr? 'highlight', nil, false
1502
- unless (highlight_lines = resolve_lines_to_highlight source, (attr 'highlight', nil, false)).empty?
1503
- opts[:hl_lines] = highlight_lines.join ' '
1504
- end
1505
- end
1506
- # NOTE highlight can return nil if something goes wrong; fallback to source if this happens
1507
- # TODO we could add the line numbers in ourselves instead of having to strip out the junk
1508
- if (attr? 'linenums', nil, false) && (opts[:linenostart] = (start = attr 'start', 1, false).to_i < 1 ? 1 : start) &&
1509
- (opts[:linenos] = @document.attributes['pygments-linenums-mode'] || 'table') == 'table'
1510
- linenums_mode = :table
1511
- if (result = lexer.highlight source, :options => opts)
1512
- result = (result.sub PygmentsWrapperDivRx, '\1').gsub PygmentsWrapperPreRx, '\1'
1513
- else
1514
- result = sub_specialchars source
1515
- end
1516
- elsif (result = lexer.highlight source, :options => opts)
1517
- if PygmentsWrapperPreRx =~ result
1518
- result = $1
1519
1365
  end
1366
+ end.join LF
1367
+ if last_lineno
1368
+ source = %(#{source}#{LF}) if last_lineno == lineno
1520
1369
  else
1521
- result = sub_specialchars source
1370
+ callout_marks = nil
1522
1371
  end
1523
1372
  end
1524
1373
 
1374
+ doc_attrs = @document.attributes
1375
+ syntax_hl_name = syntax_hl.name
1376
+ if (linenums_mode = (attr? 'linenums') ? (doc_attrs[%(#{syntax_hl_name}-linenums-mode)] || :table).to_sym : nil)
1377
+ start_line_number = 1 if (start_line_number = (attr 'start', 1).to_i) < 1
1378
+ end
1379
+ highlight_lines = resolve_lines_to_highlight source, (attr 'highlight') if attr? 'highlight'
1380
+
1381
+ highlighted, source_offset = syntax_hl.highlight self, source, (attr 'language'),
1382
+ callouts: callout_marks,
1383
+ css_mode: (doc_attrs[%(#{syntax_hl_name}-css)] || :class).to_sym,
1384
+ highlight_lines: highlight_lines,
1385
+ number_lines: linenums_mode,
1386
+ start_line_number: start_line_number,
1387
+ style: doc_attrs[%(#{syntax_hl_name}-style)]
1388
+
1525
1389
  # fix passthrough placeholders that got caught up in syntax highlighting
1526
- result = result.gsub HighlightedPassSlotRx, %(#{PASS_START}\\1#{PASS_END}) unless @passthroughs.empty?
1390
+ highlighted = highlighted.gsub HighlightedPassSlotRx, %(#{PASS_START}\\1#{PASS_END}) unless @passthroughs.empty?
1527
1391
 
1528
- if process_callouts && callout_marks
1529
- lineno = 0
1530
- autonum = 0
1531
- reached_code = linenums_mode != :table
1532
- result.split(LF, -1).map {|line|
1533
- unless reached_code
1534
- next line unless line.include?('<td class="code">')
1535
- reached_code = true
1536
- end
1537
- lineno += 1
1538
- if (conums = callout_marks.delete(lineno))
1539
- tail = nil
1540
- if callout_on_last && callout_marks.empty? && linenums_mode == :table
1541
- if highlighter == 'coderay' && (pos = line.index '</pre>')
1542
- line, tail = (line.slice 0, pos), (line.slice pos, line.length)
1543
- elsif highlighter == 'pygments' && (pos = line.start_with? '</td>')
1544
- line, tail = '', line
1545
- end
1546
- end
1392
+ # NOTE highlight method may have depleted callouts
1393
+ if callout_marks.nil_or_empty?
1394
+ highlighted
1395
+ else
1396
+ if source_offset
1397
+ preamble = highlighted.slice 0, source_offset
1398
+ highlighted = highlighted.slice source_offset, highlighted.length
1399
+ else
1400
+ preamble = ''
1401
+ end
1402
+ autonum = lineno = 0
1403
+ preamble + ((highlighted.split LF, -1).map do |line|
1404
+ if (conums = callout_marks.delete lineno += 1)
1547
1405
  if conums.size == 1
1548
1406
  guard, conum = conums[0]
1549
- %(#{line}#{Inline.new(self, :callout, conum == '.' ? (autonum += 1).to_s : conum, :id => @document.callouts.read_next_id, :attributes => { 'guard' => guard }).convert}#{tail})
1407
+ %(#{line}#{Inline.new(self, :callout, conum == '.' ? (autonum += 1).to_s : conum, id: @document.callouts.read_next_id, attributes: { 'guard' => guard }).convert})
1550
1408
  else
1551
- conums_markup = conums.map {|guard_it, conum_it| Inline.new(self, :callout, conum_it == '.' ? (autonum += 1).to_s : conum_it, :id => @document.callouts.read_next_id, :attributes => { 'guard' => guard_it }).convert }.join ' '
1552
- %(#{line}#{conums_markup}#{tail})
1409
+ %(#{line}#{conums.map do |guard_it, conum_it|
1410
+ Inline.new(self, :callout, conum_it == '.' ? (autonum += 1).to_s : conum_it, id: @document.callouts.read_next_id, attributes: { 'guard' => guard_it }).convert
1411
+ end.join ' '})
1553
1412
  end
1554
1413
  else
1555
1414
  line
1556
1415
  end
1557
- }.join LF
1558
- else
1559
- result
1416
+ end.join LF)
1560
1417
  end
1561
1418
  end
1562
1419
 
@@ -1571,9 +1428,9 @@ module Substitutors
1571
1428
  negate = true
1572
1429
  end
1573
1430
  if (delim = (entry.include? '..') ? '..' : ((entry.include? '-') ? '-' : nil))
1574
- from, to = entry.split delim, 2
1431
+ from, delim, to = entry.partition delim
1575
1432
  to = (source.count LF) + 1 if to.empty? || (to = to.to_i) < 0
1576
- line_nums = (::Range.new from.to_i, to).to_a
1433
+ line_nums = (from.to_i..to).to_a
1577
1434
  if negate
1578
1435
  lines -= line_nums
1579
1436
  else
@@ -1600,6 +1457,9 @@ module Substitutors
1600
1457
  process_callouts ? sub_callouts(sub_specialchars source) : (sub_specialchars source)
1601
1458
  end
1602
1459
 
1460
+ # Internal: Inserts text into a formatted text enclosure; used by xreftext
1461
+ alias sub_placeholder sprintf unless RUBY_ENGINE == 'opal'
1462
+
1603
1463
  # Internal: Lock-in the substitutions for this block
1604
1464
  #
1605
1465
  # Looks for an attribute named "subs". If present, resolves substitutions
@@ -1615,16 +1475,11 @@ module Substitutors
1615
1475
  when :simple
1616
1476
  default_subs = NORMAL_SUBS
1617
1477
  when :verbatim
1618
- if @context == :listing || (@context == :literal && !(option? 'listparagraph'))
1619
- default_subs = VERBATIM_SUBS
1620
- elsif @context == :verse
1621
- default_subs = NORMAL_SUBS
1622
- else
1623
- default_subs = BASIC_SUBS
1624
- end
1478
+ # NOTE :literal with listparagraph-option gets folded into text of list item later
1479
+ default_subs = @context == :verse ? NORMAL_SUBS : VERBATIM_SUBS
1625
1480
  when :raw
1626
1481
  # TODO make pass subs a compliance setting; AsciiDoc Python performs :attributes and :macros on a pass block
1627
- default_subs = @context == :stem ? BASIC_SUBS : NONE_SUBS
1482
+ default_subs = @context == :stem ? BASIC_SUBS : NO_SUBS
1628
1483
  else
1629
1484
  return @subs
1630
1485
  end
@@ -1637,8 +1492,8 @@ module Substitutors
1637
1492
  end
1638
1493
 
1639
1494
  # QUESION delegate this logic to a method?
1640
- if @context == :listing && @style == 'source' && (@attributes.key? 'language') && (@document.basebackend? 'html') &&
1641
- (SUB_HIGHLIGHT.include? @document.attributes['source-highlighter']) && (idx = @subs.index :specialcharacters)
1495
+ if @context == :listing && @style == 'source' && (syntax_hl = @document.syntax_highlighter) &&
1496
+ syntax_hl.highlight? && (idx = @subs.index :specialcharacters)
1642
1497
  @subs[idx] = :highlight
1643
1498
  end
1644
1499