asciidoctor 1.5.5 → 1.5.6

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +216 -1
  3. data/CONTRIBUTING.adoc +2 -2
  4. data/Gemfile +20 -1
  5. data/LICENSE.adoc +1 -1
  6. data/README-fr.adoc +4 -3
  7. data/README-jp.adoc +11 -10
  8. data/README-zh_CN.adoc +4 -3
  9. data/README.adoc +17 -202
  10. data/Rakefile +41 -25
  11. data/asciidoctor.gemspec +9 -10
  12. data/data/locale/attributes.adoc +216 -34
  13. data/data/stylesheets/asciidoctor-default.css +23 -16
  14. data/features/step_definitions.rb +15 -19
  15. data/features/xref.feature +584 -20
  16. data/lib/asciidoctor.rb +292 -278
  17. data/lib/asciidoctor/abstract_block.rb +155 -94
  18. data/lib/asciidoctor/abstract_node.rb +108 -94
  19. data/lib/asciidoctor/attribute_list.rb +30 -22
  20. data/lib/asciidoctor/block.rb +7 -7
  21. data/lib/asciidoctor/cli/invoker.rb +47 -34
  22. data/lib/asciidoctor/cli/options.rb +22 -11
  23. data/lib/asciidoctor/converter.rb +3 -3
  24. data/lib/asciidoctor/converter/base.rb +2 -2
  25. data/lib/asciidoctor/converter/composite.rb +1 -1
  26. data/lib/asciidoctor/converter/docbook45.rb +2 -2
  27. data/lib/asciidoctor/converter/docbook5.rb +132 -87
  28. data/lib/asciidoctor/converter/factory.rb +0 -1
  29. data/lib/asciidoctor/converter/html5.rb +116 -98
  30. data/lib/asciidoctor/converter/manpage.rb +51 -52
  31. data/lib/asciidoctor/converter/template.rb +47 -36
  32. data/lib/asciidoctor/core_ext.rb +8 -2
  33. data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +4 -0
  34. data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +6 -0
  35. data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +5 -0
  36. data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +1 -1
  37. data/lib/asciidoctor/core_ext/1.8.7/string/{limit.rb → limit_bytesize.rb} +7 -6
  38. data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +6 -0
  39. data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +1 -1
  40. data/lib/asciidoctor/core_ext/nil_or_empty.rb +5 -5
  41. data/lib/asciidoctor/core_ext/regexp/is_match.rb +3 -0
  42. data/lib/asciidoctor/core_ext/string/{limit.rb → limit_bytesize.rb} +2 -2
  43. data/lib/asciidoctor/document.rb +216 -213
  44. data/lib/asciidoctor/extensions.rb +318 -185
  45. data/lib/asciidoctor/helpers.rb +35 -35
  46. data/lib/asciidoctor/inline.rb +32 -1
  47. data/lib/asciidoctor/list.rb +22 -6
  48. data/lib/asciidoctor/parser.rb +1008 -1038
  49. data/lib/asciidoctor/path_resolver.rb +46 -50
  50. data/lib/asciidoctor/reader.rb +275 -251
  51. data/lib/asciidoctor/section.rb +86 -58
  52. data/lib/asciidoctor/stylesheets.rb +6 -6
  53. data/lib/asciidoctor/substitutors.rb +567 -649
  54. data/lib/asciidoctor/table.rb +163 -108
  55. data/lib/asciidoctor/version.rb +1 -1
  56. data/man/asciidoctor.1 +18 -16
  57. data/man/asciidoctor.adoc +15 -13
  58. data/test/attributes_test.rb +138 -22
  59. data/test/blocks_test.rb +377 -97
  60. data/test/converter_test.rb +13 -0
  61. data/test/document_test.rb +244 -34
  62. data/test/extensions_test.rb +409 -42
  63. data/test/fixtures/asciidoc_index.txt +521 -0
  64. data/test/fixtures/basic-docinfo-footer.html +6 -0
  65. data/test/fixtures/basic-docinfo-footer.xml +8 -0
  66. data/test/fixtures/basic-docinfo.html +1 -0
  67. data/test/fixtures/basic-docinfo.xml +4 -0
  68. data/test/fixtures/basic.asciidoc +5 -0
  69. data/test/fixtures/chapter-a.adoc +3 -0
  70. data/test/fixtures/child-include.adoc +5 -0
  71. data/test/fixtures/circle.svg +9 -0
  72. data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
  73. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
  74. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
  75. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
  76. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
  77. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
  78. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
  79. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
  80. data/test/fixtures/custom-docinfodir/basic-docinfo.html +1 -0
  81. data/test/fixtures/custom-docinfodir/docinfo.html +1 -0
  82. data/test/fixtures/docinfo-footer.html +1 -0
  83. data/test/fixtures/docinfo-footer.xml +9 -0
  84. data/test/fixtures/docinfo.html +1 -0
  85. data/test/fixtures/docinfo.xml +3 -0
  86. data/test/fixtures/dot.gif +0 -0
  87. data/test/fixtures/encoding.asciidoc +13 -0
  88. data/test/fixtures/grandchild-include.adoc +3 -0
  89. data/test/fixtures/hello-asciidoctor.pdf +69 -0
  90. data/test/fixtures/include-file.asciidoc +24 -0
  91. data/test/fixtures/include-file.ml +3 -0
  92. data/test/fixtures/include-file.xml +5 -0
  93. data/test/fixtures/master.adoc +5 -0
  94. data/test/fixtures/mismatched-end-tag.adoc +7 -0
  95. data/test/fixtures/parent-include-restricted.adoc +5 -0
  96. data/test/fixtures/parent-include.adoc +5 -0
  97. data/test/fixtures/sample.asciidoc +26 -0
  98. data/test/fixtures/stylesheets/custom.css +3 -0
  99. data/test/fixtures/subs-docinfo.html +2 -0
  100. data/test/fixtures/subs.adoc +7 -0
  101. data/test/fixtures/tagged-class-enclosed.rb +26 -0
  102. data/test/fixtures/tagged-class.rb +23 -0
  103. data/test/fixtures/tip.gif +0 -0
  104. data/test/invoker_test.rb +82 -4
  105. data/test/links_test.rb +312 -37
  106. data/test/lists_test.rb +204 -25
  107. data/test/manpage_test.rb +191 -4
  108. data/test/options_test.rb +18 -1
  109. data/test/paragraphs_test.rb +32 -7
  110. data/test/parser_test.rb +150 -30
  111. data/test/paths_test.rb +47 -13
  112. data/test/preamble_test.rb +1 -1
  113. data/test/reader_test.rb +366 -126
  114. data/test/sections_test.rb +203 -56
  115. data/test/substitutions_test.rb +339 -131
  116. data/test/tables_test.rb +315 -15
  117. data/test/test_helper.rb +400 -0
  118. data/test/text_test.rb +5 -5
  119. metadata +110 -22
