asciidoctor 2.0.10 → 2.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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))