asciidoctor 2.0.10 → 2.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +89 -5
  3. data/LICENSE +1 -1
  4. data/README-de.adoc +4 -4
  5. data/README-fr.adoc +4 -4
  6. data/README-jp.adoc +4 -4
  7. data/README-zh_CN.adoc +6 -6
  8. data/README.adoc +14 -10
  9. data/asciidoctor.gemspec +3 -3
  10. data/data/locale/attributes-ar.adoc +4 -3
  11. data/data/locale/attributes-bg.adoc +4 -3
  12. data/data/locale/attributes-ca.adoc +6 -5
  13. data/data/locale/attributes-cs.adoc +4 -3
  14. data/data/locale/attributes-da.adoc +6 -5
  15. data/data/locale/attributes-de.adoc +4 -4
  16. data/data/locale/attributes-en.adoc +4 -4
  17. data/data/locale/attributes-es.adoc +6 -5
  18. data/data/locale/attributes-fa.adoc +4 -3
  19. data/data/locale/attributes-fi.adoc +4 -3
  20. data/data/locale/attributes-fr.adoc +6 -5
  21. data/data/locale/attributes-hu.adoc +4 -3
  22. data/data/locale/attributes-id.adoc +4 -3
  23. data/data/locale/attributes-it.adoc +4 -3
  24. data/data/locale/attributes-ja.adoc +4 -3
  25. data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
  26. data/data/locale/attributes-nb.adoc +4 -3
  27. data/data/locale/attributes-nl.adoc +4 -3
  28. data/data/locale/attributes-nn.adoc +4 -3
  29. data/data/locale/attributes-pl.adoc +8 -7
  30. data/data/locale/attributes-pt.adoc +6 -5
  31. data/data/locale/attributes-pt_BR.adoc +6 -5
  32. data/data/locale/attributes-ro.adoc +4 -3
  33. data/data/locale/attributes-ru.adoc +6 -5
  34. data/data/locale/attributes-sr.adoc +4 -4
  35. data/data/locale/attributes-sr_Latn.adoc +4 -4
  36. data/data/locale/attributes-sv.adoc +4 -4
  37. data/data/locale/attributes-tr.adoc +4 -3
  38. data/data/locale/attributes-uk.adoc +6 -5
  39. data/data/locale/attributes-zh_CN.adoc +4 -3
  40. data/data/locale/attributes-zh_TW.adoc +4 -3
  41. data/data/stylesheets/asciidoctor-default.css +22 -20
  42. data/lib/asciidoctor.rb +33 -7
  43. data/lib/asciidoctor/abstract_block.rb +9 -4
  44. data/lib/asciidoctor/abstract_node.rb +16 -6
  45. data/lib/asciidoctor/attribute_list.rb +59 -67
  46. data/lib/asciidoctor/cli/invoker.rb +2 -0
  47. data/lib/asciidoctor/cli/options.rb +3 -3
  48. data/lib/asciidoctor/convert.rb +167 -162
  49. data/lib/asciidoctor/converter.rb +13 -12
  50. data/lib/asciidoctor/converter/docbook5.rb +3 -7
  51. data/lib/asciidoctor/converter/html5.rb +59 -42
  52. data/lib/asciidoctor/converter/manpage.rb +12 -11
  53. data/lib/asciidoctor/converter/template.rb +3 -0
  54. data/lib/asciidoctor/document.rb +23 -38
  55. data/lib/asciidoctor/extensions.rb +2 -2
  56. data/lib/asciidoctor/helpers.rb +17 -9
  57. data/lib/asciidoctor/load.rb +101 -101
  58. data/lib/asciidoctor/parser.rb +26 -23
  59. data/lib/asciidoctor/path_resolver.rb +14 -12
  60. data/lib/asciidoctor/reader.rb +14 -7
  61. data/lib/asciidoctor/rx.rb +5 -4
  62. data/lib/asciidoctor/substitutors.rb +57 -38
  63. data/lib/asciidoctor/syntax_highlighter.rb +8 -5
  64. data/lib/asciidoctor/syntax_highlighter/coderay.rb +1 -1
  65. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +12 -4
  66. data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
  67. data/lib/asciidoctor/syntax_highlighter/pygments.rb +2 -3
  68. data/lib/asciidoctor/syntax_highlighter/rouge.rb +15 -7
  69. data/lib/asciidoctor/table.rb +49 -20
  70. data/lib/asciidoctor/version.rb +1 -1
  71. data/man/asciidoctor.1 +6 -6
  72. data/man/asciidoctor.adoc +3 -3
  73. metadata +8 -8
