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
@@ -1,5 +1,14 @@
1
1
  module Asciidoctor
2
2
  class AbstractBlock < AbstractNode
3
+ # Public: The types of content that this block can accomodate
4
+ attr_accessor :content_model
5
+
6
+ # Public: Substitutions to be applied to content in this block
7
+ attr_reader :subs
8
+
9
+ # Public: Get/Set the String name of the render template
10
+ attr_accessor :template_name
11
+
3
12
  # Public: Get the Array of Asciidoctor::AbstractBlock sub-blocks for this block
4
13
  attr_reader :blocks
5
14
 
@@ -9,15 +18,22 @@ class AbstractBlock < AbstractNode
9
18
  # Public: Set the String block title.
10
19
  attr_writer :title
11
20
 
21
+ # Public: Get/Set the String style (block type qualifier) for this block.
22
+ attr_accessor :style
23
+
12
24
  # Public: Get/Set the caption for this block
13
25
  attr_accessor :caption
14
26
 
15
27
  def initialize(parent, context)
16
28
  super(parent, context)
29
+ @content_model = :compound
30
+ @subs = []
31
+ @template_name = "block_#{context}"
17
32
  @blocks = []
18
33
  @id = nil
19
34
  @title = nil
20
35
  @caption = nil
36
+ @style = nil
21
37
  if context == :document
22
38
  @level = 0
23
39
  elsif !parent.nil? && !self.is_a?(Section)
@@ -25,7 +41,34 @@ class AbstractBlock < AbstractNode
25
41
  else
26
42
  @level = nil
27
43
  end
28
- @next_section_index = 0
44
+ @next_section_index = 0
45
+ @next_section_number = 1
46
+ end
47
+
48
+ # Public: Get the rendered String content for this Block. If the block
49
+ # has child blocks, the content method should cause them to be
50
+ # rendered and returned as content that can be included in the
51
+ # parent block's template.
52
+ def render
53
+ @document.playback_attributes @attributes
54
+ renderer.render(@template_name, self)
55
+ end
56
+
57
+ # Public: Get an rendered version of the block content, rendering the
58
+ # children appropriate to content model that this block supports.
59
+ def content
60
+ @blocks.map {|b| b.render } * EOL
61
+ end
62
+
63
+ # Public: A convenience method that checks whether the specified
64
+ # substitution is enabled for this block.
65
+ #
66
+ # name - The Symbol substitution name
67
+ #
68
+ # Returns A Boolean indicating whether the specified substitution is
69
+ # enabled for this block
70
+ def sub? name
71
+ @subs.include? name
29
72
  end
30
73
 
31
74
  # Public: A convenience method that indicates whether the title instance
@@ -58,120 +101,49 @@ class AbstractBlock < AbstractNode
58
101
  end
59
102
  end
60
103
 
