asciidoctor 0.1.3 → 0.1.4

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +387 -0
  3. data/README.adoc +358 -348
  4. data/asciidoctor.gemspec +30 -9
  5. data/bin/asciidoctor +3 -0
  6. data/bin/asciidoctor-safe +3 -0
  7. data/compat/asciidoc.conf +76 -4
  8. data/lib/asciidoctor.rb +174 -79
  9. data/lib/asciidoctor/abstract_block.rb +131 -101
  10. data/lib/asciidoctor/abstract_node.rb +108 -26
  11. data/lib/asciidoctor/attribute_list.rb +1 -1
  12. data/lib/asciidoctor/backends/_stylesheets.rb +204 -62
  13. data/lib/asciidoctor/backends/base_template.rb +11 -22
  14. data/lib/asciidoctor/backends/docbook45.rb +158 -163
  15. data/lib/asciidoctor/backends/docbook5.rb +103 -0
  16. data/lib/asciidoctor/backends/html5.rb +662 -445
  17. data/lib/asciidoctor/block.rb +54 -44
  18. data/lib/asciidoctor/cli/invoker.rb +41 -20
  19. data/lib/asciidoctor/cli/options.rb +66 -20
  20. data/lib/asciidoctor/debug.rb +1 -1
  21. data/lib/asciidoctor/document.rb +265 -100
  22. data/lib/asciidoctor/extensions.rb +443 -0
  23. data/lib/asciidoctor/helpers.rb +38 -6
  24. data/lib/asciidoctor/inline.rb +5 -5
  25. data/lib/asciidoctor/lexer.rb +532 -250
  26. data/lib/asciidoctor/{list_item.rb → list.rb} +33 -13
  27. data/lib/asciidoctor/path_resolver.rb +28 -2
  28. data/lib/asciidoctor/reader.rb +814 -455
  29. data/lib/asciidoctor/renderer.rb +128 -42
  30. data/lib/asciidoctor/section.rb +55 -41
  31. data/lib/asciidoctor/substituters.rb +380 -107
  32. data/lib/asciidoctor/table.rb +40 -30
  33. data/lib/asciidoctor/version.rb +1 -1
  34. data/man/asciidoctor.1 +32 -96
  35. data/man/{asciidoctor.ad → asciidoctor.adoc} +57 -48
  36. data/test/attributes_test.rb +200 -27
  37. data/test/blocks_test.rb +361 -22
  38. data/test/document_test.rb +496 -81
  39. data/test/extensions_test.rb +448 -0
  40. data/test/fixtures/basic-docinfo-footer.html +6 -0
  41. data/test/fixtures/basic-docinfo-footer.xml +8 -0
  42. data/test/fixtures/basic-docinfo.xml +3 -3
  43. data/test/fixtures/basic.asciidoc +1 -0
  44. data/test/fixtures/child-include.adoc +5 -0
  45. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
  46. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
  47. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
  48. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
  49. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
  50. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
  51. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
  52. data/test/fixtures/docinfo-footer.html +1 -0
  53. data/test/fixtures/docinfo-footer.xml +9 -0
  54. data/test/fixtures/docinfo.xml +1 -0
  55. data/test/fixtures/grandchild-include.adoc +3 -0
  56. data/test/fixtures/parent-include-restricted.adoc +5 -0
  57. data/test/fixtures/parent-include.adoc +5 -0
  58. data/test/invoker_test.rb +82 -8
  59. data/test/lexer_test.rb +21 -3
  60. data/test/links_test.rb +34 -2
  61. data/test/lists_test.rb +304 -7
  62. data/test/options_test.rb +19 -3
  63. data/test/paragraphs_test.rb +13 -0
  64. data/test/paths_test.rb +22 -0
  65. data/test/preamble_test.rb +20 -0
  66. data/test/reader_test.rb +1096 -644
  67. data/test/renderer_test.rb +152 -12
  68. data/test/sections_test.rb +417 -76
  69. data/test/substitutions_test.rb +339 -138
  70. data/test/tables_test.rb +109 -4
  71. data/test/test_helper.rb +79 -13
  72. data/test/text_test.rb +111 -11
  73. metadata +54 -18
@@ -0,0 +1,8 @@
1
+ <revhistory>
2
+ <revision>
3
+ <revnumber>{revnumber}</revnumber>
4
+ <date>01 Jan 2013</date>
5
+ <authorinitials>abc</authorinitials>
6
+ <revremark>Unleashed into the wild</revremark>
7
+ </revision>
8
+ </revhistory>
@@ -1,4 +1,4 @@
1
- <copyright>
2
- <year>2013</year>
3
- <holder>Acme, Inc.</holder>
1
+ <copyright><!-- don't remove the indent! -->
2
+ <year>2013</year>
3
+ <holder>Acme, Inc.</holder>
4
4
  </copyright>
