asciidoctor 2.0.9 → 2.0.14
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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +193 -16
- data/LICENSE +1 -1
- data/README-de.adoc +12 -13
- data/README-fr.adoc +11 -15
- data/README-jp.adoc +242 -185
- data/README-zh_CN.adoc +17 -18
- data/README.adoc +133 -131
- data/asciidoctor.gemspec +6 -6
- data/data/locale/attributes-ar.adoc +4 -3
- data/data/locale/attributes-be.adoc +23 -0
- data/data/locale/attributes-bg.adoc +4 -3
- data/data/locale/attributes-ca.adoc +6 -5
- data/data/locale/attributes-cs.adoc +4 -3
- data/data/locale/attributes-da.adoc +6 -5
- data/data/locale/attributes-de.adoc +4 -4
- data/data/locale/attributes-en.adoc +4 -4
- data/data/locale/attributes-es.adoc +6 -5
- data/data/locale/attributes-fa.adoc +4 -3
- data/data/locale/attributes-fi.adoc +4 -3
- data/data/locale/attributes-fr.adoc +6 -5
- data/data/locale/attributes-hu.adoc +4 -3
- data/data/locale/attributes-id.adoc +4 -3
- data/data/locale/attributes-it.adoc +6 -5
- data/data/locale/attributes-ja.adoc +4 -3
- data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
- data/data/locale/attributes-nb.adoc +4 -3
- data/data/locale/attributes-nl.adoc +6 -5
- data/data/locale/attributes-nn.adoc +4 -3
- data/data/locale/attributes-pl.adoc +8 -7
- data/data/locale/attributes-pt.adoc +6 -5
- data/data/locale/attributes-pt_BR.adoc +6 -5
- data/data/locale/attributes-ro.adoc +4 -3
- data/data/locale/attributes-ru.adoc +6 -5
- data/data/locale/attributes-sr.adoc +4 -4
- data/data/locale/attributes-sr_Latn.adoc +4 -4
- data/data/locale/attributes-sv.adoc +4 -4
- data/data/locale/attributes-tr.adoc +4 -3
- data/data/locale/attributes-uk.adoc +6 -5
- data/data/locale/attributes-zh_CN.adoc +4 -3
- data/data/locale/attributes-zh_TW.adoc +4 -3
- data/data/reference/syntax.adoc +14 -7
- data/data/stylesheets/asciidoctor-default.css +30 -30
- data/lib/asciidoctor.rb +40 -14
- data/lib/asciidoctor/abstract_block.rb +9 -4
- data/lib/asciidoctor/abstract_node.rb +16 -6
- data/lib/asciidoctor/attribute_list.rb +63 -71
- data/lib/asciidoctor/cli/invoker.rb +2 -0
- data/lib/asciidoctor/cli/options.rb +10 -9
- data/lib/asciidoctor/convert.rb +167 -162
- data/lib/asciidoctor/converter.rb +13 -12
- data/lib/asciidoctor/converter/docbook5.rb +5 -9
- data/lib/asciidoctor/converter/html5.rb +58 -45
- data/lib/asciidoctor/converter/manpage.rb +61 -38
- data/lib/asciidoctor/converter/template.rb +3 -0
- data/lib/asciidoctor/document.rb +44 -51
- data/lib/asciidoctor/extensions.rb +2 -4
- data/lib/asciidoctor/helpers.rb +20 -15
- data/lib/asciidoctor/load.rb +102 -101
- data/lib/asciidoctor/parser.rb +40 -32
- data/lib/asciidoctor/path_resolver.rb +14 -12
- data/lib/asciidoctor/reader.rb +20 -13
- data/lib/asciidoctor/rx.rb +7 -6
- data/lib/asciidoctor/substitutors.rb +69 -50
- data/lib/asciidoctor/syntax_highlighter.rb +15 -7
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +1 -1
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +12 -4
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +6 -7
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +33 -19
- data/lib/asciidoctor/table.rb +52 -23
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +8 -8
- data/man/asciidoctor.adoc +4 -4
- metadata +16 -15
@@ -1,7 +1,7 @@
|
|
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
|
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
|
|
@@ -371,9 +371,7 @@ class Converter::DocBook5Converter < Converter::Base
|
|
371
371
|
has_body = false
|
372
372
|
result = []
|
373
373
|
pgwide_attribute = (node.option? 'pgwide') ? ' pgwide="1"' : ''
|
374
|
-
if (frame = node.attr 'frame', 'all', 'table-frame') == 'ends'
|
375
|
-
frame = 'topbot'
|
376
|
-
end
|
374
|
+
frame = 'topbot' if (frame = node.attr 'frame', 'all', 'table-frame') == 'ends'
|
377
375
|
grid = node.attr 'grid', nil, 'table-grid'
|
378
376
|
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"' : ''}>)
|
379
377
|
if (node.option? 'unbreakable')
|
@@ -401,12 +399,10 @@ class Converter::DocBook5Converter < Converter::Base
|
|
401
399
|
rows.each do |row|
|
402
400
|
result << '<row>'
|
403
401
|
row.each do |cell|
|
404
|
-
halign_attribute = (cell.attr? 'halign') ? %( align="#{cell.attr 'halign'}") : ''
|
405
|
-
valign_attribute = (cell.attr? 'valign') ? %( valign="#{cell.attr 'valign'}") : ''
|
406
402
|
colspan_attribute = cell.colspan ? %( namest="col_#{colnum = cell.column.attr 'colnumber'}" nameend="col_#{colnum + cell.colspan - 1}") : ''
|
407
403
|
rowspan_attribute = cell.rowspan ? %( morerows="#{cell.rowspan - 1}") : ''
|
408
404
|
# NOTE <entry> may not have whitespace (e.g., line breaks) as a direct descendant according to DocBook rules
|
409
|
-
entry_start = %(<entry#{
|
405
|
+
entry_start = %(<entry align="#{cell.attr 'halign'}" valign="#{cell.attr 'valign'}"#{colspan_attribute}#{rowspan_attribute}>)
|
410
406
|
if tsec == :head
|
411
407
|
cell_content = cell.text
|
412
408
|
else
|
@@ -487,7 +483,7 @@ class Converter::DocBook5Converter < Converter::Base
|
|
487
483
|
when :link
|
488
484
|
%(<link xl:href="#{node.target}">#{node.text}</link>)
|
489
485
|
when :bibref
|
490
|
-
%(<anchor#{common_attributes node.id, nil, "[#{node.reftext || node.id}]"}/>#{text})
|
486
|
+
%(<anchor#{common_attributes node.id, nil, (text = "[#{node.reftext || node.id}]")}/>#{text})
|
491
487
|
else
|
492
488
|
logger.warn %(unknown anchor type: #{node.type.inspect})
|
493
489
|
nil
|
@@ -517,7 +513,7 @@ class Converter::DocBook5Converter < Converter::Base
|
|
517
513
|
def convert_inline_image node
|
518
514
|
width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : ''
|
519
515
|
depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : ''
|
520
|
-
%(<inlinemediaobject>
|
516
|
+
%(<inlinemediaobject#{common_attributes nil, node.role}>
|
521
517
|
<imageobject>
|
522
518
|
<imagedata fileref="#{node.type == 'icon' ? (node.icon_uri node.target) : (node.image_uri node.target)}"#{width_attribute}#{depth_attribute}/>
|
523
519
|
</imageobject>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
3
|
# A built-in {Converter} implementation that generates HTML 5 output
|
4
|
-
# consistent with the html5 backend from AsciiDoc
|
4
|
+
# consistent with the html5 backend from AsciiDoc.py.
|
5
5
|
class Converter::Html5Converter < Converter::Base
|
6
6
|
register_for 'html5'
|
7
7
|
|
@@ -21,15 +21,15 @@ class Converter::Html5Converter < Converter::Base
|
|
21
21
|
#latexmath: INLINE_MATH_DELIMITERS[:latexmath] + [false],
|
22
22
|
}).default = ['', '']
|
23
23
|
|
24
|
-
DropAnchorRx = /<(?:a[
|
24
|
+
DropAnchorRx = /<(?:a\b[^>]*|\/a)>/
|
25
25
|
StemBreakRx = / *\\\n(?:\\?\n)*|\n\n+/
|
26
26
|
if RUBY_ENGINE == 'opal'
|
27
27
|
# NOTE In JavaScript, ^ matches the start of the string when the m flag is not set
|
28
|
-
SvgPreambleRx = /^#{CC_ALL}*?(?=<svg\
|
29
|
-
SvgStartTagRx = /^<svg[^>]
|
28
|
+
SvgPreambleRx = /^#{CC_ALL}*?(?=<svg[\s>])/
|
29
|
+
SvgStartTagRx = /^<svg(?:\s[^>]*)?>/
|
30
30
|
else
|
31
|
-
SvgPreambleRx = /\A.*?(?=<svg\
|
32
|
-
SvgStartTagRx = /\A<svg[^>]
|
31
|
+
SvgPreambleRx = /\A.*?(?=<svg[\s>])/m
|
32
|
+
SvgStartTagRx = /\A<svg(?:\s[^>]*)?>/
|
33
33
|
end
|
34
34
|
DimensionAttributeRx = /\s(?:width|height|style)=(["'])#{CC_ANY}*?\1/
|
35
35
|
|
@@ -96,6 +96,7 @@ class Converter::Html5Converter < Converter::Base
|
|
96
96
|
end
|
97
97
|
cdn_base_url = %(#{asset_uri_scheme}//cdnjs.cloudflare.com/ajax/libs)
|
98
98
|
linkcss = node.attr? 'linkcss'
|
99
|
+
max_width_attr = (node.attr? 'max-width') ? %( style="max-width: #{node.attr 'max-width'};") : ''
|
99
100
|
result = ['<!DOCTYPE html>']
|
100
101
|
lang_attribute = (node.attr? 'nolang') ? '' : %( lang="#{node.attr 'lang', 'en'}")
|
101
102
|
result << %(<html#{@xml_mode ? ' xmlns="http://www.w3.org/1999/xhtml"' : ''}#{lang_attribute}>)
|
@@ -138,7 +139,7 @@ class Converter::Html5Converter < Converter::Base
|
|
138
139
|
result << %(<link rel="stylesheet" href="#{node.normalize_web_path((node.attr 'stylesheet'), (node.attr 'stylesdir', ''))}"#{slash}>)
|
139
140
|
else
|
140
141
|
result << %(<style>
|
141
|
-
#{node.
|
142
|
+
#{node.read_contents (node.attr 'stylesheet'), start: (node.attr 'stylesdir'), warn_on_failure: true, label: 'stylesheet'}
|
142
143
|
</style>)
|
143
144
|
end
|
144
145
|
end
|
@@ -152,8 +153,8 @@ class Converter::Html5Converter < Converter::Base
|
|
152
153
|
end
|
153
154
|
end
|
154
155
|
|
155
|
-
if (syntax_hl = node.syntax_highlighter)
|
156
|
-
result << (
|
156
|
+
if (syntax_hl = node.syntax_highlighter)
|
157
|
+
result << (syntax_hl_docinfo_head_idx = result.size)
|
157
158
|
end
|
158
159
|
|
159
160
|
unless (docinfo_content = node.docinfo).empty?
|
@@ -161,29 +162,27 @@ class Converter::Html5Converter < Converter::Base
|
|
161
162
|
end
|
162
163
|
|
163
164
|
result << '</head>'
|
164
|
-
|
165
|
+
id_attr = node.id ? %( id="#{node.id}") : ''
|
165
166
|
if (sectioned = node.sections?) && (node.attr? 'toc-class') && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
|
166
167
|
classes = [node.doctype, (node.attr 'toc-class'), %(toc-#{node.attr 'toc-position', 'header'})]
|
167
168
|
else
|
168
169
|
classes = [node.doctype]
|
169
170
|
end
|
170
171
|
classes << node.role if node.role?
|
171
|
-
|
172
|
-
body_attrs << %(style="max-width: #{node.attr 'max-width'};") if node.attr? 'max-width'
|
173
|
-
result << %(<body #{body_attrs.join ' '}>)
|
172
|
+
result << %(<body#{id_attr} class="#{classes.join ' '}">)
|
174
173
|
|
175
174
|
unless (docinfo_content = node.docinfo :header).empty?
|
176
175
|
result << docinfo_content
|
177
176
|
end
|
178
177
|
|
179
178
|
unless node.noheader
|
180
|
-
result <<
|
179
|
+
result << %(<div id="header"#{max_width_attr}>)
|
181
180
|
if node.doctype == 'manpage'
|
182
181
|
result << %(<h1>#{node.doctitle} Manual Page</h1>)
|
183
182
|
if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
|
184
183
|
result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
|
185
184
|
<div id="toctitle">#{node.attr 'toc-title'}</div>
|
186
|
-
#{
|
185
|
+
#{node.converter.convert node, 'outline'}
|
187
186
|
</div>)
|
188
187
|
end
|
189
188
|
result << (generate_manname_section node) if node.attr? 'manpurpose'
|
@@ -216,19 +215,19 @@ class Converter::Html5Converter < Converter::Base
|
|
216
215
|
if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
|
217
216
|
result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
|
218
217
|
<div id="toctitle">#{node.attr 'toc-title'}</div>
|
219
|
-
#{
|
218
|
+
#{node.converter.convert node, 'outline'}
|
220
219
|
</div>)
|
221
220
|
end
|
222
221
|
end
|
223
222
|
result << '</div>'
|
224
223
|
end
|
225
224
|
|
226
|
-
result << %(<div id="content">
|
225
|
+
result << %(<div id="content"#{max_width_attr}>
|
227
226
|
#{node.content}
|
228
227
|
</div>)
|
229
228
|
|
230
229
|
if node.footnotes? && !(node.attr? 'nofootnotes')
|
231
|
-
result << %(<div id="footnotes">
|
230
|
+
result << %(<div id="footnotes"#{max_width_attr}>
|
232
231
|
<hr#{slash}>)
|
233
232
|
node.footnotes.each do |footnote|
|
234
233
|
result << %(<div class="footnote" id="_footnotedef_#{footnote.index}">
|
@@ -239,7 +238,7 @@ class Converter::Html5Converter < Converter::Base
|
|
239
238
|
end
|
240
239
|
|
241
240
|
unless node.nofooter
|
242
|
-
result <<
|
241
|
+
result << %(<div id="footer"#{max_width_attr}>)
|
243
242
|
result << '<div id="footer-text">'
|
244
243
|
result << %(#{node.attr 'version-label'} #{node.attr 'revnumber'}#{br}) if node.attr? 'revnumber'
|
245
244
|
result << %(#{node.attr 'last-update-label'} #{node.attr 'docdatetime'}) if (node.attr? 'last-update-label') && !(node.attr? 'reproducible')
|
@@ -250,8 +249,15 @@ class Converter::Html5Converter < Converter::Base
|
|
250
249
|
# JavaScript (and auxiliary stylesheets) loaded at the end of body for performance reasons
|
251
250
|
# See http://www.html5rocks.com/en/tutorials/speed/script-loading/
|
252
251
|
|
253
|
-
if syntax_hl
|
254
|
-
|
252
|
+
if syntax_hl
|
253
|
+
if syntax_hl.docinfo? :head
|
254
|
+
result[syntax_hl_docinfo_head_idx] = syntax_hl.docinfo :head, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash
|
255
|
+
else
|
256
|
+
result.delete_at syntax_hl_docinfo_head_idx
|
257
|
+
end
|
258
|
+
if syntax_hl.docinfo? :footer
|
259
|
+
result << (syntax_hl.docinfo :footer, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash)
|
260
|
+
end
|
255
261
|
end
|
256
262
|
|
257
263
|
if node.attr? 'stem'
|
@@ -275,7 +281,7 @@ MathJax.Hub.Config({
|
|
275
281
|
})
|
276
282
|
MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
|
277
283
|
MathJax.InputJax.AsciiMath.postfilterHooks.Add(function (data, node) {
|
278
|
-
if ((node = data.script.parentNode) && (node = node.parentNode) && node.classList.contains(
|
284
|
+
if ((node = data.script.parentNode) && (node = node.parentNode) && node.classList.contains("stemblock")) {
|
279
285
|
data.math.root.display = "block"
|
280
286
|
}
|
281
287
|
return data
|
@@ -311,7 +317,7 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
|
|
311
317
|
if node.sections? && (node.attr? 'toc') && (toc_p = node.attr 'toc-placement') != 'macro' && toc_p != 'preamble'
|
312
318
|
result << %(<div id="toc" class="toc">
|
313
319
|
<div id="toctitle">#{node.attr 'toc-title'}</div>
|
314
|
-
#{
|
320
|
+
#{node.converter.convert node, 'outline'}
|
315
321
|
</div>)
|
316
322
|
end
|
317
323
|
|
@@ -628,9 +634,7 @@ Your browser does not support the audio tag.
|
|
628
634
|
end
|
629
635
|
end
|
630
636
|
img ||= %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
|
631
|
-
if node.attr? 'link'
|
632
|
-
img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>)
|
633
|
-
end
|
637
|
+
img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
|
634
638
|
id_attr = node.id ? %( id="#{node.id}") : ''
|
635
639
|
classes = ['imageblock']
|
636
640
|
classes << (node.attr 'float') if node.attr? 'float'
|
@@ -689,8 +693,8 @@ Your browser does not support the audio tag.
|
|
689
693
|
open, close = BLOCK_MATH_DELIMITERS[style = node.style.to_sym]
|
690
694
|
if (equation = node.content)
|
691
695
|
if style == :asciimath && (equation.include? LF)
|
692
|
-
br = %(<br#{@void_element_slash}
|
693
|
-
equation = equation.gsub(StemBreakRx) { %(#{close}#{br * ($&.count LF)}#{open}) }
|
696
|
+
br = %(#{LF}<br#{@void_element_slash}>)
|
697
|
+
equation = equation.gsub(StemBreakRx) { %(#{close}#{br * (($&.count LF) - 1)}#{LF}#{open}) }
|
694
698
|
end
|
695
699
|
unless (equation.start_with? open) && (equation.end_with? close)
|
696
700
|
equation = %(#{open}#{equation}#{close})
|
@@ -796,7 +800,7 @@ Your browser does not support the audio tag.
|
|
796
800
|
toc = %(
|
797
801
|
<div id="toc" class="#{doc.attr 'toc-class', 'toc'}">
|
798
802
|
<div id="toctitle">#{doc.attr 'toc-title'}</div>
|
799
|
-
#{
|
803
|
+
#{doc.converter.convert doc, 'outline'}
|
800
804
|
</div>)
|
801
805
|
else
|
802
806
|
toc = ''
|
@@ -848,7 +852,8 @@ Your browser does not support the audio tag.
|
|
848
852
|
def convert_table node
|
849
853
|
result = []
|
850
854
|
id_attribute = node.id ? %( id="#{node.id}") : ''
|
851
|
-
|
855
|
+
frame = 'ends' if (frame = node.attr 'frame', 'all', 'table-frame') == 'topbot'
|
856
|
+
classes = ['tableblock', %(frame-#{frame}), %(grid-#{node.attr 'grid', 'all', 'table-grid'})]
|
852
857
|
if (stripes = node.attr 'stripes', nil, 'table-stripes')
|
853
858
|
classes << %(stripes-#{stripes})
|
854
859
|
end
|
@@ -934,7 +939,7 @@ Your browser does not support the audio tag.
|
|
934
939
|
|
935
940
|
%(<div#{id_attr} class="#{role}">
|
936
941
|
<div#{title_id_attr} class="title">#{title}</div>
|
937
|
-
#{
|
942
|
+
#{doc.converter.convert doc, 'outline', toclevels: levels}
|
938
943
|
</div>)
|
939
944
|
end
|
940
945
|
|
@@ -1089,7 +1094,7 @@ Your browser does not support the audio tag.
|
|
1089
1094
|
time_anchor = (start_t || end_t) ? %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''}) : ''
|
1090
1095
|
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
1091
1096
|
<div class="content">
|
1092
|
-
<video src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{width_attribute}#{height_attribute}#{poster_attribute}#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) : ''}#{(node.option? 'nocontrols') ? '' : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : ''}#{preload_attribute}>
|
1097
|
+
<video src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{width_attribute}#{height_attribute}#{poster_attribute}#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) : ''}#{(node.option? 'muted') ? (append_boolean_attribute 'muted', xml) : ''}#{(node.option? 'nocontrols') ? '' : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : ''}#{preload_attribute}>
|
1093
1098
|
Your browser does not support the video tag.
|
1094
1099
|
</video>
|
1095
1100
|
</div>
|
@@ -1107,8 +1112,13 @@ Your browser does not support the video tag.
|
|
1107
1112
|
attrs = node.role ? %( class="#{node.role}") : ''
|
1108
1113
|
unless (text = node.text)
|
1109
1114
|
refid = node.attributes['refid']
|
1110
|
-
if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid])
|
1111
|
-
text =
|
1115
|
+
if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid]) && (@resolving_xref ||= (outer = true)) && outer
|
1116
|
+
if !(text = ref.xreftext node.attr 'xrefstyle', nil, true)
|
1117
|
+
text = %([#{refid}])
|
1118
|
+
elsif text.include? '<a'
|
1119
|
+
text = text.gsub DropAnchorRx, ''
|
1120
|
+
end
|
1121
|
+
@resolving_xref = nil
|
1112
1122
|
else
|
1113
1123
|
text = %([#{refid}])
|
1114
1124
|
end
|
@@ -1144,8 +1154,10 @@ Your browser does not support the video tag.
|
|
1144
1154
|
elsif node.document.attr? 'icons'
|
1145
1155
|
src = node.icon_uri("callouts/#{node.text}")
|
1146
1156
|
%(<img src="#{src}" alt="#{node.text}"#{@void_element_slash}>)
|
1157
|
+
elsif ::Array === (guard = node.attributes['guard'])
|
1158
|
+
%(<!--<b class="conum">(#{node.text})</b>-->)
|
1147
1159
|
else
|
1148
|
-
%(#{
|
1160
|
+
%(#{guard}<b class="conum">(#{node.text})</b>)
|
1149
1161
|
end
|
1150
1162
|
end
|
1151
1163
|
|
@@ -1186,9 +1198,7 @@ Your browser does not support the video tag.
|
|
1186
1198
|
end
|
1187
1199
|
img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
|
1188
1200
|
end
|
1189
|
-
if node.attr? 'link'
|
1190
|
-
img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>)
|
1191
|
-
end
|
1201
|
+
img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
|
1192
1202
|
if (role = node.role)
|
1193
1203
|
if node.attr? 'float'
|
1194
1204
|
class_attr_val = %(#{type} #{node.attr 'float'} #{role})
|
@@ -1252,16 +1262,19 @@ Your browser does not support the video tag.
|
|
1252
1262
|
|
1253
1263
|
# NOTE expose read_svg_contents for Bespoke converter
|
1254
1264
|
def read_svg_contents node, target
|
1255
|
-
if (svg = node.read_contents target, start: (node.document.attr 'imagesdir'), normalize: true, label: 'SVG')
|
1265
|
+
if (svg = node.read_contents target, start: (node.document.attr 'imagesdir'), normalize: true, label: 'SVG', warn_if_empty: true)
|
1266
|
+
return if svg.empty?
|
1256
1267
|
svg = svg.sub SvgPreambleRx, '' unless svg.start_with? '<svg'
|
1257
|
-
old_start_tag = new_start_tag = nil
|
1268
|
+
old_start_tag = new_start_tag = start_tag_match = nil
|
1258
1269
|
# NOTE width, height and style attributes are removed if either width or height is specified
|
1259
1270
|
['width', 'height'].each do |dim|
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
new_start_tag =
|
1271
|
+
next unless node.attr? dim
|
1272
|
+
unless new_start_tag
|
1273
|
+
next if (start_tag_match ||= (svg.match SvgStartTagRx) || :no_match) == :no_match
|
1274
|
+
new_start_tag = (old_start_tag = start_tag_match[0]).gsub DimensionAttributeRx, ''
|
1264
1275
|
end
|
1276
|
+
# NOTE a unitless value in HTML is assumed to be px, so we can pass the value straight through
|
1277
|
+
new_start_tag = %(#{new_start_tag.chop} #{dim}="#{node.attr dim}">)
|
1265
1278
|
end
|
1266
1279
|
svg = %(#{new_start_tag}#{svg[old_start_tag.length..-1]}) if new_start_tag
|
1267
1280
|
end
|
@@ -1297,7 +1310,7 @@ Your browser does not support the video tag.
|
|
1297
1310
|
manname_id_attr = (manname_id = node.attr 'manname-id') ? %( id="#{manname_id}") : ''
|
1298
1311
|
%(<h2#{manname_id_attr}>#{manname_title}</h2>
|
1299
1312
|
<div class="sectionbody">
|
1300
|
-
<p>#{node.attr '
|
1313
|
+
<p>#{(node.attr 'mannames').join ', '} - #{node.attr 'manpurpose'}</p>
|
1301
1314
|
</div>)
|
1302
1315
|
end
|
1303
1316
|
|
@@ -2,8 +2,12 @@
|
|
2
2
|
module Asciidoctor
|
3
3
|
# A built-in {Converter} implementation that generates the man page (troff) format.
|
4
4
|
#
|
5
|
-
# The output
|
6
|
-
#
|
5
|
+
# The output of this converter adheres to the man definition as defined by
|
6
|
+
# groff and uses the manpage output of the DocBook toolchain as a foundation.
|
7
|
+
# That means if you've previously been generating man pages using the a2x tool
|
8
|
+
# from AsciiDoc.py, you should be able to achieve a very similar result
|
9
|
+
# using this converter. Though you'll also get to enjoy some notable
|
10
|
+
# enhancements that have been added since, such as the customizable linkstyle.
|
7
11
|
#
|
8
12
|
# See http://www.gnu.org/software/groff/manual/html_node/Man-usage.html#Man-usage
|
9
13
|
class Converter::ManPageConverter < Converter::Base
|
@@ -15,13 +19,16 @@ class Converter::ManPageConverter < Converter::Base
|
|
15
19
|
ESC_BS = %(#{ESC}\\) # escaped backslash (indicates troff formatting sequence)
|
16
20
|
ESC_FS = %(#{ESC}.) # escaped full stop (indicates troff macro)
|
17
21
|
|
18
|
-
LiteralBackslashRx =
|
22
|
+
LiteralBackslashRx = /\A\\|(#{ESC})?\\/
|
19
23
|
LeadingPeriodRx = /^\./
|
20
24
|
EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) "#{CC_ANY}*?" "#{CC_ANY}*?" )( |[^\s]*)(#{CC_ANY}*?)(?: *#{ESC}\\c)?$/
|
21
|
-
|
25
|
+
MalformedEscapedMacroRx = /(#{ESC}\\c) (#{ESC}\.(?:URL|MTO) )/
|
26
|
+
MockMacroRx = /<\/?(#{ESC}\\[^>]+)>/
|
22
27
|
EmDashCharRefRx = /—(?:​)?/
|
23
28
|
EllipsisCharRefRx = /…(?:​)?/
|
24
29
|
WrappedIndentRx = /#{CG_BLANK}*#{LF}#{CG_BLANK}*/
|
30
|
+
XMLMarkupRx = /&#?[a-z\d]+;|</
|
31
|
+
PCDATAFilterRx = /(&#?[a-z\d]+;|<[^>]+>)|([^&<]+)/
|
25
32
|
|
26
33
|
def initialize backend, opts = {}
|
27
34
|
@backend = backend
|
@@ -91,17 +98,14 @@ class Converter::ManPageConverter < Converter::Base
|
|
91
98
|
if node.attr? 'manpurpose'
|
92
99
|
mannames = node.attr 'mannames', [manname]
|
93
100
|
result << %(.SH "#{(node.attr 'manname-title', 'NAME').upcase}"
|
94
|
-
#{mannames.map {|n| manify n }.join ', '} \\- #{manify node.attr('manpurpose'), whitespace: :normalize})
|
101
|
+
#{mannames.map {|n| (manify n).gsub '\-', '-' }.join ', '} \\- #{manify node.attr('manpurpose'), whitespace: :normalize})
|
95
102
|
end
|
96
103
|
end
|
97
104
|
|
98
105
|
result << node.content
|
99
106
|
|
100
107
|
# QUESTION should NOTES come after AUTHOR(S)?
|
101
|
-
|
102
|
-
result << '.SH "NOTES"'
|
103
|
-
result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
|
104
|
-
end
|
108
|
+
append_footnotes result, node
|
105
109
|
|
106
110
|
unless (authors = node.authors).empty?
|
107
111
|
if authors.size > 1
|
@@ -124,10 +128,7 @@ class Converter::ManPageConverter < Converter::Base
|
|
124
128
|
def convert_embedded node
|
125
129
|
result = [node.content]
|
126
130
|
|
127
|
-
|
128
|
-
result << '.SH "NOTES"'
|
129
|
-
result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
|
130
|
-
end
|
131
|
+
append_footnotes result, node
|
131
132
|
|
132
133
|
# QUESTION should we add an AUTHOR(S) section?
|
133
134
|
|
@@ -142,7 +143,7 @@ class Converter::ManPageConverter < Converter::Base
|
|
142
143
|
stitle = node.captioned_title
|
143
144
|
else
|
144
145
|
macro = 'SH'
|
145
|
-
stitle = node.title
|
146
|
+
stitle = uppercase_pcdata node.title
|
146
147
|
end
|
147
148
|
result << %(.#{macro} "#{manify stitle}"
|
148
149
|
#{node.content})
|
@@ -247,7 +248,9 @@ r lw(\n(.lu*75u/100u).'
|
|
247
248
|
result << %(.sp
|
248
249
|
.if n .RS 4
|
249
250
|
.nf
|
251
|
+
.fam C
|
250
252
|
#{manify node.content, whitespace: :preserve}
|
253
|
+
.fam
|
251
254
|
.fi
|
252
255
|
.if n .RE)
|
253
256
|
result.join LF
|
@@ -261,7 +264,9 @@ r lw(\n(.lu*75u/100u).'
|
|
261
264
|
result << %(.sp
|
262
265
|
.if n .RS 4
|
263
266
|
.nf
|
267
|
+
.fam C
|
264
268
|
#{manify node.content, whitespace: :preserve}
|
269
|
+
.fam
|
265
270
|
.fi
|
266
271
|
.if n .RE)
|
267
272
|
result.join LF
|
@@ -284,15 +289,16 @@ r lw(\n(.lu*75u/100u).'
|
|
284
289
|
.B #{manify node.title}
|
285
290
|
.br) if node.title?
|
286
291
|
|
292
|
+
start = (node.attr 'start', 1).to_i
|
287
293
|
node.items.each_with_index do |item, idx|
|
288
294
|
result << %(.sp
|
289
295
|
.RS 4
|
290
296
|
.ie n \\{\\
|
291
|
-
\\h'-04' #{idx +
|
297
|
+
\\h'-04' #{numeral = idx + start}.\\h'+01'\\c
|
292
298
|
.\\}
|
293
299
|
.el \\{\\
|
294
300
|
. sp -1
|
295
|
-
. IP " #{
|
301
|
+
. IP " #{numeral}." 4.2
|
296
302
|
.\\}
|
297
303
|
#{manify item.text, whitespace: :normalize})
|
298
304
|
result << item.content if item.blocks?
|
@@ -310,8 +316,9 @@ r lw(\n(.lu*75u/100u).'
|
|
310
316
|
end
|
311
317
|
end
|
312
318
|
|
313
|
-
|
314
|
-
|
319
|
+
def convert_page_break node
|
320
|
+
'.bp'
|
321
|
+
end
|
315
322
|
|
316
323
|
def convert_paragraph node
|
317
324
|
if node.title?
|
@@ -526,12 +533,13 @@ allbox tab(:);'
|
|
526
533
|
result.join LF
|
527
534
|
end
|
528
535
|
|
529
|
-
# FIXME git uses [verse] for the synopsis; detect this special case
|
530
536
|
def convert_verse node
|
531
537
|
result = []
|
532
|
-
|
538
|
+
if node.title?
|
539
|
+
result << %(.sp
|
533
540
|
.B #{manify node.title}
|
534
|
-
.br)
|
541
|
+
.br)
|
542
|
+
end
|
535
543
|
attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
|
536
544
|
attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
|
537
545
|
result << %(.sp
|
@@ -580,11 +588,7 @@ allbox tab(:);'
|
|
580
588
|
when :xref
|
581
589
|
unless (text = node.text)
|
582
590
|
refid = node.attributes['refid']
|
583
|
-
|
584
|
-
text = (ref.xreftext node.attr('xrefstyle', nil, true)) || %([#{refid}])
|
585
|
-
else
|
586
|
-
text = %([#{refid}])
|
587
|
-
end
|
591
|
+
text = %([#{refid}]) unless AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid]) && (@resolving_xref ||= outer = true) && outer && (text = ref.xreftext node.attr 'xrefstyle', nil, true)
|
588
592
|
end
|
589
593
|
text
|
590
594
|
when :ref, :bibref
|
@@ -608,7 +612,6 @@ allbox tab(:);'
|
|
608
612
|
%(#{ESC_BS}fB(#{node.text})#{ESC_BS}fP)
|
609
613
|
end
|
610
614
|
|
611
|
-
# TODO supposedly groff has footnotes, but we're in search of an example
|
612
615
|
def convert_inline_footnote node
|
613
616
|
if (index = node.attr 'index')
|
614
617
|
%([#{index}])
|
@@ -646,19 +649,19 @@ allbox tab(:);'
|
|
646
649
|
end
|
647
650
|
end
|
648
651
|
|
649
|
-
# NOTE use fake
|
652
|
+
# NOTE use fake XML elements to prevent creating artificial word boundaries
|
650
653
|
def convert_inline_quoted node
|
651
654
|
case node.type
|
652
655
|
when :emphasis
|
653
|
-
%(
|
656
|
+
%(<#{ESC_BS}fI>#{node.text}</#{ESC_BS}fP>)
|
654
657
|
when :strong
|
655
|
-
%(
|
658
|
+
%(<#{ESC_BS}fB>#{node.text}</#{ESC_BS}fP>)
|
656
659
|
when :monospaced
|
657
|
-
%[
|
660
|
+
%[<#{ESC_BS}f(CR>#{node.text}</#{ESC_BS}fP>]
|
658
661
|
when :single
|
659
|
-
%[
|
662
|
+
%[<#{ESC_BS}(oq>#{node.text}</#{ESC_BS}(cq>]
|
660
663
|
when :double
|
661
|
-
%[
|
664
|
+
%[<#{ESC_BS}(lq>#{node.text}</#{ESC_BS}(rq>]
|
662
665
|
else
|
663
666
|
node.text
|
664
667
|
end
|
@@ -677,6 +680,22 @@ allbox tab(:);'
|
|
677
680
|
|
678
681
|
private
|
679
682
|
|
683
|
+
def append_footnotes result, node
|
684
|
+
if node.footnotes? && !(node.attr? 'nofootnotes')
|
685
|
+
result << '.SH "NOTES"'
|
686
|
+
node.footnotes.each_with_index do |fn, idx|
|
687
|
+
result << %(.IP [#{fn.index}])
|
688
|
+
# NOTE restore newline in escaped macro that gets removed by normalize_text in substitutor
|
689
|
+
if (text = fn.text).include? %(#{ESC}\\c #{ESC}.)
|
690
|
+
text = (manify %(#{text.gsub MalformedEscapedMacroRx, %(\\1#{LF}\\2)} ), whitespace: :normalize).chomp ' '
|
691
|
+
else
|
692
|
+
text = manify text, whitespace: :normalize
|
693
|
+
end
|
694
|
+
result << text
|
695
|
+
end
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
680
699
|
# Converts HTML entity references back to their original form, escapes
|
681
700
|
# special man characters and strips trailing whitespace.
|
682
701
|
#
|
@@ -699,10 +718,11 @@ allbox tab(:);'
|
|
699
718
|
str = str.tr_s WHITESPACE, ' '
|
700
719
|
end
|
701
720
|
str = str.
|
702
|
-
gsub(LiteralBackslashRx
|
721
|
+
gsub(LiteralBackslashRx) { $1 ? $& : '\\(rs' }. # literal backslash (not a troff escape sequence)
|
722
|
+
gsub(EllipsisCharRefRx, '...'). # horizontal ellipsis
|
703
723
|
gsub(LeadingPeriodRx, '\\\&.'). # leading . is used in troff for macro call or other formatting; replace with \&.
|
704
724
|
# drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
|
705
|
-
gsub(EscapedMacroRx) { (rest = $3.lstrip).empty? ? %(.#$1"#$2") : %(.#$1"
|
725
|
+
gsub(EscapedMacroRx) { (rest = $3.lstrip).empty? ? %(.#$1"#$2") : %(.#$1"#{$2.rstrip}"#{LF}#{rest}) }.
|
706
726
|
gsub('-', '\-').
|
707
727
|
gsub('<', '<').
|
708
728
|
gsub('>', '>').
|
@@ -717,21 +737,24 @@ allbox tab(:);'
|
|
717
737
|
gsub('’', '\(cq'). # right single quotation mark
|
718
738
|
gsub('“', '\(lq'). # left double quotation mark
|
719
739
|
gsub('”', '\(rq'). # right double quotation mark
|
720
|
-
gsub(EllipsisCharRefRx, '...'). # horizontal ellipsis
|
721
740
|
gsub('←', '\(<-'). # leftwards arrow
|
722
741
|
gsub('→', '\(->'). # rightwards arrow
|
723
742
|
gsub('⇐', '\(lA'). # leftwards double arrow
|
724
743
|
gsub('⇒', '\(rA'). # rightwards double arrow
|
725
744
|
gsub('​', '\:'). # zero width space
|
726
|
-
gsub('&','&').
|
745
|
+
gsub('&', '&'). # literal ampersand (NOTE must take place after any other replacement that includes &)
|
727
746
|
gsub('\'', '\(aq'). # apostrophe-quote
|
728
|
-
gsub(
|
747
|
+
gsub(MockMacroRx, '\1'). # mock boundary
|
729
748
|
gsub(ESC_BS, '\\'). # unescape troff backslash (NOTE update if more escapes are added)
|
730
749
|
gsub(ESC_FS, '.'). # unescape full stop in troff commands (NOTE must take place after gsub(LeadingPeriodRx))
|
731
750
|
rstrip # strip trailing space
|
732
751
|
opts[:append_newline] ? %(#{str}#{LF}) : str
|
733
752
|
end
|
734
753
|
|
754
|
+
def uppercase_pcdata string
|
755
|
+
(XMLMarkupRx.match? string) ? string.gsub(PCDATAFilterRx) { $2 ? $2.upcase : $1 } : string.upcase
|
756
|
+
end
|
757
|
+
|
735
758
|
def enclose_content node
|
736
759
|
node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content, whitespace: :normalize})
|
737
760
|
end
|