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
@@ -147,7 +147,6 @@ A | here| a | there
147
147
  input = <<-EOS
148
148
  |====
149
149
  |first |second |third |fourth
150
-
151
150
  |1 |2 |3
152
151
  |4
153
152
  |====
@@ -224,6 +223,90 @@ A | here| a | there
224
223
  assert_css 'table > tbody > tr', output, 3
225
224
  end
226
225
 
226
+ test 'table with implicit header row' do
227
+ input = <<-EOS
228
+ |===
229
+ |Column 1 |Column 2
230
+
231
+ |Data A1
232
+ |Data B1
233
+
234
+ |Data A2
235
+ |Data B2
236
+ |===
237
+ EOS
238
+ output = render_embedded_string input
239
+ assert_css 'table', output, 1
240
+ assert_css 'table > colgroup > col', output, 2
241
+ assert_css 'table > thead', output, 1
242
+ assert_css 'table > thead > tr', output, 1
243
+ assert_css 'table > thead > tr > th', output, 2
244
+ assert_css 'table > tbody', output, 1
245
+ assert_css 'table > tbody > tr', output, 2
246
+ end
247
+
248
+ test 'no implicit header row if second line not blank' do
249
+ input = <<-EOS
250
+ |===
251
+ |Column 1 |Column 2
252
+ |Data A1
253
+ |Data B1
254
+
255
+ |Data A2
256
+ |Data B2
257
+ |===
258
+ EOS
259
+ output = render_embedded_string input
260
+ assert_css 'table', output, 1
261
+ assert_css 'table > colgroup > col', output, 2
262
+ assert_css 'table > thead', output, 0
263
+ assert_css 'table > tbody', output, 1
264
+ assert_css 'table > tbody > tr', output, 3
265
+ end
266
+
267
+ test 'no implicit header row if first line blank' do
268
+ input = <<-EOS
269
+ |===
270
+
271
+ |Column 1 |Column 2
272
+
273
+ |Data A1
274
+ |Data B1
275
+
276
+ |Data A2
277
+ |Data B2
278
+
279
+ |===
280
+ EOS
281
+ output = render_embedded_string input
282
+ assert_css 'table', output, 1
283
+ assert_css 'table > colgroup > col', output, 2
284
+ assert_css 'table > thead', output, 0
285
+ assert_css 'table > tbody', output, 1
286
+ assert_css 'table > tbody > tr', output, 3
287
+ end
288
+
289
+ test 'no implicit header row if options is specified' do
290
+ input = <<-EOS
291
+ [options=""]
292
+ |===
293
+ |Column 1 |Column 2
294
+
295
+ |Data A1
296
+ |Data B1
297
+
298
+ |Data A2
299
+ |Data B2
300
+ |===
301
+ EOS
302
+ output = render_embedded_string input
303
+ assert_css 'table', output, 1
304
+ assert_css 'table > colgroup > col', output, 2
305
+ assert_css 'table > thead', output, 0
306
+ assert_css 'table > tbody', output, 1
307
+ assert_css 'table > tbody > tr', output, 3
308
+ end
309
+
227
310
  test 'styles not applied to header cells' do
228
311
  input = <<-EOS
229
312
  [cols="1h,1s,1e",options="header,footer"]
@@ -292,6 +375,20 @@ I am getting in shape!
292
375
  assert_xpath '/table/tbody/tr[3]/td[4]/p[2][text()="I am getting in shape!"]', output, 1
293
376
  end
294
377
 
378
+ test 'percentages as column widths' do
379
+ input = <<-EOS
380
+ [width="100%", cols="<.^10%,<90%"]
381
+ |===
382
+ |column A |column B
383
+ |===
384
+ EOS
385
+
386
+ output = render_embedded_string input
387
+ assert_xpath '/table/colgroup/col', output, 2
388
+ assert_xpath '(/table/colgroup/col)[1][@style="width:10%;"]', output, 1
389
+ assert_xpath '(/table/colgroup/col)[2][@style="width:90%;"]', output, 1
390
+ end
391
+
295
392
  test 'spans, alignments and styles' do
296
393
  input = <<-EOS
297
394
  [cols="e,m,^,>s",width="25%"]
@@ -452,19 +549,27 @@ output file name is used.
452
549
  assert_css 'table > tbody > tr:nth-child(2) > td:nth-child(3) div.dlist', output, 1
453
550
  end
454
551
 
