asciidoctor 2.0.10 → 2.0.17

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 (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
@@ -11,7 +11,7 @@ class AbstractBlock < AbstractNode
11
11
  # * :compound - this block contains other blocks
12
12
  # * :simple - this block holds a paragraph of prose that receives normal substitutions
13
13
  # * :verbatim - this block holds verbatim text (displayed "as is") that receives verbatim substitutions
14
- # * :raw - this block holds unprocessed content passed directly to the output with no sustitutions applied
14
+ # * :raw - this block holds unprocessed content passed directly to the output with no substitutions applied
15
15
  # * :empty - this block has no content
16
16
  attr_accessor :content_model
17
17
 
@@ -90,7 +90,7 @@ class AbstractBlock < AbstractNode
90
90
  #
91
91
  # context - the context Symbol context to assign to this block
92
92
  #
93
- # Returns the new context Symbol assigned to this block
93
+ # Returns the specified Symbol context
94
94
  def context= context
95
95
  @node_name = (@context = context).to_s
96
96
  end
@@ -129,11 +129,13 @@ class AbstractBlock < AbstractNode
129
129
 
130
130
  # Public: Check whether this block has any child Section objects.
131
131
  #
132
- # Only applies to Document and Section instances
132
+ # Acts an an abstract method that always returns false unless this block is an
133
+ # instance of Document or Section.
134
+ # Both Document and Section provide overrides for this method.
133
135
  #
134
- # Returns A [Boolean] to indicate whether this block has child Section objects
136
+ # Returns false
135
137
  def sections?
136
- @next_section_index > 0
138
+ false
137
139
  end
138
140
 
139
141
  # Deprecated: Legacy property to get the String or Integer numeral of this section.
@@ -141,6 +143,11 @@ class AbstractBlock < AbstractNode
141
143
  (Integer @numeral) rescue @numeral
142
144
  end
143
145
 
146
+ # Deprecated: Legacy property to set the numeral of this section by coercing the value to a String.
147
+ def number= val
148
+ @numeral = val.to_s
149
+ end
150
+
144
151
  # Public: Walk the document tree and find all block-level nodes that match the specified selector (context, style, id,
145
152
  # role, and/or custom filter).
146
153
  #
@@ -264,7 +271,7 @@ class AbstractBlock < AbstractNode
264
271
  ORDERED_LIST_KEYWORDS[list_type || @style]
265
272
  end
266
273
 
267
- # Public: Get the String title of this Block with title substitions applied
274
+ # Public: Get the String title of this Block with title substitutions applied
268
275
  #
269
276
  # The following substitutions are applied to block and section titles:
270
277
  #
@@ -291,7 +298,7 @@ class AbstractBlock < AbstractNode
291
298
 
292
299
  # Public: Set the String block title.
293
300
  #
294
- # Returns the new String title assigned to this Block
301
+ # Returns the specified String title
295
302
  def title= val
296
303
  @converted_title = nil
297
304
  @title = val
@@ -338,17 +345,17 @@ class AbstractBlock < AbstractNode
338
345
  if (val = reftext) && !val.empty?
339
346
  val
340
347
  # NOTE xrefstyle only applies to blocks with a title and a caption or number
341
- elsif xrefstyle && @title && @caption
348
+ elsif xrefstyle && @title && !@caption.nil_or_empty?
342
349
  case xrefstyle
343
350
  when 'full'
344
351
  quoted_title = sub_placeholder (sub_quotes @document.compat_mode ? %q(``%s'') : '"`%s`"'), title
345
- if @numeral && (caption_attr_name = CAPTION_ATTR_NAMES[@context]) && (prefix = @document.attributes[caption_attr_name])
352
+ if @numeral && (caption_attr_name = CAPTION_ATTRIBUTE_NAMES[@context]) && (prefix = @document.attributes[caption_attr_name])
346
353
  %(#{prefix} #{@numeral}, #{quoted_title})
347
354
  else
348
355
  %(#{@caption.chomp '. '}, #{quoted_title})
349
356
  end
350
357
  when 'short'
351
- if @numeral && (caption_attr_name = CAPTION_ATTR_NAMES[@context]) && (prefix = @document.attributes[caption_attr_name])
358
+ if @numeral && (caption_attr_name = CAPTION_ATTRIBUTE_NAMES[@context]) && (prefix = @document.attributes[caption_attr_name])
352
359
  %(#{prefix} #{@numeral})
353
360
  else
354
361
  @caption.chomp '. '
@@ -380,7 +387,7 @@ class AbstractBlock < AbstractNode
380
387
  # Returns nothing.
381
388
  def assign_caption value, caption_context = @context
382
389
  unless @caption || !@title || (@caption = value || @document.attributes['caption'])
383
- if (attr_name = CAPTION_ATTR_NAMES[caption_context]) && (prefix = @document.attributes[attr_name])
390
+ if (attr_name = CAPTION_ATTRIBUTE_NAMES[caption_context]) && (prefix = @document.attributes[attr_name])
384
391
  @caption = %(#{prefix} #{@numeral = @document.increment_and_store_counter %(#{caption_context}-number), self}. )
385
392
  nil
386
393
  end
@@ -475,7 +482,7 @@ class AbstractBlock < AbstractNode
475
482
  @header.find_by_internal selector, result, &block
476
483
  end
477
484
  @blocks.each do |b|
478
- next if (context_selector == :section && b.context != :section) # optimization
485
+ next if context_selector == :section && b.context != :section # optimization
479
486
  b.find_by_internal selector, result, &block
480
487
  end
481
488
  end
@@ -500,7 +507,7 @@ class AbstractBlock < AbstractNode
500
507
  end
501
508
  else
502
509
  @blocks.each do |b|
503
- next if (context_selector == :section && b.context != :section) # optimization
510
+ next if context_selector == :section && b.context != :section # optimization
504
511
  b.find_by_internal selector, result, &block
505
512
  end
506
513
  end
@@ -4,7 +4,8 @@ module Asciidoctor
4
4
  # node of AsciiDoc content. The state and methods on this class are common to
5
5
  # all content segments in an AsciiDoc document.
6
6
  class AbstractNode
7
- include Substitutors, Logging
7
+ include Logging
8
+ include Substitutors
8
9
 
9
10
  # Public: Get the Hash of attributes for this node
10
11
  attr_reader :attributes
@@ -65,7 +66,7 @@ class AbstractNode
65
66
  #
66
67
  # parent - The Block to set as the parent of this Block
67
68
  #
68
- # Returns the new parent Block associated with this Block
69
+ # Returns the the specified Block parent
69
70
  def parent= parent
70
71
  @parent, @document = parent, parent.document
71
72
  end
@@ -155,7 +156,7 @@ class AbstractNode
155
156
  #
156
157
  # name - the String name of the option
157
158
  #
158
- # Returns Nothing
159
+ # Returns nothing
159
160
  def set_option name
160
161
  @attributes[%(#{name}-option)] = ''
161
162
  nil
@@ -216,6 +217,15 @@ class AbstractNode
216
217
  (val = @attributes['role']) ? (%( #{val} ).include? %( #{name} )) : false
217
218
  end
218
219
 
220
+ # Public: Sets the value of the role attribute on this node.
221
+ #
222
+ # names - A single role name, a space-separated String of role names, or an Array of role names
223
+ #
224
+ # Returns the specified String role name or Array of role names
225
+ def role= names
226
+ @attributes['role'] = (::Array === names) ? (names.join ' ') : names
227
+ end
228
+
219
229
  # Public: Adds the given role directly to this node.
220
230
  #
221
231
  # Returns a [Boolean] indicating whether the role was added.
@@ -453,8 +463,8 @@ class AbstractNode
453
463
  start = doc.base_dir
454
464
  end
455
465
  else
456
- start = doc.base_dir unless start
457
- jail = doc.base_dir unless jail
466
+ start ||= doc.base_dir
467
+ jail ||= doc.base_dir
458
468
  end
459
469
  doc.path_resolver.system_path target, start, jail, opts
460
470
  end
@@ -514,6 +524,7 @@ class AbstractNode
514
524
  # * :normalize a Boolean that indicates whether the data should be normalized (default: false)
515
525
  # * :start the String relative base path to use when resolving the target (default: nil)
516
526
  # * :warn_on_failure a Boolean that indicates whether warnings are issued if the target cannot be read (default: true)
527
+ # * :warn_if_empty a Boolean that indicates whether a warning is issued if contents of target is empty (default: false)
517
528
  # Returns the contents of the resolved target or nil if the resolved target cannot be read
518
529
  # --
519
530
  # TODO refactor other methods in this class to use this method were possible (repurposing if necessary)
@@ -525,22 +536,22 @@ class AbstractNode
525
536
  Helpers.require_library 'open-uri/cached', 'open-uri-cached' if doc.attr? 'cache-uri'
526
537
  begin
527
538
  if opts[:normalize]
528
- (Helpers.prepare_source_string ::OpenURI.open_uri(target, URI_READ_MODE) {|f| f.read }).join LF
539
+ contents = (Helpers.prepare_source_string ::OpenURI.open_uri(target, URI_READ_MODE) {|f| f.read }).join LF
529
540
  else
530
- ::OpenURI.open_uri(target, URI_READ_MODE) {|f| f.read }
541
+ contents = ::OpenURI.open_uri(target, URI_READ_MODE) {|f| f.read }
531
542
  end
532
543
  rescue
533
544
  logger.warn %(could not retrieve contents of #{opts[:label] || 'asset'} at URI: #{target}) if opts.fetch :warn_on_failure, true
534
- return
535
545
  end
536
- else
537
- logger.warn %(cannot retrieve contents of #{opts[:label] || 'asset'} at URI: #{target} (allow-uri-read attribute not enabled)) if opts.fetch :warn_on_failure, true
538
- return
546
+ elsif opts.fetch :warn_on_failure, true
547
+ logger.warn %(cannot retrieve contents of #{opts[:label] || 'asset'} at URI: #{target} (allow-uri-read attribute not enabled))
539
548
  end
540
549
  else
541
550
  target = normalize_system_path target, opts[:start], nil, target_name: (opts[:label] || 'asset')
542
- read_asset target, normalize: opts[:normalize], warn_on_failure: (opts.fetch :warn_on_failure, true), label: opts[:label]
551
+ contents = read_asset target, normalize: opts[:normalize], warn_on_failure: (opts.fetch :warn_on_failure, true), label: opts[:label]
543
552
  end
553
+ logger.warn %(contents of #{opts[:label] || 'asset'} is empty: #{target}) if contents && opts[:warn_if_empty] && contents.empty?
554
+ contents
544
555
  end
545
556
 
546
557
  # Deprecated: Check whether the specified String is a URI by
@@ -22,19 +22,20 @@ module Asciidoctor
22
22
  # => { 'style' => 'quote', 'attribution' => 'Famous Person', 'citetitle' => 'Famous Book (2001)' }
23
23
  #
24
24
  class AttributeList
25
- BACKSLASH = '\\'
26
25
  APOS = '\''
26
+ BACKSLASH = '\\'
27
+ QUOT = '"'
27
28
 
28
29
  # Public: Regular expressions for detecting the boundary of a value
29
- BoundaryRxs = {
30
- '"' => /.*?[^\\](?=")/,
30
+ BoundaryRx = {
31
+ QUOT => /.*?[^\\](?=")/,
31
32
  APOS => /.*?[^\\](?=')/,
32
33
  ',' => /.*?(?=[ \t]*(,|$))/
33
34
  }
34
35
 
35
36
  # Public: Regular expressions for unescaping quoted characters
36
37
  EscapedQuotes = {
37
- '"' => '\\"',
38
+ QUOT => '\\"',
38
39
  APOS => '\\\''
39
40
  }
40
41
 
@@ -45,14 +46,16 @@ class AttributeList
45
46
  BlankRx = /[ \t]+/
46
47
 
47
48
  # Public: Regular expressions for skipping delimiters
48
- SkipRxs = { ',' => /[ \t]*(,|$)/ }
49
+ SkipRx = {
50
+ ',' => /[ \t]*(,|$)/
51
+ }
49
52
 
50
53
  def initialize source, block = nil, delimiter = ','
51
54
  @scanner = ::StringScanner.new source
52
55
  @block = block
53
56
  @delimiter = delimiter
54
- @delimiter_skip_pattern = SkipRxs[delimiter]
55
- @delimiter_boundary_pattern = BoundaryRxs[delimiter]
57
+ @delimiter_skip_pattern = SkipRx[delimiter]
58
+ @delimiter_boundary_pattern = BoundaryRx[delimiter]
56
59
  @attributes = nil
57
60
  end
58
61
 
@@ -65,8 +68,6 @@ class AttributeList
65
68
  return @attributes if @attributes
66
69
 
67
70
  @attributes = {}
68
- # QUESTION do we want to store the attribute list as the zero-index attribute?
69
- #attributes[0] = @scanner.string
70
71
  index = 0
71
72
 
72
73
  while parse_attribute index, positional_attrs
@@ -83,75 +84,73 @@ class AttributeList
83
84
  end
84
85
 
85
86
  def self.rekey attributes, positional_attrs
86
- index = 0
87
- positional_attrs.each do |key|
88
- index += 1
89
- if (val = attributes[index])
87
+ positional_attrs.each_with_index do |key, index|
88
+ if key && (val = attributes[index + 1])
90
89
  # QUESTION should we delete the positional key?
91
90
  attributes[key] = val
92
- end if key
91
+ end
93
92
  end
94
93
  attributes
95
94
  end
96
95
 
97
96
  private
98
97
 
99
- def parse_attribute index = 0, positional_attrs = []
100
- single_quoted_value = false
98
+ def parse_attribute index, positional_attrs
99
+ continue = true
101
100
  skip_blank
102
- # example: "quote"
103
- if (first = @scanner.peek(1)) == '"'
101
+ case @scanner.peek 1
102
+ # example: "quote" || "foo
103
+ when QUOT
104
104
  name = parse_attribute_value @scanner.get_byte
105
- value = nil
106
- # example: 'quote'
107
- elsif first == APOS
105
+ # example: 'quote' || 'foo
106
+ when APOS
108
107
  name = parse_attribute_value @scanner.get_byte
109
- value = nil
110
- single_quoted_value = true unless name.start_with? APOS
108
+ single_quoted = true unless name.start_with? APOS
111
109
  else
112
- name = scan_name
113
-
114
- skipped = 0
115
- c = nil
110
+ skipped = ((name = scan_name) && skip_blank) || 0
116
111
  if @scanner.eos?
117
- return false unless name
118
- else
119
- skipped = skip_blank || 0
120
- c = @scanner.get_byte
121
- end
122
-
123
- # example: quote
124
- if !c || c == @delimiter
125
- value = nil
126
- # example: Sherlock Holmes || =foo=
127
- elsif c != '=' || !name
128
- name = %(#{name}#{' ' * skipped}#{c}#{scan_to_delimiter})
129
- value = nil
130
- else
131
- skip_blank
132
- if @scanner.peek(1)
133
- # example: foo="bar" || foo="ba\"zaar"
134
- if (c = @scanner.get_byte) == '"'
112
+ return unless name || (@scanner.string.rstrip.end_with? @delimiter)
113
+ # example: quote (at eos)
114
+ continue = nil
115
+ # example: quote,
116
+ elsif (c = @scanner.get_byte) == @delimiter
117
+ @scanner.unscan
118
+ elsif name
119
+ # example: foo=...
120
+ if c == '='
121
+ skip_blank
122
+ case (c = @scanner.get_byte)
123
+ # example: foo="bar" || foo="ba\"zaar" || foo="bar
124
+ when QUOT
135
125
  value = parse_attribute_value c
136
- # example: foo='bar' || foo='ba\'zaar' || foo='ba"zaar'
137
- elsif c == APOS
126
+ # example: foo='bar' || foo='ba\'zaar' || foo='ba"zaar' || foo='bar
127
+ when APOS
138
128
  value = parse_attribute_value c
139
- single_quoted_value = true unless value.start_with? APOS
129
+ single_quoted = true unless value.start_with? APOS
140
130
  # example: foo=,
141
- elsif c == @delimiter
131
+ when @delimiter
132
+ value = ''
133
+ @scanner.unscan
134
+ # example: foo= (at eos)
135
+ when nil
142
136
  value = ''
143
- # example: foo=bar (all spaces ignored)
137
+ # example: foo=bar || foo=None
144
138
  else
145
139
  value = %(#{c}#{scan_to_delimiter})
146
140
  return true if value == 'None'
147
141
  end
142
+ # example: foo bar
143
+ else
144
+ name = %(#{name}#{' ' * skipped}#{c}#{scan_to_delimiter})
148
145
  end
146
+ # example: =foo= || !foo
147
+ else
148
+ name = %(#{c}#{scan_to_delimiter})
149
149
  end
150
150
  end
151
151
 
152
152
  if value
153
- # example: options="opt1,opt2,opt3"
154
- # opts is an alias for options
153
+ # example: options="opt1,opt2,opt3" || opts="opts1,opt2,opt3"
155
154
  case name
156
155
  when 'options', 'opts'
157
156
  if value.include? ','
@@ -161,7 +160,7 @@ class AttributeList
161
160
  @attributes[%(#{value}-option)] = '' unless value.empty?
162
161
  end
163
162
  else
164
- if single_quoted_value && @block
163
+ if single_quoted && @block
165
164
  case name
166
165
  when 'title', 'reftext'
167
166
  @attributes[name] = value
@@ -173,33 +172,26 @@ class AttributeList
173
172
  end
174
173
  end
175
174
  else
176
- resolved_name = single_quoted_value && @block ? (@block.apply_subs name) : name
177
- if (positional_attr_name = positional_attrs[index])
178
- @attributes[positional_attr_name] = resolved_name
175
+ name = @block.apply_subs name if single_quoted && @block
176
+ if (positional_attr_name = positional_attrs[index]) && name
177
+ @attributes[positional_attr_name] = name
179
178
  end
180
- # QUESTION should we always assign the positional key?
181
- @attributes[index + 1] = resolved_name
182
- # QUESTION should we assign the resolved name as an attribute?
183
- #@attributes[resolved_name] = nil
179
+ # QUESTION should we assign the positional key even when it's claimed by a positional attribute?
180
+ @attributes[index + 1] = name
184
181
  end
185
182
 
186
- true
183
+ continue
187
184
  end
188
185
 
189
186
  def parse_attribute_value quote
190
187
  # empty quoted value
191
- if @scanner.peek(1) == quote
188
+ if (@scanner.peek 1) == quote
192
189
  @scanner.get_byte
193
- return ''
194
- end
195
-
196
- if (value = scan_to_quote quote)
190
+ ''
191
+ elsif (value = scan_to_quote quote)
197
192
  @scanner.get_byte
198
- if value.include? BACKSLASH
199
- value.gsub EscapedQuotes[quote], quote
200
- else
201
- value
202
- end
193
+ (value.include? BACKSLASH) ? (value.gsub EscapedQuotes[quote], quote) : value
194
+ # leading quote only
203
195
  else
204
196
  %(#{quote}#{scan_to_delimiter})
205
197
  end
@@ -222,7 +214,7 @@ class AttributeList
222
214
  end
223
215
 
224
216
  def scan_to_quote quote
225
- @scanner.scan BoundaryRxs[quote]
217
+ @scanner.scan BoundaryRx[quote]
226
218
  end
227
219
  end
228
220
  end
@@ -54,21 +54,21 @@ class Block < AbstractBlock
54
54
  # FIXME feels funky; we have to be defensive to get commit_subs to honor override
55
55
  # FIXME does not resolve substitution groups inside Array (e.g., [:normal])
56
56
  if (subs = opts[:subs])
57
- # e.g., subs: :defult
57
+ case subs
58
+ # e.g., subs: :default
58
59
  # subs attribute is honored; falls back to opts[:default_subs], then built-in defaults based on context
59
- if subs == :default
60
+ when :default
60
61
  @default_subs = opts[:default_subs]
61
62
  # e.g., subs: [:quotes]
62
63
  # subs attribute is not honored
63
- elsif ::Array === subs
64
+ when ::Array
64
65
  @default_subs = subs.drop 0
65
66
  @attributes.delete 'subs'
66
67
  # e.g., subs: :normal or subs: 'normal'
67
68
  # subs attribute is not honored
68
69
  else
69
70
  @default_subs = nil
70
- # interpolation is the fastest way to dup subs as a string
71
- @attributes['subs'] = %(#{subs})
71
+ @attributes['subs'] = subs.to_s
72
72
  end
73
73
  # resolve the subs eagerly only if subs option is specified
74
74
  # QUESTION should we skip subsequent calls to commit_subs?
@@ -123,7 +123,7 @@ class Block < AbstractBlock
123
123
  result.join LF
124
124
  end
125
125
  else
126
- logger.warn %(Unknown content model '#{@content_model}' for block: #{to_s}) unless @content_model == :empty
126
+ logger.warn %(Unknown content model '#{@content_model}' for block: #{self}) unless @content_model == :empty
127
127
  nil
128
128
  end
129
129
  end
@@ -42,6 +42,8 @@ module Asciidoctor
42
42
  non_posix_env = ::File::ALT_SEPARATOR == RS
43
43
  err = @err || $stderr
44
44
  show_timings = false
45
+ # NOTE in Ruby 2.7, RubyGems sets SOURCE_DATE_EPOCH if it's not set
46
+ ::ENV.delete 'SOURCE_DATE_EPOCH' if (::ENV.key? 'IGNORE_SOURCE_DATE_EPOCH') && (::Gem.respond_to? :source_date_epoch)
45
47
 
46
48
  @options.map do |key, val|
47
49
  case key
@@ -86,7 +88,6 @@ module Asciidoctor
86
88
  end
87
89
 
88
90
  if outfile == '-'
89
- # NOTE set_encoding returns nil on JRuby 9.1
90
91
  (tofile = @out) || ((tofile = $stdout).set_encoding UTF_8)
91
92
  elsif outfile
92
93
  opts[:mkdirs] = true
@@ -141,7 +142,7 @@ module Asciidoctor
141
142
  raise e
142
143
  else
143
144
  err.puts ::RuntimeError === e ? %(#{e.message} (#{e.class})) : e.message
144
- err.puts ' Use --trace for backtrace'
145
+ err.puts ' Use --trace to show backtrace'
145
146
  end
146
147
  end
147
148
  nil
@@ -39,19 +39,20 @@ module Asciidoctor
39
39
  # NOTE don't use squiggly heredoc to maintain compatibility with Ruby < 2.3
40
40
  opts.banner = <<-'EOS'.gsub ' ', ''
41
41
  Usage: asciidoctor [OPTION]... FILE...
42
- Translate the AsciiDoc source FILE or FILE(s) into the backend output format (e.g., HTML 5, DocBook 5, etc.)
43
- By default, the output is written to a file with the basename of the source file and the appropriate extension.
44
- Example: asciidoctor -b html5 source.asciidoc
42
+ Convert the AsciiDoc input FILE(s) to the backend output format (e.g., HTML 5, DocBook 5, etc.)
43
+ Unless specified otherwise, the output is written to a file whose name is derived from the input file.
44
+ Application log messages are printed to STDERR.
45
+ Example: asciidoctor input.adoc
45
46
 
46
47
  EOS
47
48
 
48
- opts.on('-b', '--backend BACKEND', 'set output format backend: [html5, xhtml5, docbook5, manpage] (default: html5)',
49
- 'additional backends are supported via extensions (e.g., pdf, latex)') do |backend|
49
+ opts.on('-b', '--backend BACKEND', 'set backend output format: [html5, xhtml5, docbook5, manpage] (default: html5)',
50
+ 'additional backends are supported via extended converters (e.g., pdf, epub3)') do |backend|
50
51
  self[:attributes]['backend'] = backend
51
52
  end
52
53
  opts.on('-d', '--doctype DOCTYPE', ['article', 'book', 'manpage', 'inline'],
53
- 'document type to use when converting document: [article, book, manpage, inline] (default: article)') do |doc_type|
54
- self[:attributes]['doctype'] = doc_type
54
+ 'document type to use when converting document: [article, book, manpage, inline] (default: article)') do |doctype|
55
+ self[:attributes]['doctype'] = doctype
55
56
  end
56
57
  opts.on('-e', '--embedded', 'suppress enclosing document structure and output an embedded document (default: false)') do
57
58
  self[:standalone] = false
@@ -60,14 +61,14 @@ module Asciidoctor
60
61
  self[:output_file] = output_file
61
62
  end
62
63
  opts.on('--safe',
63
- 'set safe mode level to safe (default: unsafe)',
64
- 'enables include directives, but prevents access to ancestor paths of source file',
65
- 'provided for compatibility with the asciidoc command') do
64
+ 'set safe mode level to safe (default: unsafe)',
65
+ 'enables include directives, but prevents access to ancestor paths of source file',
66
+ 'provided for compatibility with the asciidoc command') do
66
67
  self[:safe] = SafeMode::SAFE
67
68
  end
68
69
  opts.on('-S', '--safe-mode SAFE_MODE', (safe_mode_names = SafeMode.names),
69
- %(set safe mode level explicitly: [#{safe_mode_names.join ', '}] (default: unsafe)),
70
- 'disables potentially dangerous macros in source files, such as include::[]') do |name|
70
+ %(set safe mode level explicitly: [#{safe_mode_names.join ', '}] (default: unsafe)),
71
+ 'disables potentially dangerous macros in source files, such as include::[]') do |name|
71
72
  self[:safe] = SafeMode.value_for_name name
72
73
  end
73
74
  opts.on('-s', '--no-header-footer', 'suppress enclosing document structure and output an embedded document (default: false)') do
@@ -76,20 +77,20 @@ module Asciidoctor
76
77
  opts.on('-n', '--section-numbers', 'auto-number section titles in the HTML backend; disabled by default') do
77
78
  self[:attributes]['sectnums'] = ''
78
79
  end
79
- opts.on('--eruby ERUBY', ['erb', 'erubis'],
80
- 'specify eRuby implementation to use when rendering custom ERB templates: [erb, erubis] (default: erb)') do |eruby|
80
+ opts.on('--eruby ERUBY', ['erb', 'erubi', 'erubis'],
81
+ 'specify eRuby implementation to use when rendering custom ERB templates: [erb, erubi, erubis] (default: erb)') do |eruby|
81
82
  self[:eruby] = eruby
82
83
  end
83
84
  opts.on('-a', '--attribute name[=value]', 'a document attribute to set in the form of name, name!, or name=value pair',
84
- 'this attribute takes precedence over the same attribute defined in the source document',
85
- 'unless either the name or value ends in @ (i.e., name@=value or name=value@)') do |attr|
85
+ 'this attribute takes precedence over the same attribute defined in the source document',
86
+ 'unless either the name or value ends in @ (i.e., name@=value or name=value@)') do |attr|
86
87
  next if (attr = attr.rstrip).empty? || attr == '='
87
88
  attr = attr.encode UTF_8 unless attr.encoding == UTF_8
88
89
  name, _, val = attr.partition '='
89
90
  self[:attributes][name] = val
90
91
  end
91
92
  opts.on('-T', '--template-dir DIR', 'a directory containing custom converter templates that override the built-in converter (requires tilt gem)',
92
- 'may be specified multiple times') do |template_dir|
93
+ 'may be specified multiple times') do |template_dir|
93
94
  if self[:template_dirs].nil?
94
95
  self[:template_dirs] = [template_dir]
95
96
  elsif ::Array === self[:template_dirs]
@@ -118,23 +119,23 @@ module Asciidoctor
118
119
  'may be specified more than once') do |path|
119
120
  (self[:requires] ||= []).concat(path.split ',')
120
121
  end
121
- opts.on('--failure-level LEVEL', %w(warning WARNING error ERROR info INFO), 'set minimum logging level that triggers non-zero exit code: [WARN, ERROR, INFO] (default: FATAL)') do |level|
122
+ opts.on('--failure-level LEVEL', %w(info INFO warning WARNING error ERROR fatal FATAL), 'set minimum log level that yields a non-zero exit code: [INFO, WARN, ERROR, FATAL] (default: FATAL)') do |level|
122
123
  level = 'WARN' if (level = level.upcase) == 'WARNING'
123
- self[:failure_level] = ::Logger::Severity.const_get level, false
124
+ self[:failure_level] = ::Logger::Severity.const_get level
124
125
  end
125
- opts.on('-q', '--quiet', 'silence application log messages and script warnings (default: false)') do |verbose|
126
+ opts.on('-q', '--quiet', 'silence application log messages and script warnings (default: false)') do
126
127
  self[:verbose] = 0
127
128
  end
128
- opts.on('--trace', 'include backtrace information when reporting errors (default: false)') do |trace|
129
+ opts.on('--trace', 'include backtrace information when reporting errors (default: false)') do
129
130
  self[:trace] = true
130
131
  end
131
- opts.on('-v', '--verbose', 'enable verbose mode (default: false)') do |verbose|
132
+ opts.on('-v', '--verbose', 'directs application messages logged at DEBUG or INFO level to STDERR (default: false)') do
132
133
  self[:verbose] = 2
133
134
  end
134
- opts.on('-w', '--warnings', 'turn on script warnings (default: false)') do |warnings|
135
+ opts.on('-w', '--warnings', 'turn on script warnings (default: false)') do
135
136
  self[:warnings] = true
136
137
  end
137
- opts.on('-t', '--timings', 'print timings report (default: false)') do |timing|
138
+ opts.on('-t', '--timings', 'print timings report (default: false)') do
138
139
  self[:timings] = true
139
140
  end
140
141
  opts.on_tail('-h', '--help [TOPIC]', 'print a help message',
@@ -160,7 +161,7 @@ module Asciidoctor
160
161
  elsif ::File.exist? (manpage_path = (::File.join ROOT_DIR, 'man', 'asciidoctor.1'))
161
162
  $stdout.puts ::File.read manpage_path
162
163
  else
163
- manpage_path = `man -w asciidoctor`.chop rescue ''
164
+ manpage_path = %x(man -w asciidoctor).chop rescue ''
164
165
  if manpage_path.empty?
165
166
  $stderr.puts 'asciidoctor: FAILED: manual page not found; try `man asciidoctor`'
166
167
  return 1
@@ -205,7 +206,7 @@ module Asciidoctor
205
206
  # shave off the file to process so that options errors appear correctly
206
207
  if args.size == 1 && args[0] == '-'
207
208
  infiles << args.pop
208
- elsif
209
+ else
209
210
  args.each do |file|
210
211
  if file.start_with? '-'
211
212
  # warn, but don't panic; we may have enough to proceed, so we won't force a failure
@@ -248,7 +249,7 @@ module Asciidoctor
248
249
 
249
250
  self[:input_files] = infiles
250
251
 
251
- self.delete :attributes if self[:attributes].empty?
252
+ delete :attributes if self[:attributes].empty?
252
253
 
253
254
  if self[:template_dirs]
254
255
  begin
@@ -257,7 +258,7 @@ module Asciidoctor
257
258
  raise $! if self[:trace]
258
259
  $stderr.puts 'asciidoctor: FAILED: \'tilt\' could not be loaded'
259
260
  $stderr.puts ' You must have the tilt gem installed (gem install tilt) to use custom backend templates'
260
- $stderr.puts ' Use --trace for backtrace'
261
+ $stderr.puts ' Use --trace to show backtrace'
261
262
  return 1
262
263
  rescue ::SystemExit
263
264
  # not permitted here
@@ -277,7 +278,7 @@ module Asciidoctor
277
278
  rescue ::LoadError
278
279
  raise $! if self[:trace]
279
280
  $stderr.puts %(asciidoctor: FAILED: '#{path}' could not be loaded)
280
- $stderr.puts ' Use --trace for backtrace'
281
+ $stderr.puts ' Use --trace to show backtrace'
281
282
  return 1
282
283
  rescue ::SystemExit
283
284
  # not permitted here
@@ -289,11 +290,11 @@ module Asciidoctor
289
290
  rescue ::OptionParser::MissingArgument
290
291
  $stderr.puts %(asciidoctor: option #{$!.message})
291
292
  $stdout.puts opts_parser
292
- return 1
293
+ 1
293
294
  rescue ::OptionParser::InvalidOption, ::OptionParser::InvalidArgument
294
295
  $stderr.puts %(asciidoctor: #{$!.message})
295
296
  $stdout.puts opts_parser
296
- return 1
297
+ 1
297
298
  ensure
298
299
  $VERBOSE = old_verbose
299
300
  end