asciidoctor 2.0.10 → 2.0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +294 -30
  3. data/LICENSE +1 -1
  4. data/README-de.adoc +16 -20
  5. data/README-fr.adoc +15 -22
  6. data/README-jp.adoc +15 -26
  7. data/README-zh_CN.adoc +21 -25
  8. data/README.adoc +161 -138
  9. data/asciidoctor.gemspec +6 -13
  10. data/data/locale/attributes-ar.adoc +4 -3
  11. data/data/locale/attributes-be.adoc +23 -0
  12. data/data/locale/attributes-bg.adoc +4 -3
  13. data/data/locale/attributes-ca.adoc +6 -5
  14. data/data/locale/attributes-cs.adoc +4 -3
  15. data/data/locale/attributes-da.adoc +6 -5
  16. data/data/locale/attributes-de.adoc +4 -4
  17. data/data/locale/attributes-en.adoc +4 -4
  18. data/data/locale/attributes-es.adoc +6 -5
  19. data/data/locale/attributes-fa.adoc +4 -3
  20. data/data/locale/attributes-fi.adoc +4 -3
  21. data/data/locale/attributes-fr.adoc +8 -7
  22. data/data/locale/attributes-hu.adoc +4 -3
  23. data/data/locale/attributes-id.adoc +4 -3
  24. data/data/locale/attributes-it.adoc +6 -5
  25. data/data/locale/attributes-ja.adoc +4 -3
  26. data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
  27. data/data/locale/attributes-nb.adoc +4 -3
  28. data/data/locale/attributes-nl.adoc +6 -5
  29. data/data/locale/attributes-nn.adoc +4 -3
  30. data/data/locale/attributes-pl.adoc +8 -7
  31. data/data/locale/attributes-pt.adoc +6 -5
  32. data/data/locale/attributes-pt_BR.adoc +6 -5
  33. data/data/locale/attributes-ro.adoc +4 -3
  34. data/data/locale/attributes-ru.adoc +6 -5
  35. data/data/locale/attributes-sr.adoc +4 -4
  36. data/data/locale/attributes-sr_Latn.adoc +4 -4
  37. data/data/locale/attributes-sv.adoc +4 -4
  38. data/data/locale/attributes-th.adoc +23 -0
  39. data/data/locale/attributes-tr.adoc +4 -3
  40. data/data/locale/attributes-uk.adoc +6 -5
  41. data/data/locale/attributes-vi.adoc +23 -0
  42. data/data/locale/attributes-zh_CN.adoc +4 -3
  43. data/data/locale/attributes-zh_TW.adoc +4 -3
  44. data/data/reference/syntax.adoc +14 -7
  45. data/data/stylesheets/asciidoctor-default.css +76 -76
  46. data/data/stylesheets/coderay-asciidoctor.css +9 -9
  47. data/lib/asciidoctor/abstract_block.rb +20 -13
  48. data/lib/asciidoctor/abstract_node.rb +23 -12
  49. data/lib/asciidoctor/attribute_list.rb +64 -72
  50. data/lib/asciidoctor/block.rb +6 -6
  51. data/lib/asciidoctor/cli/invoker.rb +3 -2
  52. data/lib/asciidoctor/cli/options.rb +32 -31
  53. data/lib/asciidoctor/convert.rb +168 -162
  54. data/lib/asciidoctor/converter/docbook5.rb +49 -34
  55. data/lib/asciidoctor/converter/html5.rb +180 -139
  56. data/lib/asciidoctor/converter/manpage.rb +118 -90
  57. data/lib/asciidoctor/converter/template.rb +15 -13
  58. data/lib/asciidoctor/converter.rb +19 -16
  59. data/lib/asciidoctor/core_ext/hash/merge.rb +1 -1
  60. data/lib/asciidoctor/document.rb +77 -86
  61. data/lib/asciidoctor/extensions.rb +22 -16
  62. data/lib/asciidoctor/helpers.rb +20 -15
  63. data/lib/asciidoctor/list.rb +2 -6
  64. data/lib/asciidoctor/load.rb +103 -101
  65. data/lib/asciidoctor/logging.rb +10 -8
  66. data/lib/asciidoctor/parser.rb +211 -220
  67. data/lib/asciidoctor/path_resolver.rb +17 -15
  68. data/lib/asciidoctor/reader.rb +87 -79
  69. data/lib/asciidoctor/rx.rb +9 -7
  70. data/lib/asciidoctor/section.rb +7 -0
  71. data/lib/asciidoctor/substitutors.rb +167 -148
  72. data/lib/asciidoctor/syntax_highlighter/coderay.rb +3 -2
  73. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +13 -5
  74. data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
  75. data/lib/asciidoctor/syntax_highlighter/pygments.rb +19 -11
  76. data/lib/asciidoctor/syntax_highlighter/rouge.rb +35 -20
  77. data/lib/asciidoctor/syntax_highlighter.rb +16 -16
  78. data/lib/asciidoctor/table.rb +70 -43
  79. data/lib/asciidoctor/timings.rb +3 -3
  80. data/lib/asciidoctor/version.rb +1 -1
  81. data/lib/asciidoctor.rb +45 -19
  82. data/man/asciidoctor.1 +29 -31
  83. data/man/asciidoctor.adoc +35 -29
  84. metadata +17 -70