552
+ test 'preprocessor directive on first line of AsciiDoc cell should be processed' do
553
+ input = <<-EOS
554
+ |===
555
+ a|include::fixtures/include-file.asciidoc[]
556
+ |===
557
+ EOS
558
+
559
+ output = render_embedded_string input, :safe => :safe, :base_dir => File.dirname(__FILE__)
560
+ assert_match(/included content/, output)
561
+ end
562
+
455
563
  test 'nested table' do
456
564
  input = <<-EOS
457
565
  [cols="1,2a"]
458
566
  |===
459
567
  |Normal cell
460
-
461
568
  |Cell with nested table
462
-
463
569
  [cols="2,1"]
464
570
  !===
465
571
  !Nested table cell 1 !Nested table cell 2
466
572
  !===
467
-
468
573
  |===
469
574
  EOS
470
575
  output = render_embedded_string input
@@ -1,3 +1,6 @@
1
+ if RUBY_VERSION < '1.9'
2
+ require 'rubygems'
3
+ end
1
4
  require 'fileutils'
2
5
  require 'pathname'
3
6
  require 'test/unit'
@@ -5,10 +8,11 @@ require 'test/unit'
5
8
  require "#{File.expand_path(File.dirname(__FILE__))}/../lib/asciidoctor.rb"
6
9
 
7
10
  require 'nokogiri'
8
- require 'pending'
9
11
 
10
12
  ENV['SUPPRESS_DEBUG'] ||= 'true'
11
13
 
14
+ RE_XMLNS_ATTRIBUTE = / xmlns="[^"]+"/
15
+
12
16
  class Test::Unit::TestCase
13
17
  def windows?
14
18
  RbConfig::CONFIG['host_os'] =~ /win|ming/
@@ -18,6 +22,15 @@ class Test::Unit::TestCase
18
22
  "#{windows? ? File.expand_path(__FILE__).split('/').first : nil}/"
19
23
  end
20
24
 
25
+ def empty_document options = {}
26
+ Asciidoctor::Document.new [], options
27
+ end
28
+
29
+ def empty_safe_document options = {}
30
+ options[:safe] = :safe
31
+ Asciidoctor::Document.new [], options
32
+ end
33
+
21
34
  def sample_doc_path(name)
22
35
  name = name.to_s
23
36
  unless name.include?('.')
@@ -36,8 +49,7 @@ class Test::Unit::TestCase
36
49
  end
37
50
 
38
51
  def example_document(name, opts = {})
39
- opts[:header_footer] = true unless opts.has_key?(:header_footer)
40
- Asciidoctor::Document.new(File.readlines(sample_doc_path(name)), opts)
52
+ document_from_string File.read(sample_doc_path(name)), opts
41
53
  end
42
54
 
43
55
  def assert_difference(expression, difference = 1, message = nil, &block)
@@ -69,7 +81,8 @@ class Test::Unit::TestCase
69
81
  doc = xmldoc_from_string content
70
82
  case type
71
83
  when :xpath
72
- results = doc.xpath("#{path.sub('/', './')}")
84
+ namespaces = doc.respond_to?(:root) ? doc.root.namespaces : {}
85
+ results = doc.xpath("#{path.sub('/', './')}", namespaces)
73
86
  when :css
74
87
  results = doc.css(path)
75
88
  end
@@ -115,10 +128,14 @@ class Test::Unit::TestCase
115
128
  end
116
129
 
117
130
  def xmldoc_from_string(content)
118
- match = content.match(/\s*<!DOCTYPE (.*)/)
119
- if !match
120
- doc = Nokogiri::HTML::DocumentFragment.parse(content)
121
- elsif match[1].start_with? 'html'
131
+ doctype_match = content.match(/\s*<!DOCTYPE (.*)/)
132
+ if !doctype_match
133
+ if content.match(RE_XMLNS_ATTRIBUTE)
134
+ doc = Nokogiri::XML::Document.parse(content)
135
+ else
136
+ doc = Nokogiri::HTML::DocumentFragment.parse(content)
137
+ end
138
+ elsif doctype_match[1].start_with? 'html'
122
139
  doc = Nokogiri::HTML::Document.parse(content)
123
140
  else
124
141
  doc = Nokogiri::XML::Document.parse(content)
@@ -126,18 +143,26 @@ class Test::Unit::TestCase
126
143
  end
127
144
 
128
145
  def document_from_string(src, opts = {})
129
- opts[:header_footer] = true unless opts.has_key?(:header_footer)
146
+ assign_default_test_options opts
130
147
  Asciidoctor::Document.new(src.lines.entries, opts)