@@ -1,4 +1,5 @@
1
1
  = Document Title
2
2
  Doc Writer <doc.writer@asciidoc.org>
3
+ v1.0, 2013-01-01
3
4
 
4
5
  Body content.
@@ -0,0 +1,5 @@
1
+ first line of child
2
+
3
+ include::grandchild-include.adoc[]
4
+
5
+ last line of child
@@ -0,0 +1,6 @@
1
+ - if title?
2
+ %formalpara{:id=>@id, :role=>(attr :role), :xreflabel=>(attr :reftext)}
3
+ %title=title
4
+ %para=content
5
+ - else
6
+ %para{:id=>@id, :role=>(attr :role), :xreflabel=>(attr :reftext)}=content
@@ -0,0 +1,3 @@
1
+ - if title?
2
+ .title=title
3
+ %p{:id=>@id, :class=>(attr 'role')}=content
@@ -0,0 +1,5 @@
1
+ %aside{:id=>@id, :class=>(attr 'role')}
2
+ - if title?
3
+ %header
4
+ %h1=title
5
+ =content.chomp
@@ -0,0 +1,6 @@
1
+ - if title?
2
+ formalpara id=@id role=(attr :role) xreflabel=(attr :reftext)
3
+ title=title
4
+ para=content
5
+ - else
6
+ para id=@id role=(attr :role) xreflabel=(attr :reftext) =content
@@ -0,0 +1,3 @@
1
+ - if title?
2
+ .title=title
3
+ p id=@id class=(attr 'role') =content
@@ -0,0 +1,5 @@
1
+ aside id=@id class=(attr 'role')
2
+ - if title?
3
+ header
4
+ h1=title
5
+ =content
@@ -0,0 +1 @@
1
+ <a id="top" href="#">Back to top</a>
@@ -0,0 +1,9 @@
1
+ <glossary id="_glossary">
2
+ <title>Glossary</title>
3
+ <glossentry>
4
+ <glossterm>term</glossterm>
5
+ <glossdef>
6
+ <simpara>definition</simpara>
7
+ </glossdef>
8
+ </glossentry>
9
+ </glossary>
@@ -1,2 +1,3 @@
1
1
  <productname>Asciidoctor</productname>
2
2
  <productnumber>1.0.0</productnumber>
3
+ <edition>{revnumber}</edition>
@@ -0,0 +1,3 @@
1
+ first line of grandchild
2
+
3
+ last line of grandchild
@@ -0,0 +1,5 @@
1
+ first line of parent
2
+
3
+ include::child-include.adoc[depth=1]
4
+
5
+ last line of parent
@@ -0,0 +1,5 @@
1
+ first line of parent
2
+
3
+ include::child-include.adoc[]
4
+
5
+ last line of parent
@@ -58,6 +58,25 @@ context 'Invoker' do
58
58
  assert_xpath '/*[@class="paragraph"]/p[text()="content"]', output, 1
59
59
  end
60
60
 
61
+ test 'should accept document from stdin and write to output file' do
62
+ sample_outpath = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'sample-output.html'))
63
+ begin
64
+ invoker = invoke_cli(%W(-s -o #{sample_outpath}), '-') { 'content' }
65
+ doc = invoker.document
66
+ assert !doc.attr?('docname')
67
+ assert !doc.attr?('docfile')
68
+ assert_equal Dir.pwd, doc.attr('docdir')
69
+ assert_equal doc.attr('docdate'), doc.attr('localdate')
70
+ assert_equal doc.attr('doctime'), doc.attr('localtime')
71
+ assert_equal doc.attr('docdatetime'), doc.attr('localdatetime')
72
+ assert doc.attr?('outfile')
73
+ assert_equal sample_outpath, doc.attr('outfile')
74
+ assert File.exist?(sample_outpath)
75
+ ensure
76
+ FileUtils::rm_f(sample_outpath)
77
+ end
78
+ end
79
+
61
80
  test 'should allow docdir to be specified when input is a string' do
62
81
  expected_docdir = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures'))
63
82
  invoker = invoke_cli_to_buffer(%w(-s --base-dir test/fixtures -o /dev/null), '-') { 'content' }
@@ -88,10 +107,10 @@ context 'Invoker' do
88
107
  end
89
108
  end
90
109
 
91
- test 'should warn if extra arguments are detected' do
110
+ test 'should treat extra arguments as files' do
92
111
  redirect_streams do |stdout, stderr|
93
112
  invoker = invoke_cli %w(-o /dev/null extra arguments sample.asciidoc), nil
94
- assert_match(/extra arguments detected/, stderr.string)
113
+ assert_match(/input file .* missing/, stderr.string)
95
114
  assert_equal 1, invoker.code
96
115
  end
97
116
  end
@@ -144,20 +163,59 @@ context 'Invoker' do
144
163
  end
145
164
  end
146
165
 
147
- test 'should copy default css to target directory if copycss is specified' do
166
+ test 'should copy default css to target directory if linkcss is specified' do
167
+ sample_outpath = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'sample-output.html'))
168
+ asciidoctor_stylesheet = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'asciidoctor.css'))
169
+ coderay_stylesheet = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'asciidoctor-coderay.css'))
170
+ begin
171
+ invoker = invoke_cli %W(-o #{sample_outpath} -a linkcss -a source-highlighter=coderay)
172
+ invoker.document
173
+ assert File.exist?(sample_outpath)
174
+ assert File.exist?(asciidoctor_stylesheet)
175
+ assert File.exist?(coderay_stylesheet)
176
+ ensure
177
+ FileUtils::rm_f(sample_outpath)
178
+ FileUtils::rm_f(asciidoctor_stylesheet)
179
+ FileUtils::rm_f(coderay_stylesheet)
180
+ end
181
+ end
182
+
183
+ test 'should not copy default css to target directory if linkcss is set and copycss is unset' do
148
184
  sample_outpath = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'sample-output.html'))