@@ -14,50 +14,20 @@ class Table < AbstractBlock
14
14
  @body = body
15
15
  end
16
16
 
17
- alias :[] :send
17
+ alias [] send
18
+
19
+ # Public: Returns the rows grouped by section.
20
+ #
21
+ # Creates a 2-dimensional array of two element entries. The first element
22
+ # is the section name as a symbol. The second element is the Array of rows
23
+ # in that section. The entries are in document order (head, foot, body).
24
+ #
25
+ # Returns a 2-dimentional Array of rows grouped by section.
26
+ def by_section
27
+ [[:head, @head], [:foot, @foot], [:body, @body]]
28
+ end
18
29
  end
19
30
 
20
- # Public: A String key that specifies the default table format in AsciiDoc (psv)
21
- DEFAULT_DATA_FORMAT = 'psv'
22
-
23
- # Public: An Array of String keys that represent the table formats in AsciiDoc
24
- DATA_FORMATS = ['psv', 'dsv', 'csv']
25
-
26
- # Public: A Hash mapping the AsciiDoc table formats to their default delimiters
27
- DEFAULT_DELIMITERS = {
28
- 'psv' => '|',
29
- 'dsv' => ':',
30
- 'csv' => ','
31
- }
32
-
33
- # Public: A Hash mapping styles abbreviations to styles that can be applied
34
- # to a table column or cell
35
- TEXT_STYLES = {
36
- 'd' => :none,
37
- 's' => :strong,
38
- 'e' => :emphasis,
39
- 'm' => :monospaced,
40
- 'h' => :header,
41
- 'l' => :literal,
42
- 'v' => :verse,
43
- 'a' => :asciidoc
44
- }
45
-
46
- # Public: A Hash mapping alignment abbreviations to alignments (horizontal
47
- # and vertial) that can be applies to a table column or cell
48
- ALIGNMENTS = {
49
- :h => {
50
- '<' => 'left',
51
- '>' => 'right',
52
- '^' => 'center'
53
- },
54
- :v => {
55
- '<' => 'top',
56
- '>' => 'bottom',
57
- '^' => 'middle'
58
- }
59
- }
60
-
61
31
  # Public: Get/Set the columns for this table