104
+ # Public: Convenience method that returns the interpreted title of the Block
105
+ # with the caption prepended.
106
+ #
107
+ # Concatenates the value of this Block's caption instance variable and the
108
+ # return value of this Block's title method. No space is added between the
109
+ # two values. If the Block does not have a caption, the interpreted title is
110
+ # returned.
111
+ #
112
+ # Returns the String title prefixed with the caption, or just the title if no
113
+ # caption is set
114
+ def captioned_title
115
+ %(#{@caption}#{title})
116
+ end
117
+
61
118
  # Public: Determine whether this Block contains block content
62
119
  #
63
- # returns Whether this Block has block content
64
- #
65
- #--
66
- # TODO we still need another method that answers
67
- # whether this Block *can* have block content
68
- # that should be the option 'sectionbody'
120
+ # Returns A Boolean indicating whether this Block has block content
69
121
  def blocks?
70
122
  !@blocks.empty?
71
123
  end
72
124
 
73
- # Public: Get the element at i in the array of blocks.
74
- #
75
- # i - The Integer array index number.
76
- #
77
- # section = Section.new
78
- #
79
- # section << 'foo'
80
- # section << 'bar'
81
- # section[1]
82
- # => "bar"
83
- def [](i)
84
- @blocks[i]
85
- end
86
-
87
125
  # Public: Append a content block to this block's list of blocks.
88
126
  #
89
127
  # block - The new child block.
90
128
  #
91
129
  # Examples
92
130
  #
93
- # block = Block.new(parent, :preamble)
131
+ # block = Block.new(parent, :preamble, :content_model => :compound)
94
132
  #
95
- # block << Block.new(block, :paragraph, 'p1')
96
- # block << Block.new(block, :paragraph, 'p2')
97
- # block.blocks
98
- # # => ["p1", "p2"]
133
+ # block << Block.new(block, :paragraph, :source => 'p1')
134
+ # block << Block.new(block, :paragraph, :source => 'p2')
135
+ # block.blocks?
136
+ # # => true
137
+ # block.blocks.size
138
+ # # => 2
99
139
  #
100
140
  # Returns nothing.
101
141
  def <<(block)
102
- if block.is_a?(Section)
103
- assign_index(block)
104
- end
142
+ # parent assignment pending refactor
143
+ #block.parent = self
105
144
  @blocks << block
106
145
  end
107
146
 
108
- # Public: Insert a content block at the specified index in this block's
109
- # list of blocks.
110
- #
111
- # i - The Integer array index number.
112
- # val = The content block to insert.
113
- #
114
- # section = Section.new
115
- #
116
- # section << 'foo'
117
- # section << 'baz'
118
- # section.insert(1, 'bar')
119
- # section.blocks
120
- # ["foo", "bar", "baz"]
121
- def insert(i, block)
122
- @blocks.insert(i, block)
123
- end
124
-
125
- # Public: Delete the element at i in the array of section blocks,
126
- # returning that element or nil if i is out of range.
127
- #
128
- # i - The Integer array index number.
129
- #
130
- # section = Section.new
131
- #
132
- # section << 'foo'
133
- # section << 'bar'
134
- # section.delete_at(1)
135
- # => "bar"
136
- #
137
- # section.blocks
138
- # => ["foo"]
139
- def delete_at(i)
140
- @blocks.delete_at(i)
141
- end
142
-
143
- # Public: Clear this Block's list of blocks.
144
- #
145
- # section = Section.new
146
- #
147
- # section << 'foo'
148
- # section << 'bar'
149
- # section.blocks
150
- # => ["foo", "bar"]
151
- # section.clear_blocks
152
- # section.blocks
153
- # => []
154
- def clear_blocks
155
- @blocks = []
156
- end
157
-
158
- # Public: Get the Integer number of blocks in this block
159
- #
160
- # Examples
161
- #
162
- # section = Section.new
163
- #
164
- # section.size
165
- # => 0
166
- #
167
- # section << 'foo'
168
- # section << 'bar'
169
- # section.size
170
- # => 2
171
- def size
172
- @blocks.size
173
- end
174
-
175
147
  # Public: Get the Array of child Section objects
176
148
  #
177
149
  # Only applies to Document and Section instances
@@ -179,9 +151,13 @@ class AbstractBlock < AbstractNode
179
151
  # Examples
180
152
  #
181
153
  # section = Section.new(parent)
182
- # section << Block.new(section, :paragraph, 'paragraph 1')
154
+ # section << Block.new(section, :paragraph, :source => 'paragraph 1')
183
155
  # section << Section.new(parent)
184
- # section << Block.new(section, :paragraph, 'paragraph 2')
156
+ # section << Block.new(section, :paragraph, :source => 'paragraph 2')
157
+ # section.blocks?
158
+ # # => true
159
+ # section.blocks.size
160
+ # # => 3
185
161
  # section.sections.size
186
162
  # # => 1
187
163
  #
@@ -193,6 +169,55 @@ class AbstractBlock < AbstractNode
193
169
  }
194
170
  end
195
171
 
172
+ # Internal: Lock-in the substitutions for this block
173
+ #
174
+ # Looks for an attribute named "subs". If present, resolves the
175
+ # substitutions and assigns it to the subs property on this block.
176
+ # Otherwise, assigns a set of default substitutions based on the
177
+ # content model of the block.
178
+ #
179
+ # Returns nothing
180
+ def lock_in_subs
181
+ default_subs = []
182
+ case @content_model
183
+ when :simple
184
+ default_subs = SUBS[:normal]
185
+ when :verbatim
186
+ if @context == :listing || (@context == :literal && !(option? 'listparagraph'))
187
+ default_subs = SUBS[:verbatim]
188
+ else
189
+ default_subs = SUBS[:basic]
190
+ end
191
+ when :raw
192
+ default_subs = SUBS[:pass]
193
+ else
194
+ return
195
+ end
196
+
197
+ if (custom_subs = @attributes['subs'])
198
+ @subs = resolve_block_subs custom_subs, @context
199
+ else
200
+ @subs = default_subs.dup
201
+ end
202
+
203
+ # QUESION delegate this logic to method?
204
+ if @context == :listing && @style == 'source' && (@document.basebackend? 'html') &&
205
+ ((highlighter = @document.attributes['source-highlighter']) == 'coderay' ||
206
+ highlighter == 'pygments') && (attr? 'language')
207
+ @subs = @subs.map {|sub| sub == :specialcharacters ? :highlight : sub }
208
+ end
209
+ end
210
+
211
+ # Public: Remove a substitution from this block
212
+ #
213
+ # sub - The Symbol substitution name
214
+ #
215
+ # Returns nothing
216
+ def remove_sub sub
217
+ @subs.delete sub
218
+ nil
219
+ end
220
+
196
221
  # Public: Generate a caption and assign it to this block if one