149
185
  default_stylesheet = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'asciidoctor.css'))
150
186
  begin
151
- invoker = invoke_cli %W(-o #{sample_outpath} -a copycss)
187
+ invoker = invoke_cli %W(-o #{sample_outpath} -a linkcss -a copycss!)
152
188
  invoker.document
153
189
  assert File.exist?(sample_outpath)
154
- assert File.exist?(default_stylesheet)
190
+ assert !File.exist?(default_stylesheet)
191
+ ensure
192
+ FileUtils::rm_f(sample_outpath)
193
+ end
194
+ end
195
+
196
+ test 'should render all passed files' do
197
+ basic_outpath = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'basic.html'))
198
+ sample_outpath = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'sample.html'))
199
+ begin
200
+ invoke_cli_with_filenames %w(), %w(basic.asciidoc sample.asciidoc)
201
+ assert File.exist?(basic_outpath)
202
+ assert File.exist?(sample_outpath)
155
203
  ensure
204
+ FileUtils::rm_f(basic_outpath)
156
205
  FileUtils::rm_f(sample_outpath)
157
- FileUtils::rm_f(default_stylesheet)
158
206
  end
159
207
  end
160
208
 
209
+ test 'should render all files that matches a glob expression' do
210
+ basic_outpath = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'basic.html'))
211
+ begin
212
+ invoke_cli_to_buffer %w(), "ba*.asciidoc"
213
+ assert File.exist?(basic_outpath)
214
+ ensure
215
+ FileUtils::rm_f(basic_outpath)
216
+ end
217
+ end
218
+
161
219
  test 'should suppress header footer if specified' do
162
220
  invoker = invoke_cli_to_buffer %w(-s -o -)
163
221
  output = invoker.read_output
@@ -208,7 +266,7 @@ context 'Invoker' do
208
266
  assert_equal 'docbook45', doc.attr('backend')
209
267
  assert_equal '.xml', doc.attr('outfilesuffix')
210
268
  output = invoker.read_output
211
- assert_xpath '/article', output, 1
269
+ assert_xpath '/xmlns:article', output, 1
212
270
  end
213
271
 
214
272
  test 'should set doctype to article if specified' do
@@ -227,6 +285,22 @@ context 'Invoker' do
227
285
  assert_xpath '/html/body[@class="book"]', output, 1
228
286
  end
229
287
 