62
32
  attr_accessor :columns
63
33
 
@@ -68,6 +38,9 @@ class Table < AbstractBlock
68
38
  # Public: Boolean specifies whether this table has a header row
69
39
  attr_accessor :has_header_option
70
40
 
41
+ # Public: Get the caption for this table
42
+ attr_reader :caption
43
+
71
44
  def initialize parent, attributes
72
45
  super parent, :table
73
46
  @rows = Rows.new
@@ -87,6 +60,7 @@ class Table < AbstractBlock
87
60
  @attributes['tablepcwidth'] = pcwidth_intval
88
61
 
89
62
  if @document.attributes.key? 'pagewidth'
63
+ # FIXME calculate more accurately (only used in DocBook output)
90
64
  @attributes['tableabswidth'] ||=
91
65
  ((@attributes['tablepcwidth'].to_f / 100) * @document.attributes['pagewidth']).round
92
66
  end
@@ -135,12 +109,22 @@ class Table < AbstractBlock
135
109
  @columns.each {|col| total_width += (col_pcwidth = col.assign_width nil, width_base, pf) }
136
110
  else
137
111
  col_pcwidth = ((100 * pf / @columns.size).to_i) / pf
112
+ # or...
113
+ #col_pcwidth = (100.0 / @columns.size).truncate 4
138
114
  col_pcwidth = col_pcwidth.to_i if col_pcwidth.to_i == col_pcwidth
139
115
  @columns.each {|col| total_width += col.assign_width col_pcwidth }
140
116
  end
141
117
 
142
- # donate balance, if any, to final column
143
- @columns[-1].assign_width(((100 - total_width + col_pcwidth) * pf).round / pf) unless total_width == 100
118
+ # donate balance, if any, to final column (using half up rounding)
119
+ unless total_width == 100
120
+ @columns[-1].assign_width(((100 - total_width + col_pcwidth) * pf).round / pf)
121
+ # or (manual half up rounding)...
122
+ #numerator = (raw_numerator = (100 - total_width + col_pcwidth) * pf).to_i
123
+ #numerator += 1 if raw_numerator >= numerator + 0.5
124
+ #@columns[-1].assign_width numerator / pf
125
+ # or...
126
+ #@columns[-1].assign_width((100 - total_width + col_pcwidth).round 4)
127
+ end
144
128
 
145
129
  nil
146
130
  end
@@ -149,7 +133,7 @@ class Table < AbstractBlock
149
133
  # by the options on the table
150
134
  #
151
135
  # returns nothing
152
- def partition_header_footer(attributes)
136
+ def partition_header_footer(attrs)
153
137
  # set rowcount before splitting up body rows
154
138
  @attributes['rowcount'] = @rows.body.size
155
139
 
@@ -164,7 +148,7 @@ class Table < AbstractBlock
164
148
  @rows.head = [head]
165
149
  end
166
150
 
167
- if num_body_rows > 0 && attributes.key?('footer-option')
151
+ if num_body_rows > 0 && attrs.key?('footer-option')
168
152
  @rows.foot = [@rows.body.pop]
169
153
  end
170
154
 
@@ -189,7 +173,7 @@ class Table::Column < AbstractNode
189
173
  end
190
174
 
191
175
  # Public: An alias to the parent block (which is always a Table)
192
- alias :table :parent
176
+ alias table parent
193
177
 
194
178
  # Internal: Calculate and assign the widths (percentage and absolute) for this column
195
179
  #
@@ -199,6 +183,8 @@ class Table::Column < AbstractNode
199
183
  def assign_width col_pcwidth, width_base = nil, pf = 10000.0
200
184
  if width_base
201
185
  col_pcwidth = ((@attributes['width'].to_f / width_base) * 100 * pf).to_i / pf
186
+ # or...
187
+ #col_pcwidth = (@attributes['width'].to_f * 100.0 / width_base).truncate 4
202
188
  col_pcwidth = col_pcwidth.to_i if col_pcwidth.to_i == col_pcwidth
203
189
  end
204
190
  @attributes['colpcwidth'] = col_pcwidth