197
222
  # is not already assigned.
198
223
  #
@@ -215,12 +240,12 @@ class AbstractBlock < AbstractNode
215
240
  end
216
241
 
217
242
  if caption.nil?
218
- if @document.attr?('caption')
219
- @caption = @document.attr('caption')
243
+ if @document.attributes.has_key? 'caption'
244
+ @caption = @document.attributes['caption']
220
245
  elsif title?
221
246
  key ||= @context.to_s
222
247
  caption_key = "#{key}-caption"
223
- if @document.attributes.has_key?(caption_key)
248
+ if @document.attributes.has_key? caption_key
224
249
  caption_title = @document.attributes["#{key}-caption"]
225
250
  caption_num = @document.counter_increment("#{key}-number", self)
226
251
  @caption = "#{caption_title} #{caption_num}. "
@@ -243,6 +268,10 @@ class AbstractBlock < AbstractNode
243
268
  def assign_index(section)
244
269
  section.index = @next_section_index
245
270
  @next_section_index += 1
271
+ if section.numbered
272
+ section.number = @next_section_number
273
+ @next_section_number += 1
274
+ end
246
275
  end
247
276
 
248
277
  # Internal: Reassign the section indexes
@@ -254,6 +283,7 @@ class AbstractBlock < AbstractNode
254
283
  # returns nothing
255
284
  def reindex_sections
256
285
  @next_section_index = 0