@@ -32,7 +32,7 @@ class SyntaxHighlighter::CodeRayAdapter < SyntaxHighlighter::Base
32
32
  end
33
33
 
34
34
  def docinfo? location
35
- @requires_stylesheet && location == :footer
35
+ @requires_stylesheet && location == :head
36
36
  end
37
37
 
38
38
  def docinfo location, doc, opts
@@ -78,7 +78,8 @@ class SyntaxHighlighter::CodeRayAdapter < SyntaxHighlighter::Base
78
78
  end
79
79
 
80
80
  extend Styles # exports static methods
81
- include Loader, Styles # adds methods to instance
81
+ include Styles # adds methods to instance
82
+ include Loader # adds methods to instance
82
83
 
83
84
  CodeCellStartTagCs = '<td class="code"><pre>'
84
85
 
@@ -9,18 +9,26 @@ class SyntaxHighlighter::HighlightJsAdapter < SyntaxHighlighter::Base
9
9
  end
10
10
 
11
11
  def format node, lang, opts
12
- super node, lang, (opts.merge transform: proc {|_, code| code['class'] = %(language-#{lang || 'none'} hljs) } )
12
+ super node, lang, (opts.merge transform: proc {|_, code| code['class'] = %(language-#{lang || 'none'} hljs) })
13
13
  end
14
14
 
15
15
  def docinfo? location
16
- location == :footer
16
+ true
17
17
  end
18
18
 
19
19
  def docinfo location, doc, opts
20
20
  base_url = doc.attr 'highlightjsdir', %(#{opts[:cdn_base_url]}/highlight.js/#{HIGHLIGHT_JS_VERSION})
21
- %(<link rel="stylesheet" href="#{base_url}/styles/#{doc.attr 'highlightjs-theme', 'github'}.min.css"#{opts[:self_closing_tag_slash]}>
22
- <script src="#{base_url}/highlight.min.js"></script>
23
- #{(doc.attr? 'highlightjs-languages') ? ((doc.attr 'highlightjs-languages').split ',').map {|lang| %[<script src="#{base_url}/languages/#{lang.lstrip}.min.js"></script>\n] }.join : ''}<script>hljs.initHighlighting()</script>)
21
+ if location == :head
22
+ %(<link rel="stylesheet" href="#{base_url}/styles/#{doc.attr 'highlightjs-theme', 'github'}.min.css"#{opts[:self_closing_tag_slash]}>)
23
+ else # :footer
24
+ %(<script src="#{base_url}/highlight.min.js"></script>
25
+ #{(doc.attr? 'highlightjs-languages') ? ((doc.attr 'highlightjs-languages').split ',').map {|lang| %[<script src="#{base_url}/languages/#{lang.lstrip}.min.js"></script>\n] }.join : ''}<script>
26
+ if (!hljs.initHighlighting.called) {
27
+ hljs.initHighlighting.called = true
28
+ ;[].slice.call(document.querySelectorAll('pre.highlight > code')).forEach(function (el) { hljs.highlightBlock(el) })
29
+ }
30
+ </script>)
31
+ end
24
32
  end
25
33
  end
26
34
  end
@@ -14,14 +14,17 @@ class SyntaxHighlighter::PrettifyAdapter < SyntaxHighlighter::Base
14
14
  end
15
15
 
16
16
  def docinfo? location
17
- location == :footer
17
+ true
18
18
  end
19
19
 
20
20
  def docinfo location, doc, opts
21
21
  base_url = doc.attr 'prettifydir', %(#{opts[:cdn_base_url]}/prettify/r298)
22
- prettify_theme_url = ((prettify_theme = doc.attr 'prettify-theme', 'prettify').start_with? 'http://', 'https://') ? prettify_theme : %(#{base_url}/#{prettify_theme}.min.css)
23
- %(<link rel="stylesheet" href="#{prettify_theme_url}"#{opts[:self_closing_tag_slash]}>
24
- <script src="#{base_url}/run_prettify.min.js"></script>)
22
+ if location == :head
23
+ prettify_theme_url = ((prettify_theme = doc.attr 'prettify-theme', 'prettify').start_with? 'http://', 'https://') ? prettify_theme : %(#{base_url}/#{prettify_theme}.min.css)
24
+ %(<link rel="stylesheet" href="#{prettify_theme_url}"#{opts[:self_closing_tag_slash]}>)
25
+ else # :footer
26
+ %(<script src="#{base_url}/run_prettify.min.js"></script>)
27
+ end
25
28
  end
26
29
  end
27
30
  end
@@ -5,8 +5,7 @@ class SyntaxHighlighter::PygmentsAdapter < SyntaxHighlighter::Base
5
5
 
6
6
  def initialize *args
7
7
  super
8
- @requires_stylesheet = nil
9
- @style = nil
8
+ @requires_stylesheet = @style = nil
10
9
  end
11
10
 
12
11
  def highlight?
@@ -34,13 +33,19 @@ class SyntaxHighlighter::PygmentsAdapter < SyntaxHighlighter::Base
34
33
  highlighted = highlighted.sub WrapperTagRx, PreTagCs
35
34
  opts[:callouts] ? [highlighted, (idx = highlighted.index CodeCellStartTagCs) ? idx + CodeCellStartTagCs.length : nil] : highlighted
36
35
  else
37
- node.sub_specialchars source # handles nil response from ::Pygments::Lexer#highlight
36
+ node.sub_source source, false # handles nil response from ::Pygments::Lexer#highlight
38
37
  end
39
38
  elsif (highlighted = lexer.highlight source, options: highlight_opts)
40
- highlighted = highlighted.gsub StyledLinenoSpanTagRx, LinenoSpanTagCs if linenos && noclasses
39
+ if linenos
40
+ if noclasses
41
+ highlighted = highlighted.gsub StyledLinenoSpanTagRx, LinenoSpanTagCs
42
+ elsif highlighted.include? LegacyLinenoSpanStartTagCs
43
+ highlighted = highlighted.gsub LegacyLinenoSpanTagRx, LinenoSpanTagCs
44
+ end
45
+ end
41
46
  highlighted.sub WrapperTagRx, '\1'
42
47
  else
43
- node.sub_specialchars source # handles nil response from ::Pygments::Lexer#highlight
48
+ node.sub_source source, false # handles nil response from ::Pygments::Lexer#highlight
44
49
  end
45
50
  end
46
51
 
@@ -53,7 +58,7 @@ class SyntaxHighlighter::PygmentsAdapter < SyntaxHighlighter::Base
53
58
  end
54
59
 
55
60
  def docinfo? location
56
- @requires_stylesheet && location == :footer
61
+ @requires_stylesheet && location == :head
57
62
  end
58
63
 
59
64
  def docinfo location, doc, opts
@@ -115,6 +120,7 @@ class SyntaxHighlighter::PygmentsAdapter < SyntaxHighlighter::Base
115
120
  end
116
121
  @@stylesheet_cache = ::Hash.new do |cache, key|
117
122
  if (stylesheet = ::Pygments.css BASE_SELECTOR, classprefix: TOKEN_CLASS_PREFIX, style: key)
123
+ stylesheet = stylesheet.slice (stylesheet.index BASE_SELECTOR), stylesheet.length unless stylesheet.start_with? BASE_SELECTOR
118
124
  @@stylesheet_cache = cache.merge key => stylesheet
119
125
  stylesheet
120
126
  end
@@ -123,27 +129,29 @@ class SyntaxHighlighter::PygmentsAdapter < SyntaxHighlighter::Base
123
129
  DEFAULT_STYLE = 'default'
124
130
  BASE_SELECTOR = 'pre.pygments'
125
131
  TOKEN_CLASS_PREFIX = 'tok-'
126
-
127
132
  BaseStyleRx = /^#{BASE_SELECTOR.gsub '.', '\\.'} +\{([^}]+?)\}/
128
133
 
129
134
  private_constant :BASE_SELECTOR, :TOKEN_CLASS_PREFIX, :BaseStyleRx
130
135
  end
131
136
 
132
137
  extend Styles # exports static methods
133
- include Loader, Styles # adds methods to instance
138
+ include Styles # adds methods to instance
139
+ include Loader # adds methods to instance
134
140
 
135
141
  CodeCellStartTagCs = '<td class="code">'
142
+ LegacyLinenoSpanStartTagCs = '<span class="lineno">'
143
+ LegacyLinenoSpanTagRx = %r(#{LegacyLinenoSpanStartTagCs}( *\d+) ?</span>)
136
144
  LinenoColumnStartTagsCs = '<td class="linenos"><div class="linenodiv"><pre>'
137
- LinenoSpanTagCs = '<span class="lineno">\1</span>'
145
+ LinenoSpanTagCs = '<span class="linenos">\1</span>'
138
146
  PreTagCs = '<pre>\1</pre>'
139
147
  StyledLinenoColumnStartTagsRx = /<td><div class="linenodiv" style="[^"]+?"><pre style="[^"]+?">/
140
- StyledLinenoSpanTagRx = %r(<span style="background-color: #f0f0f0; padding: 0 5px 0 5px">( *\d+ )</span>)
148
+ StyledLinenoSpanTagRx = %r((?<=^|<span></span>)<span style="[^"]+">( *\d+) ?</span>)
141
149
  WRAPPER_CLASS = 'lineno' # doesn't appear in output; Pygments appends "table" to this value to make nested table class
142
150
  # NOTE <pre> has style attribute when pygments-css=style
143
151
  # NOTE <div> has trailing newline when pygments-linenums-mode=table
144
152
  # NOTE initial <span></span> preserves leading blank lines
145
153
  WrapperTagRx = %r(<div class="#{WRAPPER_CLASS}"><pre\b[^>]*?>(.*)</pre></div>\n*)m
146
154
 
147
- private_constant :CodeCellStartTagCs, :LinenoColumnStartTagsCs, :LinenoSpanTagCs, :PreTagCs, :StyledLinenoColumnStartTagsRx, :StyledLinenoSpanTagRx, :WrapperTagRx, :WRAPPER_CLASS
155
+ private_constant :CodeCellStartTagCs, :LegacyLinenoSpanStartTagCs, :LegacyLinenoSpanTagRx, :LinenoColumnStartTagsCs, :LinenoSpanTagCs, :PreTagCs, :StyledLinenoColumnStartTagsRx, :StyledLinenoSpanTagRx, :WrapperTagRx, :WRAPPER_CLASS
148
156
  end
149
157
  end
@@ -13,40 +13,30 @@ class SyntaxHighlighter::RougeAdapter < SyntaxHighlighter::Base
13
13
  end
14
14
 
15
15
  def highlight node, source, lang, opts
16
- lexer = (::Rouge::Lexer.find_fancy lang) || ::Rouge::Lexers::PlainText
17
- lexer_opts = lexer.tag == 'php' && !(node.option? 'mixed') ? { start_inline: true } : {}
18
16
  @style ||= (style = opts[:style]) && (style_available? style) || DEFAULT_STYLE
19
- if opts[:css_mode] == :class
20
- @requires_stylesheet = true
21
- formatter = ::Rouge::Formatters::HTML.new inline_theme: @style
17
+ @requires_stylesheet = true if opts[:css_mode] == :class
18
+ lexer = create_lexer node, source, lang, opts
19
+ formatter = create_formatter node, source, lang, opts
20
+ highlighted = formatter.format lexer.lex source
21
+ if opts[:number_lines] && opts[:callouts]
22
+ [highlighted, (idx = highlighted.index CodeCellStartTagCs) ? idx + CodeCellStartTagCs.length : nil]
22
23
  else
23
- formatter = ::Rouge::Formatters::HTMLInline.new (::Rouge::Theme.find @style).new
24
+ highlighted
24
25
  end
25
- if (highlight_lines = opts[:highlight_lines])
26
- formatter = RougeExt::Formatters::HTMLLineHighlighter.new formatter, lines: highlight_lines
27
- end
28
- if opts[:number_lines]
29
- formatter = RougeExt::Formatters::HTMLTable.new formatter, start_line: opts[:start_line_number]
30
- if opts[:callouts]
31
- return [(highlighted = formatter.format lexer.lex source, lexer_opts), (idx = highlighted.index CodeCellStartTagCs) ? idx + CodeCellStartTagCs.length : nil]
32
- end
33
- end
34
- formatter.format lexer.lex source, lexer_opts
35
26
  end
36
27
 
37
28
  def format node, lang, opts
38
29
  if (query_idx = lang && (lang.index '?'))
39
30
  lang = lang.slice 0, query_idx
40
31
  end
41
- if opts[:css_mode] != :class && (@style = (style = opts[:style]) && (style_available? style) || DEFAULT_STYLE) &&
42
- (pre_style_attr_val = base_style @style)
32
+ if opts[:css_mode] != :class && (@style = (style = opts[:style]) && (style_available? style) || DEFAULT_STYLE) && (pre_style_attr_val = base_style @style)
43
33
  opts[:transform] = proc {|pre| pre['style'] = pre_style_attr_val }
44
34
  end
45
35
  super
46
36
  end
47
37
 
48
38
  def docinfo? location
49
- @requires_stylesheet && location == :footer
39
+ @requires_stylesheet && location == :head
50
40
  end
51
41
 
52
42
  def docinfo location, doc, opts
@@ -67,6 +57,30 @@ class SyntaxHighlighter::RougeAdapter < SyntaxHighlighter::Base
67
57
  ::File.write (::File.join to_dir, (stylesheet_basename @style)), (read_stylesheet @style), mode: FILE_WRITE_MODE
68
58
  end
69
59
 
60
+ def create_lexer node, source, lang, opts
61
+ if lang.include? '?'
62
+ # NOTE cgi-style options only properly supported in Rouge >= 2.1
63
+ if (lexer = ::Rouge::Lexer.find_fancy lang)
64
+ unless lexer.tag != 'php' || (node.option? 'mixed') || ((lexer_opts = lexer.options).key? 'start_inline')
65
+ lexer = lexer.class.new lexer_opts.merge 'start_inline' => true
66
+ end
67
+ end
68
+ elsif (lexer = ::Rouge::Lexer.find lang)
69
+ lexer = lexer.tag == 'php' && !(node.option? 'mixed') ? (lexer.new start_inline: true) : lexer.new
70
+ end if lang
71
+ lexer || ::Rouge::Lexers::PlainText.new
72
+ end
73
+
74
+ def create_formatter node, source, lang, opts
75
+ formatter = opts[:css_mode] == :class ?
76
+ (::Rouge::Formatters::HTML.new inline_theme: @style) :
77
+ (::Rouge::Formatters::HTMLInline.new (::Rouge::Theme.find @style).new)
78
+ if (highlight_lines = opts[:highlight_lines])
79
+ formatter = RougeExt::Formatters::HTMLLineHighlighter.new formatter, lines: highlight_lines
80
+ end
81
+ opts[:number_lines] ? (RougeExt::Formatters::HTMLTable.new formatter, start_line: opts[:start_line_number]) : formatter
82
+ end
83
+
70
84
  module Loader
71
85
  private
72
86
 
@@ -119,7 +133,8 @@ class SyntaxHighlighter::RougeAdapter < SyntaxHighlighter::Base
119
133
  end
120
134
 
121
135
  extend Styles # exports static methods
122
- include Loader, Styles # adds methods to instance
136
+ include Styles # adds methods to instance
137
+ include Loader # adds methods to instance
123
138
 
124
139
  CodeCellStartTagCs = '<td class="code">'
125
140
 
@@ -20,7 +20,7 @@ module SyntaxHighlighter
20
20
  end
21
21
 
22
22
  # Public: Indicates whether this syntax highlighter has docinfo (i.e., markup) to insert into the output document at
23
- # the specified location.
23
+ # the specified location. Should be called by converter after main content has been converted.
24
24
  #
25
25
  # location - The Symbol representing the location slot (:head or :footer).
26
26
  #
@@ -28,6 +28,7 @@ module SyntaxHighlighter
28
28
  def docinfo? location; end
29
29
 
30
30
  # Public: Generates docinfo markup for this syntax highlighter to insert at the specified location in the output document.
31
+ # Should be called by converter after main content has been converted.
31
32
  #
32
33
  # location - The Symbol representing the location slot (:head or :footer).
33
34
  # doc - The Document in which this syntax highlighter is being used.
@@ -98,9 +99,10 @@ module SyntaxHighlighter
98
99
  raise ::NotImplementedError, %(#{SyntaxHighlighter} subclass #{self.class} must implement the ##{__method__} method since #write_stylesheet? returns true)
99
100
  end
100
101
 
101
- private_class_method def self.included into
102
+ def self.included into
102
103
  into.extend Config
103
- end || :included
104
+ end
105
+ private_class_method :included # use separate declaration for Ruby 2.0.x
104
106
 
105
107
  module Config
106
108
  # Public: Statically register the current class in the registry for the specified names.
@@ -139,7 +141,7 @@ module SyntaxHighlighter
139
141
  # name - The String name of the syntax highlighter to create.
140
142
  # backend - The String name of the backend for which this syntax highlighter is being used (default: 'html5').
141
143
  # opts - A Hash of options providing information about the context in which this syntax highlighter is used:
142
- # :doc - The Document for which this syntax highlighter was created.
144
+ # :document - The Document for which this syntax highlighter was created.
143
145
  #
144
146
  # Returns a [SyntaxHighlighter] instance for the specified name.
145
147
  def create name, backend = 'html5', opts = {}
@@ -151,7 +153,7 @@ module SyntaxHighlighter
151
153
  end
152
154
 
153
155
  private
154
-
156
+
155
157
  def registry
156
158
  raise ::NotImplementedError, %(#{Factory} subclass #{self.class} must implement the ##{__method__} method)
157
159
  end
@@ -165,19 +167,17 @@ module SyntaxHighlighter
165
167
  end
166
168
 
167
169
  private
168
-
169
- def registry
170
- @registry
171
- end
170
+
171
+ attr_reader :registry
172
172
  end
173
173
 
174
174
  module DefaultFactory
175
175
  include Factory
176
176
 
177
- private
178
-
179
177
  @@registry = {}
180
178
 
179
+ private
180
+
181
181
  def registry
182
182
  @@registry
183
183
  end
@@ -215,8 +215,6 @@ module SyntaxHighlighter
215
215
  'rouge' => %(#{__dir__}/syntax_highlighter/rouge),
216
216
  }
217
217
 
218
- private
219
-
220
218
  @@mutex = ::Mutex.new
221
219
  end
222
220
  end
@@ -235,9 +233,11 @@ module SyntaxHighlighter
235
233
  def format node, lang, opts
236
234
  class_attr_val = opts[:nowrap] ? %(#{@pre_class} highlight nowrap) : %(#{@pre_class} highlight)
237
235
  if (transform = opts[:transform])
238
- pre = { 'class' => class_attr_val }
239
- code = lang ? { 'data-lang' => lang } : {}
240
- transform[pre, code]
236
+ transform[(pre = { 'class' => class_attr_val }), (code = lang ? { 'data-lang' => lang } : {})]
237
+ # NOTE: make sure data-lang is the last attribute on the code tag to remain consistent with 1.5.x
238
+ if (lang = code.delete 'data-lang')
239
+ code['data-lang'] = lang
240
+ end
241
241
  %(<pre#{pre.map {|k, v| %[ #{k}="#{v}"] }.join}><code#{code.map {|k, v| %[ #{k}="#{v}"] }.join}>#{node.content}</code></pre>)
242
242
  else
243
243
  %(<pre class="#{class_attr_val}"><code#{lang ? %[ data-lang="#{lang}"] : ''}>#{node.content}</code></pre>)
@@ -58,7 +58,7 @@ class Table < AbstractBlock
58
58
  @rows = Rows.new
59
59
  @columns = []
60
60
 
61
- @has_header_option = attributes['header-option'] ? true : false
61
+ @has_header_option = false
62
62
 
63
63
  # smells like we need a utility method here
64
64
  # to resolve an integer width from potential bogus input
@@ -78,10 +78,10 @@ class Table < AbstractBlock
78
78
  @attributes['orientation'] = 'landscape' if attributes['rotate-option']
79
79
  end
80
80
 
81
- # Internal: Returns whether the current row being processed is
82
- # the header row
81
+ # Internal: Returns the current state of the header option (true or :implicit) if
82
+ # the row being processed is (or is assumed to be) the header row, otherwise nil
83
83
  def header_row?
84
- @has_header_option && @rows.body.empty?
84
+ (val = @has_header_option) && @rows.body.empty? ? val : nil
85
85
  end
86
86
 
87
87
  # Internal: Creates the Column objects from the column spec
@@ -154,22 +154,19 @@ class Table < AbstractBlock
154
154
  # returns nothing
155
155
  def partition_header_footer(attrs)
156
156
  # set rowcount before splitting up body rows
157
- @attributes['rowcount'] = @rows.body.size
158
-
159
- num_body_rows = @rows.body.size
160
- if num_body_rows > 0 && @has_header_option
161
- head = @rows.body.shift
162
- num_body_rows -= 1
163
- # styles aren't applied to header row
164
- head.each {|c| c.style = nil }
165
- # QUESTION why does AsciiDoc use an array for head? is it
166
- # possible to have more than one based on the syntax?
167
- @rows.head = [head]
157
+ num_body_rows = @attributes['rowcount'] = (body = @rows.body).size
158
+
159
+ if num_body_rows > 0
160
+ if @has_header_option
161
+ @rows.head = [body.shift.map {|cell| cell.reinitialize true }]
162
+ num_body_rows -= 1
163
+ elsif @has_header_option.nil?
164
+ @has_header_option = false
165
+ body.unshift(body.shift.map {|cell| cell.reinitialize false })
166
+ end
168
167
  end
169
168
 
170
- if num_body_rows > 0 && attrs['footer-option']
171
- @rows.foot = [@rows.body.pop]
172
- end
169
+ @rows.foot = [body.pop] if num_body_rows > 0 && attrs['footer-option']
173
170
 
174
171
  nil
175
172
  end
@@ -232,14 +229,23 @@ class Table::Cell < AbstractBlock
232
229
  # Public: An alias to the parent block (which is always a Column)
233
230
  alias column parent
234
231
 
235
- # Internal: Returns the nested Document in an AsciiDoc table cell (only set when style is :asciidoc)
232
+ # Public: Returns the nested Document in an AsciiDoc table cell (only set when style is :asciidoc)
236
233
  attr_reader :inner_document
237
234
 
238
235
  def initialize column, cell_text, attributes = {}, opts = {}
239
236
  super column, :table_cell
237
+ @cursor = @reinitialize_args = nil
240
238
  @source_location = opts[:cursor].dup if @document.sourcemap
239
+ # NOTE: column is always set when parsing; may not be set when building table from the API
241
240
  if column
242
- cell_style = column.attributes['style'] unless (in_header_row = column.table.header_row?)
241
+ if (in_header_row = column.table.header_row?)
242
+ if in_header_row == :implicit && (cell_style = column.style || (attributes && attributes['style']))
243
+ @reinitialize_args = [column, cell_text, attributes && attributes.merge, opts] if cell_style == :asciidoc || cell_style == :literal
244
+ cell_style = nil
245
+ end
246
+ else
247
+ cell_style = column.style
248
+ end
243
249
  # REVIEW feels hacky to inherit all attributes from column
244
250
  update_attributes column.attributes
245
251
  end
@@ -253,7 +259,8 @@ class Table::Cell < AbstractBlock
253
259
  cell_style = attributes['style'] || cell_style unless in_header_row
254
260
  update_attributes attributes
255
261
  end
256
- if cell_style == :asciidoc
262
+ case cell_style
263
+ when :asciidoc
257
264
  asciidoc = true
258
265
  inner_document_cursor = opts[:cursor]
259
266
  if (cell_text = cell_text.rstrip).start_with? LF
@@ -264,7 +271,7 @@ class Table::Cell < AbstractBlock
264
271
  else
265
272
  cell_text = cell_text.lstrip
266
273
  end
267
- elsif cell_style == :literal
274
+ when :literal
268
275
  literal = true
269
276
  cell_text = cell_text.rstrip
270
277
  # QUESTION should we use same logic as :asciidoc cell? strip leading space if text doesn't start with newline?
@@ -306,8 +313,12 @@ class Table::Cell < AbstractBlock
306
313
  @content_model = :verbatim
307
314
  @subs = BASIC_SUBS
308
315
  else
309
- if normal_psv && (cell_text.start_with? '[[') && LeadingInlineAnchorRx =~ cell_text
310
- Parser.catalog_inline_anchor $1, $2, self, opts[:cursor], @document
316
+ if normal_psv
317
+ if in_header_row
318
+ @cursor = opts[:cursor] # used in deferred catalog_inline_anchor call
319
+ else
320
+ catalog_inline_anchor cell_text, opts[:cursor]
321
+ end
311
322
  end
312
323
  @content_model = :simple
313
324
  @subs = NORMAL_SUBS
@@ -316,6 +327,25 @@ class Table::Cell < AbstractBlock
316
327
  @style = cell_style
317
328
  end
318
329
 
330
+ def reinitialize has_header
331
+ if has_header
332
+ @reinitialize_args = nil
333
+ elsif @reinitialize_args
334
+ return Table::Cell.new(*@reinitialize_args)
335
+ else
336
+ @style = @attributes['style']
337
+ end
338
+ catalog_inline_anchor if @cursor
339
+ self
340
+ end
341
+
342
+ def catalog_inline_anchor cell_text = @text, cursor = nil
343
+ cursor, @cursor = @cursor, nil unless cursor
344
+ if (cell_text.start_with? '[[') && LeadingInlineAnchorRx =~ cell_text
345
+ Parser.catalog_inline_anchor $1, $2, self, cursor, @document
346
+ end
347
+ end
348
+
319
349
  # Public: Get the String text of this cell with substitutions applied.
320
350
  #
321
351
  # Used for cells in the head row as well as text-only (non-AsciiDoc) cells in
@@ -328,18 +358,14 @@ class Table::Cell < AbstractBlock
328
358
  apply_subs @text, @subs
329
359
  end
330
360
 
331
- # Public: Set the String text.
361
+ # Public: Set the String text for this cell.
332
362
  #
333
363
  # This method shouldn't be used for cells that have the AsciiDoc style.
334
- #
335
- # Returns the new String text assigned to this Cell
336
- def text= val
337
- @text = val
338
- end
364
+ attr_writer :text
339
365
 
340
366
  # Public: Handles the body data (tbody, tfoot), applying styles and partitioning into paragraphs
341
367
  #
342
- # This method should not be used for cells in the head row or that have the literal or verse style.
368
+ # This method should not be used for cells in the head row or that have the literal style.
343
369
  #
344
370
  # Returns the converted String for this Cell
345
371
  def content
@@ -377,7 +403,7 @@ class Table::Cell < AbstractBlock
377
403
  end
378
404
 
379
405
  def to_s
380
- "#{super.to_s} - [text: #@text, colspan: #{@colspan || 1}, rowspan: #{@rowspan || 1}, attributes: #@attributes]"
406
+ %(#{super} - [text: #{@text}, colspan: #{@colspan || 1}, rowspan: #{@rowspan || 1}, attributes: #{@attributes}])
381
407
  end
382
408
  end
383
409
 
@@ -385,7 +411,7 @@ end
385
411
  # class are primarily responsible for tracking the buffer of a cell as the parser
386
412
  # moves through the lines of the table using tail recursion. When a cell boundary
387
413
  # is located, the previous cell is closed, an instance of Table::Cell is
388
- # instantiated, the row is closed if the cell satisifies the column count and,
414
+ # instantiated, the row is closed if the cell satisfies the column count and,
389
415
  # finally, a new buffer is allocated to track the next cell.
390
416
  class Table::ParserContext
391
417
  include Logging
@@ -505,12 +531,13 @@ class Table::ParserContext
505
531
  #
506
532
  # returns true if the buffer has unclosed quotes, false if it doesn't or it
507
533
  # isn't quoted data
508
- def buffer_has_unclosed_quotes? append = nil
509
- if (record = append ? (@buffer + append).strip : @buffer.strip) == '"'
534
+ def buffer_has_unclosed_quotes? append = nil, q = '"'
535
+ if (record = append ? (@buffer + append).strip : @buffer.strip) == q
510
536
  true
511
- elsif record.start_with? '"'
512
- if ((trailing_quote = record.end_with? '"') && (record.end_with? '""')) || (record.start_with? '""')
513
- ((record = record.gsub '""', '').start_with? '"') && !(record.end_with? '"')
537
+ elsif record.start_with? q
538
+ qq = q + q
539
+ if ((trailing_quote = record.end_with? q) && (record.end_with? qq)) || (record.start_with? qq)
540
+ ((record = record.gsub qq, '').start_with? q) && !(record.end_with? q)
514
541
  else
515
542
  !trailing_quote
516
543
  end
@@ -603,20 +630,20 @@ class Table::ParserContext
603
630
  @buffer = ''
604
631
  cellspec = nil
605
632
  repeat = 1
606
- if @format == 'csv' && !cell_text.empty? && cell_text.include?('"')
633
+ if @format == 'csv' && !cell_text.empty? && (cell_text.include? (q = '"'))
607
634
  # this may not be perfect logic, but it hits the 99%
608
- if cell_text.start_with?('"') && cell_text.end_with?('"')
635
+ if (cell_text.start_with? q) && (cell_text.end_with? q)
609
636
  # unquote
610
637
  if (cell_text = cell_text.slice(1, cell_text.length - 2))
611
638
  # trim whitespace and collapse escaped quotes
612
- cell_text = cell_text.strip.squeeze('"')
639
+ cell_text = cell_text.strip.squeeze q
613
640
  else
614
641
  logger.error message_with_context 'unclosed quote in CSV data; setting cell to empty', source_location: @reader.cursor_at_prev_line
615
642
  cell_text = ''
616
643
  end
617
644
  else
618
645
  # collapse escaped quotes
619
- cell_text = cell_text.squeeze('"')
646
+ cell_text = cell_text.squeeze q
620
647
  end
621
648
  end
622
649
  end
@@ -635,7 +662,7 @@ class Table::ParserContext
635
662
  # QUESTION is this right for cells that span columns?
636
663
  unless (column = @table.columns[@current_row.size])
637
664
  logger.error message_with_context 'dropping cell because it exceeds specified number of columns', source_location: @reader.cursor_before_mark
638
- return
665
+ return nil
639
666
  end
640
667
  end
641
668
 
@@ -49,9 +49,9 @@ module Asciidoctor
49
49
 
50
50
  def print_report to = $stdout, subject = nil
51
51
  to.puts %(Input file: #{subject}) if subject
52
- to.puts %( Time to read and parse source: #{'%05.5f' % read_parse.to_f})
53
- to.puts %( Time to convert document: #{'%05.5f' % convert.to_f})
54
- to.puts %( Total time (read, parse and convert): #{'%05.5f' % read_parse_convert.to_f})
52
+ to.puts %( Time to read and parse source: #{sprintf '%05.5f', read_parse.to_f})
53
+ to.puts %( Time to convert document: #{sprintf '%05.5f', convert.to_f})
54
+ to.puts %( Total time (read, parse and convert): #{sprintf '%05.5f', read_parse_convert.to_f})
55
55
  end
56
56
 
57
57
  private
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Asciidoctor
3
- VERSION = '2.0.10'
3
+ VERSION = '2.0.17'
4
4
  end