asciidoctor 0.1.4 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of asciidoctor might be problematic. Click here for more details.

Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +209 -25
  3. data/{LICENSE → LICENSE.adoc} +4 -3
  4. data/README.adoc +392 -395
  5. data/Rakefile +94 -137
  6. data/benchmark/benchmark.rb +127 -0
  7. data/benchmark/sample-data/mdbasics.adoc +334 -0
  8. data/bin/asciidoctor +5 -8
  9. data/bin/asciidoctor-safe +4 -8
  10. data/compat/asciidoc.conf +78 -11
  11. data/compat/font-awesome-3-compat.css +397 -0
  12. data/data/stylesheets/asciidoctor-default.css +399 -0
  13. data/data/stylesheets/coderay-asciidoctor.css +89 -0
  14. data/features/open_block.feature +92 -0
  15. data/features/pass_block.feature +66 -0
  16. data/features/step_definitions.rb +42 -0
  17. data/features/text_formatting.feature +55 -0
  18. data/features/xref.feature +116 -0
  19. data/lib/asciidoctor.rb +1155 -605
  20. data/lib/asciidoctor/abstract_block.rb +157 -71
  21. data/lib/asciidoctor/abstract_node.rb +150 -93
  22. data/lib/asciidoctor/attribute_list.rb +85 -90
  23. data/lib/asciidoctor/block.rb +51 -24
  24. data/lib/asciidoctor/callouts.rb +4 -7
  25. data/lib/asciidoctor/cli.rb +3 -0
  26. data/lib/asciidoctor/cli/invoker.rb +86 -76
  27. data/lib/asciidoctor/cli/options.rb +111 -61
  28. data/lib/asciidoctor/converter.rb +232 -0
  29. data/lib/asciidoctor/converter/base.rb +58 -0
  30. data/lib/asciidoctor/converter/composite.rb +66 -0
  31. data/lib/asciidoctor/converter/docbook45.rb +94 -0
  32. data/lib/asciidoctor/converter/docbook5.rb +684 -0
  33. data/lib/asciidoctor/converter/factory.rb +225 -0
  34. data/lib/asciidoctor/converter/html5.rb +1081 -0
  35. data/lib/asciidoctor/converter/template.rb +296 -0
  36. data/lib/asciidoctor/core_ext.rb +7 -0
  37. data/lib/asciidoctor/core_ext/object/nil_or_empty.rb +23 -0
  38. data/lib/asciidoctor/core_ext/string/chr.rb +6 -0
  39. data/lib/asciidoctor/core_ext/symbol/length.rb +6 -0
  40. data/lib/asciidoctor/document.rb +590 -304
  41. data/lib/asciidoctor/extensions.rb +1100 -308
  42. data/lib/asciidoctor/helpers.rb +109 -46
  43. data/lib/asciidoctor/inline.rb +16 -9
  44. data/lib/asciidoctor/list.rb +23 -15
  45. data/lib/asciidoctor/opal_ext.rb +4 -0
  46. data/lib/asciidoctor/opal_ext/comparable.rb +38 -0
  47. data/lib/asciidoctor/opal_ext/dir.rb +13 -0
  48. data/lib/asciidoctor/opal_ext/error.rb +2 -0
  49. data/lib/asciidoctor/opal_ext/file.rb +125 -0
  50. data/lib/asciidoctor/{lexer.rb → parser.rb} +646 -455
  51. data/lib/asciidoctor/path_resolver.rb +141 -77
  52. data/lib/asciidoctor/reader.rb +257 -187
  53. data/lib/asciidoctor/section.rb +12 -16
  54. data/lib/asciidoctor/stylesheets.rb +91 -0
  55. data/lib/asciidoctor/substitutors.rb +1548 -0
  56. data/lib/asciidoctor/table.rb +73 -57
  57. data/lib/asciidoctor/timings.rb +39 -0
  58. data/lib/asciidoctor/version.rb +1 -1
  59. data/man/asciidoctor.1 +22 -14
  60. data/man/asciidoctor.adoc +18 -10
  61. data/test/attributes_test.rb +314 -14
  62. data/test/blocks_test.rb +763 -118
  63. data/test/converter_test.rb +352 -0
  64. data/test/document_test.rb +518 -199
  65. data/test/extensions_test.rb +273 -103
  66. data/test/fixtures/asciidoc_index.txt +27 -13
  67. data/test/fixtures/basic-docinfo.xml +1 -1
  68. data/test/fixtures/chapter-a.adoc +3 -0
  69. data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
  70. data/test/fixtures/docinfo.xml +1 -1
  71. data/test/fixtures/include-file.asciidoc +2 -0
  72. data/test/fixtures/master.adoc +5 -0
  73. data/test/invoker_test.rb +173 -61
  74. data/test/links_test.rb +97 -21
  75. data/test/lists_test.rb +181 -22
  76. data/test/options_test.rb +86 -2
  77. data/test/paragraphs_test.rb +47 -5
  78. data/test/{lexer_test.rb → parser_test.rb} +128 -57
  79. data/test/paths_test.rb +36 -1
  80. data/test/preamble_test.rb +25 -17
  81. data/test/reader_test.rb +404 -249
  82. data/test/sections_test.rb +623 -58
  83. data/test/substitutions_test.rb +609 -132
  84. data/test/tables_test.rb +198 -24
  85. data/test/test_helper.rb +101 -31
  86. data/test/text_test.rb +88 -31
  87. metadata +160 -64
  88. data/Gemfile +0 -12
  89. data/Guardfile +0 -18
  90. data/asciidoctor.gemspec +0 -143
  91. data/lib/asciidoctor/backends/_stylesheets.rb +0 -466
  92. data/lib/asciidoctor/backends/base_template.rb +0 -114
  93. data/lib/asciidoctor/backends/docbook45.rb +0 -774
  94. data/lib/asciidoctor/backends/docbook5.rb +0 -103
  95. data/lib/asciidoctor/backends/html5.rb +0 -1214
  96. data/lib/asciidoctor/renderer.rb +0 -259
  97. data/lib/asciidoctor/substituters.rb +0 -1083
  98. data/test/fixtures/asciidoc.txt +0 -105
  99. data/test/fixtures/ascshort.txt +0 -32
  100. data/test/fixtures/list_elements.asciidoc +0 -10
  101. data/test/renderer_test.rb +0 -162
@@ -1,4 +1,8 @@
1
- require 'test_helper'
1
+ # encoding: UTF-8
2
+ unless defined? ASCIIDOCTOR_PROJECT_DIR
3
+ $: << File.dirname(__FILE__); $:.uniq!
4
+ require 'test_helper'
5
+ end
2
6
 
3
7
  # TODO
4
8
  # - test negatives
@@ -6,7 +10,7 @@ require 'test_helper'
6
10
  context 'Substitutions' do
7
11
  context 'Dispatcher' do
8
12
  test 'apply normal substitutions' do
9
- para = block_from_string("[blue]'http://asciidoc.org[AsciiDoc]' & [red]*Ruby*\n&#167; Making +++<u>documentation</u>+++ together +\nsince (C) {inception_year}.")
13
+ para = block_from_string("[blue]_http://asciidoc.org[AsciiDoc]_ & [red]*Ruby*\n&#167; Making +++<u>documentation</u>+++ together +\nsince (C) {inception_year}.")
10
14
  para.document.attributes['inception_year'] = '2012'
11
15
  result = para.apply_normal_subs(para.lines)