286
+ @next_section_number = 0
257
287
  @blocks.each {|block|
258
288
  if block.is_a?(Section)
259
289
  assign_index(block)
@@ -22,19 +22,30 @@ class AbstractNode
22
22
  attr_reader :attributes
23
23
 
24
24
  def initialize(parent, context)
25
- @parent = (context != :document ? parent : nil)
26
-
27
- if !parent.nil?
28
- @document = parent.is_a?(Document) ? parent : parent.document
25
+ # document is a special case, should refer to itself
26
+ if context == :document
27
+ @parent = nil
28
+ @document = parent
29
29
  else
30
- @document = nil
30
+ @parent = parent
31
+ @document = (parent.nil? ? nil : parent.document)
31
32
  end
32
-
33
33
  @context = context
34
34
  @attributes = {}
35
35
  @passthroughs = []
36
36
  end
37
37
 
38
+ # Public: Associate this Block with a new parent Block
39
+ #
40
+ # parent - The Block to set as the parent of this Block
41
+ #
42
+ # Returns nothing
43
+ def parent=(parent)
44
+ @parent = parent
45
+ @document = parent.document
46
+ nil
47
+ end
48
+
38
49
  # Public: Get the value of the specified attribute
39
50
  #
40
51
  # Get the value for the specified attribute. First look in the attributes on
@@ -53,11 +64,10 @@ class AbstractNode
53
64
  def attr(name, default = nil, inherit = true)
54
65
  name = name.to_s if name.is_a?(Symbol)
55
66
  inherit = false if self == @document
56
- if !inherit
57
- default.nil? ? @attributes[name] : @attributes.fetch(name, default)
67
+ if inherit
68
+ @attributes[name] || @document.attributes[name] || default
58
69
  else
59
- default.nil? ? @attributes.fetch(name, @document.attr(name)) :
60
- @attributes.fetch(name, @document.attr(name, default))
70
+ @attributes[name] || default
61
71
  end
62
72
  end
63
73
 
@@ -82,21 +92,11 @@ class AbstractNode
82
92
  name = name.to_s if name.is_a?(Symbol)
83
93
  inherit = false if self == @document
84
94
  if expect.nil?
85
- if @attributes.has_key? name
86
- true
87
- elsif inherit
88
- @document.attributes.has_key? name
89
- else
90
- false
91
- end
95
+ @attributes.has_key?(name) || (inherit && @document.attributes.has_key?(name))
96
+ elsif inherit
97
+ expect == (@attributes[name] || @document.attributes[name])
92
98
  else
93
- if @attributes.has_key? name
94
- @attributes[name] == expect
95
- elsif inherit && @document.attributes.has_key?(name)
96
- @document.attributes[name] == expect
97
- else
98
- false
99
- end
99
+ expect == @attributes[name]
100
100
  end
101
101
  end
102
102
 
@@ -121,6 +121,29 @@ class AbstractNode
121
121
  end
122
122
  end
123
123
 
124
+ # TODO document me
125
+ def set_option(name)
126
+ if @attributes.has_key? 'options'
127
+ @attributes['options'] = "#{@attributes['options']},#{name}"
128
+ else
129
+ @attributes['options'] = name
130
+ end
131
+ @attributes["#{name}-option"] = ''
132
+ end
133
+
134
+ # Public: A convenience method to check if the specified option attribute is
135
+ # enabled on the current node.
136
+ #
137
+ # Check if the option is enabled. This method simply checks to see if the
138
+ # {name}-option attribute is defined on the current node.
139
+ #
140
+ # name - the String or Symbol name of the option
141
+ #
142
+ # return a Boolean indicating whether the option has been specified
143
+ def option?(name)
144
+ @attributes.has_key? "#{name}-option"
145
+ end
146
+
124
147
  # Public: Get the execution context of this object (via Kernel#binding).
125
148
  #
126
149
  # This method is used to set the 'self' reference as well as local variables
@@ -161,6 +184,49 @@ class AbstractNode
161
184
  @document.renderer
162
185
  end
163
186
 
187
+ # Public: A convenience method that checks if the role attribute is specified
188
+ def role?(expect = nil)
189
+ if expect.nil?
190
+ @attributes.has_key?('role') || @document.attributes.has_key?('role')
191
+ else
192
+ expect == (@attributes['role'] || @document.attributes['role'])
193
+ end
194
+ end
195
+
196
+ # Public: A convenience method that returns the value of the role attribute
197
+ def role
198
+ @attributes['role'] || @document.attributes['role']
199
+ end
200
+
201
+ # Public: A convenience method that checks if the specified role is present
202
+ # in the list of roles on this node
203
+ def has_role?(name)
204
+ if (val = (@attributes['role'] || @document.attributes['role']))
205
+ val.split(' ').include?(name)
206
+ else
207
+ false
208
+ end
209
+ end
210
+
211
+ # Public: A convenience method that returns the role names as an Array
212
+ def roles
213
+ if (val = (@attributes['role'] || @document.attributes['role']))
214
+ val.split(' ')
215
+ else
216
+ []
217
+ end
218
+ end
219
+
220
+ # Public: A convenience method that checks if the reftext attribute is specified
221
+ def reftext?
222
+ @attributes.has_key?('reftext') || @document.attributes.has_key?('reftext')
223
+ end
224
+
225
+ # Public: A convenience method that returns the value of the reftext attribute
226
+ def reftext
227
+ @attributes['reftext'] || @document.attributes['reftext']
228
+ end
229
+
164
230
  # Public: Construct a reference or data URI to an icon image for the
165
231
  # specified icon name.
166
232
  #
@@ -269,7 +335,7 @@ class AbstractNode
269
335
  end
270
336
 
271
337
  if !File.readable? image_path
272
- puts "asciidoctor: WARNING: image to embed not found or not readable: #{image_path}"
338
+ warn "asciidoctor: WARNING: image to embed not found or not readable: #{image_path}"
273
339
  return "data:#{mimetype}:base64,"
274
340
  #return 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
275
341
  end
@@ -297,7 +363,7 @@ class AbstractNode
297
363
  if File.readable? path
298
364
  File.read(path).chomp
299
365
  else
300
- puts "asciidoctor: WARNING: file does not exist or cannot be read: #{path}" if warn_on_failure
366
+ warn "asciidoctor: WARNING: file does not exist or cannot be read: #{path}" if warn_on_failure
301
367
  nil
302
368
  end
303
369
  end
@@ -358,5 +424,21 @@ class AbstractNode
358
424
  :target_name => asset_name, :recover => autocorrect)
359
425
  end
360
426
 
427
+ # Public: Calculate the relative path to this absolute filename from the Document#base_dir
428
+ def relative_path(filename)
429
+ PathResolver.new.relative_path filename, @document.base_dir
430
+ end
431
+
432
+ # Public: Retrieve the list marker keyword for the specified list type.
433
+ #
434
+ # For use in the HTML type attribute.
435
+ #
436
+ # list_type - the type of list; default to the @style if not specified
437
+ #
438
+ # returns the single-character String keyword that represents the marker for the specified list type
439
+ def list_marker_keyword(list_type = nil)
440
+ ORDERED_LIST_KEYWORDS[list_type || @style]
441
+ end
442
+
361
443
  end
362
444
  end