131
148
  end
132
149
 
133
150
  def block_from_string(src, opts = {})
134
151
  opts[:header_footer] = false
135
- doc = Asciidoctor::Document.new(src.lines.entries, opts)
152
+ doc = document_from_string src, opts
136
153
  doc.blocks.first
137
154
  end
138
155
 
139
156
  def render_string(src, opts = {})
140
- document_from_string(src, opts).render
157
+ keep_namespaces = opts.delete(:keep_namespaces)
158
+ if keep_namespaces
159
+ document_from_string(src, opts).render
160
+ else
161
+ # this is required because nokogiri is ignorant
162
+ result = document_from_string(src, opts).render
163
+ result = result.sub(RE_XMLNS_ATTRIBUTE, '')
164
+ result
165
+ end
141
166
  end
142
167
 
143
168
  def render_embedded_string(src, opts = {})
@@ -150,11 +175,52 @@ class Test::Unit::TestCase
150
175
  [Asciidoctor::Lexer.parse_header_metadata(reader), reader]
151
176
  end
152
177
 
153
- # Expand the character for an entity such as &#8212; so
154
- # it can be used to match in an XPath expression
178
+ def assign_default_test_options(opts)
179
+ opts[:header_footer] = true unless opts.has_key?(:header_footer)
180
+ if opts[:header_footer]
181
+ # don't embed stylesheet unless test requests the default behavior
182
+ if opts.has_key? :linkcss_default
183
+ opts.delete(:linkcss_default)
184
+ else
185
+ opts[:attributes] ||= {}
186
+ opts[:attributes]['linkcss'] = ''
187
+ end
188
+ end
189
+ #opts[:template_dir] = File.join(File.dirname(__FILE__), '..', '..', 'asciidoctor-backends', 'slim')
190
+ nil
191
+ end
192
+
193
+ # Expand the character for an entity such as &#8212; into a glyph
194
+ # so it can be used to match in an XPath expression
195
+ #
196
+ # Examples
197
+ #
198
+ # expand_entity 60
199
+ # # => "<"
200
+ #
201
+ # Returns the String entity expanded to its equivalent UTF-8 glyph
155
202
  def expand_entity(number)
156
203
  [number].pack('U*')
157
204
  end
205
+ alias :entity :expand_entity
206
+
207
+ def invoke_cli_with_filenames(argv = [], filenames = [], &block)
208
+
209
+ filepaths = Array.new
210
+
211
+ filenames.each { |filename|
212
+ if filenames.nil?|| ::Pathname.new(filename).absolute?
213
+ filepaths.push(filename)
214
+ else
215
+ filepaths.push(File.join(File.dirname(__FILE__), 'fixtures', filename))
216
+ end
217
+ }
218
+
219
+ invoker = Asciidoctor::Cli::Invoker.new(argv + filepaths)
220
+
221
+ invoker.invoke!(&block)
222
+ invoker
223
+ end
158
224
 
159
225
  def invoke_cli_to_buffer(argv = [], filename = 'sample.asciidoc', &block)
160
226
  invoke_cli(argv, filename, [StringIO.new, StringIO.new], &block)
@@ -16,8 +16,8 @@ context "Text" do
16
16
 
17
17
  test "proper encoding to handle utf8 characters in document using docbook backend" do
18
18
  output = example_document(:encoding, :attributes => {'backend' => 'docbook'}).render
19
- assert_xpath '//simpara', output, 4
20
- assert_xpath '//ulink', output, 1
19
+ assert_xpath '//xmlns:simpara', output, 4
20
+ assert_xpath '//xmlns:ulink', output, 1
21
21
  end
22
22
 
23
23
  test "proper encoding to handle utf8 characters in embedded document using docbook backend" do
@@ -27,22 +27,22 @@ context "Text" do
27
27
  end
28
28
 
29
29
  # NOTE this test ensures we have the encoding line on block templates too
30
- test "proper encoding to handle utf8 characters in arbitrary block" do
30
+ test 'proper encoding to handle utf8 characters in arbitrary block' do
31
31
  input = []
32
32
  input << "[verse]\n"
33
33
  input.concat(File.readlines(sample_doc_path(:encoding)))
34
- doc = Asciidoctor::Document.new
35
- reader = Asciidoctor::Reader.new(input, doc, true)
34
+ doc = empty_document
35
+ reader = Asciidoctor::PreprocessorReader.new doc, input
36
36
  block = Asciidoctor::Lexer.next_block(reader, doc)