@@ -222,58 +208,90 @@ class Table::Cell < AbstractNode
222
208
  attr_accessor :rowspan
223
209
 
224
210
  # Public: An alias to the parent block (which is always a Column)
225
- alias :column :parent
211
+ alias column parent
226
212
 
227
213
  # Public: The internal Asciidoctor::Document for a cell that has the asciidoc style
228
214
  attr_reader :inner_document
229
215
 
230
- def initialize column, text, attributes = {}, cursor = nil
216
+ def initialize column, cell_text, attributes = {}, opts = {}
231
217
  super column, :cell
232
- @text = text
233
- @style = nil
234
- @colspan = nil
235
- @rowspan = nil
236
- # TODO feels hacky
237
218
  if column
238
- @style = column.attributes['style']
239
- update_attributes(column.attributes)
219
+ cell_style = (in_header_row = column.table.header_row?) ? nil : column.attributes['style']
220
+ # REVIEW feels hacky to inherit all attributes from column
221
+ update_attributes column.attributes
222
+ else
223
+ in_header_row = cell_style = nil
240
224
  end
241
225
  if attributes
242
- @colspan = attributes.delete('colspan')
243
- @rowspan = attributes.delete('rowspan')
244
- # TODO eventualy remove the style attribute from the attributes hash
245
- #@style = attributes.delete('style') if attributes.key? 'style'
246
- @style = attributes['style'] if attributes.key? 'style'
247
- update_attributes(attributes)
226
+ @colspan = attributes.delete 'colspan'
227
+ @rowspan = attributes.delete 'rowspan'
228
+ # TODO eventually remove the style attribute from the attributes hash
229
+ #cell_style = attributes.delete 'style' unless in_header_row || !(attributes.key? 'style')
230
+ cell_style = attributes['style'] unless in_header_row || !(attributes.key? 'style')
231
+ if opts[:strip_text]
232
+ if cell_style == :literal || cell_style == :verse
233
+ cell_text = cell_text.rstrip
234
+ cell_text = cell_text.slice 1, cell_text.length - 1 while cell_text.start_with? LF
235
+ else
236
+ cell_text = cell_text.strip
237
+ end
238
+ end
239
+ update_attributes attributes
240
+ else
241
+ @colspan = nil
242
+ @rowspan = nil
248
243
  end
249
- # only allow AsciiDoc cells in non-header rows
250
- if @style == :asciidoc && !column.table.header_row?
244
+ # NOTE only true for non-header rows
245
+ if cell_style == :asciidoc
251
246
  # FIXME hide doctitle from nested document; temporary workaround to fix
252
247
  # nested document seeing doctitle and assuming it has its own document title
253
248
  parent_doctitle = @document.attributes.delete('doctitle')
254
249
  # NOTE we need to process the first line of content as it may not have been processed
255
250
  # the included content cannot expect to match conditional terminators in the remaining
256
251
  # lines of table cell content, it must be self-contained logic
257
- inner_document_lines = @text.split(EOL)
258
- unless inner_document_lines.empty? || !inner_document_lines[0].include?('::')
259
- unprocessed_lines = inner_document_lines[0]
260
- processed_lines = PreprocessorReader.new(@document, unprocessed_lines).readlines
261
- if processed_lines != unprocessed_lines
252
+ # QUESTION should we reset cell_text to nil?
253
+ # QUESTION is is faster to check for :: before splitting?
254
+ inner_document_lines = cell_text.split LF, -1
255
+ if (unprocessed_line1 = inner_document_lines[0]).include? '::'
256
+ preprocessed_lines = (PreprocessorReader.new @document, [unprocessed_line1]).readlines
257
+ unless unprocessed_line1 == preprocessed_lines[0] && preprocessed_lines.size < 2
262
258
  inner_document_lines.shift
263
- inner_document_lines.unshift(*processed_lines)
259
+ inner_document_lines.unshift(*preprocessed_lines) unless preprocessed_lines.empty?
264
260
  end
265
- end
266
- @inner_document = Document.new(inner_document_lines, :header_footer => false, :parent => @document, :cursor => cursor)
261
+ end unless inner_document_lines.empty?
262
+ @inner_document = Document.new(inner_document_lines, :header_footer => false, :parent => @document, :cursor => opts[:cursor])
267
263
  @document.attributes['doctitle'] = parent_doctitle unless parent_doctitle.nil?
268
264
  end