@@ -425,7 +425,7 @@ module Extensions
425
425
  # TIP: Postprocessors can also be used to relocate assets needed by the published
426
426
  # document.
427
427
  #
428
- # Postprocessor implementations must Postprocessor.
428
+ # Postprocessor implementations must extend Postprocessor.
429
429
  class Postprocessor < Processor
430
430
  def process document, output
431
431
  raise ::NotImplementedError, %(#{Postprocessor} subclass #{self.class} must implement the ##{__method__} method)
@@ -610,7 +610,7 @@ module Extensions
610
610
  #--
611
611
  # TODO break this out into different pattern types
612
612
  # for example, FullInlineMacro, ShortInlineMacro (no target) and other patterns
613
- # FIXME for inline passthrough, we need to have some way to specify the text as a passthrough
613
+ # FIXME for inline macro, we need to have some way to specify the text as a passthrough
614
614
  class InlineMacroProcessor < MacroProcessor
615
615
  @@rx_cache = {}
616
616
 
@@ -56,25 +56,27 @@ module Helpers
56
56
  # If a BOM is found at the beginning of the data, a best attempt is made to
57
57
  # encode it to UTF-8 from the specified source encoding.
58
58
  #
59
- # data - the source data Array to prepare (no nil entries allowed)
59
+ # data - the source data Array to prepare (no nil entries allowed)
60
+ # trim_end - whether to trim whitespace from the end of each line;
61
+ # (true cleans all whitespace; false only removes trailing newline) (default: true)
60
62
  #
61
63
  # returns a String Array of prepared lines
62
- def prepare_source_array data
64
+ def prepare_source_array data, trim_end = true
63
65
  return [] if data.empty?
64
66
  if (leading_2_bytes = (leading_bytes = (first = data[0]).unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
65
67
  data[0] = first.byteslice 2, first.bytesize
66
68
  # NOTE you can't split a UTF-16LE string using .lines when encoding is UTF-8; doing so will cause this line to fail
67
- return data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).rstrip }
69
+ return trim_end ? data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).rstrip } : data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).chomp }
68
70
  elsif leading_2_bytes == BOM_BYTES_UTF_16BE
69
71
  data[0] = first.byteslice 2, first.bytesize
70
- return data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).rstrip }
72
+ return trim_end ? data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).rstrip } : data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).chomp }
71
73
  elsif leading_bytes == BOM_BYTES_UTF_8
72
74
  data[0] = first.byteslice 3, first.bytesize
73
75
  end
74
76
  if first.encoding == UTF_8
75
- data.map {|line| line.rstrip }
77
+ trim_end ? data.map {|line| line.rstrip } : data.map {|line| line.chomp }
76
78
  else
77
- data.map {|line| (line.encode UTF_8).rstrip }
79
+ trim_end ? data.map {|line| (line.encode UTF_8).rstrip } : data.map {|line| (line.encode UTF_8).chomp }
78
80
  end
79
81
  end
80
82
 
@@ -86,10 +88,12 @@ module Helpers
86
88
  # If a BOM is found at the beginning of the data, a best attempt is made to
87
89
  # encode it to UTF-8 from the specified source encoding.
88
90
  #
89
- # data - the source data String to prepare
91
+ # data - the source data String to prepare
92
+ # trim_end - whether to trim whitespace from the end of each line;
93
+ # (true cleans all whitespace; false only removes trailing newline) (default: true)
90
94
  #
91
95
  # returns a String Array of prepared lines
92
- def prepare_source_string data
96
+ def prepare_source_string data, trim_end = true
93
97
  return [] if data.nil_or_empty?
