asciidoctor 2.0.13 → 2.0.17

Sign up to get free protection for your applications and to get access to all the features.
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>)