265
+ @text = cell_text
266
+ @style = cell_style
269
267
  end
270
268
 
271
- # Public: Get the text with normal substitutions applied for this cell. Used for cells in the head rows
269
+ # Public: Get the String text of this cell with substitutions applied.
270
+ #
271
+ # Used for cells in the head row as well as text-only (non-AsciiDoc) cells in
272
+ # the foot row and body.
273
+ #
274
+ # This method shouldn't be used for cells that have the AsciiDoc style.
275
+ #
276
+ # Returns the converted String text for this Cell
272
277
  def text
273
- apply_normal_subs(@text).strip
278
+ apply_subs @text, (@style == :literal ? BASIC_SUBS : NORMAL_SUBS)
279
+ end
280
+
281
+ # Public: Set the String text.
282
+ #
283
+ # This method shouldn't be used for cells that have the AsciiDoc style.
284
+ #
285
+ # Returns the new String text assigned to this Cell
286
+ def text= val
287
+ @text = val
274
288
  end
275
289
 
276
290
  # Public: Handles the body data (tbody, tfoot), applying styles and partitioning into paragraphs
291
+ #
292
+ # This method should not be used for cells in the head row or that have the literal or verse style.
293
+ #
294
+ # Returns the converted String for this Cell
277
295
  def content
278
296
  if @style == :asciidoc
279
297
  @inner_document.convert
@@ -296,11 +314,24 @@ end
296
314
  # instantiated, the row is closed if the cell satisifies the column count and,
297
315
  # finally, a new buffer is allocated to track the next cell.
298
316
  class Table::ParserContext
317
+ # Public: An Array of String keys that represent the table formats in AsciiDoc
318
+ #--
319
+ # QUESTION should we recognize !sv as a valid format value?
320
+ FORMATS = ['psv', 'csv', 'dsv', 'tsv'].to_set
321
+
322
+ # Public: A Hash mapping the AsciiDoc table formats to default delimiters
323
+ DELIMITERS = {
324
+ 'psv' => ['|', /\|/],
325
+ 'csv' => [',', /,/],
326
+ 'dsv' => [':', /:/],
327
+ 'tsv' => [%(\t), /\t/],
328
+ '!sv' => ['!', /!/]
329
+ }
299
330
 
300
331
  # Public: The Table currently being parsed
301
332
  attr_accessor :table
302
333
 
303
- # Public: The AsciiDoc table format (psv, dsv or csv)
334
+ # Public: The AsciiDoc table format (psv, dsv, or csv)
304
335
  attr_accessor :format
305
336
 
306
337
  # Public: Get the expected column count for a row
@@ -319,25 +350,41 @@ class Table::ParserContext
319
350
  # Public: The cell delimiter compiled Regexp for this table.
320
351
  attr_reader :delimiter_re
321
352
 
322
- def initialize(reader, table, attributes = {})
353
+ def initialize reader, table, attributes = {}
323
354
  @reader = reader
324
355
  @table = table
325
- # TODO if reader.cursor becomes a reference, this would require .dup
356
+ # IMPORTANT if reader.cursor becomes a reference, this assignment would require .dup
326
357
  @last_cursor = reader.cursor