94
98
  if (leading_2_bytes = (leading_bytes = data.unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
95
99
  data = (data.byteslice 2, data.bytesize).encode UTF_8, ::Encoding::UTF_16LE
@@ -101,7 +105,11 @@ module Helpers
101
105
  elsif data.encoding != UTF_8
102
106
  data = data.encode UTF_8
103
107
  end
104
- [].tap {|lines| data.each_line {|line| lines << line.rstrip } }
108
+ if trim_end
109
+ [].tap {|lines| data.each_line {|line| lines << line.rstrip } }
110
+ else
111
+ [].tap {|lines| data.each_line {|line| lines << line.chomp } }
112
+ end
105
113
  end
106
114
 
107
115
  # Internal: Efficiently checks whether the specified String resembles a URI
@@ -1,117 +1,117 @@
1
1
  module Asciidoctor
2
- module_function
2
+ class << self
3
+ # Public: Parse the AsciiDoc source input into a {Document}
4
+ #
5
+ # Accepts input as an IO (or StringIO), String or String Array object. If the
6
+ # input is a File, the object is expected to be opened for reading and is not
7
+ # closed afterwards by this method. Information about the file (filename,
8
+ # directory name, etc) gets assigned to attributes on the Document object.
9
+ #
10
+ # input - the AsciiDoc source as a IO, String or Array.
11
+ # options - a String, Array or Hash of options to control processing (default: {})
12
+ # String and Array values are converted into a Hash.
13
+ # See {Document#initialize} for details about these options.
14
+ #
15
+ # Returns the Document
16
+ def load input, options = {}
17
+ options = options.merge
3
18
 
4
- # Public: Parse the AsciiDoc source input into a {Document}
5
- #
6
- # Accepts input as an IO (or StringIO), String or String Array object. If the
7
- # input is a File, the object is expected to be opened for reading and is not
8
- # closed afterwards by this method. Information about the file (filename,
9
- # directory name, etc) gets assigned to attributes on the Document object.
10
- #
11
- # input - the AsciiDoc source as a IO, String or Array.
12
- # options - a String, Array or Hash of options to control processing (default: {})
13
- # String and Array values are converted into a Hash.
14
- # See {Document#initialize} for details about these options.
15
- #
16
- # Returns the Document
17
- def load input, options = {}
18
- options = options.merge
19
-
20
- if (timings = options[:timings])
21
- timings.start :read
22
- end
19
+ if (timings = options[:timings])
20
+ timings.start :read
21
+ end
23
22
 
24
- if (logger = options[:logger]) && logger != LoggerManager.logger
25
- LoggerManager.logger = logger
26
- end
23
+ if (logger = options[:logger]) && logger != LoggerManager.logger
24
+ LoggerManager.logger = logger
25
+ end
27
26
 
28
- if !(attrs = options[:attributes])
29
- attrs = {}
30
- elsif ::Hash === attrs
31
- attrs = attrs.merge
32
- elsif (defined? ::Java::JavaUtil::Map) && ::Java::JavaUtil::Map === attrs
33
- attrs = attrs.dup
34
- elsif ::Array === attrs
35
- attrs = {}.tap do |accum|
36
- attrs.each do |entry|
37
- k, _, v = entry.partition '='
38
- accum[k] = v
27
+ if !(attrs = options[:attributes])
28
+ attrs = {}
29
+ elsif ::Hash === attrs
30
+ attrs = attrs.merge
31
+ elsif (defined? ::Java::JavaUtil::Map) && ::Java::JavaUtil::Map === attrs
32
+ attrs = attrs.dup
33
+ elsif ::Array === attrs
34
+ attrs = {}.tap do |accum|
35
+ attrs.each do |entry|
36
+ k, _, v = entry.partition '='
37
+ accum[k] = v
38
+ end
39
39
  end
40
- end
41
- elsif ::String === attrs
42
- # condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null
43
- attrs = {}.tap do |accum|
44
- attrs.gsub(SpaceDelimiterRx, '\1' + NULL).gsub(EscapedSpaceRx, '\1').split(NULL).each do |entry|
45
- k, _, v = entry.partition '='
46
- accum[k] = v
40
+ elsif ::String === attrs
41
+ # condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null
42
+ attrs = {}.tap do |accum|
43
+ attrs.gsub(SpaceDelimiterRx, '\1' + NULL).gsub(EscapedSpaceRx, '\1').split(NULL).each do |entry|
44
+ k, _, v = entry.partition '='
45
+ accum[k] = v
46
+ end
47
47
  end
48
+ elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
49
+ # coerce attrs to a real Hash
50
+ attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } }
51
+ else
52
+ raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '})
48
53
  end