37
37
  assert_xpath '//pre', block.render.gsub(/^\s*\n/, ''), 1
38
38
  end
39
39
 
40
- test "proper encoding to handle utf8 characters from included file" do
40
+ test 'proper encoding to handle utf8 characters from included file' do
41
41
  input = <<-EOS
42
42
  include::fixtures/encoding.asciidoc[tags=romé]
43
43
  EOS
44
- doc = Asciidoctor::Document.new [], :safe => Asciidoctor::SafeMode::SAFE, :base_dir => File.expand_path(File.dirname(__FILE__))
45
- reader = Asciidoctor::Reader.new(input, doc, true)
44
+ doc = empty_safe_document :base_dir => File.expand_path(File.dirname(__FILE__))
45
+ reader = Asciidoctor::PreprocessorReader.new doc, input
46
46
  block = Asciidoctor::Lexer.next_block(reader, doc)
47
47
  output = block.render
48
48
  assert_css '.paragraph', output, 1
@@ -63,9 +63,109 @@ include::fixtures/encoding.asciidoc[tags=romé]
63
63
  assert_match(/&#8216;The New Yorker.&#8217;/, rendered)
64
64
  end
65
65
 
66
- test "separator" do
67
- # for some reason, the html enclosure breaks the xpath //*[@id='content']//hr
68
- assert_xpath "//*[@id='content']//hr", render_string("This is separated.\n\n'''\n\n...from this!"), 1
66
+ test 'horizontal rule' do
67
+ input = <<-EOS
68
+ This line is separated by a horizontal rule...
69
+
70
+ '''
71
+
72
+ ...from this line.
73
+ EOS
74
+ output = render_embedded_string input
75
+ assert_xpath "//hr", output, 1
76
+ assert_xpath "/*[@class='paragraph']", output, 2
77
+ assert_xpath "(/*[@class='paragraph'])[1]/following-sibling::hr", output, 1
78
+ assert_xpath "/hr/following-sibling::*[@class='paragraph']", output, 1
79
+ end
80
+
81
+ test 'markdown horizontal rules' do
82
+ variants = [
83
+ '---',
84
+ '- - -',
85
+ '***',
86
+ '* * *',
87
+ '___',
88
+ '_ _ _'
89
+ ]
90
+
91
+ offsets = [
92
+ '',
93
+ ' ',
94
+ ' ',
95
+ ' '
96
+ ]
97
+
98
+ variants.each do |variant|
99
+ offsets.each do |offset|
100
+ input = <<-EOS
101
+ This line is separated by a horizontal rule...
102
+
103
+ #{offset}#{variant}
104
+
105
+ ...from this line.
106
+ EOS
107
+ output = render_embedded_string input
108
+ assert_xpath "//hr", output, 1
109
+ assert_xpath "/*[@class='paragraph']", output, 2
110
+ assert_xpath "(/*[@class='paragraph'])[1]/following-sibling::hr", output, 1
111
+ assert_xpath "/hr/following-sibling::*[@class='paragraph']", output, 1
112
+ end
113
+ end
114
+ end
115
+
116
+ test 'markdown horizontal rules negative case' do
117
+
118
+ bad_variants = [
119
+ '- - - -',
120
+ '* * * *',
121
+ '_ _ _ _'
122
+ ]
123
+
124
+ good_offsets = [
125
+ '',
126
+ ' ',
127
+ ' ',
128
+ ' '
129
+ ]
130
+
131
+ bad_variants.each do |variant|
132
+ good_offsets.each do |offset|
133
+ input = <<-EOS
134
+ This line is separated something that is not a horizontal rule...
135
+
136
+ #{offset}#{variant}
137
+
138
+ ...from this line.
139
+ EOS
140
+ output = render_embedded_string input
141
+ assert_xpath '//hr', output, 0
142
+ end
143
+ end
144
+
145
+ good_variants = [
146
+ '- - -',
147
+ '* * *',
148
+ '_ _ _'
149
+ ]
150
+
151
+ bad_offsets = [
152
+ "\t",
153
+ ' '
154
+ ]
155
+
156
+ good_variants.each do |variant|
157
+ bad_offsets.each do |offset|
158
+ input = <<-EOS
159
+ This line is separated something that is not a horizontal rule...
160
+
161
+ #{offset}#{variant}
162
+
163
+ ...from this line.
164
+ EOS
165
+ output = render_embedded_string input
166
+ assert_xpath '//hr', output, 0
167
+ end
168
+ end
69
169
  end
70
170
 
71
171
  test "emphasized text" do