12
16
  assert_equal %{<em class="blue"><a href="http://asciidoc.org">AsciiDoc</a></em> &amp; <strong class="red">Ruby</strong>\n&#167; Making <u>documentation</u> together<br>\nsince &#169; 2012.}, result
@@ -14,84 +18,147 @@ context 'Substitutions' do
14
18
  end
15
19
 
16
20
  context 'Quotes' do
21
+ BACKSLASH = '\\'
22
+
17
23
  test 'single-line double-quoted string' do
18
- para = block_from_string(%q{``a few quoted words''})
24
+ para = block_from_string(%q{``a few quoted words''}, :attributes => {'compat-mode' => ''})
25
+ assert_equal '&#8220;a few quoted words&#8221;', para.sub_quotes(para.source)
26
+
27
+ para = block_from_string(%q{"`a few quoted words`"})
19
28
  assert_equal '&#8220;a few quoted words&#8221;', para.sub_quotes(para.source)
20
29
  end
21
30
 
22
31
  test 'escaped single-line double-quoted string' do
23
- para = block_from_string(%q{\``a few quoted words''})
32
+ para = block_from_string %(#{BACKSLASH}``a few quoted words''), :attributes => {'compat-mode' => ''}
24
33
  assert_equal %q(&#8216;`a few quoted words&#8217;'), para.sub_quotes(para.source)
34
+
35
+ para = block_from_string %(#{BACKSLASH * 2}``a few quoted words''), :attributes => {'compat-mode' => ''}
36
+ assert_equal %q(``a few quoted words''), para.sub_quotes(para.source)
37
+
38
+ para = block_from_string(%(#{BACKSLASH}"`a few quoted words`"))
39
+ assert_equal %q("`a few quoted words`"), para.sub_quotes(para.source)
40
+
41
+ para = block_from_string(%(#{BACKSLASH * 2}"`a few quoted words`"))
42
+ assert_equal %(#{BACKSLASH}"`a few quoted words`"), para.sub_quotes(para.source)
25
43
  end
26
44
 
27
45
  test 'multi-line double-quoted string' do
28
- para = block_from_string(%Q{``a few\nquoted words''})
46
+ para = block_from_string(%Q{``a few\nquoted words''}, :attributes => {'compat-mode' => ''})
47
+ assert_equal "&#8220;a few\nquoted words&#8221;", para.sub_quotes(para.source)
48
+
49
+ para = block_from_string(%Q{"`a few\nquoted words`"})
29
50
  assert_equal "&#8220;a few\nquoted words&#8221;", para.sub_quotes(para.source)
30
51
  end
31
52
 
32
53
  test 'double-quoted string with inline single quote' do
33
- para = block_from_string(%q{``Here's Johnny!''})
54
+ para = block_from_string(%q{``Here's Johnny!''}, :attributes => {'compat-mode' => ''})
55
+ assert_equal %q{&#8220;Here's Johnny!&#8221;}, para.sub_quotes(para.source)
56
+
57
+ para = block_from_string(%q{"`Here's Johnny!`"})
34
58
  assert_equal %q{&#8220;Here's Johnny!&#8221;}, para.sub_quotes(para.source)
35
59
  end
36
60
 
37
61
  test 'double-quoted string with inline backquote' do
38
- para = block_from_string(%q{``Here`s Johnny!''})
62
+ para = block_from_string(%q{``Here`s Johnny!''}, :attributes => {'compat-mode' => ''})
39
63
  assert_equal %q{&#8220;Here`s Johnny!&#8221;}, para.sub_quotes(para.source)
64
+
65
+ para = block_from_string(%q{"`Here`s Johnny!`"})
66
+ assert_equal %q{&#8220;Here`s Johnny!&#8221;}, para.sub_quotes(para.source)
67
+ end
68
+
69
+ test 'double-quoted string around monospaced text' do
70
+ para = block_from_string(%q("``E=mc^2^` is the solution!`"))
71
+ assert_equal %q(&#8220;`E=mc<sup>2</sup>` is the solution!&#8221;), para.apply_subs(para.source);
72
+
73
+ para = block_from_string(%q("```E=mc^2^`` is the solution!`"))
74
+ assert_equal %q(&#8220;<code>E=mc<sup>2</sup></code> is the solution!&#8221;), para.apply_subs(para.source);
40
75
  end
41
76
 
42
77
  test 'single-line single-quoted string' do
43
- para = block_from_string(%q{`a few quoted words'})
78
+ para = block_from_string(%q{`a few quoted words'}, :attributes => {'compat-mode' => ''})
79
+ assert_equal '&#8216;a few quoted words&#8217;', para.sub_quotes(para.source)
80
+
81
+ para = block_from_string(%q{'`a few quoted words`'})
44
82
  assert_equal '&#8216;a few quoted words&#8217;', para.sub_quotes(para.source)
45
83
  end
46
84
 
47
85
  test 'escaped single-line single-quoted string' do
48
- para = block_from_string(%q{\`a few quoted words'})
86
+ para = block_from_string(%(#{BACKSLASH}`a few quoted words'), :attributes => {'compat-mode' => ''})
49
87
  assert_equal %(`a few quoted words'), para.sub_quotes(para.source)
88
+
89
+ para = block_from_string(%(#{BACKSLASH}'`a few quoted words`'))
90
+ assert_equal %('`a few quoted words`'), para.sub_quotes(para.source)
50
91
  end
51
92
 
52
93
  test 'multi-line single-quoted string' do
53
- para = block_from_string(%Q{`a few\nquoted words'})
94
+ para = block_from_string(%Q{`a few\nquoted words'}, :attributes => {'compat-mode' => ''})
95
+ assert_equal "&#8216;a few\nquoted words&#8217;", para.sub_quotes(para.source)
96
+
97
+ para = block_from_string(%Q{'`a few\nquoted words`'})
54
98
  assert_equal "&#8216;a few\nquoted words&#8217;", para.sub_quotes(para.source)
55
99
  end
56
100
 
57
101
  test 'single-quoted string with inline single quote' do
58
- para = block_from_string(%q{`That isn't what I did.'})
102
+ para = block_from_string(%q{`That isn't what I did.'}, :attributes => {'compat-mode' => ''})
103
+ assert_equal %q{&#8216;That isn't what I did.&#8217;}, para.sub_quotes(para.source)
104
+
105
+ para = block_from_string(%q{'`That isn't what I did.`'})
59
106
  assert_equal %q{&#8216;That isn't what I did.&#8217;}, para.sub_quotes(para.source)
60
107
  end
61
108
 
62
109
  test 'single-quoted string with inline backquote' do
63
- para = block_from_string(%q{`Here`s Johnny!'})
110
+ para = block_from_string(%q{`Here`s Johnny!'}, :attributes => {'compat-mode' => ''})
111
+ assert_equal %q{&#8216;Here`s Johnny!&#8217;}, para.sub_quotes(para.source)
112
+
113
+ para = block_from_string(%q{'`Here`s Johnny!`'})
64
114
  assert_equal %q{&#8216;Here`s Johnny!&#8217;}, para.sub_quotes(para.source)
65
115
  end
66
116
 
67
- test 'single-line constrained unquoted string' do
117
+ test 'single-line constrained marked string' do
118
+ #para = block_from_string(%q{#a few words#}, :attributes => {'compat-mode' => ''})
119
+ #assert_equal 'a few words', para.sub_quotes(para.source)
120
+
68
121
  para = block_from_string(%q{#a few words#})
69
- assert_equal 'a few words', para.sub_quotes(para.source)
122
+ assert_equal '<mark>a few words</mark>', para.sub_quotes(para.source)
70
123
  end
71
124
 
72
- test 'escaped single-line constrained unquoted string' do
73
- para = block_from_string(%q{\#a few words#})
125
+ test 'escaped single-line constrained marked string' do
126
+ para = block_from_string(%(#{BACKSLASH}#a few words#))
74
127
  assert_equal '#a few words#', para.sub_quotes(para.source)
75
128
  end
76
129
 
77
- test 'multi-line constrained unquoted string' do
130
+ test 'multi-line constrained marked string' do
131
+ #para = block_from_string(%Q{#a few\nwords#}, :attributes => {'compat-mode' => ''})
132
+ #assert_equal "a few\nwords", para.sub_quotes(para.source)
133
+
78
134
  para = block_from_string(%Q{#a few\nwords#})
79
- assert_equal "a few\nwords", para.sub_quotes(para.source)
135
+ assert_equal "<mark>a few\nwords</mark>", para.sub_quotes(para.source)
80
136
  end
81
137
 
82
- test 'single-line unconstrained unquoted string' do
138
+ test 'single-line unconstrained marked string' do
139
+ #para = block_from_string(%q{##--anything goes ##}, :attributes => {'compat-mode' => ''})
140
+ #assert_equal '--anything goes ', para.sub_quotes(para.source)
141
+
83
142
  para = block_from_string(%q{##--anything goes ##})
84
- assert_equal '--anything goes ', para.sub_quotes(para.source)
143
+ assert_equal '<mark>--anything goes </mark>', para.sub_quotes(para.source)
85
144
  end
86
145
 
87
- test 'escaped single-line unconstrained unquoted string' do
88
- para = block_from_string(%q{\##--anything goes ##})
89
- assert_equal '#--anything goes #', para.sub_quotes(para.source)
146
+ test 'escaped single-line unconstrained marked string' do
147
+ para = block_from_string(%(#{BACKSLASH}#{BACKSLASH}##--anything goes ##))
148
+ assert_equal '##--anything goes ##', para.sub_quotes(para.source)
90
149
  end
91
150
 
92
- test 'multi-line unconstrained unquoted string' do
151
+ test 'multi-line unconstrained marked string' do
152
+ #para = block_from_string(%Q{##--anything\ngoes ##}, :attributes => {'compat-mode' => ''})
153
+ #assert_equal "--anything\ngoes ", para.sub_quotes(para.source)
154
+
93
155
  para = block_from_string(%Q{##--anything\ngoes ##})
94
- assert_equal "--anything\ngoes ", para.sub_quotes(para.source)
156
+ assert_equal "<mark>--anything\ngoes </mark>", para.sub_quotes(para.source)
157
+ end
158
+
159
+ test 'single-line constrained marked string with role' do
160
+ para = block_from_string(%q{[statement]#a few words#})
161
+ assert_equal '<span class="statement">a few words</span>', para.sub_quotes(para.source)
95
162
  end
96
163
 
97
164
  test 'single-line constrained strong string' do
@@ -100,7 +167,7 @@ context 'Substitutions' do
100
167
  end
101
168
 
102
169
  test 'escaped single-line constrained strong string' do
103
- para = block_from_string(%q{\*a few strong words*})
170
+ para = block_from_string(%(#{BACKSLASH}*a few strong words*))
104
171
  assert_equal '*a few strong words*', para.sub_quotes(para.source)
105
172
  end
106
173
 
@@ -114,30 +181,46 @@ context 'Substitutions' do
114
181
  assert_equal '<strong>bl*ck</strong>-eye', para.sub_quotes(para.source)
115
182
  end
116
183
 
184
+ test 'constrained strong string containing an asterisk and multibyte word chars' do
185
+ para = block_from_string(%q{*黑*眼圈*})
186
+ assert_equal '<strong>黑*眼圈</strong>', para.sub_quotes(para.source)
187
+ end if ::RUBY_MIN_VERSION_1_9
188
+
117
189
  test 'single-line constrained quote variation emphasized string' do
118
- para = block_from_string(%q{'a few emphasized words'})
190
+ para = block_from_string(%q{_a few emphasized words_})
119
191
  assert_equal '<em>a few emphasized words</em>', para.sub_quotes(para.source)
120
192
  end
121
193
 
122
194
  test 'escaped single-line constrained quote variation emphasized string' do
123
- para = block_from_string(%q{\'a few emphasized words'})
124
- assert_equal %q('a few emphasized words'), para.sub_quotes(para.source)
195
+ para = block_from_string(%(#{BACKSLASH}_a few emphasized words_))
196
+ assert_equal %q(_a few emphasized words_), para.sub_quotes(para.source)
197
+ end
198
+
199
+ test 'escaped single quoted string' do
200
+ para = block_from_string(%(#{BACKSLASH}'a few emphasized words'))
201
+ # NOTE the \' is replaced with ' by the :replacements substitution, later in the substitution pipeline
202
+ assert_equal %(#{BACKSLASH}'a few emphasized words'), para.sub_quotes(para.source)
125
203
  end
126
204
 
127
205
  test 'multi-line constrained emphasized quote variation string' do
128
- para = block_from_string(%Q{'a few\nemphasized words'})
206
+ para = block_from_string(%Q{_a few\nemphasized words_})
129
207
  assert_equal "<em>a few\nemphasized words</em>", para.sub_quotes(para.source)
130
208
  end
131
209
 
132
210
  test 'single-quoted string containing an emphasized phrase' do
133
- para = block_from_string(%q{`I told him, 'Just go for it!''})
211
+ para = block_from_string(%q{`I told him, 'Just go for it!''}, :attributes => {'compat-mode' => ''})
134
212
  assert_equal '&#8216;I told him, <em>Just go for it!</em>&#8217;', para.sub_quotes(para.source)
213
+
214
+ para = block_from_string(%q{'`I told him, 'Just go for it!'`'})
215
+ assert_equal %q(&#8216;I told him, 'Just go for it!'&#8217;), para.sub_quotes(para.source)
135
216
  end
136
217
 
137
218
  test 'escaped single-quotes inside emphasized words are restored' do
138
- para = block_from_string(%q{'Here\'s Johnny!'})
139
- # NOTE the \' is replaced with ' by the :replacements substitution, later in the substitution pipeline
140
- assert_equal %q{<em>Here\'s Johnny!</em>}, para.sub_quotes(para.source)
219
+ para = block_from_string(%('Here#{BACKSLASH}'s Johnny!'), :attributes => {'compat-mode' => ''})
220
+ assert_equal %q(<em>Here's Johnny!</em>), para.apply_normal_subs(para.lines)
221
+
222
+ para = block_from_string(%('Here#{BACKSLASH}'s Johnny!'))
223
+ assert_equal %q('Here's Johnny!'), para.apply_normal_subs(para.lines)
141
224
  end
142
225
 
143
226
  test 'single-line constrained emphasized underline variation string' do
@@ -146,7 +229,7 @@ context 'Substitutions' do
146
229
  end
147
230
 
148
231
  test 'escaped single-line constrained emphasized underline variation string' do
149
- para = block_from_string(%q{\_a few emphasized words_})
232
+ para = block_from_string(%(#{BACKSLASH}_a few emphasized words_))
150
233
  assert_equal '_a few emphasized words_', para.sub_quotes(para.source)
151
234
  end
152
235
 
@@ -155,46 +238,67 @@ context 'Substitutions' do
155
238
  assert_equal "<em>a few\nemphasized words</em>", para.sub_quotes(para.source)
156
239
  end
157
240
 
241
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
158
242
  test 'single-line constrained monospaced string' do
159
- para = block_from_string(%q{`a few <\{monospaced\}> words`})
160
- # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
243
+ para = block_from_string(%(`a few <{monospaced}> words`), :attributes => {'monospaced' => 'monospaced', 'compat-mode' => ''})
161
244
  assert_equal '<code>a few &lt;{monospaced}&gt; words</code>', para.apply_normal_subs(para.lines)
245
+
246
+ para = block_from_string(%(`a few <{monospaced}> words`), :attributes => {'monospaced' => 'monospaced'})
247
+ assert_equal '<code>a few &lt;monospaced&gt; words</code>', para.apply_normal_subs(para.lines)
162
248
  end
163
249
 
250
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
164
251
  test 'single-line constrained monospaced string with role' do
165
- para = block_from_string(%q{[input]`a few <\{monospaced\}> words`})
166
- # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
252
+ para = block_from_string(%([input]`a few <{monospaced}> words`), :attributes => {'monospaced' => 'monospaced', 'compat-mode' => ''})
167
253
  assert_equal '<code class="input">a few &lt;{monospaced}&gt; words</code>', para.apply_normal_subs(para.lines)
254
+
255
+ para = block_from_string(%([input]`a few <{monospaced}> words`), :attributes => {'monospaced' => 'monospaced'})
256
+ assert_equal '<code class="input">a few &lt;monospaced&gt; words</code>', para.apply_normal_subs(para.lines)
168
257
  end
169
258
 
259
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
170
260
  test 'escaped single-line constrained monospaced string' do
171
- para = block_from_string(%q{\`a few <monospaced> words`})
172
- # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
261
+ para = block_from_string(%(#{BACKSLASH}`a few <monospaced> words`), :attributes => {'compat-mode' => ''})
262
+ assert_equal '`a few &lt;monospaced&gt; words`', para.apply_normal_subs(para.lines)
263
+
264
+ para = block_from_string(%(#{BACKSLASH}`a few <monospaced> words`))
173
265
  assert_equal '`a few &lt;monospaced&gt; words`', para.apply_normal_subs(para.lines)
174
266
  end
175
267
 
268
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
176
269
  test 'escaped single-line constrained monospaced string with role' do
177
- para = block_from_string(%q{[input]\`a few <monospaced> words`})
178
- # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
270
+ para = block_from_string(%([input]#{BACKSLASH}`a few <monospaced> words`), :attributes => {'compat-mode' => ''})
271
+ assert_equal '[input]`a few &lt;monospaced&gt; words`', para.apply_normal_subs(para.lines)
272
+
273
+ para = block_from_string(%([input]#{BACKSLASH}`a few <monospaced> words`))
179
274
  assert_equal '[input]`a few &lt;monospaced&gt; words`', para.apply_normal_subs(para.lines)
180
275
  end
181
276
 
277
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
182
278
  test 'escaped role on single-line constrained monospaced string' do
183
- para = block_from_string(%q{\[input]`a few <monospaced> words`})
184
- # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
279
+ para = block_from_string(%(#{BACKSLASH}[input]`a few <monospaced> words`), :attributes => {'compat-mode' => ''})
280
+ assert_equal '[input]<code>a few &lt;monospaced&gt; words</code>', para.apply_normal_subs(para.lines)
281
+
282
+ para = block_from_string(%(#{BACKSLASH}[input]`a few <monospaced> words`))
185
283
  assert_equal '[input]<code>a few &lt;monospaced&gt; words</code>', para.apply_normal_subs(para.lines)
186
284
  end
187
285
 
286
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
188
287
  test 'escaped role on escaped single-line constrained monospaced string' do
189
- para = block_from_string(%q{\[input]\`a few <monospaced> words`})
190
- # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
191
- assert_equal '\[input]`a few &lt;monospaced&gt; words`', para.apply_normal_subs(para.lines)
288
+ para = block_from_string(%(#{BACKSLASH}[input]#{BACKSLASH}`a few <monospaced> words`), :attributes => {'compat-mode' => ''})
289
+ assert_equal %(#{BACKSLASH}[input]`a few &lt;monospaced&gt; words`), para.apply_normal_subs(para.lines)
290
+
291
+ para = block_from_string(%(#{BACKSLASH}[input]#{BACKSLASH}`a few <monospaced> words`))
292
+ assert_equal %(#{BACKSLASH}[input]`a few &lt;monospaced&gt; words`), para.apply_normal_subs(para.lines)
192
293
  end
193
294
 
295
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
194
296
  test 'multi-line constrained monospaced string' do
195
- para = block_from_string(%Q{`a few\n<\{monospaced\}> words`})
196
- # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
297
+ para = block_from_string(%(`a few\n<{monospaced}> words`), :attributes => {'monospaced' => 'monospaced', 'compat-mode' => ''})
197
298
  assert_equal "<code>a few\n&lt;{monospaced}&gt; words</code>", para.apply_normal_subs(para.lines)
299
+
300
+ para = block_from_string(%(`a few\n<{monospaced}> words`), :attributes => {'monospaced' => 'monospaced'})
301
+ assert_equal "<code>a few\n&lt;monospaced&gt; words</code>", para.apply_normal_subs(para.lines)
198
302
  end
199
303
 
200
304
  test 'single-line unconstrained strong chars' do
@@ -203,7 +307,7 @@ context 'Substitutions' do
203
307
  end
204
308
 
205
309
  test 'escaped single-line unconstrained strong chars' do
206
- para = block_from_string(%q{\**Git**Hub})
310
+ para = block_from_string(%(#{BACKSLASH}**Git**Hub))
207
311
  assert_equal '<strong>*Git</strong>*Hub', para.sub_quotes(para.source)
208
312
  end
209
313
 
@@ -224,7 +328,7 @@ context 'Substitutions' do
224
328
 
225
329
  # TODO this is not the same result as AsciiDoc, though I don't understand why AsciiDoc gets what it gets
226
330
  test 'escaped unconstrained strong chars with role' do
227
- para = block_from_string(%q{Git\[blue]**Hub**})
331
+ para = block_from_string(%(Git#{BACKSLASH}[blue]**Hub**))
228
332
  assert_equal %q{Git[blue]<strong>*Hub</strong>*}, para.sub_quotes(para.source)
229
333
  end
230
334
 
@@ -234,10 +338,15 @@ context 'Substitutions' do
234
338
  end
235
339
 
236
340
  test 'escaped single-line unconstrained emphasized chars' do
237
- para = block_from_string(%q{\__Git__Hub})
341
+ para = block_from_string(%(#{BACKSLASH}__Git__Hub))
238
342
  assert_equal '__Git__Hub', para.sub_quotes(para.source)
239
343
  end
240
344
 
345
+ test 'escaped single-line unconstrained emphasized chars around word' do
346
+ para = block_from_string(%(#{BACKSLASH}#{BACKSLASH}__GitHub__))
347
+ assert_equal '__GitHub__', para.sub_quotes(para.source)
348
+ end
349
+
241
350
  test 'multi-line unconstrained emphasized chars' do
242
351
  para = block_from_string(%Q{__G\ni\nt\n__Hub})
243
352
  assert_equal "<em>G\ni\nt\n</em>Hub", para.sub_quotes(para.source)
@@ -249,68 +358,120 @@ context 'Substitutions' do
249
358
  end
250
359
 
251
360
  test 'escaped unconstrained emphasis chars with role' do
252
- para = block_from_string(%q{\[gray]__Git__Hub})
361
+ para = block_from_string(%(#{BACKSLASH}[gray]__Git__Hub))
253
362
  assert_equal %q{[gray]__Git__Hub}, para.sub_quotes(para.source)
254
363
  end
255
364
 
256
365
  test 'single-line constrained monospaced chars' do
257
- para = block_from_string(%q{call +save()+ to persist the changes})
366
+ para = block_from_string(%q{call +save()+ to persist the changes}, :attributes => {'compat-mode' => ''})
367
+ assert_equal 'call <code>save()</code> to persist the changes', para.sub_quotes(para.source)
368
+
369
+ para = block_from_string(%q{call [x-]+save()+ to persist the changes})
370
+ assert_equal 'call <code>save()</code> to persist the changes', para.apply_subs(para.source)
371
+
372
+ para = block_from_string(%q{call `save()` to persist the changes})
258
373
  assert_equal 'call <code>save()</code> to persist the changes', para.sub_quotes(para.source)
259
374
  end
260
375
 
261
376
  test 'single-line constrained monospaced chars with role' do
262
- para = block_from_string(%q{call [method]+save()+ to persist the changes})
377
+ para = block_from_string(%q{call [method]+save()+ to persist the changes}, :attributes => {'compat-mode' => ''})
378
+ assert_equal 'call <code class="method">save()</code> to persist the changes', para.sub_quotes(para.source)
379
+
380
+ para = block_from_string(%q{call [method x-]+save()+ to persist the changes})
381
+ assert_equal 'call <code class="method">save()</code> to persist the changes', para.apply_subs(para.source)
382
+
383
+ para = block_from_string(%q{call [method]`save()` to persist the changes})
263
384
  assert_equal 'call <code class="method">save()</code> to persist the changes', para.sub_quotes(para.source)
264
385
  end
265
386
 
266
387
  test 'escaped single-line constrained monospaced chars' do
267
- para = block_from_string(%q{call \+save()+ to persist the changes})
388
+ para = block_from_string(%(call #{BACKSLASH}+save()+ to persist the changes), :attributes => {'compat-mode' => ''})
268
389
  assert_equal 'call +save()+ to persist the changes', para.sub_quotes(para.source)
390
+
391
+ para = block_from_string(%(call #{BACKSLASH}`save()` to persist the changes))
392
+ assert_equal 'call `save()` to persist the changes', para.sub_quotes(para.source)
269
393
  end
270
394
 
271
395
  test 'escaped single-line constrained monospaced chars with role' do
272
- para = block_from_string(%q{call [method]\+save()+ to persist the changes})
396
+ para = block_from_string(%(call [method]#{BACKSLASH}+save()+ to persist the changes), :attributes => {'compat-mode' => ''})
273
397
  assert_equal 'call [method]+save()+ to persist the changes', para.sub_quotes(para.source)
398
+
399
+ para = block_from_string(%(call [method]#{BACKSLASH}`save()` to persist the changes))
400
+ assert_equal 'call [method]`save()` to persist the changes', para.sub_quotes(para.source)
274
401
  end
275
402
 
276
403
  test 'escaped role on single-line constrained monospaced chars' do
277
- para = block_from_string(%q{call \[method]+save()+ to persist the changes})
404
+ para = block_from_string(%(call #{BACKSLASH}[method]+save()+ to persist the changes), :attributes => {'compat-mode' => ''})
405
+ assert_equal 'call [method]<code>save()</code> to persist the changes', para.sub_quotes(para.source)
406
+
407
+ para = block_from_string(%(call #{BACKSLASH}[method]`save()` to persist the changes))
278
408
  assert_equal 'call [method]<code>save()</code> to persist the changes', para.sub_quotes(para.source)
279
409
  end
280
410
 
281
411
  test 'escaped role on escaped single-line constrained monospaced chars' do
282
- para = block_from_string(%q{call \[method]\+save()+ to persist the changes})
283
- assert_equal 'call \[method]+save()+ to persist the changes', para.sub_quotes(para.source)
412
+ para = block_from_string(%(call #{BACKSLASH}[method]#{BACKSLASH}+save()+ to persist the changes), :attributes => {'compat-mode' => ''})
413
+ assert_equal %(call #{BACKSLASH}[method]+save()+ to persist the changes), para.sub_quotes(para.source)
414
+
415
+ para = block_from_string(%(call #{BACKSLASH}[method]#{BACKSLASH}`save()` to persist the changes))
416
+ assert_equal %(call #{BACKSLASH}[method]`save()` to persist the changes), para.sub_quotes(para.source)
284
417
  end
285
418
 
286
419
  test 'single-line unconstrained monospaced chars' do
287
- para = block_from_string(%q{Git++Hub++})
420
+ para = block_from_string(%q{Git++Hub++}, :attributes => {'compat-mode' => ''})
421
+ assert_equal 'Git<code>Hub</code>', para.sub_quotes(para.source)
422
+
423
+ para = block_from_string(%q{Git[x-]++Hub++})
424
+ assert_equal 'Git<code>Hub</code>', para.apply_subs(para.source)
425
+
426
+ para = block_from_string(%q{Git``Hub``})
288
427
  assert_equal 'Git<code>Hub</code>', para.sub_quotes(para.source)
289
428
  end
290
429
 
291
430
  test 'escaped single-line unconstrained monospaced chars' do
292
- para = block_from_string(%q{Git\++Hub++})
431
+ para = block_from_string(%(Git#{BACKSLASH}++Hub++), :attributes => {'compat-mode' => ''})
293
432
  assert_equal 'Git+<code>Hub</code>+', para.sub_quotes(para.source)
433
+
434
+ para = block_from_string(%(Git#{BACKSLASH * 2}++Hub++), :attributes => {'compat-mode' => ''})
435
+ assert_equal 'Git++Hub++', para.sub_quotes(para.source)
436
+
437
+ para = block_from_string(%(Git#{BACKSLASH}``Hub``))
438
+ assert_equal 'Git``Hub``', para.sub_quotes(para.source)
294
439
  end
295
440
 
296
441
  test 'multi-line unconstrained monospaced chars' do
297
- para = block_from_string(%Q{Git++\nH\nu\nb++})
442
+ para = block_from_string(%Q{Git++\nH\nu\nb++}, :attributes => {'compat-mode' => ''})
443
+ assert_equal "Git<code>\nH\nu\nb</code>", para.sub_quotes(para.source)
444
+
445
+ para = block_from_string(%Q{Git[x-]++\nH\nu\nb++})
446
+ assert_equal %(Git<code>\nH\nu\nb</code>), para.apply_subs(para.source)
447
+
448
+ para = block_from_string(%Q{Git``\nH\nu\nb``})
298
449
  assert_equal "Git<code>\nH\nu\nb</code>", para.sub_quotes(para.source)
299
450
  end
300
451
 
301
452
  test 'single-line superscript chars' do
302
- para = block_from_string(%q{x^2^ = x * x, e = mc^2^, there's a 1^st^ time for everything})
303
- assert_equal 'x<sup>2</sup> = x * x, e = mc<sup>2</sup>, there\'s a 1<sup>st</sup> time for everything', para.sub_quotes(para.source)
453
+ para = block_from_string(%(x^2^ = x * x, e = mc^2^, there's a 1^st^ time for everything))
454
+ assert_equal %(x<sup>2</sup> = x * x, e = mc<sup>2</sup>, there\'s a 1<sup>st</sup> time for everything), para.sub_quotes(para.source)
304
455
  end
305
456
 
306
457
  test 'escaped single-line superscript chars' do
307
- para = block_from_string(%q{x\^2^ = x * x})
458
+ para = block_from_string(%(x#{BACKSLASH}^2^ = x * x))
308
459
  assert_equal 'x^2^ = x * x', para.sub_quotes(para.source)
309
460
  end
310
461
 
311
- test 'multi-line superscript chars' do
462
+ test 'does not match superscript across whitespace' do
312
463
  para = block_from_string(%Q{x^(n\n-\n1)^})
313
- assert_equal "x<sup>(n\n-\n1)</sup>", para.sub_quotes(para.source)
464
+ assert_equal para.source, para.sub_quotes(para.source)
465
+ end
466
+
467
+ test 'does not match adjacent superscript chars' do
468
+ para = block_from_string 'a ^^ b'
469
+ assert_equal 'a ^^ b', para.sub_quotes(para.source)
470
+ end
471
+
472
+ test 'does not confuse superscript and links with blank window shorthand' do
473
+ para = block_from_string(%Q{http://localhost[Text^] on the 21^st^ and 22^nd^})
474
+ assert_equal '<a href="http://localhost" target="_blank">Text</a> on the 21<sup>st</sup> and 22<sup>nd</sup>', para.content
314
475
  end
315
476
 
316
477
  test 'single-line subscript chars' do
@@ -319,13 +480,23 @@ context 'Substitutions' do
319
480
  end
320
481
 
321
482
  test 'escaped single-line subscript chars' do
322
- para = block_from_string(%q{H\~2~O})
483
+ para = block_from_string(%(H#{BACKSLASH}~2~O))
323
484
  assert_equal 'H~2~O', para.sub_quotes(para.source)
324
485
  end
325
486
 
326
- test 'multi-line subscript chars' do
487
+ test 'does not match subscript across whitespace' do
327
488
  para = block_from_string(%Q{project~ view\non\nGitHub~})
328
- assert_equal "project<sub> view\non\nGitHub</sub>", para.sub_quotes(para.source)
489
+ assert_equal para.source, para.sub_quotes(para.source)
490
+ end
491
+
492
+ test 'does not match adjacent subscript chars' do
493
+ para = block_from_string 'a ~~ b'
494
+ assert_equal 'a ~~ b', para.sub_quotes(para.source)
495
+ end
496
+
497
+ test 'does not match subscript across distinct URLs' do
498
+ para = block_from_string(%Q{http://www.abc.com/~def[DEF] and http://www.abc.com/~ghi[GHI]})
499
+ assert_equal para.source, para.sub_quotes(para.source)
329
500
  end
330
501
 
331
502
  test 'quoted text with role shorthand' do
@@ -344,7 +515,7 @@ context 'Substitutions' do
344
515
  end
345
516
 
346
517
  test 'quoted text with id and role shorthand using docbook backend' do
347
- para = block_from_string(%q{[#bond.white.red-background]#007#}, :backend => 'docbook')
518
+ para = block_from_string(%q{[#bond.white.red-background]#007#}, :backend => 'docbook45')
348
519
  assert_equal '<anchor id="bond" xreflabel="007"/><phrase role="white red-background">007</phrase>', para.sub_quotes(para.source)
349
520
  end
350
521
 
@@ -352,12 +523,26 @@ context 'Substitutions' do
352
523
  para = block_from_string(%q{[red, foobar]#alert#})
353
524
  assert_equal '<span class="red">alert</span>', para.sub_quotes(para.source)
354
525
  end
526
+
527
+ test 'should assign role attribute when shorthand style contains a role' do
528
+ para = block_from_string 'blah'
529
+ result = para.parse_quoted_text_attributes '.red#idref'
530
+ expect = {'id' => 'idref', 'role' => 'red'}
531
+ assert_equal expect, result
532
+ end
533
+
534
+ test 'should not assign role attribute if shorthand style has no roles' do
535
+ para = block_from_string 'blah'
536
+ result = para.parse_quoted_text_attributes '#idref'
537
+ expect = {'id' => 'idref'}
538
+ assert_equal expect, result
539
+ end
355
540
  end
356
541
 
357
542
  context 'Macros' do
358
543
  test 'a single-line link macro should be interpreted as a link' do
359
544
  para = block_from_string('link:/home.html[]')
360
- assert_equal %q{<a href="/home.html">/home.html</a>}, para.sub_macros(para.source)
545
+ assert_equal %q{<a href="/home.html" class="bare">/home.html</a>}, para.sub_macros(para.source)
361
546
  end
362
547
 
363
548
  test 'a single-line link macro with text should be interpreted as a link' do
@@ -397,13 +582,13 @@ context 'Substitutions' do
397
582
  end
398
583
 
399
584
  test 'should ignore escaped inline email address' do
400
- para = block_from_string('\doc.writer@asciidoc.org')
585
+ para = block_from_string(%(#{BACKSLASH}doc.writer@asciidoc.org))
401
586
  assert_equal %q{doc.writer@asciidoc.org}, para.sub_macros(para.source)
402
587
  end
403
588
 
404
589
  test 'a single-line raw url should be interpreted as a link' do
405
590
  para = block_from_string('http://google.com')
406
- assert_equal %q{<a href="http://google.com">http://google.com</a>}, para.sub_macros(para.source)
591
+ assert_equal %q{<a href="http://google.com" class="bare">http://google.com</a>}, para.sub_macros(para.source)
407
592
  end
408
593
 
409
594
  test 'a single-line raw url with text should be interpreted as a link' do
@@ -423,13 +608,13 @@ context 'Substitutions' do
423
608
  end
424
609
 
425
610
  test 'a single-line escaped raw url should not be interpreted as a link' do
426
- para = block_from_string('\http://google.com')
611
+ para = block_from_string(%(#{BACKSLASH}http://google.com))
427
612
  assert_equal %q{http://google.com}, para.sub_macros(para.source)
428
613
  end
429
614
 
430
615
  test 'a comma separated list of links should not include commas in links' do
431
616
  para = block_from_string('http://foo.com, http://bar.com, http://example.org')
432
- assert_equal %q{<a href="http://foo.com">http://foo.com</a>, <a href="http://bar.com">http://bar.com</a>, <a href="http://example.org">http://example.org</a>}, para.sub_macros(para.source)
617
+ assert_equal %q{<a href="http://foo.com" class="bare">http://foo.com</a>, <a href="http://bar.com" class="bare">http://bar.com</a>, <a href="http://example.org" class="bare">http://example.org</a>}, para.sub_macros(para.source)
433
618
  end
434
619
 
435
620
  test 'a single-line image macro should be interpreted as an image' do
@@ -443,7 +628,7 @@ context 'Substitutions' do
443
628
  end
444
629
 
445
630
  test 'a single-line image macro with text containing escaped square bracket should be interpreted as an image with alt text' do
446
- para = block_from_string('image:tiger.png[[Another\] Tiger]')
631
+ para = block_from_string(%(image:tiger.png[[Another#{BACKSLASH}] Tiger]))
447
632
  assert_equal %{<span class="image"><img src="tiger.png" alt="[Another] Tiger"></span>}, para.sub_macros(para.source).gsub(/>\s+</, '><')
448
633
  end
449
634
 
@@ -453,6 +638,12 @@ context 'Substitutions' do
453
638
  para.sub_macros(para.source).gsub(/>\s+</, '><')
454
639
  end
455
640
 
641
+ test 'a single-line image macro with text and dimensions should be interpreted as an image with alt text and dimensions in docbook' do
642
+ para = block_from_string 'image:tiger.png[Tiger, 200, 100]', :backend => 'docbook'
643
+ assert_equal %{<inlinemediaobject><imageobject><imagedata fileref="tiger.png" contentwidth="200" contentdepth="100"/></imageobject><textobject><phrase>Tiger</phrase></textobject></inlinemediaobject>},
644
+ para.sub_macros(para.source).gsub(/>\s+</, '><')
645
+ end
646
+
456
647
  test 'a single-line image macro with text and link should be interpreted as a linked image with alt text' do
457
648
  para = block_from_string('image:tiger.png[Tiger, link="http://en.wikipedia.org/wiki/Tiger"]')
458
649
  assert_equal %{<span class="image"><a class="image" href="http://en.wikipedia.org/wiki/Tiger"><img src="tiger.png" alt="Tiger"></a></span>},
@@ -471,7 +662,7 @@ context 'Substitutions' do
471
662
  para.sub_macros(para.source).gsub(/>\s+</, '><')
472
663
  end
473
664
 
474
- test 'wip an inline image macro with a float attribute should be interpreted as a floating image' do
665
+ test 'an inline image macro with a float attribute should be interpreted as a floating image' do
475
666
  para = block_from_string %(image:http://example.com/images/tiger.png[tiger, float="right"] Beware of the tigers!)
476
667
  assert_equal %{<span class="image" style="float: right"><img src="http://example.com/images/tiger.png" alt="tiger"></span> Beware of the tigers!},
477
668
  para.sub_macros(para.source).gsub(/>\s+</, '><')
@@ -519,17 +710,17 @@ context 'Substitutions' do
519
710
 
520
711
  test 'an icon macro should be interpreted as a font-based icon when icons=font' do
521
712
  para = block_from_string 'icon:github[]', :attributes => {'icons' => 'font'}
522
- assert_equal %{<span class="icon"><i class="icon-github"></i></span>}, para.sub_macros(para.source).gsub(/>\s+</, '><')
713
+ assert_equal %{<span class="icon"><i class="fa fa-github"></i></span>}, para.sub_macros(para.source).gsub(/>\s+</, '><')
523
714
  end
524
715
 
525
716
  test 'an icon macro with a size should be interpreted as a font-based icon with a size when icons=font' do
526
717
  para = block_from_string 'icon:github[4x]', :attributes => {'icons' => 'font'}
527
- assert_equal %{<span class="icon"><i class="icon-github icon-4x"></i></span>}, para.sub_macros(para.source).gsub(/>\s+</, '><')
718
+ assert_equal %{<span class="icon"><i class="fa fa-github fa-4x"></i></span>}, para.sub_macros(para.source).gsub(/>\s+</, '><')
528
719
  end
529
720
 
530
721
  test 'an icon macro with a role and title should be interpreted as a font-based icon with a class and title when icons=font' do
531
722
  para = block_from_string 'icon:heart[role="red", title="Heart me"]', :attributes => {'icons' => 'font'}
532
- assert_equal %{<span class="icon red"><i class="icon-heart" title="Heart me"></i></span>}, para.sub_macros(para.source).gsub(/>\s+</, '><')
723
+ assert_equal %{<span class="icon red"><i class="fa fa-heart" title="Heart me"></i></span>}, para.sub_macros(para.source).gsub(/>\s+</, '><')
533
724
  end
534
725
 
535
726
  test 'a single-line footnote macro should be registered and rendered as a footnote' do
@@ -542,14 +733,22 @@ context 'Substitutions' do
542
733
  assert_equal 'An example footnote.', footnote.text
543
734
  end
544
735
 
545
- test 'a multi-line footnote macro should be registered and rendered as a footnote' do
736
+ test 'a multi-line footnote macro should be registered and rendered as a footnote without endline' do
546
737
  para = block_from_string("Sentence text footnote:[An example footnote\nwith wrapped text.].")
547
738
  assert_equal %(Sentence text <span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>.), para.sub_macros(para.source)
548
739
  assert_equal 1, para.document.references[:footnotes].size
549
740
  footnote = para.document.references[:footnotes].first
550
741
  assert_equal 1, footnote.index
551
742
  assert footnote.id.nil?
552
- assert_equal "An example footnote\nwith wrapped text.", footnote.text
743
+ assert_equal "An example footnote with wrapped text.", footnote.text
744
+ end
745
+
746
+ test 'an escaped closing square bracket in a footnote should be unescaped when rendered' do
747
+ para = block_from_string(%(footnote:[a #{BACKSLASH}] b].))
748
+ assert_equal %(<span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>.), para.sub_macros(para.source)
749
+ assert_equal 1, para.document.references[:footnotes].size
750
+ footnote = para.document.references[:footnotes].first
751
+ assert_equal "a ] b", footnote.text
553
752
  end
554
753
 
555
754
  test 'a footnote macro can be directly adjacent to preceding word' do
@@ -557,7 +756,19 @@ context 'Substitutions' do
557
756
  assert_equal %(Sentence text<span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>.), para.sub_macros(para.source)
558
757
  end
559
758
 
560
- test 'a footnote macro may contain a macro' do
759
+ test 'a footnote macro may contain an escaped backslash' do
760
+ para = block_from_string("footnote:[\\]]\nfootnote:[a \\] b]\nfootnote:[a \\]\\] b]")
761
+ para.sub_macros(para.source)
762
+ assert_equal 3, para.document.references[:footnotes].size
763
+ footnote1 = para.document.references[:footnotes][0]
764
+ assert_equal ']', footnote1.text
765
+ footnote2 = para.document.references[:footnotes][1]
766
+ assert_equal 'a ] b', footnote2.text
767
+ footnote3 = para.document.references[:footnotes][2]
768
+ assert_equal 'a ]] b', footnote3.text
769
+ end
770
+
771
+ test 'a footnote macro may contain a link macro' do
561
772
  para = block_from_string('Share your code. footnote:[http://github.com[GitHub]]')
562
773
  assert_equal %(Share your code. <span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>), para.sub_macros(para.source)
563
774
  assert_equal 1, para.document.references[:footnotes].size
@@ -571,7 +782,7 @@ context 'Substitutions' do
571
782
  assert_equal %(the JLine <span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>\nlibrary.), result
572
783
  assert_equal 1, para.document.references[:footnotes].size
573
784
  fn1 = para.document.references[:footnotes].first
574
- assert_equal '<a href="https://github.com/jline/jline2">https://github.com/jline/jline2</a>', fn1.text
785
+ assert_equal '<a href="https://github.com/jline/jline2" class="bare">https://github.com/jline/jline2</a>', fn1.text
575
786
  end
576
787
 
577
788
  test 'a footnote macro followed by a semi-colon may contain a plain URL' do
@@ -580,7 +791,32 @@ context 'Substitutions' do
580
791
  assert_equal %(the JLine <span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>;\nlibrary.), result
581
792
  assert_equal 1, para.document.references[:footnotes].size
582
793
  fn1 = para.document.references[:footnotes].first
583
- assert_equal '<a href="https://github.com/jline/jline2">https://github.com/jline/jline2</a>', fn1.text
794
+ assert_equal '<a href="https://github.com/jline/jline2" class="bare">https://github.com/jline/jline2</a>', fn1.text
795
+ end
796
+
797
+ test 'a footnote macro may contain an xref macro' do
798
+ # specialcharacters escaping is simulated
799
+ para = block_from_string('text footnote:[&lt;&lt;_install,Install&gt;&gt;]')
800
+ assert_equal %(text <span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>), para.sub_macros(para.source)
801
+ assert_equal 1, para.document.references[:footnotes].size
802
+ footnote1 = para.document.references[:footnotes][0]
803
+ assert_equal '<a href="#_install">Install</a>', footnote1.text
804
+ end
805
+
806
+ test 'a footnote macro may contain an anchor macro' do
807
+ para = block_from_string('text footnote:[a [[b\]\] \[[c\]\] d]')
808
+ assert_equal %(text <span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>), para.sub_macros(para.source)
809
+ assert_equal 1, para.document.references[:footnotes].size
810
+ footnote1 = para.document.references[:footnotes][0]
811
+ assert_equal 'a <a id="b"></a> [[c]] d', footnote1.text
812
+ end
813
+
814
+ test 'a footnote macro may contain a bibliographic anchor macro' do
815
+ para = block_from_string('text footnote:[a [[[b\]\]\] c]')
816
+ assert_equal %(text <span class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>), para.sub_macros(para.source)
817
+ assert_equal 1, para.document.references[:footnotes].size
818
+ footnote1 = para.document.references[:footnotes][0]
819
+ assert_equal 'a <a id="b"></a>[b] c', footnote1.text
584
820
  end
585
821
 
586
822
  test 'should increment index of subsequent footnote macros' do
@@ -607,14 +843,14 @@ context 'Substitutions' do
607
843
  assert_equal 'An example footnote.', footnote.text
608
844
  end
609
845
 
610
- test 'a footnoteref macro with id and multi-line text should be registered and rendered as a footnote' do
846
+ test 'a footnoteref macro with id and multi-line text should be registered and rendered as a footnote without endlines' do
611
847
  para = block_from_string("Sentence text footnoteref:[ex1, An example footnote\nwith wrapped text.].")
612
848
  assert_equal %(Sentence text <span class="footnote" id="_footnote_ex1">[<a id="_footnoteref_1" class="footnote" href="#_footnote_1" title="View footnote.">1</a>]</span>.), para.sub_macros(para.source)
613
849
  assert_equal 1, para.document.references[:footnotes].size
614
850
  footnote = para.document.references[:footnotes].first
615
851
  assert_equal 1, footnote.index
616
852
  assert_equal 'ex1', footnote.id
617
- assert_equal "An example footnote\nwith wrapped text.", footnote.text
853
+ assert_equal "An example footnote with wrapped text.", footnote.text
618
854
  end
619
855
 
620
856
  test 'a footnoteref macro with id should refer to footnoteref with same id' do
@@ -627,6 +863,11 @@ context 'Substitutions' do
627
863
  assert_equal 'An example footnote.', footnote.text
628
864
  end
629
865
 
866
+ test 'an unresolved footnoteref should not crash the processor' do
867
+ para = block_from_string('Sentence text footnoteref:[ex1].')
868
+ para.sub_macros para.source
869
+ end
870
+
630
871
  test 'a single-line index term macro with a primary term should be registered as an index reference' do
631
872
  sentence = "The tiger (Panthera tigris) is the largest cat species.\n"
632
873
  macros = ['indexterm:[Tigers]', '(((Tigers)))']
@@ -675,6 +916,31 @@ context 'Substitutions' do
675
916
  end
676
917
  end
677
918
 
919
+ test 'should not split index terms on commas inside of quoted terms' do
920
+ inputs = []
921
+ inputs.push <<-EOS
922
+ Tigers are big, scary cats.
923
+ indexterm:[Tigers, "[Big\\],
924
+ scary cats"]
925
+ EOS
926
+ inputs.push <<-EOS
927
+ Tigers are big, scary cats.
928
+ (((Tigers, "[Big],
929
+ scary cats")))
930
+ EOS
931
+
932
+ inputs.each do |input|
933
+ para = block_from_string input
934
+ output = para.sub_macros(para.source)
935
+ assert_equal input.lines.first, output
936
+ assert_equal 1, para.document.references[:indexterms].size
937
+ terms = para.document.references[:indexterms].first
938
+ assert_equal 2, terms.size
939
+ assert_equal 'Tigers', terms.first
940
+ assert_equal '[Big], scary cats', terms.last
941
+ end
942
+ end
943
+
678
944
  test 'normal substitutions are performed on an index term macro' do
679
945
  sentence = "The tiger (Panthera tigris) is the largest cat species.\n"
680
946
  macros = ['indexterm:[*Tigers*]', '(((*Tigers*)))']
@@ -768,8 +1034,8 @@ context 'Substitutions' do
768
1034
  assert_equal "The panthera tigris is the largest cat species.\n", output
769
1035
  terms = para.document.references[:indexterms]
770
1036
  assert_equal 2, terms.size
771
- assert_equal ['Big cats', 'Tigers'], terms[0]
772
- assert_equal ['panthera tigris'], terms[1]
1037
+ assert_equal ['panthera tigris'], terms[0]
1038
+ assert_equal ['Big cats', 'Tigers'], terms[1]
773
1039
  end
774
1040
 
775
1041
  context 'Button macro' do
@@ -797,37 +1063,37 @@ context 'Substitutions' do
797
1063
 
798
1064
  test 'kbd macro with key combination' do
799
1065
  para = block_from_string('kbd:[Ctrl+Shift+T]', :attributes => {'experimental' => ''})
800
- assert_equal %q{<kbd class="keyseq"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd></kbd>}, para.sub_macros(para.source)
1066
+ assert_equal %q{<span class="keyseq"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd></span>}, para.sub_macros(para.source)
801
1067
  end
802
1068
 
803
1069
  test 'kbd macro with key combination with spaces' do
804
1070
  para = block_from_string('kbd:[Ctrl + Shift + T]', :attributes => {'experimental' => ''})
805
- assert_equal %q{<kbd class="keyseq"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd></kbd>}, para.sub_macros(para.source)
1071
+ assert_equal %q{<span class="keyseq"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd></span>}, para.sub_macros(para.source)
806
1072
  end
807
1073
 
808
1074
  test 'kbd macro with key combination delimited by commas' do
809
1075
  para = block_from_string('kbd:[Ctrl,Shift,T]', :attributes => {'experimental' => ''})
810
- assert_equal %q{<kbd class="keyseq"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd></kbd>}, para.sub_macros(para.source)
1076
+ assert_equal %q{<span class="keyseq"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd></span>}, para.sub_macros(para.source)
811
1077
  end
812
1078
 
813
1079
  test 'kbd macro with key combination containing a plus key no spaces' do
814
1080
  para = block_from_string('kbd:[Ctrl++]', :attributes => {'experimental' => ''})
815
- assert_equal %q{<kbd class="keyseq"><kbd>Ctrl</kbd>+<kbd>+</kbd></kbd>}, para.sub_macros(para.source)
1081
+ assert_equal %q{<span class="keyseq"><kbd>Ctrl</kbd>+<kbd>+</kbd></span>}, para.sub_macros(para.source)
816
1082
  end
817
1083
 
818
1084
  test 'kbd macro with key combination delimited by commands containing a comma key' do
819
1085
  para = block_from_string('kbd:[Ctrl,,]', :attributes => {'experimental' => ''})
820
- assert_equal %q{<kbd class="keyseq"><kbd>Ctrl</kbd>+<kbd>,</kbd></kbd>}, para.sub_macros(para.source)
1086
+ assert_equal %q{<span class="keyseq"><kbd>Ctrl</kbd>+<kbd>,</kbd></span>}, para.sub_macros(para.source)
821
1087
  end
822
1088
 
823
1089
  test 'kbd macro with key combination containing a plus key with spaces' do
824
1090
  para = block_from_string('kbd:[Ctrl + +]', :attributes => {'experimental' => ''})
825
- assert_equal %q{<kbd class="keyseq"><kbd>Ctrl</kbd>+<kbd>+</kbd></kbd>}, para.sub_macros(para.source)
1091
+ assert_equal %q{<span class="keyseq"><kbd>Ctrl</kbd>+<kbd>+</kbd></span>}, para.sub_macros(para.source)
826
1092
  end
827
1093
 
828
1094
  test 'kbd macro with key combination containing escaped bracket' do
829
1095
  para = block_from_string('kbd:[Ctrl + \]]', :attributes => {'experimental' => ''})
830
- assert_equal %q{<kbd class="keyseq"><kbd>Ctrl</kbd>+<kbd>]</kbd></kbd>}, para.sub_macros(para.source)
1096
+ assert_equal %q{<span class="keyseq"><kbd>Ctrl</kbd>+<kbd>]</kbd></span>}, para.sub_macros(para.source)
831
1097
  end
832
1098
 
833
1099
  test 'kbd macro with key combination, docbook backend' do
@@ -886,6 +1152,16 @@ context 'Substitutions' do
886
1152
  para = block_from_string('<span class="xmltag">&lt;node&gt;</span><span class="classname">r</span>', :attributes => {'experimental' => ''})
887
1153
  assert_equal %q{<span class="xmltag">&lt;node&gt;</span><span class="classname">r</span>}, para.sub_macros(para.source)
888
1154
  end
1155
+
1156
+ test 'should process menu macro with items containing multibyte characters' do
1157
+ para = block_from_string('menu:视图[放大, 重置]', :attributes => {'experimental' => ''})
1158
+ assert_equal %q{<span class="menuseq"><span class="menu">视图</span>&#160;&#9656; <span class="submenu">放大</span>&#160;&#9656; <span class="menuitem">重置</span></span>}, para.sub_macros(para.source)
1159
+ end if ::RUBY_MIN_VERSION_1_9
1160
+
1161
+ test 'should process inline menu with items containing multibyte characters' do
1162
+ para = block_from_string('"视图 &gt; 放大 &gt; 重置"', :attributes => {'experimental' => ''})
1163
+ assert_equal %q{<span class="menuseq"><span class="menu">视图</span>&#160;&#9656; <span class="submenu">放大</span>&#160;&#9656; <span class="menuitem">重置</span></span>}, para.sub_macros(para.source)
1164
+ end if ::RUBY_MIN_VERSION_1_9
889
1165
  end
890
1166
  end
891
1167
 
@@ -893,74 +1169,111 @@ context 'Substitutions' do
893
1169
  test 'collect inline triple plus passthroughs' do
894
1170
  para = block_from_string('+++<code>inline code</code>+++')
895
1171
  result = para.extract_passthroughs(para.source)
896
- assert_equal "\e" + '0' + "\e", result
1172
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
897
1173
  assert_equal 1, para.passthroughs.size
898
- assert_equal '<code>inline code</code>', para.passthroughs.first[:text]
899
- assert para.passthroughs.first[:subs].empty?
1174
+ assert_equal '<code>inline code</code>', para.passthroughs[0][:text]
1175
+ assert para.passthroughs[0][:subs].empty?
900
1176
  end
901
1177
 
902
1178
  test 'collect multi-line inline triple plus passthroughs' do
903
1179
  para = block_from_string("+++<code>inline\ncode</code>+++")
904
1180
  result = para.extract_passthroughs(para.source)
905
- assert_equal "\e" + '0' + "\e", result
1181
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
906
1182
  assert_equal 1, para.passthroughs.size
907
- assert_equal "<code>inline\ncode</code>", para.passthroughs.first[:text]
908
- assert para.passthroughs.first[:subs].empty?
1183
+ assert_equal "<code>inline\ncode</code>", para.passthroughs[0][:text]
1184
+ assert para.passthroughs[0][:subs].empty?
909
1185
  end
910
1186
 
911
1187
  test 'collect inline double dollar passthroughs' do
912
1188
  para = block_from_string('$$<code>{code}</code>$$')
913
1189
  result = para.extract_passthroughs(para.source)
914
- assert_equal "\e" + '0' + "\e", result
1190
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
1191
+ assert_equal 1, para.passthroughs.size
1192
+ assert_equal '<code>{code}</code>', para.passthroughs[0][:text]
1193
+ assert_equal [:specialcharacters], para.passthroughs[0][:subs]
1194
+ end
1195
+
1196
+ test 'collect inline double plus passthroughs' do
1197
+ para = block_from_string('++<code>{code}</code>++')
1198
+ result = para.extract_passthroughs(para.source)
1199
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
915
1200
  assert_equal 1, para.passthroughs.size
916
- assert_equal '<code>{code}</code>', para.passthroughs.first[:text]
917
- assert_equal [:specialcharacters], para.passthroughs.first[:subs]
1201
+ assert_equal '<code>{code}</code>', para.passthroughs[0][:text]
1202
+ assert_equal [:specialcharacters], para.passthroughs[0][:subs]
918
1203
  end
919
1204
 
920
1205
  test 'collect multi-line inline double dollar passthroughs' do
921
1206
  para = block_from_string("$$<code>\n{code}\n</code>$$")
922
1207
  result = para.extract_passthroughs(para.source)
923
- assert_equal "\e" + '0' + "\e", result
1208
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
924
1209
  assert_equal 1, para.passthroughs.size
925
- assert_equal "<code>\n{code}\n</code>", para.passthroughs.first[:text]
926
- assert_equal [:specialcharacters], para.passthroughs.first[:subs]
1210
+ assert_equal "<code>\n{code}\n</code>", para.passthroughs[0][:text]
1211
+ assert_equal [:specialcharacters], para.passthroughs[0][:subs]
1212
+ end
1213
+
1214
+ test 'collect multi-line inline double plus passthroughs' do
1215
+ para = block_from_string("++<code>\n{code}\n</code>++")
1216
+ result = para.extract_passthroughs(para.source)
1217
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
1218
+ assert_equal 1, para.passthroughs.size
1219
+ assert_equal "<code>\n{code}\n</code>", para.passthroughs[0][:text]
1220
+ assert_equal [:specialcharacters], para.passthroughs[0][:subs]
927
1221
  end
928
1222
 
929
1223
  test 'collect passthroughs from inline pass macro' do
930
1224
  para = block_from_string(%Q{pass:specialcharacters,quotes[<code>['code'\\]</code>]})
931
1225
  result = para.extract_passthroughs(para.source)
932
- assert_equal "\e" + '0' + "\e", result
1226
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
933
1227
  assert_equal 1, para.passthroughs.size
934
- assert_equal %q{<code>['code']</code>}, para.passthroughs.first[:text]
935
- assert_equal [:specialcharacters, :quotes], para.passthroughs.first[:subs]
1228
+ assert_equal %q{<code>['code']</code>}, para.passthroughs[0][:text]
1229
+ assert_equal [:specialcharacters, :quotes], para.passthroughs[0][:subs]
936
1230
  end
937
1231
 
938
1232
  test 'collect multi-line passthroughs from inline pass macro' do
939
1233
  para = block_from_string(%Q{pass:specialcharacters,quotes[<code>['more\ncode'\\]</code>]})
940
1234
  result = para.extract_passthroughs(para.source)
941
- assert_equal "\e" + '0' + "\e", result
1235
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
1236
+ assert_equal 1, para.passthroughs.size
1237
+ assert_equal %Q{<code>['more\ncode']</code>}, para.passthroughs[0][:text]
1238
+ assert_equal [:specialcharacters, :quotes], para.passthroughs[0][:subs]
1239
+ end
1240
+
1241
+ test 'resolves sub shorthands on inline pass macro' do
1242
+ para = block_from_string 'pass:q,a[*<{backend}>*]'
1243
+ result = para.extract_passthroughs para.source
942
1244
  assert_equal 1, para.passthroughs.size
943
- assert_equal %Q{<code>['more\ncode']</code>}, para.passthroughs.first[:text]
944
- assert_equal [:specialcharacters, :quotes], para.passthroughs.first[:subs]
1245
+ assert_equal [:quotes, :attributes], para.passthroughs[0][:subs]
1246
+ result = para.restore_passthroughs result
1247
+ assert_equal '<strong><html5></strong>', result
945
1248
  end
946
1249
 
947
1250
  # NOTE placeholder is surrounded by text to prevent reader from stripping trailing boundary char (unique to test scenario)
948
1251
  test 'restore inline passthroughs without subs' do
949
- para = block_from_string("some \e" + '0' + "\e to study")
950
- para.passthroughs << {:text => '<code>inline code</code>', :subs => []}
1252
+ para = block_from_string("some #{Asciidoctor::Substitutors::PASS_START}" + '0' + "#{Asciidoctor::Substitutors::PASS_END} to study")
1253
+ para.passthroughs[0] = {:text => '<code>inline code</code>', :subs => []}
951
1254
  result = para.restore_passthroughs(para.source)
952
1255
  assert_equal "some <code>inline code</code> to study", result
953
1256
  end
954
1257
 
955
1258
  # NOTE placeholder is surrounded by text to prevent reader from stripping trailing boundary char (unique to test scenario)
956
1259
  test 'restore inline passthroughs with subs' do
957
- para = block_from_string("some \e" + '0' + "\e to study in the \e" + '1' + "\e programming language")
958
- para.passthroughs << {:text => '<code>{code}</code>', :subs => [:specialcharacters]}
959
- para.passthroughs << {:text => '{language}', :subs => [:specialcharacters]}
1260
+ para = block_from_string("some #{Asciidoctor::Substitutors::PASS_START}" + '0' + "#{Asciidoctor::Substitutors::PASS_END} to study in the #{Asciidoctor::Substitutors::PASS_START}" + '1' + "#{Asciidoctor::Substitutors::PASS_END} programming language")
1261
+ para.passthroughs[0] = {:text => '<code>{code}</code>', :subs => [:specialcharacters]}
1262
+ para.passthroughs[1] = {:text => '{language}', :subs => [:specialcharacters]}
960
1263
  result = para.restore_passthroughs(para.source)
961
1264
  assert_equal 'some &lt;code&gt;{code}&lt;/code&gt; to study in the {language} programming language', result
962
1265
  end
963
1266
 
1267
+ test 'should restore nested passthroughs' do
1268
+ result = render_embedded_string %q(+Sometimes you feel pass:q[`mono`].+ Sometimes you +$$don't$$+.), :doctype => :inline
1269
+ assert_equal %q(Sometimes you feel <code>mono</code>. Sometimes you don't.), result
1270
+ end
1271
+
1272
+ test 'should honor role on double plus passthrough' do
1273
+ result = render_embedded_string 'Print the version using [var]++{asciidoctor-version}++.', :doctype => :inline
1274
+ assert_equal 'Print the version using <span class="var">{asciidoctor-version}</span>.', result
1275
+ end
1276
+
964
1277
  test 'complex inline passthrough macro' do
965
1278
  text_to_escape = %q{[(] <'basic form'> <'logical operator'> <'basic form'> [)]}
966
1279
  para = block_from_string %($$#{text_to_escape}$$)
@@ -979,6 +1292,112 @@ context 'Substitutions' do
979
1292
  para = block_from_string %(pass:verbatim[<{backend}>])
980
1293
  assert_equal '&lt;{backend}&gt;', para.content
981
1294
  end
1295
+
1296
+ context 'Math macros' do
1297
+ test 'should passthrough text in asciimath macro and surround with AsciiMath delimiters' do
1298
+ input = 'asciimath:[x/x={(1,if x!=0),(text{undefined},if x=0):}]'
1299
+ para = block_from_string input
1300
+ assert_equal '\$x/x={(1,if x!=0),(text{undefined},if x=0):}\$', para.content
1301
+ end
1302
+
1303
+ test 'should not recognize asciimath macro with no content' do
1304
+ input = 'asciimath:[]'
1305
+ para = block_from_string input
1306
+ assert_equal 'asciimath:[]', para.content
1307
+ end
1308
+
1309
+ test 'should perform specialcharacters subs on asciimath macro content in html backend by default' do
1310
+ input = 'asciimath:[a < b]'
1311
+ para = block_from_string input
1312
+ assert_equal '\$a &lt; b\$', para.content
1313
+ end
1314
+
1315
+ test 'should not perform specialcharacters subs on asciimath macro content in docbook backend by default' do
1316
+ input = 'asciimath:[a < b]'
1317
+ para = block_from_string input, :backend => :docbook
1318
+ assert_equal 'a < b', para.content
1319
+ end
1320
+
1321
+ test 'should honor explicit subslist on asciimath macro' do
1322
+ input = 'asciimath:attributes[{expr}]'
1323
+ para = block_from_string input, :attributes => {'expr' => 'x != 0'}
1324
+ assert_equal '\$x != 0\$', para.content
1325
+ end
1326
+
1327
+ test 'should passthrough text in latexmath macro and surround with LaTeX math delimiters' do
1328
+ input = 'latexmath:[C = \alpha + \beta Y^{\gamma} + \epsilon]'
1329
+ para = block_from_string input
1330
+ assert_equal '\(C = \alpha + \beta Y^{\gamma} + \epsilon\)', para.content
1331
+ end
1332
+
1333
+ test 'should not recognize latexmath macro with no content' do
1334
+ input = 'latexmath:[]'
1335
+ para = block_from_string input
1336
+ assert_equal 'latexmath:[]', para.content
1337
+ end
1338
+
1339
+ test 'should perform specialcharacters subs on latexmath macro in html backend by default' do
1340
+ input = 'latexmath:[a < b]'
1341
+ para = block_from_string input
1342
+ assert_equal '\(a &lt; b\)', para.content
1343
+ end
1344
+
1345
+ test 'should not perform specialcharacters subs on latexmath macro content in docbook backend by default' do
1346
+ input = 'latexmath:[a < b]'
1347
+ para = block_from_string input, :backend => :docbook
1348
+ assert para.content.include?('<alt><![CDATA[a < b]]></alt>')
1349
+ end
1350
+
1351
+ test 'should honor explicit subslist on latexmath macro' do
1352
+ input = 'latexmath:attributes[{expr}]'
1353
+ para = block_from_string input, :attributes => {'expr' => '\sqrt{4} = 2'}
1354
+ assert_equal '\(\sqrt{4} = 2\)', para.content
1355
+ end
1356
+
1357
+ test 'should passthrough math macro inside another passthrough' do
1358
+ input = 'the text `asciimath:[x = y]` should be passed through as +literal+ text'
1359
+ para = block_from_string input, :attributes => {'compat-mode' => ''}
1360
+ assert_equal 'the text <code>asciimath:[x = y]</code> should be passed through as <code>literal</code> text', para.content
1361
+
1362
+ input = 'the text [x-]`asciimath:[x = y]` should be passed through as `literal` text'
1363
+ para = block_from_string input
1364
+ assert_equal 'the text <code>asciimath:[x = y]</code> should be passed through as <code>literal</code> text', para.content
1365
+
1366
+ input = 'the text `+asciimath:[x = y]+` should be passed through as `literal` text'
1367
+ para = block_from_string input
1368
+ assert_equal 'the text <code>asciimath:[x = y]</code> should be passed through as <code>literal</code> text', para.content
1369
+ end
1370
+
1371
+ test 'should not recognize stem macro with no content' do
1372
+ input = 'stem:[]'
1373
+ para = block_from_string input
1374
+ assert_equal input, para.content
1375
+ end
1376
+
1377
+ test 'should passthrough text in stem macro and surround with AsciiMath delimiters if stem attribute != latexmath' do
1378
+ [
1379
+ {},
1380
+ {'stem' => ''},
1381
+ {'stem' => 'asciimath'}
1382
+ ].each do |attributes|
1383
+ input = 'stem:[x/x={(1,if x!=0),(text{undefined},if x=0):}]'
1384
+ para = block_from_string input, :attributes => attributes
1385
+ assert_equal '\$x/x={(1,if x!=0),(text{undefined},if x=0):}\$', para.content
1386
+ end
1387
+ end
1388
+
1389
+ test 'should passthrough text in stem macro and surround with LaTeX math delimiters if stem attribute = latexmath' do
1390
+ input = 'stem:[C = \alpha + \beta Y^{\gamma} + \epsilon]'
1391
+ para = block_from_string input, :attributes => {'stem' => 'latexmath'}
1392
+ assert_equal '\(C = \alpha + \beta Y^{\gamma} + \epsilon\)', para.content
1393
+ end
1394
+
1395
+ test 'should find and replace placeholder duplicated by substitution' do
1396
+ input = %q(+first passthrough+ followed by link:$$http://example.com/__u_no_format_me__$$[] with passthrough)
1397
+ result = render_embedded_string input, :doctype => :inline
1398
+ assert_equal 'first passthrough followed by <a href="http://example.com/__u_no_format_me__" class="bare">http://example.com/__u_no_format_me__</a> with passthrough', result
1399
+ end
1400
+ end
982
1401
  end
983
1402
 
984
1403
  context 'Replacements' do
@@ -993,29 +1412,72 @@ context 'Substitutions' do
993
1412
  end
994
1413
 
995
1414
  test 'replaces dashes' do
996
- para = block_from_string %(-- foo foo--bar foo\\--bar foo -- bar foo \\-- bar
1415
+ para = block_from_string %(-- foo foo--bar foo\\--bar foo -- bar foo \\-- bar
997
1416
  stuff in between
998
1417
  -- foo
999
1418
  stuff in between
1000
1419
  foo --
1001
1420
  stuff in between
1002
1421
  foo --)
1003
- expected = %(&#8201;&#8212;&#8201;foo foo&#8212;bar foo--bar foo&#8201;&#8212;&#8201;bar foo -- bar
1422
+ expected = '&#8201;&#8212;&#8201;foo foo&#8212;&#8203;bar foo--bar foo&#8201;&#8212;&#8201;bar foo -- bar
1004
1423
  stuff in between&#8201;&#8212;&#8201;foo
1005
1424
  stuff in between
1006
1425
  foo&#8201;&#8212;&#8201;stuff in between
1007
- foo&#8201;&#8212;&#8201;)
1426
+ foo&#8201;&#8212;&#8201;'
1008
1427
  assert_equal expected, para.sub_replacements(para.source)
1009
1428
  end
1010
1429
 
1430
+ test 'replaces dashes between multibyte word characters' do
1431
+ para = block_from_string %(富--巴)
1432
+ expected = '富&#8212;&#8203;巴'
1433
+ assert_equal expected, para.sub_replacements(para.source)
1434
+ end if ::RUBY_MIN_VERSION_1_9
1435
+
1011
1436
  test 'replaces marks' do
1012
1437
  para = block_from_string '(C) (R) (TM) \(C) \(R) \(TM)'
1013
1438
  assert_equal '&#169; &#174; &#8482; (C) (R) (TM)', para.sub_replacements(para.source)
1014
1439
  end
1015
1440
 
1441
+ test 'preserves entity references' do
1442
+ input = '&amp; &#169; &#10004; &#x2022;'
1443
+ result = render_embedded_string input, :doctype => :inline
1444
+ assert_equal input, result
1445
+ end
1446
+
1016
1447
  test 'replaces punctuation' do
1017
- para = block_from_string %(John's Hideout... foo\\'bar)
1018
- assert_equal "John&#8217;s Hideout&#8230; foo'bar", para.sub_replacements(para.source)
1448
+ para = block_from_string %(John's Hideout is the Whites`' place... foo\\'bar)
1449
+ assert_equal "John&#8217;s Hideout is the Whites&#8217; place&#8230;&#8203; foo'bar", para.sub_replacements(para.source)
1450
+ end
1451
+
1452
+ test 'should replace right single quote marks' do
1453
+ given = [
1454
+ %(`'Twas the night),
1455
+ %(a `'57 Chevy!),
1456
+ %(the whites`' place),
1457
+ %(the whites`'.),
1458
+ %(the whites`'--where the wild things are),
1459
+ %(the whites`'\nhave),
1460
+ %(It's Mary`'s little lamb.),
1461
+ %(consecutive single quotes '' are not modified),
1462
+ %(he is 6' tall),
1463
+ %(\\`')
1464
+ ]
1465
+ expected = [
1466
+ %(&#8217;Twas the night),
1467
+ %(a &#8217;57 Chevy!),
1468
+ %(the whites&#8217; place),
1469
+ %(the whites&#8217;.),
1470
+ %(the whites&#8217;--where the wild things are),
1471
+ %(the whites&#8217;\nhave),
1472
+ %(It&#8217;s Mary&#8217;s little lamb.),
1473
+ %(consecutive single quotes '' are not modified),
1474
+ %(he is 6' tall),
1475
+ %(`')
1476
+ ]
1477
+ given.size.times {|i|
1478
+ para = block_from_string given[i]
1479
+ assert_equal expected[i], para.sub_replacements(para.source)
1480
+ }
1019
1481
  end
1020
1482
  end
1021
1483
 
@@ -1023,25 +1485,25 @@ foo&#8201;&#8212;&#8201;)
1023
1485
  test 'line break inserted after line with line break character' do
1024
1486
  para = block_from_string("First line +\nSecond line")
1025
1487
  result = para.apply_subs(para.lines, :post_replacements, true)
1026
- assert_equal "First line<br>\n", result.first
1488
+ assert_equal 'First line<br>', result.first
1027
1489
  end
1028
1490
 
1029
1491
  test 'line break inserted after line wrap with hardbreaks enabled' do
1030
1492
  para = block_from_string("First line\nSecond line", :attributes => {'hardbreaks' => ''})
1031
1493
  result = para.apply_subs(para.lines, :post_replacements, true)
1032
- assert_equal "First line<br>\n", result.first
1494
+ assert_equal 'First line<br>', result.first
1033
1495
  end
1034
1496
 
1035
1497
  test 'line break character stripped from end of line with hardbreaks enabled' do
1036
1498
  para = block_from_string("First line +\nSecond line", :attributes => {'hardbreaks' => ''})
1037
1499
  result = para.apply_subs(para.lines, :post_replacements, true)
1038
- assert_equal "First line<br>\n", result.first
1500
+ assert_equal 'First line<br>', result.first
1039
1501
  end
1040
1502
 
1041
1503
  test 'line break not inserted for single line with hardbreaks enabled' do
1042
- para = block_from_string("First line", :attributes => {'hardbreaks' => ''})
1504
+ para = block_from_string('First line', :attributes => {'hardbreaks' => ''})
1043
1505
  result = para.apply_subs(para.lines, :post_replacements, true)
1044
- assert_equal "First line", result.first
1506
+ assert_equal 'First line', result.first
1045
1507
  end
1046
1508
  end
1047
1509
 
@@ -1083,4 +1545,19 @@ foo&#8201;&#8212;&#8201;)
1083
1545
  assert_equal [:specialcharacters], block.subs
1084
1546
  end
1085
1547
  end
1548
+
1549
+ # TODO move to helpers_test.rb
1550
+ context 'Helpers' do
1551
+ test 'should URI encode non-word characters generally' do
1552
+ given = ' /%&?\\'
1553
+ expect = '%20%2F%25%26%3F%5C'
1554
+ assert_equal expect, (Asciidoctor::Helpers.encode_uri given)
1555
+ end
1556
+
1557
+ test 'should not URI select non-word characters' do
1558
+ given = '-.!~*\';:@=+$,()[]'
1559
+ expect = given
1560
+ assert_equal expect, (Asciidoctor::Helpers.encode_uri given)
1561
+ end
1562
+ end
1086
1563
  end