327
- if (@format = attributes['format'])
328
- unless Table::DATA_FORMATS.include? @format
329
- raise %(Illegal table format: #{@format})
358
+
359
+ if attributes.key? 'format'
360
+ if FORMATS.include?(xsv = attributes['format'])
361
+ if xsv == 'tsv'
362
+ # NOTE tsv is just an alias for csv with a tab separator
363
+ @format = 'csv'
364
+ elsif (@format = xsv) == 'psv' && table.document.nested?
365
+ xsv = '!sv'
366
+ end
367
+ else
368
+ warn %(asciidoctor: ERROR: #{reader.prev_line_info}: illegal table format: #{xsv})
369
+ @format, xsv = 'psv', (table.document.nested? ? '!sv' : 'psv')
330
370
  end
331
371
  else
332
- @format = Table::DEFAULT_DATA_FORMAT
372
+ @format, xsv = 'psv', (table.document.nested? ? '!sv' : 'psv')
333
373
  end
334
374
 
335
- @delimiter = if @format == 'psv' && !(attributes.key? 'separator') && table.document.nested?
336
- '!'
375
+ if attributes.key? 'separator'
376
+ if (sep = attributes['separator']).nil_or_empty?
377
+ @delimiter, @delimiter_re = DELIMITERS[xsv]
378
+ # QUESTION should we support any other escape codes or multiple tabs?
379
+ elsif sep == '\t'
380
+ @delimiter, @delimiter_re = DELIMITERS['tsv']
381
+ else
382
+ @delimiter, @delimiter_re = sep, /#{::Regexp.escape sep}/
383
+ end
337
384
  else
338
- attributes['separator'] || Table::DEFAULT_DELIMITERS[@format]
385
+ @delimiter, @delimiter_re = DELIMITERS[xsv]
339
386
  end
340
- @delimiter_re = /#{Regexp.escape @delimiter}/
387
+
341
388
  @colcount = table.columns.empty? ? -1 : table.columns.size
342
389
  @buffer = ''
343
390
  @cellspecs = []
@@ -364,31 +411,36 @@ class Table::ParserContext
364
411
  @delimiter_re.match(line)
365
412
  end
366
413
 
367
- # Public: Skip beyond the matched delimiter because it was a false positive
368
- # (either because it was escaped or in a quoted context)
414
+ # Public: Skip past the matched delimiter because it's inside quoted text.
369
415
  #
370
416
  # returns the String after the match
371
- def skip_matched_delimiter(match, escaped = false)
372
- @buffer = %(#{@buffer}#{escaped ? match.pre_match.chop : match.pre_match}#{@delimiter})
417
+ def skip_past_delimiter(match)
418
+ @buffer = %(#{@buffer}#{match.pre_match}#{@delimiter})
373
419
  match.post_match
374
420
  end
375
421
 
376
- # Public: Determines whether the buffer has unclosed quotes. Used for CSV data.
422
+ # Public: Skip past the matched delimiter because it's escaped.
377
423
  #
378
- # returns true if the buffer has unclosed quotes, false if it doesn't or it
379
- # isn't quoted data
380
- def buffer_has_unclosed_quotes?(append = nil)
381
- record = %(#{@buffer}#{append}).strip
382
- record.start_with?('"') && !record.start_with?('""') && !record.end_with?('"')
424
+ # returns the String after the match
425
+ def skip_past_escaped_delimiter(match)
426
+ @buffer = %(#{@buffer}#{match.pre_match.chop}#{@delimiter})
427
+ match.post_match
383
428
  end
384
429
 
385
- # Public: Determines whether the buffer contains quoted data. Used for CSV data.
430
+ # Public: Determines whether the buffer has unclosed quotes. Used for CSV data.
386
431
  #
387
- # returns true if the buffer starts with a double quote (and not an escaped double quote),
388
- # false otherwise
389
- def buffer_quoted?
390
- @buffer = @buffer.lstrip
391
- @buffer.start_with?('"') && !@buffer.start_with?('""')
432
+ # returns true if the buffer has unclosed quotes, false if it doesn't or it
433
+ # isn't quoted data
434
+ def buffer_has_unclosed_quotes? append = nil
435
+ if (record = append ? (buffer + append).strip : buffer.strip).start_with? '"'
436
+ if ((trailing_quote = record.end_with? '"') && (record.end_with? '""')) || (record.start_with? '""')
437
+ ((record = record.gsub '""', '').start_with? '"') && !(record.end_with? '"')
438
+ else
439
+ !trailing_quote
440
+ end
441
+ else
442
+ false
443
+ end
392
444
  end
393
445
 
394
446
  # Public: Takes a cell spec from the stack. Cell specs precede the delimiter, so a
@@ -460,11 +512,11 @@ class Table::ParserContext
460
512
  #
461
513
  # returns nothing
462
514
  def close_cell(eol = false)
463
- cell_text = @buffer.strip
464
- @buffer = ''
465
515
  if @format == 'psv'
466
- cellspec = take_cellspec
467
- if cellspec
516
+ strip_text = true
517
+ cell_text = @buffer
518
+ @buffer = ''
519
+ if (cellspec = take_cellspec)
468
520
  repeat = cellspec.delete('repeatcol') || 1
469
521
  else
470
522
  warn %(asciidoctor: ERROR: #{@last_cursor.line_info}: table missing leading separator, recovering automatically)
@@ -472,6 +524,9 @@ class Table::ParserContext
472
524
  repeat = 1
473
525
  end
474
526
  else
527
+ strip_text = false
528
+ cell_text = @buffer.strip
529
+ @buffer = ''
475
530
  cellspec = nil
476
531
  repeat = 1
477
532
  if @format == 'csv'
@@ -482,8 +537,8 @@ class Table::ParserContext
482
537
  cell_text = cell_text[1...-1].strip
483
538
  end
484
539
 
485
- # collapses escaped quotes
486
- cell_text = cell_text.tr_s('"', '"')
540
+ # collapse escaped quotes
541
+ cell_text = cell_text.squeeze('"')
487
542
  end
488
543
  end
489
544
  end
@@ -506,7 +561,7 @@ class Table::ParserContext
506
561
  end
507
562
  end
508
563
 
509
- cell = Table::Cell.new(column, cell_text, cellspec, @last_cursor)
564
+ cell = Table::Cell.new(column, cell_text, cellspec, :cursor => @last_cursor, :strip_text => strip_text)
510
565
  @last_cursor = @reader.cursor
511
566
  unless !cell.rowspan || cell.rowspan == 1
512
567
  activate_rowspan(cell.rowspan, (cell.colspan || 1))
@@ -1,3 +1,3 @@
1
1
  module Asciidoctor
2
- VERSION = '1.5.5'
2
+ VERSION = '1.5.6'
3
3
  end
@@ -1,13 +1,13 @@
1
1
  '\" t
2
2
  .\" Title: asciidoctor
3
3
  .\" Author: Dan Allen, Sarah White, Ryan Waldron
4
- .\" Generator: Asciidoctor 1.5.5
5
- .\" Date: 2016-10-05
4
+ .\" Generator: Asciidoctor 1.5.6
5
+ .\" Date: 2017-07-12
6
6
  .\" Manual: Asciidoctor Manual
7
- .\" Source: Asciidoctor 1.5.5
7
+ .\" Source: Asciidoctor 1.5.6
8
8
  .\" Language: English
9
9
  .\"
10
- .TH "ASCIIDOCTOR" "1" "2016-10-05" "Asciidoctor 1.5.5" "Asciidoctor Manual"
10
+ .TH "ASCIIDOCTOR" "1" "2017-07-12" "Asciidoctor 1.5.6" "Asciidoctor Manual"
11
11
  .ie \n(.g .ds Aq \(aq
12
12
  .el .ds Aq '
13
13
  .ss \n[.ss] 0
@@ -35,7 +35,7 @@ If \fIFILE\fP is \fI\-\fP then the AsciiDoc source is read from standard input.
35
35
  .RS 4
36
36
  Base directory containing the document and resources.
37
37
  Defaults to the directory containing the source file, or the working directory if the source is read from a stream.
38
- Can be used as a way to chroot the execution of the program.
38
+ When combined with the safe mode setting, can be used to chroot the execution of the program.
39
39
  .RE
40
40
  .sp
41
41
  \fB\-S, \-\-safe\-mode\fP=\fISAFE_MODE\fP
@@ -48,7 +48,7 @@ If not set, the safe mode level defaults to \fIunsafe\fP when Asciidoctor is inv
48
48
  \fB\-\-safe\fP
49
49
  .RS 4
50
50
  Set safe mode level to \fIsafe\fP.
51
- Enables include macros, but restricts access to ancestor paths of source file.
51
+ Enables include directives, but prevents access to ancestor paths of source file.
52
52
  Provided for compatibility with the asciidoc command.
53
53
  If not set, the safe mode level defaults to \fIunsafe\fP when Asciidoctor is invoked using this script.
54
54
  .RE
@@ -57,7 +57,7 @@ If not set, the safe mode level defaults to \fIunsafe\fP when Asciidoctor is inv
57
57
  \fB\-a, \-\-attribute\fP=\fIATTRIBUTE\fP
58
58
  .RS 4
59
59
  Define, override or delete a document attribute.
60
- Command\-line attributes take precedence over attributes defined in the source file.
60
+ Command\-line attributes take precedence over attributes defined in the source file unless the value ends with \fI@\fP.
61
61
  .sp
62
62
  \fIATTRIBUTE\fP is normally formatted as a key\-value pair, in the form \fINAME=VALUE\fP.
63
63
  Alternate acceptable forms are \fINAME\fP (where the \fIVALUE\fP defaults to an empty string), \fINAME!\fP (unassigns the \fINAME\fP attribute) and \fINAME=VALUE@\fP (where \fIVALUE\fP does not override value of \fINAME\fP attribute if it\(cqs already defined in the source document).
@@ -70,8 +70,8 @@ This option may be specified more than once.
70
70
  .RS 4
71
71
  Backend output file format: \fIhtml5\fP, \fIdocbook5\fP, \fIdocbook45\fP and \fImanpage\fP are supported out of the box.
72
72
  You can also use the backend alias names \fIhtml\fP (aliased to \fIhtml5\fP) or \fIdocbook\fP (aliased to \fIdocbook5\fP).
73
+ Other values can be passed, but if Asciidoctor cannot resolve the backend to a converter, it will fail.
73
74
  Defaults to \fIhtml5\fP.
74
- Other options can be passed, but if Asciidoctor cannot find the backend, it will fail during conversion.
75
75
  .RE
76
76
  .sp
77
77
  \fB\-d, \-\-doctype\fP=\fIDOCTYPE\fP
@@ -79,7 +79,7 @@ Other options can be passed, but if Asciidoctor cannot find the backend, it will
79
79
  Document type: \fIarticle\fP, \fIbook\fP, \fImanpage\fP or \fIinline\fP.
80
80
  Sets the root element when using the \fIdocbook\fP backend and the style class on the HTML body element when using the \fIhtml\fP backend.
81
81
  The \fIbook\fP document type allows multiple level\-0 section titles in a single document.
82
- The \fImanpage\fP document type enables parsing of metadata necessary to produce a manpage.
82
+ The \fImanpage\fP document type enables parsing of metadata necessary to produce a man page.
83
83
  The \fIinline\fP document type allows the content of a single paragraph to be formatted and returned without wrapping it in a containing element.
84
84
  Defaults to \fIarticle\fP.
85
85
  .RE
@@ -129,9 +129,9 @@ Synonym for \fB\-\-attribute numbered\fP.
129
129
  .RS 4
130
130
  Write output to file \fIOUT_FILE\fP.
131
131
  Defaults to the base name of the input file suffixed with \fIbackend\fP extension.
132
- If the input is read from standard input, then the output file defaults to stdout.
133
- If \fIOUT_FILE\fP is \fI\-\fP then the standard output is also used.
134
- If specified, the file is resolved relative to the working directory.
132
+ The file is resolved relative to the working directory.
133
+ If the input is read from standard input or a named pipe (fifo), then the output file defaults to stdout.
134
+ If \fIOUT_FILE\fP is \fI\-\fP, then the output file is written to standard output.
135
135
  .RE
136
136
  .sp
137
137
  \fB\-r, \-\-require\fP=\fILIBRARY\fP
@@ -180,9 +180,11 @@ Display timings information (time to read, parse and convert).
180
180
  .RE
181
181
  .SS "Program Information"
182
182
  .sp
183
- \fB\-h, \-\-help\fP
183
+ \fB\-h, \-\-help\fP [\fITOPIC\fP]
184
184
  .RS 4
185
- Show the help message.
185
+ Print the help message.
186
+ Show the command usage if \fITOPIC\fP is not specified (or not recognized).
187
+ Dump the Asciidoctor man page (in troff/groff format) if \fITOPIC\fP is \fImanpage\fP.
186
188
  .RE
187
189
  .sp
188
190
  \fB\-V, \-\-version\fP
@@ -193,7 +195,7 @@ Print program version number.
193
195
  .RE
194
196
  .SH "ENVIRONMENT"
195
197
  .sp
196
- \fBAsciidoctor\fP honors the SOURCE_DATE_EPOCH environment variable.
198
+ \fBAsciidoctor\fP honors the \fBSOURCE_DATE_EPOCH\fP environment variable.
197
199
  If this variable is assigned an integer value, that value is used as the epoch of all input documents and as the local date and time.
198
200
  See \c
199
201
  .URL "https://reproducible\-builds.org/specs/source\-date\-epoch/" "" " "
@@ -233,7 +235,7 @@ Refer to the \fBAsciidoctor\fP issue tracker at \c
233
235
  .URL "http://discuss.asciidoctor.org" "" ""
234
236
  .SH "COPYING"
235
237
  .sp
236
- Copyright (C) 2012\-2016 Dan Allen, Ryan Waldron and the Asciidoctor Project.
238
+ Copyright (C) 2012\-2017 Dan Allen, Ryan Waldron and the Asciidoctor Project.
237
239
  Free use of this software is granted under the terms of the MIT License.
238
240
  .SH "AUTHOR(S)"
239
241
  .sp