49
- elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
50
- # coerce attrs to a real Hash
51
- attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } }
52
- else
53
- raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '})
54
- end
55
54
 
56
- if ::File === input
57
- options[:input_mtime] = input.mtime
58
- # NOTE defer setting infile and indir until we get a better sense of their purpose
59
- # TODO cli checks if input path can be read and is file, but might want to add check to API too
60
- attrs['docfile'] = input_path = ::File.absolute_path input.path
61
- attrs['docdir'] = ::File.dirname input_path
62
- attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = Helpers.extname input_path)
63
- source = input.read
64
- elsif input.respond_to? :read
65
- # NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either
66
- # just fail the rewind operation silently to handle all cases
67
- input.rewind rescue nil
68
- source = input.read
69
- elsif ::String === input
70
- source = input
71
- elsif ::Array === input
72
- source = input.drop 0
73
- elsif input
74
- raise ::ArgumentError, %(unsupported input type: #{input.class})
75
- end
55
+ if ::File === input
56
+ options[:input_mtime] = input.mtime
57
+ # NOTE defer setting infile and indir until we get a better sense of their purpose
58
+ # TODO cli checks if input path can be read and is file, but might want to add check to API too
59
+ attrs['docfile'] = input_path = ::File.absolute_path input.path
60
+ attrs['docdir'] = ::File.dirname input_path
61
+ attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = Helpers.extname input_path)
62
+ source = input.read
63
+ elsif input.respond_to? :read
64
+ # NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either
65
+ # just fail the rewind operation silently to handle all cases
66
+ input.rewind rescue nil
67
+ source = input.read
68
+ elsif ::String === input
69
+ source = input
70
+ elsif ::Array === input
71
+ source = input.drop 0
72
+ elsif input
73
+ raise ::ArgumentError, %(unsupported input type: #{input.class})
74
+ end
76
75
 
77
- if timings
78
- timings.record :read
79
- timings.start :parse
80
- end
76
+ if timings
77
+ timings.record :read
78
+ timings.start :parse
79
+ end
81
80
 
82
- options[:attributes] = attrs
83
- doc = options[:parse] == false ? (Document.new source, options) : (Document.new source, options).parse
81
+ options[:attributes] = attrs
82
+ doc = options[:parse] == false ? (Document.new source, options) : (Document.new source, options).parse
84
83
 
85
- timings.record :parse if timings
86
- doc
87
- rescue => ex
88
- begin
89
- context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
90
- if ex.respond_to? :exception
91
- # The original message must be explicitly preserved when wrapping a Ruby exception
92
- wrapped_ex = ex.exception %(#{context} - #{ex.message})
93
- # JRuby automatically sets backtrace; MRI did not until 2.6
94
- wrapped_ex.set_backtrace ex.backtrace
95
- else
96
- # Likely a Java exception class
97
- wrapped_ex = ex.class.new context, ex
98
- wrapped_ex.stack_trace = ex.stack_trace
84
+ timings.record :parse if timings
85
+ doc
86
+ rescue => ex
87
+ begin
88
+ context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
89
+ if ex.respond_to? :exception
90
+ # The original message must be explicitly preserved when wrapping a Ruby exception
91
+ wrapped_ex = ex.exception %(#{context} - #{ex.message})
92
+ # JRuby automatically sets backtrace; MRI did not until 2.6
93
+ wrapped_ex.set_backtrace ex.backtrace
94
+ else
95
+ # Likely a Java exception class
96
+ wrapped_ex = ex.class.new context, ex
97
+ wrapped_ex.stack_trace = ex.stack_trace
98
+ end
99
+ rescue
100
+ wrapped_ex = ex
99
101
  end
100
- rescue
101
- wrapped_ex = ex
102
+ raise wrapped_ex
102
103
  end
103
- raise wrapped_ex
104
- end
105
104
 
106
- # Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
107
- #
108
- # input - the String AsciiDoc source filename
109
- # options - a String, Array or Hash of options to control processing (default: {})
110
- # String and Array values are converted into a Hash.
111
- # See Asciidoctor::Document#initialize for details about options.
112
- #
113
- # Returns the Asciidoctor::Document
114
- def load_file filename, options = {}
115
- ::File.open(filename, FILE_READ_MODE) {|file| load file, options }
105
+ # Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
106
+ #
107
+ # input - the String AsciiDoc source filename
108
+ # options - a String, Array or Hash of options to control processing (default: {})
109
+ # String and Array values are converted into a Hash.
110
+ # See Asciidoctor::Document#initialize for details about options.
111
+ #
112
+ # Returns the Asciidoctor::Document
113
+ def load_file filename, options = {}
114
+ ::File.open(filename, FILE_READ_MODE) {|file| load file, options }
115
+ end
116
116
  end
117
117
  end
@@ -433,8 +433,10 @@ class Parser
433
433
  # is treated like an untitled section
434
434
  elsif preamble # implies parent == document
435
435
  if preamble.blocks?
436
+ if book || document.blocks[1] || !Compliance.unwrap_standalone_preamble
437
+ preamble.source_location = preamble.blocks[0].source_location if document.sourcemap
436
438
  # unwrap standalone preamble (i.e., document has no sections) except for books, if permissible
437
- unless book || document.blocks[1] || !Compliance.unwrap_standalone_preamble
439
+ else
438
440
  document.blocks.shift
439
441
  while (child_block = preamble.blocks.shift)
440
442
  document << child_block
@@ -858,6 +860,7 @@ class Parser
858
860
  when :literal
859
861
  block = build_block(block_context, :verbatim, terminator, parent, reader, attributes)
860
862
  when :example
863
+ attributes['caption'] = '' if attributes['collapsible-option']
861
864
  block = build_block(block_context, :compound, terminator, parent, reader, attributes)
862
865
  when :quote, :verse
863
866
  AttributeList.rekey(attributes, [nil, 'attribution', 'citetitle'])
@@ -899,7 +902,7 @@ class Parser
899
902
  # FIXME title and caption should be assigned when block is constructed (though we need to handle all cases)
900
903
  if attributes['title']
901
904
  block.title = block_title = attributes.delete 'title'
902
- if (caption_attr_name = CAPTION_ATTR_NAMES[block.context]) && document.attributes[caption_attr_name]
905
+ if (caption_attr_name = CAPTION_ATTRIBUTE_NAMES[block.context]) && document.attributes[caption_attr_name]
903
906
  block.assign_caption (attributes.delete 'caption')
904
907
  end
905
908
  end
@@ -1148,14 +1151,16 @@ class Parser
1148
1151
  def self.catalog_inline_anchors text, block, document, reader
1149
1152
  text.scan InlineAnchorScanRx do
1150
1153
  if (id = $1)
1151
- if (reftext = $2)
1152
- next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
1153
- end
1154
+ next if (reftext = $2) && (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
1154
1155
  else
1155
1156
  id = $3
1156
1157
  if (reftext = $4)
1157
- reftext = reftext.gsub '\]', ']' if reftext.include? ']'
1158
- next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
1158
+ if reftext.include? ']'
1159
+ reftext = reftext.gsub '\]', ']'
1160
+ reftext = document.sub_attributes reftext if reftext.include? ATTR_REF_HEAD
1161
+ elsif (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
1162
+ next
1163
+ end
1159
1164
  end
1160
1165
  end
1161
1166
  unless document.register :refs, [id, (Inline.new block, :anchor, reftext, type: :ref, id: id)]
@@ -2119,6 +2124,8 @@ class Parser
2119
2124
  name = 'sectnums'
2120
2125
  elsif name == 'hardbreaks'
2121
2126
  name = 'hardbreaks-option'
2127
+ elsif name == 'showtitle'
2128
+ store_attribute 'notitle', (value ? nil : ''), doc, attrs
2122
2129
  end
2123
2130
 
2124
2131
  if doc
@@ -2273,9 +2280,14 @@ class Parser
2273
2280
  end
2274
2281
 
2275
2282
  skipped = table_reader.skip_blank_lines || 0
2283
+ if attributes['header-option']
2284
+ table.has_header_option = true
2285
+ elsif skipped == 0 && !attributes['noheader-option']
2286
+ # NOTE: assume table has header until we know otherwise; if it doesn't (nil), cells in first row get reprocessed
2287
+ table.has_header_option = implicit_header = true
2288
+ end
2276
2289
  parser_ctx = Table::ParserContext.new table_reader, table, attributes
2277
2290
  format, loop_idx, implicit_header_boundary = parser_ctx.format, -1, nil
2278
- implicit_header = true unless skipped > 0 || attributes['header-option'] || attributes['noheader-option']
2279
2291
 
2280
2292
  while (line = table_reader.read_line)
2281
2293
  if (beyond_first = (loop_idx += 1) > 0) && line.empty?
@@ -2295,7 +2307,7 @@ class Parser
2295
2307
  implicit_header_boundary = nil if implicit_header_boundary
2296
2308
  # otherwise, the cell continues from previous line
2297
2309
  elsif implicit_header_boundary && implicit_header_boundary == loop_idx
2298
- implicit_header, implicit_header_boundary = false, nil
2310
+ table.has_header_option = implicit_header = implicit_header_boundary = nil
2299
2311
  end
2300
2312
  end
2301
2313
  end
@@ -2307,7 +2319,7 @@ class Parser
2307
2319
  if table_reader.has_more_lines? && table_reader.peek_line.empty?
2308
2320
  implicit_header_boundary = 1
2309
2321
  else
2310
- implicit_header = false
2322
+ table.has_header_option = implicit_header = nil
2311
2323
  end
2312
2324
  end
2313
2325
  end
@@ -2358,7 +2370,7 @@ class Parser
2358
2370
  case format
2359
2371
  when 'csv'
2360
2372
  if parser_ctx.buffer_has_unclosed_quotes?
2361
- implicit_header, implicit_header_boundary = false, nil if implicit_header_boundary && loop_idx == 0
2373
+ table.has_header_option = implicit_header = implicit_header_boundary = nil if implicit_header_boundary && loop_idx == 0
2362
2374
  parser_ctx.keep_cell_open
2363
2375
  else
2364
2376
  parser_ctx.close_cell true
@@ -2380,15 +2392,8 @@ class Parser
2380
2392
  end
2381
2393
  end
2382
2394
 
2383
- unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
2384
- table.assign_column_widths
2385
- end
2386
-
2387
- if implicit_header
2388
- table.has_header_option = true
2389
- attributes['header-option'] = ''
2390
- end
2391
-
2395
+ table.assign_column_widths unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
2396
+ attributes['header-option'] = '' if implicit_header
2392
2397
  table.partition_header_footer attributes
2393
2398
 
2394
2399
  table
@@ -2579,9 +2584,7 @@ class Parser
2579
2584
  attributes['role'] = (existing_role = attributes['role']).nil_or_empty? ? (parsed_attrs[:role].join ' ') : %(#{existing_role} #{parsed_attrs[:role].join ' '})
2580
2585
  end
2581
2586
 
2582
- if parsed_attrs.key? :option
2583
- (opts = parsed_attrs[:option]).each {|opt| attributes[%(#{opt}-option)] = '' }
2584
- end
2587
+ parsed_attrs[:option].each {|opt| attributes[%(#{opt}-option)] = '' } if parsed_attrs.key? :option
2585
2588
 
2586
2589
  parsed_style
2587
2590
  else
@@ -331,11 +331,12 @@ class PathResolver
331
331
 
332
332
  # Public: Securely resolve a system path
333
333
  #
334
- # Resolve a system path from the target relative to the start path, jail path, or working
335
- # directory (specified in the constructor), in that order. If a jail path is specified, enforce
336
- # that the resolved path descends from the jail path. If a jail path is not provided, the resolved
337
- # path may be any location on the system. If the resolved path is absolute, use it as is (unless
338
- # it breaches the jail path). Expand all parent and self references in the resolved path.
334
+ # Resolves the target to an absolute path on the current filesystem. The target is assumed to be
335
+ # relative to the start path, jail path, or working directory (specified in the constructor), in
336
+ # that order. If a jail path is specified, the resolved path is forced to descend from the jail
337
+ # path. If a jail path is not provided, the resolved path may be any location on the system. If
338
+ # the target is an absolute path, use it as is (unless it breaches the jail path). Expands all
339
+ # parent and self references in the resolved path.
339
340
  #
340
341
  # target - the String target path
341
342
  # start - the String start path from which to resolve a relative target; falls back to jail, if
@@ -347,8 +348,9 @@ class PathResolver
347
348
  # automatically recover when an illegal path is encountered
348
349
  # * :target_name is used in messages to refer to the path being resolved
349
350
  #
350
- # returns a String path relative to the start path, if specified, and confined to the jail path,
351
- # if specified. The path is posixified and all parent and self references in the path are expanded.
351
+ # Returns an absolute String path relative to the start path, if specified, and confined to the
352
+ # jail path, if specified. The path is posixified and all parent and self references in the path
353
+ # are expanded.
352
354
  def system_path target, start = nil, jail = nil, opts = {}
353
355
  if jail
354
356
  raise ::SecurityError, %(Jail is not an absolute path: #{jail}) unless root? jail
@@ -362,7 +364,7 @@ class PathResolver
362
364
  if jail && !(descends_from? target_path, jail)
363
365
  if opts.fetch :recover, true
364
366
  logger.warn %(#{opts[:target_name] || 'path'} is outside of jail; recovering automatically)
365
- target_segments, _ = partition_path target_path
367
+ target_segments, = partition_path target_path
366
368
  jail_segments, jail_root = partition_path jail
367
369
  return join_path jail_segments + target_segments, jail_root
368
370
  else
@@ -371,7 +373,7 @@ class PathResolver
371
373
  end
372
374
  return target_path
373
375
  else
374
- target_segments, _ = partition_path target
376
+ target_segments, = partition_path target
375
377
  end
376
378
  else
377
379
  target_segments = []
@@ -387,7 +389,7 @@ class PathResolver
387
389
  return expand_path start
388
390
  end
389
391
  else
390
- target_segments, _ = partition_path start
392
+ target_segments, = partition_path start
391
393
  start = jail || @working_dir
392
394
  end
393
395
  elsif start.nil_or_empty?
@@ -419,7 +421,7 @@ class PathResolver
419
421
  if (resolved_segments = start_segments + target_segments).include? DOT_DOT
420
422
  unresolved_segments, resolved_segments = resolved_segments, []
421
423
  if jail
422
- jail_segments, _ = partition_path jail unless jail_segments
424
+ jail_segments, = partition_path jail unless jail_segments
423
425
  warned = false
424
426
  unresolved_segments.each do |segment|
425
427
  if segment == DOT_DOT
@@ -450,7 +452,7 @@ class PathResolver
450
452
  target_path
451
453
  elsif opts.fetch :recover, true
452
454
  logger.warn %(#{opts[:target_name] || 'path'} is outside of jail; recovering automatically)
453
- jail_segments, _ = partition_path jail unless jail_segments
455
+ jail_segments, = partition_path jail unless jail_segments
454
456
  join_path jail_segments + target_segments, jail_root
455
457
  else
456
458
  raise ::SecurityError, %(#{opts[:target_name] || 'path'} #{target} is outside of jail: #{jail} (disallowed in safe mode))