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
@@ -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