asciidoctor 2.0.13 → 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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +151 -30
  3. data/LICENSE +1 -1
  4. data/README-de.adoc +9 -12
  5. data/README-fr.adoc +9 -12
  6. data/README-jp.adoc +10 -13
  7. data/README-zh_CN.adoc +9 -12
  8. data/README.adoc +40 -19
  9. data/asciidoctor.gemspec +2 -9
  10. data/data/locale/attributes-fr.adoc +2 -2
  11. data/data/locale/attributes-th.adoc +23 -0
  12. data/data/locale/attributes-vi.adoc +23 -0
  13. data/data/stylesheets/asciidoctor-default.css +54 -53
  14. data/data/stylesheets/coderay-asciidoctor.css +9 -9
  15. data/lib/asciidoctor/abstract_block.rb +11 -9
  16. data/lib/asciidoctor/abstract_node.rb +9 -8
  17. data/lib/asciidoctor/attribute_list.rb +1 -1
  18. data/lib/asciidoctor/block.rb +6 -6
  19. data/lib/asciidoctor/cli/invoker.rb +1 -2
  20. data/lib/asciidoctor/cli/options.rb +25 -25
  21. data/lib/asciidoctor/convert.rb +1 -0
  22. data/lib/asciidoctor/converter/docbook5.rb +45 -26
  23. data/lib/asciidoctor/converter/html5.rb +130 -102
  24. data/lib/asciidoctor/converter/manpage.rb +69 -64
  25. data/lib/asciidoctor/converter/template.rb +12 -13
  26. data/lib/asciidoctor/converter.rb +6 -4
  27. data/lib/asciidoctor/core_ext/hash/merge.rb +1 -1
  28. data/lib/asciidoctor/document.rb +61 -57
  29. data/lib/asciidoctor/extensions.rb +20 -12
  30. data/lib/asciidoctor/list.rb +2 -6
  31. data/lib/asciidoctor/load.rb +11 -9
  32. data/lib/asciidoctor/logging.rb +10 -8
  33. data/lib/asciidoctor/parser.rb +177 -193
  34. data/lib/asciidoctor/path_resolver.rb +3 -3
  35. data/lib/asciidoctor/reader.rb +73 -72
  36. data/lib/asciidoctor/rx.rb +5 -4
  37. data/lib/asciidoctor/section.rb +7 -0
  38. data/lib/asciidoctor/substitutors.rb +121 -121
  39. data/lib/asciidoctor/syntax_highlighter/coderay.rb +2 -1
  40. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +1 -1
  41. data/lib/asciidoctor/syntax_highlighter/pygments.rb +16 -7
  42. data/lib/asciidoctor/syntax_highlighter/rouge.rb +2 -1
  43. data/lib/asciidoctor/syntax_highlighter.rb +8 -11
  44. data/lib/asciidoctor/table.rb +18 -20
  45. data/lib/asciidoctor/timings.rb +3 -3
  46. data/lib/asciidoctor/version.rb +1 -1
  47. data/lib/asciidoctor.rb +10 -10
  48. data/man/asciidoctor.1 +26 -28
  49. data/man/asciidoctor.adoc +33 -27
  50. metadata +8 -62
