asciidoctor 1.5.8 → 2.0.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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