288
+ test 'should locate custom templates based on template dir, template engine and backend' do
289
+ custom_backend_root = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'custom-backends'))
290
+ invoker = invoke_cli_to_buffer %W(-E haml -T #{custom_backend_root} -o -)
291
+ doc = invoker.document
292
+ assert doc.renderer.views['block_paragraph'].is_a? Tilt::HamlTemplate
293
+ end
294
+
295
+ test 'should load custom templates from multiple template directories' do
296
+ custom_backend_1 = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'custom-backends/haml/html5'))
297
+ custom_backend_2 = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'custom-backends/haml/html5-tweaks'))
298
+ invoker = invoke_cli_to_buffer %W(-T #{custom_backend_1} -T #{custom_backend_2} -o - -s)
299
+ output = invoker.read_output
300
+ assert_css '.paragraph', output, 0
301
+ assert_css '#preamble > .sectionbody > p', output, 1
302
+ end
303
+
230
304
  test 'should set attribute with value' do
231
305
  invoker = invoke_cli_to_buffer %w(--trace -a idprefix=id -s -o -)
232
306
  doc = invoker.document
@@ -244,7 +318,7 @@ context 'Invoker' do
244
318
  end
245
319
 
246
320
  test 'should set attribute with quoted value containing a space' do
247
- # emulating commandline arguments: --trace -a toc -a note-caption="Note to self:" -o -
321
+ # emulating commandline arguments: --trace -a toc -a note-caption="Note to self:" -o -
248
322
  invoker = invoke_cli_to_buffer %w(--trace -a toc -a note-caption=Note\ to\ self: -o -)
249
323
  doc = invoker.document
250
324
  assert_equal 'Note to self:', doc.attr('note-caption')
@@ -7,6 +7,12 @@ context "Lexer" do
7
7
  assert Asciidoctor::Lexer.is_section_title?('=== AsciiDoc Home Page')
8
8
  end
9
9
 
10
+ test 'sanitize attribute name' do
11
+ assert_equal 'foobar', Asciidoctor::Lexer.sanitize_attribute_name("Foo Bar")
12
+ assert_equal 'foo', Asciidoctor::Lexer.sanitize_attribute_name("foo")
13
+ assert_equal 'foo3-bar', Asciidoctor::Lexer.sanitize_attribute_name("Foo 3^ # - Bar[")
14
+ end
15
+
10
16
  test "collect unnamed attribute" do
11
17
  attributes = {}
12
18
  line = 'quote'
@@ -201,15 +207,17 @@ context "Lexer" do
201
207
  assert_equal 'style#id.role', attributes[1]
202
208
  end
203
209
 
204
- test 'parse style attribute with style, role and id' do
205
- attributes = {1 => 'style.role#id'}
210
+ test 'parse style attribute with style, role, id and option' do
211
+ attributes = {1 => 'style.role#id%fragment'}
206
212
  style, original_style = Asciidoctor::Lexer.parse_style_attribute(attributes)
207
213
  assert_equal 'style', style
208
214
  assert_nil original_style
209
215
  assert_equal 'style', attributes['style']
210
216
  assert_equal 'id', attributes['id']
211
217
  assert_equal 'role', attributes['role']
212
- assert_equal 'style.role#id', attributes[1]
218
+ assert_equal '', attributes['fragment-option']
219
+ assert_equal 'fragment', attributes['options']
220
+ assert_equal 'style.role#id%fragment', attributes[1]
213
221
  end
214
222
 
215
223
  test 'parse style attribute with style, id and multiple roles' do
@@ -263,6 +271,16 @@ context "Lexer" do
263
271
  assert_nil attributes[1]
264
272
  end
265
273
 
274
+ test 'parse style attribute with option should preserve existing options' do
275
+ attributes = {1 => '%header', 'options' => 'footer', 'footer-option' => ''}
276
+ style, original_style = Asciidoctor::Lexer.parse_style_attribute(attributes)
277
+ assert_nil style
278
+ assert_nil original_style
279
+ assert_equal 'header,footer', attributes['options']
280
+ assert_equal '', attributes['header-option']
281
+ assert_equal '', attributes['footer-option']
282
+ end
283
+
266
284
  test "parse author first" do
267
285
  metadata, = parse_header_metadata 'Stuart'
268
286
  assert_equal 5, metadata.size
@@ -131,12 +131,44 @@ context 'Links' do
131
131
  assert_xpath '//a[@href="#tigers"][text() = "About Tigers"]', render_string('<<tigers,"About Tigers">>'), 1
132
132
  end
133
133
 
134
+ test 'xref using angled bracket syntax with path sans extension' do
135
+ doc = document_from_string '<<tigers#>>', :header_footer => false
136
+ assert_xpath '//a[@href="tigers.html"][text() = "[tigers]"]', doc.render, 1
137
+ end
138
+
139
+ test 'xref using angled bracket syntax with path and extension' do
140
+ doc = document_from_string '<<tigers.adoc#>>', :header_footer => false
141
+ assert_xpath '//a[@href="tigers.html"][text() = "[tigers]"]', doc.render, 1
142
+ end
143
+
144
+ test 'xref using angled bracket syntax with path and fragment' do
145
+ doc = document_from_string '<<tigers#about>>', :header_footer => false
146
+ assert_xpath '//a[@href="tigers.html#about"][text() = "[tigers#about]"]', doc.render, 1
147
+ end
148
+
149
+ test 'xref using angled bracket syntax with path, fragment and text' do
150
+ doc = document_from_string '<<tigers#about,About Tigers>>', :header_footer => false
151
+ assert_xpath '//a[@href="tigers.html#about"][text() = "About Tigers"]', doc.render, 1
152
+ end
153
+
154
+ test 'xref using angled bracket syntax with path which has been included in this document' do
155
+ doc = document_from_string '<<tigers#about,About Tigers>>', :header_footer => false
156
+ doc.references[:includes] << 'tigers'
157
+ assert_xpath '//a[@href="#about"][text() = "About Tigers"]', doc.render, 1
158
+ end
159
+
160
+ test 'xref using angled bracket syntax with nested path which has been included in this document' do
161
+ doc = document_from_string '<<part1/tigers#about,About Tigers>>', :header_footer => false
162
+ doc.references[:includes] << 'part1/tigers'
163
+ assert_xpath '//a[@href="#about"][text() = "About Tigers"]', doc.render, 1
164
+ end
165
+
134
166
  test 'xref using angled bracket syntax inline with text' do
135
167
  assert_xpath '//a[@href="#tigers"][text() = "about tigers"]', render_string('Want to learn <<tigers,about tigers>>?'), 1
136
168
  end
137
169
 
138
170
  test 'xref using angled bracket syntax with multi-line label inline with text' do
139
- assert_xpath %{//a[@href="#tigers"][text() = "about\ntigers"]}, render_string("Want to learn <<tigers,about\ntigers>>?"), 1
171
+ assert_xpath %{//a[@href="#tigers"][normalize-space(text()) = "about tigers"]}, render_string("Want to learn <<tigers,about\ntigers>>?"), 1
140
172
  end
141
173
 
142
174
  test 'xref with escaped text' do
@@ -162,7 +194,7 @@ context 'Links' do
162
194
  end
163
195
 
164
196
  test 'xref using macro syntax with multi-line label inline with text' do
165
- assert_xpath %{//a[@href="#tigers"][text() = "about\ntigers"]}, render_string("Want to learn xref:tigers[about\ntigers]?"), 1
197
+ assert_xpath %{//a[@href="#tigers"][normalize-space(text()) = "about tigers"]}, render_string("Want to learn xref:tigers[about\ntigers]?"), 1
166
198
  end
167
199
 
168
200
  test 'xref using invalid macro syntax does not create link' do
@@ -460,6 +460,20 @@ List
460
460
  assert_xpath '//ul/li', output, 3
461
461
  end
462
462
 
463
+ test 'should represent block style as style class' do
464
+ ['disc', 'square', 'circle'].each do |style|
465
+ input = <<-EOS
466
+ [#{style}]
467
+ * a
468
+ * b
469
+ * c
470
+ EOS
471
+ output = render_embedded_string input
472
+ assert_css ".ulist.#{style}", output, 1
473
+ assert_css ".ulist.#{style} ul.#{style}", output, 1
474
+ end
475
+ end
476
+
463
477
  test "asterisk elements separated by blank lines should merge lists" do
464
478
  input = <<-EOS
465
479
  List
@@ -1075,7 +1089,7 @@ Lists
1075
1089
  assert_xpath '//ul/li[1]/*', output, 1
1076
1090
  end
1077
1091
 
1078
- test "consecutive blocks in list continuation attach to list item" do
1092
+ test 'consecutive blocks in list continuation attach to list item' do
1079
1093
  input = <<-EOS
1080
1094
  Lists
1081
1095
  =====
@@ -1092,7 +1106,7 @@ ____
1092
1106
  +
1093
1107
  * Item two
1094
1108
  EOS
1095
- output = render_string input
1109
+ output = render_embedded_string input
1096
1110
  assert_xpath '//ul', output, 1
1097
1111
  assert_xpath '//ul/li', output, 2
1098
1112
  assert_xpath '//ul/li[1]/p', output, 1
@@ -1453,6 +1467,58 @@ List
1453
1467
  assert_xpath '//ol/li', output, 3
1454
1468
  end
1455
1469
 
1470
+ test 'should represent explicit role attribute as style class' do
1471
+ input = <<-EOS
1472
+ [role="dry"]
1473
+ . Once
1474
+ . Again
1475
+ . Refactor!
1476
+ EOS
1477
+
1478
+ output = render_embedded_string input
1479
+ assert_css '.olist.arabic.dry', output, 1
1480
+ assert_css '.olist ol.arabic', output, 1
1481
+ end
1482
+
1483
+ test 'should represent custom numbering and explicit role attribute as style classes' do
1484
+ input = <<-EOS
1485
+ [loweralpha, role="dry"]
1486
+ . Once
1487
+ . Again
1488
+ . Refactor!
1489
+ EOS
1490
+
1491
+ output = render_embedded_string input
1492
+ assert_css '.olist.loweralpha.dry', output, 1
1493
+ assert_css '.olist ol.loweralpha', output, 1
1494
+ end
1495
+
1496
+ test 'should represent implicit role attribute as style class' do
1497
+ input = <<-EOS
1498
+ [.dry]
1499
+ . Once
1500
+ . Again
1501
+ . Refactor!
1502
+ EOS
1503
+
1504
+ output = render_embedded_string input
1505
+ assert_css '.olist.arabic.dry', output, 1
1506
+ assert_css '.olist ol.arabic', output, 1
1507
+ end
1508
+
1509
+ test 'should represent custom numbering and implicit role attribute as style classes' do
1510
+ input = <<-EOS
1511
+ [loweralpha.dry]
1512
+ . Once
1513
+ . Again
1514
+ . Refactor!
1515
+ EOS
1516
+
1517
+ output = render_embedded_string input
1518
+ assert_css '.olist.loweralpha.dry', output, 1
1519
+ assert_css '.olist ol.loweralpha', output, 1
1520
+ end
1521
+
1456
1522
  test "dot elements separated by blank lines should merge lists" do
1457
1523
  input = <<-EOS
1458
1524
  List
@@ -1682,6 +1748,25 @@ term2::
1682
1748
  assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
1683
1749
  end
1684
1750
 
1751
+ test 'multi-line element with paragraph starting with multiple dashes should not be seen as list' do
1752
+ input = <<-EOS
1753
+ term1::
1754
+ def1
1755
+ -- and a note
1756
+
1757
+ term2::
1758
+ def2
1759
+ EOS
1760
+ output = render_embedded_string input
1761
+ assert_xpath '//dl', output, 1
1762
+ assert_xpath '//dl/dt', output, 2
1763
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
1764
+ assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
1765
+ assert_xpath %((//dl/dt)[1]/following-sibling::dd/p[text() = "def1#{entity 8201}#{entity 8212}#{entity 8201}and a note"]), output, 1
1766
+ assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
1767
+ assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
1768
+ end
1769
+
1685
1770
  test "multi-line element with multiple terms" do
1686
1771
  input = <<-EOS
1687
1772
  term1::
@@ -2023,6 +2108,23 @@ A new paragraph.
2023
2108
  assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]', output, 1
2024
2109
  assert_xpath '(//*[@class="dlist"]/following-sibling::*[@class="paragraph"])[1]/p[text() = "A new paragraph."]', output, 1
2025
2110
  end
2111
+
2112
+ test 'should not match comment line that looks like labeled list term' do
2113
+ input = <<-EOS
2114
+ * item
2115
+
2116
+ //::
2117
+ == Section
2118
+
2119
+ section text
2120
+ EOS
2121
+
2122
+ output = render_embedded_string input
2123
+ assert_xpath '/*[@class="ulist"]', output, 1
2124
+ assert_xpath '/*[@class="sect1"]', output, 1
2125
+ assert_xpath '/*[@class="sect1"]/h2[text()="Section"]', output, 1
2126
+ assert_xpath '/*[@class="ulist"]/following-sibling::*[@class="sect1"]', output, 1
2127
+ end
2026
2128
  end
2027
2129
 
2028
2130
  context "Nested lists" do
@@ -2284,6 +2386,17 @@ term:: def
2284
2386
  assert_xpath '(//table/colgroup/col)[2][@style="width:75%;"]', output, 1
2285
2387
  end
2286
2388
 
2389
+ test 'should add strong class to label if strong option is set' do
2390
+ input = <<-EOS
2391
+ [horizontal, options="strong"]
2392
+ term:: def
2393
+ EOS
2394
+
2395
+ output = render_embedded_string input
2396
+ assert_css '.hdlist', output, 1
2397
+ assert_css '.hdlist td.hdlist1.strong', output, 1
2398
+ end
2399
+
2287
2400
  test 'consecutive terms in horizontal list should share same cell' do
2288
2401
  input = <<-EOS
2289
2402
  [horizontal]
@@ -2296,7 +2409,9 @@ last::
2296
2409
  output = render_embedded_string input
2297
2410
  assert_xpath '//tr', output, 2
2298
2411
  assert_xpath '(//tr)[1]/td[@class="hdlist1"]', output, 1
2299
- assert_xpath '(//tr)[1]/td[@class="hdlist1"]/br', output, 2
2412
+ # NOTE I'm trimming the trailing <br> in Asciidoctor
2413
+ #assert_xpath '(//tr)[1]/td[@class="hdlist1"]/br', output, 2
2414
+ assert_xpath '(//tr)[1]/td[@class="hdlist1"]/br', output, 1
2300
2415
  assert_xpath '(//tr)[2]/td[@class="hdlist2"]', output, 1
2301
2416
  end
2302
2417
 
@@ -3631,6 +3746,121 @@ require 'asciidoctor' # \\<1>
3631
3746
  assert_xpath '//co', output, 0
3632
3747
  end
3633
3748
 
3749
+ test 'should not recognize callouts in middle of line' do
3750
+ input = <<-EOS
3751
+ [source, ruby]
3752
+ ----
3753
+ puts "The syntax <1> at the end of the line makes a code callout"
3754
+ ----
3755
+ EOS
3756
+ output = render_embedded_string input
3757
+ assert_xpath '//b', output, 0
3758
+ end
3759
+
3760
+ test 'should allow multiple callouts on the same line' do
3761
+ input = <<-EOS
3762
+ [source, ruby]
3763
+ ----
3764
+ require 'asciidoctor' <1>
3765
+ doc = Asciidoctor.load('Hello, World!') # <2> <3> <4>
3766
+ puts doc.render <5><6>
3767
+ exit 0
3768
+ ----
3769
+ <1> Require library
3770
+ <2> Load document from String
3771
+ <3> Uses default backend and doctype
3772
+ <4> One more for good luck
3773
+ <5> Renders document to String
3774
+ <6> Prints output to stdout
3775
+ EOS
3776
+ output = render_embedded_string input
3777
+ assert_xpath '//code/b', output, 6
3778
+ assert_match(/ <b>\(1\)<\/b>$/, output)
3779
+ assert_match(/ <b>\(2\)<\/b> <b>\(3\)<\/b> <b>\(4\)<\/b>$/, output)
3780
+ assert_match(/ <b>\(5\)<\/b><b>\(6\)<\/b>$/, output)
3781
+ end
3782
+
3783
+ test 'should allow XML comment-style callouts' do
3784
+ input = <<-EOS
3785
+ [source, xml]
3786
+ ----
3787
+ <section>
3788
+ <title>Section Title</title> <!--1-->
3789
+ <simpara>Just a paragraph</simpara> <!--2-->
3790
+ </section>
3791
+ ----
3792
+ <1> The title is required
3793
+ <2> The content isn't
3794
+ EOS
3795
+ output = render_embedded_string input
3796
+ assert_xpath '//b', output, 2
3797
+ assert_xpath '//b[text()="(1)"]', output, 1
3798
+ assert_xpath '//b[text()="(2)"]', output, 1
3799
+ end
3800
+
3801
+ test 'should not allow callouts with half an XML comment' do
3802
+ input = <<-EOS
3803
+ ----
3804
+ First line <1-->
3805
+ Second line <2-->
3806
+ ----
3807
+ EOS
3808
+ output = render_embedded_string input
3809
+ assert_xpath '//b', output, 0
3810
+ end
3811
+
3812
+ test 'should not recognize callouts in an indented labeled list paragraph' do
3813
+ input = <<-EOS
3814
+ foo::
3815
+ bar <1>
3816
+
3817
+ <1> Not pointing to a callout
3818
+ EOS
3819
+ output = render_embedded_string input
3820
+ assert_xpath '//dl//b', output, 0
3821
+ assert_xpath '//dl/dd/p[text()="bar <1>"]', output, 1
3822
+ assert_xpath '//ol/li/p[text()="Not pointing to a callout"]', output, 1
3823
+ end
3824
+
3825
+ test 'should not recognize callouts in an indented outline list paragraph' do
3826
+ input = <<-EOS
3827
+ * foo
3828
+ bar <1>
3829
+
3830
+ <1> Not pointing to a callout
3831
+ EOS
3832
+ output = render_embedded_string input
3833
+ assert_xpath '//ul//b', output, 0
3834
+ assert_xpath %(//ul/li/p[text()="foo\nbar <1>"]), output, 1
3835
+ assert_xpath '//ol/li/p[text()="Not pointing to a callout"]', output, 1
3836
+ end
3837
+
3838
+ test 'should remove leading line comment chars' do
3839
+ input = <<-EOS
3840
+ ----
3841
+ puts 'Hello, world!' # <1>
3842
+ ----
3843
+ <1> Ruby
3844
+
3845
+ ----
3846
+ println 'Hello, world!' // <1>
3847
+ ----
3848
+ <1> Groovy
3849
+
3850
+ ----
3851
+ (def hello (fn [] "Hello, world!")) ;; <1>
3852
+ (hello)
3853
+ ----
3854
+ <1> Clojure
3855
+ EOS
3856
+ output = render_embedded_string input
3857
+ assert_xpath '//b', output, 3
3858
+ nodes = xmlnodes_at_css 'pre', output
3859
+ assert_equal "puts 'Hello, world!' (1)", nodes[0].text
3860
+ assert_equal "println 'Hello, world!' (1)", nodes[1].text
3861
+ assert_equal %((def hello (fn [] "Hello, world!")) (1)\n(hello)), nodes[2].text
3862
+ end
3863
+
3634
3864
  test 'literal block with callouts' do
3635
3865
  input = <<-EOS
3636
3866
  ....
@@ -3680,8 +3910,8 @@ puts doc.render # <3>
3680
3910
  [source]
3681
3911
  ----
3682
3912
  require 'asciidoctor' # <1>
3683
- doc = Asciidoctor::Document.new('Hello, World!') # <2>
3684
- puts doc.render # <3>
3913
+ doc = Asciidoctor::Document.new('Hello, World!') #<2>
3914
+ puts doc.render #<3>
3685
3915
  ----
3686
3916
  <1> Describe the first line
3687
3917
  <2> Describe the second line
@@ -3690,11 +3920,78 @@ puts doc.render # <3>
3690
3920
  output = render_embedded_string input, :attributes => {'icons' => 'font'}
3691
3921
  assert_css '.listingblock code > i', output, 3
3692
3922
  (1..3).each do |i|
3693
- assert_xpath %((/div[@class="listingblock"]//code/i)[#{i}][@class="conum"][text() = "#{i}"]), output, 1
3923
+ assert_xpath %((/div[@class="listingblock"]//code/i)[#{i}]), output, 1
3924
+ assert_xpath %((/div[@class="listingblock"]//code/i)[#{i}][@class="conum"][@data-value="#{i}"]), output, 1
3925
+ assert_xpath %((/div[@class="listingblock"]//code/i)[#{i}]/following-sibling::b[text()="(#{i})"]), output, 1
3694
3926
  end
3695
3927
  assert_css '.colist table td i', output, 3
3696
3928
  (1..3).each do |i|
3697
- assert_xpath %((/div[@class="colist arabic"]//td/i)[#{i}][@class="conum"][text() = "#{i}"]), output, 1
3929
+ assert_xpath %((/div[@class="colist arabic"]//td/i)[#{i}]), output, 1
3930
+ assert_xpath %((/div[@class="colist arabic"]//td/i)[#{i}][@class="conum"][@data-value = "#{i}"]), output, 1
3931
+ assert_xpath %((/div[@class="colist arabic"]//td/i)[#{i}]/following-sibling::b[text() = "#{i}"]), output, 1
3698
3932
  end
3699
3933
  end
3700
3934
  end
3935
+
3936
+ context 'Checklists' do
3937
+ test 'should create checklist if at least one item has checkbox syntax' do
3938
+ input = <<-EOS
3939
+ - [ ] todo
3940
+ - [x] done
3941
+ - plain
3942
+ EOS
3943
+
3944
+ output = render_embedded_string input
3945
+ assert_css '.ulist.checklist', output, 1
3946
+ assert_css '.ulist.checklist li input[type="checkbox"][disabled]', output, 2
3947
+ assert_css '.ulist.checklist li input[type="checkbox"][checked]', output, 1
3948
+ assert_xpath '(/*[@class="ulist checklist"]/ul/li)[3]/p[text()="plain"]', output, 1
3949
+ end
3950
+
3951
+ test 'should create checklist with font icons if at least one item has checkbox syntax and icons attribute is font' do
3952
+ input = <<-EOS
3953
+ - [ ] todo
3954
+ - [x] done
3955
+ - plain
3956
+ EOS
3957
+
3958
+ output = render_embedded_string input, :attributes => {'icons' => 'font'}
3959
+ assert_css '.ulist.checklist', output, 1
3960
+ assert_css '.ulist.checklist li i.icon-check', output, 1
3961
+ assert_css '.ulist.checklist li i.icon-check-empty', output, 1
3962
+ assert_xpath '(/*[@class="ulist checklist"]/ul/li)[3]/p[text()="plain"]', output, 1
3963
+ end
3964
+
3965
+ test 'should create interactive checklist if interactive option is set even with icons attribute is font' do
3966
+ input = <<-EOS
3967
+ :icons: font
3968
+
3969
+ [options="interactive"]
3970
+ - [ ] todo
3971
+ - [x] done
3972
+ EOS
3973
+
3974
+ output = render_embedded_string input
3975
+ assert_css '.ulist.checklist', output, 1
3976
+ assert_css '.ulist.checklist li input[type="checkbox"]', output, 2
3977
+ assert_css '.ulist.checklist li input[type="checkbox"][disabled]', output, 0
3978
+ assert_css '.ulist.checklist li input[type="checkbox"][checked]', output, 1
3979
+ end
3980
+ end
3981
+
3982
+ context 'Lists model' do
3983
+ test 'content should return items in list' do
3984
+ input = <<-EOS
3985
+ * one
3986
+ * two
3987
+ * three
3988
+ EOS
3989
+
3990
+ doc = document_from_string input
3991
+ list = doc.blocks.first
3992
+ assert list.is_a? Asciidoctor::List
3993
+ items = list.items
3994
+ assert_equal 3, items.size
3995
+ assert_equal list.items, list.content
3996
+ end
3997
+ end