@@ -1,15 +1,15 @@
1
- /* Stylesheet for CodeRay to match GitHub theme | MIT License | http://foundation.zurb.com */
1
+ /*! Stylesheet for CodeRay to loosely match GitHub themes | MIT License */
2
2
  pre.CodeRay{background:#f7f7f8}
3
- .CodeRay .line-numbers{border-right:1px solid currentColor;opacity:.35;padding:0 .5em 0 0}
3
+ .CodeRay .line-numbers{border-right:1px solid;opacity:.35;padding:0 .5em 0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
4
4
  .CodeRay span.line-numbers{display:inline-block;margin-right:.75em}
5
5
  .CodeRay .line-numbers strong{color:#000}
6
6
  table.CodeRay{border-collapse:separate;border:0;margin-bottom:0;background:none}
7
7
  table.CodeRay td{vertical-align:top;line-height:inherit}
8
8
  table.CodeRay td.line-numbers{text-align:right}
9
9
  table.CodeRay td.code{padding:0 0 0 .75em}
10
- .CodeRay .debug{color:#fff !important;background:#000080 !important}
10
+ .CodeRay .debug{color:#fff!important;background:navy!important}
11
11
  .CodeRay .annotation{color:#007}
12
- .CodeRay .attribute-name{color:#000080}
12
+ .CodeRay .attribute-name{color:navy}
13
13
  .CodeRay .attribute-value{color:#700}
14
14
  .CodeRay .binary{color:#509}
15
15
  .CodeRay .comment{color:#998;font-style:italic}
@@ -18,7 +18,7 @@ table.CodeRay td.code{padding:0 0 0 .75em}
18
18
  .CodeRay .char .delimiter{color:#039}
19
19
  .CodeRay .class{color:#458;font-weight:bold}
20
20
  .CodeRay .complex{color:#a08}
21
- .CodeRay .constant,.CodeRay .predefined-constant{color:#008080}
21
+ .CodeRay .constant,.CodeRay .predefined-constant{color:teal}
22
22
  .CodeRay .color{color:#099}
23
23
  .CodeRay .class-variable{color:#369}
24
24
  .CodeRay .decorator{color:#b0b}
@@ -33,7 +33,7 @@ table.CodeRay td.code{padding:0 0 0 .75em}
33
33
  .CodeRay .exception{color:inherit}
34
34
  .CodeRay .filename{color:#099}
35
35
  .CodeRay .function{color:#900;font-weight:bold}
36
- .CodeRay .global-variable{color:#008080}
36
+ .CodeRay .global-variable{color:teal}
37
37
  .CodeRay .hex{color:#058}
38
38
  .CodeRay .integer,.CodeRay .float{color:#099}
39
39
  .CodeRay .include{color:#555}
@@ -44,7 +44,7 @@ table.CodeRay td.code{padding:0 0 0 .75em}
44
44
  .CodeRay .inline-delimiter{color:#d14}
45
45
  .CodeRay .important{color:#555;font-weight:bold}
46
46
  .CodeRay .interpreted{color:#b2b}
47
- .CodeRay .instance-variable{color:#008080}
47
+ .CodeRay .instance-variable{color:teal}
48
48
  .CodeRay .label{color:#970}
49
49
  .CodeRay .local-variable{color:#963}
50
50
  .CodeRay .octal{color:#40e}
@@ -54,7 +54,7 @@ table.CodeRay td.code{padding:0 0 0 .75em}
54
54
  .CodeRay .directive{font-weight:bold}
55
55
  .CodeRay .type{font-weight:bold}
56
56
  .CodeRay .predefined-type{color:inherit}
57
- .CodeRay .reserved,.CodeRay .keyword {color:#000;font-weight:bold}
57
+ .CodeRay .reserved,.CodeRay .keyword{color:#000;font-weight:bold}
58
58
  .CodeRay .key{color:#808}
59
59
  .CodeRay .key .delimiter{color:#606}
60
60
  .CodeRay .key .char{color:#80f}
@@ -74,7 +74,7 @@ table.CodeRay td.code{padding:0 0 0 .75em}
74
74
  .CodeRay .symbol{color:#990073}
75
75
  .CodeRay .symbol .content{color:#a60}
76
76
  .CodeRay .symbol .delimiter{color:#630}
77
- .CodeRay .tag{color:#008080}
77
+ .CodeRay .tag{color:teal}
78
78
  .CodeRay .tag-special{color:#d70}
79
79
  .CodeRay .variable{color:#036}
80
80
  .CodeRay .insert{background:#afa}
@@ -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.
@@ -269,7 +271,7 @@ class AbstractBlock < AbstractNode
269
271
  ORDERED_LIST_KEYWORDS[list_type || @style]
270
272
  end
271
273
 
272
- # 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
273
275
  #
274
276
  # The following substitutions are applied to block and section titles:
275
277
  #
@@ -296,7 +298,7 @@ class AbstractBlock < AbstractNode
296
298
 
297
299
  # Public: Set the String block title.
298
300
  #
299
- # Returns the new String title assigned to this Block
301
+ # Returns the specified String title
300
302
  def title= val
301
303
  @converted_title = nil
302
304
  @title = val
@@ -480,7 +482,7 @@ class AbstractBlock < AbstractNode
480
482
  @header.find_by_internal selector, result, &block
481
483
  end
482
484
  @blocks.each do |b|
483
- next if (context_selector == :section && b.context != :section) # optimization
485
+ next if context_selector == :section && b.context != :section # optimization
484
486
  b.find_by_internal selector, result, &block
485
487
  end
486
488
  end
@@ -505,7 +507,7 @@ class AbstractBlock < AbstractNode
505
507
  end
506
508
  else
507
509
  @blocks.each do |b|
508
- next if (context_selector == :section && b.context != :section) # optimization
510
+ next if context_selector == :section && b.context != :section # optimization
509
511
  b.find_by_internal selector, result, &block
510
512
  end
511
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 value of the parent argument
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
@@ -220,7 +221,7 @@ class AbstractNode
220
221
  #
221
222
  # names - A single role name, a space-separated String of role names, or an Array of role names
222
223
  #
223
- # Returns the value of the names argument
224
+ # Returns the specified String role name or Array of role names
224
225
  def role= names
225
226
  @attributes['role'] = (::Array === names) ? (names.join ' ') : names
226
227
  end
@@ -462,8 +463,8 @@ class AbstractNode
462
463
  start = doc.base_dir
463
464
  end
464
465
  else
465
- start = doc.base_dir unless start
466
- jail = doc.base_dir unless jail
466
+ start ||= doc.base_dir
467
+ jail ||= doc.base_dir
467
468
  end
468
469
  doc.path_resolver.system_path target, start, jail, opts
469
470
  end
@@ -542,8 +543,8 @@ class AbstractNode
542
543
  rescue
543
544
  logger.warn %(could not retrieve contents of #{opts[:label] || 'asset'} at URI: #{target}) if opts.fetch :warn_on_failure, true
544
545
  end
545
- else
546
- 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
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))
547
548
  end
548
549
  else
549
550
  target = normalize_system_path target, opts[:start], nil, target_name: (opts[:label] || 'asset')
@@ -173,7 +173,7 @@ class AttributeList
173
173
  end
174
174
  else
175
175
  name = @block.apply_subs name if single_quoted && @block
176
- if (positional_attr_name = positional_attrs[index])
176
+ if (positional_attr_name = positional_attrs[index]) && name
177
177
  @attributes[positional_attr_name] = name
178
178
  end
179
179
  # QUESTION should we assign the positional key even when it's claimed by a positional attribute?
@@ -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
@@ -88,7 +88,6 @@ module Asciidoctor
88
88
  end
89
89
 
90
90
  if outfile == '-'
91
- # NOTE set_encoding returns nil on JRuby 9.1
92
91
  (tofile = @out) || ((tofile = $stdout).set_encoding UTF_8)
93
92
  elsif outfile
94
93
  opts[:mkdirs] = true
@@ -143,7 +142,7 @@ module Asciidoctor
143
142
  raise e
144
143
  else
145
144
  err.puts ::RuntimeError === e ? %(#{e.message} (#{e.class})) : e.message
146
- err.puts ' Use --trace for backtrace'
145
+ err.puts ' Use --trace to show backtrace'
147
146
  end
148
147
  end
149
148
  nil
@@ -47,12 +47,12 @@ module Asciidoctor
47
47
  EOS
48
48
 
49
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
+ 'additional backends are supported via extended converters (e.g., pdf, epub3)') do |backend|
51
51
  self[:attributes]['backend'] = backend
52
52
  end
53
53
  opts.on('-d', '--doctype DOCTYPE', ['article', 'book', 'manpage', 'inline'],
54
- 'document type to use when converting document: [article, book, manpage, inline] (default: article)') do |doc_type|
55
- 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
56
56
  end
57
57
  opts.on('-e', '--embedded', 'suppress enclosing document structure and output an embedded document (default: false)') do
58
58
  self[:standalone] = false
@@ -61,14 +61,14 @@ module Asciidoctor
61
61
  self[:output_file] = output_file
62
62
  end
63
63
  opts.on('--safe',
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
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
67
67
  self[:safe] = SafeMode::SAFE
68
68
  end
69
69
  opts.on('-S', '--safe-mode SAFE_MODE', (safe_mode_names = SafeMode.names),
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|
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|
72
72
  self[:safe] = SafeMode.value_for_name name
73
73
  end
74
74
  opts.on('-s', '--no-header-footer', 'suppress enclosing document structure and output an embedded document (default: false)') do
@@ -78,19 +78,19 @@ module Asciidoctor
78
78
  self[:attributes]['sectnums'] = ''
79
79
  end
80
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
+ 'specify eRuby implementation to use when rendering custom ERB templates: [erb, erubi, erubis] (default: erb)') do |eruby|
82
82
  self[:eruby] = eruby
83
83
  end
84
84
  opts.on('-a', '--attribute name[=value]', 'a document attribute to set in the form of name, name!, or name=value pair',
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|
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|
87
87
  next if (attr = attr.rstrip).empty? || attr == '='
88
88
  attr = attr.encode UTF_8 unless attr.encoding == UTF_8
89
89
  name, _, val = attr.partition '='
90
90
  self[:attributes][name] = val
91
91
  end
92
92
  opts.on('-T', '--template-dir DIR', 'a directory containing custom converter templates that override the built-in converter (requires tilt gem)',
93
- 'may be specified multiple times') do |template_dir|
93
+ 'may be specified multiple times') do |template_dir|
94
94
  if self[:template_dirs].nil?
95
95
  self[:template_dirs] = [template_dir]
96
96
  elsif ::Array === self[:template_dirs]
@@ -119,23 +119,23 @@ module Asciidoctor
119
119
  'may be specified more than once') do |path|
120
120
  (self[:requires] ||= []).concat(path.split ',')
121
121
  end
122
- 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|
123
123
  level = 'WARN' if (level = level.upcase) == 'WARNING'
124
- self[:failure_level] = ::Logger::Severity.const_get level, false
124
+ self[:failure_level] = ::Logger::Severity.const_get level
125
125
  end
126
- 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
127
127
  self[:verbose] = 0
128
128
  end
129
- 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
130
130
  self[:trace] = true
131
131
  end
132
- opts.on('-v', '--verbose', 'directs application messages logged at DEBUG or INFO level to STDERR (default: false)') do |verbose|
132
+ opts.on('-v', '--verbose', 'directs application messages logged at DEBUG or INFO level to STDERR (default: false)') do
133
133
  self[:verbose] = 2
134
134
  end
135
- opts.on('-w', '--warnings', 'turn on script warnings (default: false)') do |warnings|
135
+ opts.on('-w', '--warnings', 'turn on script warnings (default: false)') do
136
136
  self[:warnings] = true
137
137
  end
138
- opts.on('-t', '--timings', 'print timings report (default: false)') do |timing|
138
+ opts.on('-t', '--timings', 'print timings report (default: false)') do
139
139
  self[:timings] = true
140
140
  end
141
141
  opts.on_tail('-h', '--help [TOPIC]', 'print a help message',
@@ -161,7 +161,7 @@ module Asciidoctor
161
161
  elsif ::File.exist? (manpage_path = (::File.join ROOT_DIR, 'man', 'asciidoctor.1'))
162
162
  $stdout.puts ::File.read manpage_path
163
163
  else
164
- manpage_path = `man -w asciidoctor`.chop rescue ''
164
+ manpage_path = %x(man -w asciidoctor).chop rescue ''
165
165
  if manpage_path.empty?
166
166
  $stderr.puts 'asciidoctor: FAILED: manual page not found; try `man asciidoctor`'
167
167
  return 1
@@ -249,7 +249,7 @@ module Asciidoctor
249
249
 
250
250
  self[:input_files] = infiles
251
251
 
252
- self.delete :attributes if self[:attributes].empty?
252
+ delete :attributes if self[:attributes].empty?
253
253
 
254
254
  if self[:template_dirs]
255
255
  begin
@@ -258,7 +258,7 @@ module Asciidoctor
258
258
  raise $! if self[:trace]
259
259
  $stderr.puts 'asciidoctor: FAILED: \'tilt\' could not be loaded'
260
260
  $stderr.puts ' You must have the tilt gem installed (gem install tilt) to use custom backend templates'
261
- $stderr.puts ' Use --trace for backtrace'
261
+ $stderr.puts ' Use --trace to show backtrace'
262
262
  return 1
263
263
  rescue ::SystemExit
264
264
  # not permitted here
@@ -278,7 +278,7 @@ module Asciidoctor
278
278
  rescue ::LoadError
279
279
  raise $! if self[:trace]
280
280
  $stderr.puts %(asciidoctor: FAILED: '#{path}' could not be loaded)
281
- $stderr.puts ' Use --trace for backtrace'
281
+ $stderr.puts ' Use --trace to show backtrace'
282
282
  return 1
283
283
  rescue ::SystemExit
284
284
  # not permitted here
@@ -290,11 +290,11 @@ module Asciidoctor
290
290
  rescue ::OptionParser::MissingArgument
291
291
  $stderr.puts %(asciidoctor: option #{$!.message})
292
292
  $stdout.puts opts_parser
293
- return 1
293
+ 1
294
294
  rescue ::OptionParser::InvalidOption, ::OptionParser::InvalidArgument
295
295
  $stderr.puts %(asciidoctor: #{$!.message})
296
296
  $stdout.puts opts_parser
297
- return 1
297
+ 1
298
298
  ensure
299
299
  $VERBOSE = old_verbose
300
300
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Asciidoctor
2
3
  class << self
3
4
  # Public: Parse the AsciiDoc source input into an Asciidoctor::Document and
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
  module Asciidoctor
3
3
  # A built-in {Converter} implementation that generates DocBook 5 output. The output is inspired by the output produced
4
- # by the docbook45 backend from AsciiDoc Python, except it has been migrated to the DocBook 5 specification.
4
+ # by the docbook45 backend from AsciiDoc.py, except it has been migrated to the DocBook 5 specification.
5
5
  class Converter::DocBook5Converter < Converter::Base
6
6
  register_for 'docbook5'
7
7
 
8
8
  # default represents variablelist
9
9
  (DLIST_TAGS = {
10
- 'qanda' => { list: 'qandaset', entry: 'qandaentry', label: 'question', term: 'simpara', item: 'answer' },
11
- 'glossary' => { list: nil, entry: 'glossentry', term: 'glossterm', item: 'glossdef' },
12
- }).default = { list: 'variablelist', entry: 'varlistentry', term: 'term', item: 'listitem' }
10
+ 'qanda' => { list: 'qandaset', entry: 'qandaentry', label: 'question', term: 'simpara', item: 'answer' },
11
+ 'glossary' => { list: nil, entry: 'glossentry', term: 'glossterm', item: 'glossdef' },
12
+ }).default = { list: 'variablelist', entry: 'varlistentry', term: 'term', item: 'listitem' }
13
13
 
14
14
  (QUOTE_TAGS = {
15
15
  monospaced: ['<literal>', '</literal>'],
@@ -25,7 +25,7 @@ class Converter::DocBook5Converter < Converter::Base
25
25
  MANPAGE_SECTION_TAGS = { 'section' => 'refsection', 'synopsis' => 'refsynopsisdiv' }
26
26
  TABLE_PI_NAMES = ['dbhtml', 'dbfo', 'dblatex']
27
27
 
28
- CopyrightRx = /^(#{CC_ANY}+?)(?: ((?:\d{4}\-)?\d{4}))?$/
28
+ CopyrightRx = /^(#{CC_ANY}+?)(?: ((?:\d{4}-)?\d{4}))?$/
29
29
  ImageMacroRx = /^image::?(\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
30
30
 
31
31
  def initialize backend, opts = {}
@@ -41,7 +41,8 @@ class Converter::DocBook5Converter < Converter::Base
41
41
  if (root_tag_name = node.doctype) == 'manpage'
42
42
  root_tag_name = 'refentry'
43
43
  end
44
- result << %(<#{root_tag_name} xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0"#{lang_attribute}#{common_attributes node.id}>)
44
+ root_tag_idx = result.size
45
+ id = node.id
45
46
  result << (document_info_tag node) unless node.noheader
46
47
  unless (docinfo_content = node.docinfo :header).empty?
47
48
  result << docinfo_content
@@ -50,6 +51,9 @@ class Converter::DocBook5Converter < Converter::Base
50
51
  unless (docinfo_content = node.docinfo :footer).empty?
51
52
  result << docinfo_content
52
53
  end
54
+ id, node.id = node.id, nil unless id
55
+ # defer adding root tag in case document ID is auto-generated on demand
56
+ result.insert root_tag_idx, %(<#{root_tag_name} xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0"#{lang_attribute}#{common_attributes id}>)
53
57
  result << %(</#{root_tag_name}>)
54
58
  result.join LF
55
59
  end
@@ -298,13 +302,13 @@ class Converter::DocBook5Converter < Converter::Base
298
302
  </abstract>)
299
303
  end
300
304
  when 'partintro'
301
- unless node.level == 0 && node.parent.context == :section && node.document.doctype == 'book'
302
- logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
303
- ''
304
- else
305
+ if node.level == 0 && node.parent.context == :section && node.document.doctype == 'book'
305
306
  %(<partintro#{common_attributes node.id, node.role, node.reftext}>
306
307
  #{title_tag node}#{enclose_content node}
307
308
  </partintro>)
309
+ else
310
+ logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
311
+ ''
308
312
  end
309
313
  else
310
314
  reftext = node.reftext if (id = node.id)
@@ -374,19 +378,19 @@ class Converter::DocBook5Converter < Converter::Base
374
378
  frame = 'topbot' if (frame = node.attr 'frame', 'all', 'table-frame') == 'ends'
375
379
  grid = node.attr 'grid', nil, 'table-grid'
376
380
  result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext}#{pgwide_attribute} frame="#{frame}" rowsep="#{['none', 'cols'].include?(grid) ? 0 : 1}" colsep="#{['none', 'rows'].include?(grid) ? 0 : 1}"#{(node.attr? 'orientation', 'landscape', 'table-orientation') ? ' orient="land"' : ''}>)
377
- if (node.option? 'unbreakable')
381
+ if node.option? 'unbreakable'
378
382
  result << '<?dbfo keep-together="always"?>'
379
- elsif (node.option? 'breakable')
383
+ elsif node.option? 'breakable'
380
384
  result << '<?dbfo keep-together="auto"?>'
381
385
  end
382
386
  result << %(<title>#{node.title}</title>) if tag_name == 'table'
383
- col_width_key = if (width = (node.attr? 'width') ? (node.attr 'width') : nil)
387
+ if (width = (node.attr? 'width') ? (node.attr 'width') : nil)
384
388
  TABLE_PI_NAMES.each do |pi_name|
385
389
  result << %(<?#{pi_name} table-width="#{width}"?>)
386
390
  end
387
- 'colabswidth'
391
+ col_width_key = 'colabswidth'
388
392
  else
389
- 'colpcwidth'
393
+ col_width_key = 'colpcwidth'
390
394
  end
391
395
  result << %(<tgroup cols="#{node.attr 'colcount'}">)
392
396
  node.columns.each do |col|
@@ -474,10 +478,16 @@ class Converter::DocBook5Converter < Converter::Base
474
478
  %(<anchor#{common_attributes((id = node.id), nil, node.reftext || %([#{id}]))}/>)
475
479
  when :xref
476
480
  if (path = node.attributes['path'])
477
- # QUESTION should we use refid as fallback text instead? (like the html5 backend?)
478
481
  %(<link xl:href="#{node.target}">#{node.text || path}</link>)
479
482
  else
480
- linkend = node.attributes['fragment'] || node.target
483
+ if (linkend = node.attributes['refid']).nil_or_empty?
484
+ root_doc = get_root_document node
485
+ # Q: should we warn instead of generating a document ID on demand?
486
+ linkend = (root_doc.id ||= generate_document_id root_doc)
487
+ end
488
+ # NOTE the xref tag in DocBook does not support explicit link text, so the link tag must be used instead
489
+ # The section at http://www.sagehill.net/docbookxsl/CrossRefs.html#IdrefLinks gives an explanation for this choice
490
+ # "link - a cross reference where you supply the text of the reference as the content of the link element."
481
491
  (text = node.text) ? %(<link linkend="#{linkend}">#{text}</link>) : %(<xref linkend="#{linkend}"/>)
482
492
  end
483
493
  when :link
@@ -533,9 +543,8 @@ class Converter::DocBook5Converter < Converter::Base
533
543
  %(<indexterm>
534
544
  <primary>#{node.text}</primary>#{rel}
535
545
  </indexterm>#{node.text})
536
- else
537
- if (numterms = (terms = node.attr 'terms').size) > 2
538
- %(<indexterm>
546
+ elsif (numterms = (terms = node.attr 'terms').size) > 2
547
+ %(<indexterm>
539
548
  <primary>#{terms[0]}</primary><secondary>#{terms[1]}</secondary><tertiary>#{terms[2]}</tertiary>#{rel}
540
549
  </indexterm>#{(node.document.option? 'indexterm-promotion') ? %[
541
550
  <indexterm>
@@ -544,18 +553,17 @@ class Converter::DocBook5Converter < Converter::Base
544
553
  <indexterm>
545
554
  <primary>#{terms[2]}</primary>
546
555
  </indexterm>] : ''})
547
- elsif numterms > 1
548
- %(<indexterm>
556
+ elsif numterms > 1
557
+ %(<indexterm>
549
558
  <primary>#{terms[0]}</primary><secondary>#{terms[1]}</secondary>#{rel}
550
- </indexterm>#{(node.document.option? 'indexterm-promotion') ? %[
559
+ </indexterm>#{(node.document.option? 'indexterm-promotion') ? %[
551
560
  <indexterm>
552
561
  <primary>#{terms[1]}</primary>
553
562
  </indexterm>] : ''})
554
- else
555
- %(<indexterm>
563
+ else
564
+ %(<indexterm>
556
565
  <primary>#{terms[0]}</primary>#{rel}
557
566
  </indexterm>)
558
- end
559
567
  end
560
568
  end
561
569
 
@@ -710,6 +718,17 @@ class Converter::DocBook5Converter < Converter::Base
710
718
  result.join LF
711
719
  end
712
720
 
721
+ def get_root_document node
722
+ while (node = node.document).nested?
723
+ node = node.parent_document
724
+ end
725
+ node
726
+ end
727
+
728
+ def generate_document_id doc
729
+ %(__#{doc.doctype}-root__)
730
+ end
731
+
713
732
  # FIXME this should be handled through a template mechanism
714
733
  def enclose_content node
715
734
  node.content_model == :compound ? node.content : %(<simpara>#{node.content}</simpara>)