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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +151 -30
- data/LICENSE +1 -1
- data/README-de.adoc +9 -12
- data/README-fr.adoc +9 -12
- data/README-jp.adoc +10 -13
- data/README-zh_CN.adoc +9 -12
- data/README.adoc +40 -19
- data/asciidoctor.gemspec +2 -9
- data/data/locale/attributes-fr.adoc +2 -2
- data/data/locale/attributes-th.adoc +23 -0
- data/data/locale/attributes-vi.adoc +23 -0
- data/data/stylesheets/asciidoctor-default.css +54 -53
- data/data/stylesheets/coderay-asciidoctor.css +9 -9
- data/lib/asciidoctor/abstract_block.rb +11 -9
- data/lib/asciidoctor/abstract_node.rb +9 -8
- data/lib/asciidoctor/attribute_list.rb +1 -1
- data/lib/asciidoctor/block.rb +6 -6
- data/lib/asciidoctor/cli/invoker.rb +1 -2
- data/lib/asciidoctor/cli/options.rb +25 -25
- data/lib/asciidoctor/convert.rb +1 -0
- data/lib/asciidoctor/converter/docbook5.rb +45 -26
- data/lib/asciidoctor/converter/html5.rb +130 -102
- data/lib/asciidoctor/converter/manpage.rb +69 -64
- data/lib/asciidoctor/converter/template.rb +12 -13
- data/lib/asciidoctor/converter.rb +6 -4
- data/lib/asciidoctor/core_ext/hash/merge.rb +1 -1
- data/lib/asciidoctor/document.rb +61 -57
- data/lib/asciidoctor/extensions.rb +20 -12
- data/lib/asciidoctor/list.rb +2 -6
- data/lib/asciidoctor/load.rb +11 -9
- data/lib/asciidoctor/logging.rb +10 -8
- data/lib/asciidoctor/parser.rb +177 -193
- data/lib/asciidoctor/path_resolver.rb +3 -3
- data/lib/asciidoctor/reader.rb +73 -72
- data/lib/asciidoctor/rx.rb +5 -4
- data/lib/asciidoctor/section.rb +7 -0
- data/lib/asciidoctor/substitutors.rb +121 -121
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +2 -1
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +1 -1
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +16 -7
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +2 -1
- data/lib/asciidoctor/syntax_highlighter.rb +8 -11
- data/lib/asciidoctor/table.rb +18 -20
- data/lib/asciidoctor/timings.rb +3 -3
- data/lib/asciidoctor/version.rb +1 -1
- data/lib/asciidoctor.rb +10 -10
- data/man/asciidoctor.1 +26 -28
- data/man/asciidoctor.adoc +33 -27
- metadata +8 -62
@@ -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,7 +21,7 @@ class Converter::Html5Converter < Converter::Base
|
|
21
21
|
#latexmath: INLINE_MATH_DELIMITERS[:latexmath] + [false],
|
22
22
|
}).default = ['', '']
|
23
23
|
|
24
|
-
DropAnchorRx =
|
24
|
+
DropAnchorRx = %r(<(?: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
|
@@ -48,44 +48,45 @@ class Converter::Html5Converter < Converter::Base
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def convert node, transform = node.node_name, opts = nil
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
51
|
+
case transform
|
52
|
+
when 'inline_quoted' then convert_inline_quoted node
|
53
|
+
when 'paragraph' then convert_paragraph node
|
54
|
+
when 'inline_anchor' then convert_inline_anchor node
|
55
|
+
when 'section' then convert_section node
|
56
|
+
when 'listing' then convert_listing node
|
57
|
+
when 'literal' then convert_literal node
|
58
|
+
when 'ulist' then convert_ulist node
|
59
|
+
when 'olist' then convert_olist node
|
60
|
+
when 'dlist' then convert_dlist node
|
61
|
+
when 'admonition' then convert_admonition node
|
62
|
+
when 'colist' then convert_colist node
|
63
|
+
when 'embedded' then convert_embedded node
|
64
|
+
when 'example' then convert_example node
|
65
|
+
when 'floating_title' then convert_floating_title node
|
66
|
+
when 'image' then convert_image node
|
67
|
+
when 'inline_break' then convert_inline_break node
|
68
|
+
when 'inline_button' then convert_inline_button node
|
69
|
+
when 'inline_callout' then convert_inline_callout node
|
70
|
+
when 'inline_footnote' then convert_inline_footnote node
|
71
|
+
when 'inline_image' then convert_inline_image node
|
72
|
+
when 'inline_indexterm' then convert_inline_indexterm node
|
73
|
+
when 'inline_kbd' then convert_inline_kbd node
|
74
|
+
when 'inline_menu' then convert_inline_menu node
|
75
|
+
when 'open' then convert_open node
|
76
|
+
when 'page_break' then convert_page_break node
|
77
|
+
when 'preamble' then convert_preamble node
|
78
|
+
when 'quote' then convert_quote node
|
79
|
+
when 'sidebar' then convert_sidebar node
|
80
|
+
when 'stem' then convert_stem node
|
81
|
+
when 'table' then convert_table node
|
82
|
+
when 'thematic_break' then convert_thematic_break node
|
83
|
+
when 'verse' then convert_verse node
|
84
|
+
when 'video' then convert_video node
|
85
|
+
when 'document' then convert_document node
|
86
|
+
when 'toc' then convert_toc node
|
87
|
+
when 'pass' then convert_pass node
|
88
|
+
when 'audio' then convert_audio node
|
89
|
+
else; super
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
@@ -350,9 +351,10 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
|
|
350
351
|
stitle = section.captioned_title
|
351
352
|
elsif section.numbered && slevel <= sectnumlevels
|
352
353
|
if slevel < 2 && node.document.doctype == 'book'
|
353
|
-
|
354
|
+
case section.sectname
|
355
|
+
when 'chapter'
|
354
356
|
stitle = %(#{(signifier = node.document.attributes['chapter-signifier']) ? "#{signifier} " : ''}#{section.sectnum} #{section.title})
|
355
|
-
|
357
|
+
when 'part'
|
356
358
|
stitle = %(#{(signifier = node.document.attributes['part-signifier']) ? "#{signifier} " : ''}#{section.sectnum nil, ':'} #{section.title})
|
357
359
|
else
|
358
360
|
stitle = %(#{section.sectnum} #{section.title})
|
@@ -383,9 +385,10 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
|
|
383
385
|
title = node.captioned_title
|
384
386
|
elsif node.numbered && level <= (doc_attrs['sectnumlevels'] || 3).to_i
|
385
387
|
if level < 2 && node.document.doctype == 'book'
|
386
|
-
|
388
|
+
case node.sectname
|
389
|
+
when 'chapter'
|
387
390
|
title = %(#{(signifier = doc_attrs['chapter-signifier']) ? "#{signifier} " : ''}#{node.sectnum} #{node.title})
|
388
|
-
|
391
|
+
when 'part'
|
389
392
|
title = %(#{(signifier = doc_attrs['part-signifier']) ? "#{signifier} " : ''}#{node.sectnum nil, ':'} #{node.title})
|
390
393
|
else
|
391
394
|
title = %(#{node.sectnum} #{node.title})
|
@@ -513,16 +516,16 @@ Your browser does not support the audio tag.
|
|
513
516
|
result = []
|
514
517
|
id_attribute = node.id ? %( id="#{node.id}") : ''
|
515
518
|
|
516
|
-
|
519
|
+
case node.style
|
517
520
|
when 'qanda'
|
518
|
-
['qlist', 'qanda', node.role]
|
521
|
+
classes = ['qlist', 'qanda', node.role]
|
519
522
|
when 'horizontal'
|
520
|
-
['hdlist', node.role]
|
523
|
+
classes = ['hdlist', node.role]
|
521
524
|
else
|
522
|
-
['dlist', node.style, node.role]
|
523
|
-
end
|
525
|
+
classes = ['dlist', node.style, node.role]
|
526
|
+
end
|
524
527
|
|
525
|
-
class_attribute = %( class="#{classes.join ' '}")
|
528
|
+
class_attribute = %( class="#{classes.compact.join ' '}")
|
526
529
|
|
527
530
|
result << %(<div#{id_attribute}#{class_attribute}>)
|
528
531
|
result << %(<div class="title">#{node.title}</div>) if node.title?
|
@@ -578,12 +581,11 @@ Your browser does not support the audio tag.
|
|
578
581
|
terms.each do |dt|
|
579
582
|
result << %(<dt#{dt_style_attribute}>#{dt.text}</dt>)
|
580
583
|
end
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
end
|
584
|
+
next unless dd
|
585
|
+
result << '<dd>'
|
586
|
+
result << %(<p>#{dd.text}</p>) if dd.text?
|
587
|
+
result << dd.content if dd.blocks?
|
588
|
+
result << '</dd>'
|
587
589
|
end
|
588
590
|
result << '</dl>'
|
589
591
|
end
|
@@ -624,16 +626,18 @@ Your browser does not support the audio tag.
|
|
624
626
|
target = node.attr 'target'
|
625
627
|
width_attr = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
|
626
628
|
height_attr = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : ''
|
627
|
-
if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE
|
628
|
-
|
629
|
-
if svg
|
629
|
+
if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE
|
630
|
+
if node.option? 'inline'
|
630
631
|
img = (read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>)
|
631
|
-
elsif
|
632
|
-
fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri
|
632
|
+
elsif node.option? 'interactive'
|
633
|
+
fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri node.attr 'fallback'}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>)
|
633
634
|
img = %(<object type="image/svg+xml" data="#{node.image_uri target}"#{width_attr}#{height_attr}>#{fallback}</object>)
|
635
|
+
else
|
636
|
+
img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
|
634
637
|
end
|
638
|
+
else
|
639
|
+
img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
|
635
640
|
end
|
636
|
-
img ||= %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
|
637
641
|
img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
|
638
642
|
id_attr = node.id ? %( id="#{node.id}") : ''
|
639
643
|
classes = ['imageblock']
|
@@ -759,8 +763,8 @@ Your browser does not support the audio tag.
|
|
759
763
|
logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
|
760
764
|
''
|
761
765
|
else
|
762
|
-
|
763
|
-
|
766
|
+
id_attr = node.id ? %( id="#{node.id}") : ''
|
767
|
+
title_el = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
|
764
768
|
%(<div#{id_attr} class="openblock#{style && style != 'open' ? " #{style}" : ''}#{(role = node.role) ? " #{role}" : ''}">
|
765
769
|
#{title_el}<div class="content">
|
766
770
|
#{node.content}
|
@@ -857,20 +861,19 @@ Your browser does not support the audio tag.
|
|
857
861
|
if (stripes = node.attr 'stripes', nil, 'table-stripes')
|
858
862
|
classes << %(stripes-#{stripes})
|
859
863
|
end
|
860
|
-
|
864
|
+
style_attribute = ''
|
861
865
|
if (autowidth = node.option? 'autowidth') && !(node.attr? 'width')
|
862
866
|
classes << 'fit-content'
|
863
867
|
elsif (tablewidth = node.attr 'tablepcwidth') == 100
|
864
868
|
classes << 'stretch'
|
865
869
|
else
|
866
|
-
|
870
|
+
style_attribute = %( style="width: #{tablewidth}%;")
|
867
871
|
end
|
868
872
|
classes << (node.attr 'float') if node.attr? 'float'
|
869
873
|
if (role = node.role)
|
870
874
|
classes << role
|
871
875
|
end
|
872
876
|
class_attribute = %( class="#{classes.join ' '}")
|
873
|
-
style_attribute = styles.empty? ? '' : %( style="#{styles.join ' '}")
|
874
877
|
|
875
878
|
result << %(<table#{id_attribute}#{class_attribute}#{style_attribute}>)
|
876
879
|
result << %(<caption class="title">#{node.captioned_title}</caption>) if node.title?
|
@@ -1033,12 +1036,14 @@ Your browser does not support the audio tag.
|
|
1033
1036
|
end
|
1034
1037
|
start_anchor = (node.attr? 'start') ? %(#at=#{node.attr 'start'}) : ''
|
1035
1038
|
delimiter = ['?']
|
1039
|
+
target, hash = (node.attr 'target').split '/', 2
|
1040
|
+
hash_param = (hash ||= node.attr 'hash') ? %(#{delimiter.pop || '&'}h=#{hash}) : ''
|
1036
1041
|
autoplay_param = (node.option? 'autoplay') ? %(#{delimiter.pop || '&'}autoplay=1) : ''
|
1037
1042
|
loop_param = (node.option? 'loop') ? %(#{delimiter.pop || '&'}loop=1) : ''
|
1038
1043
|
muted_param = (node.option? 'muted') ? %(#{delimiter.pop || '&'}muted=1) : ''
|
1039
1044
|
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
1040
1045
|
<div class="content">
|
1041
|
-
<iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{
|
1046
|
+
<iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{target}#{hash_param}#{autoplay_param}#{loop_param}#{muted_param}#{start_anchor}" frameborder="0"#{(node.option? 'nofullscreen') ? '' : (append_boolean_attribute 'allowfullscreen', xml)}></iframe>
|
1042
1047
|
</div>
|
1043
1048
|
</div>)
|
1044
1049
|
when 'youtube'
|
@@ -1074,7 +1079,7 @@ Your browser does not support the audio tag.
|
|
1074
1079
|
target, playlist = target.split ',', 2
|
1075
1080
|
if (playlist ||= (node.attr 'playlist'))
|
1076
1081
|
# INFO playlist bar doesn't appear in Firefox unless showinfo=1 and modestbranding=1
|
1077
|
-
list_param = %(&playlist=#{playlist})
|
1082
|
+
list_param = %(&playlist=#{target},#{playlist})
|
1078
1083
|
else
|
1079
1084
|
# NOTE for loop to work, playlist must be specified; use VIDEO_ID if there's no explicit playlist
|
1080
1085
|
list_param = has_loop_param ? %(&playlist=#{target}) : ''
|
@@ -1111,14 +1116,17 @@ Your browser does not support the video tag.
|
|
1111
1116
|
else
|
1112
1117
|
attrs = node.role ? %( class="#{node.role}") : ''
|
1113
1118
|
unless (text = node.text)
|
1114
|
-
refid = node.attributes['refid']
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1119
|
+
if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid = node.attributes['refid']] || (refid.nil_or_empty? ? (top = get_root_document node) : nil))
|
1120
|
+
if (@resolving_xref ||= (outer = true)) && outer
|
1121
|
+
if (text = ref.xreftext node.attr 'xrefstyle', nil, true)
|
1122
|
+
text = text.gsub DropAnchorRx, '' if text.include? '<a'
|
1123
|
+
else
|
1124
|
+
text = top ? '[^top]' : %([#{refid}])
|
1125
|
+
end
|
1126
|
+
@resolving_xref = nil
|
1127
|
+
else
|
1128
|
+
text = top ? '[^top]' : %([#{refid}])
|
1120
1129
|
end
|
1121
|
-
@resolving_xref = nil
|
1122
1130
|
else
|
1123
1131
|
text = %([#{refid}])
|
1124
1132
|
end
|
@@ -1175,40 +1183,49 @@ Your browser does not support the video tag.
|
|
1175
1183
|
end
|
1176
1184
|
|
1177
1185
|
def convert_inline_image node
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1186
|
+
target = node.target
|
1187
|
+
if (type = node.type || 'image') == 'icon'
|
1188
|
+
if (icons = node.document.attr 'icons') == 'font'
|
1189
|
+
i_class_attr_val = %(fa fa-#{target})
|
1190
|
+
i_class_attr_val = %(#{i_class_attr_val} fa-#{node.attr 'size'}) if node.attr? 'size'
|
1191
|
+
if node.attr? 'flip'
|
1192
|
+
i_class_attr_val = %(#{i_class_attr_val} fa-flip-#{node.attr 'flip'})
|
1193
|
+
elsif node.attr? 'rotate'
|
1194
|
+
i_class_attr_val = %(#{i_class_attr_val} fa-rotate-#{node.attr 'rotate'})
|
1195
|
+
end
|
1196
|
+
attrs = (node.attr? 'title') ? %( title="#{node.attr 'title'}") : ''
|
1197
|
+
img = %(<i class="#{i_class_attr_val}"#{attrs}></i>)
|
1198
|
+
elsif icons
|
1199
|
+
attrs = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
|
1200
|
+
attrs = %(#{attrs} height="#{node.attr 'height'}") if node.attr? 'height'
|
1201
|
+
attrs = %(#{attrs} title="#{node.attr 'title'}") if node.attr? 'title'
|
1202
|
+
img = %(<img src="#{node.icon_uri target}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
|
1203
|
+
else
|
1204
|
+
img = %([#{node.alt}])
|
1182
1205
|
end
|
1183
|
-
title_attr = (node.attr? 'title') ? %( title="#{node.attr 'title'}") : ''
|
1184
|
-
img = %(<i class="#{class_attr_val}"#{title_attr}></i>)
|
1185
|
-
elsif type == 'icon' && !(node.document.attr? 'icons')
|
1186
|
-
img = %([#{node.alt}])
|
1187
1206
|
else
|
1188
|
-
|
1189
|
-
attrs =
|
1190
|
-
|
1191
|
-
|
1192
|
-
if
|
1207
|
+
attrs = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
|
1208
|
+
attrs = %(#{attrs} height="#{node.attr 'height'}") if node.attr? 'height'
|
1209
|
+
attrs = %(#{attrs} title="#{node.attr 'title'}") if node.attr? 'title'
|
1210
|
+
if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE
|
1211
|
+
if node.option? 'inline'
|
1193
1212
|
img = (read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>)
|
1194
|
-
elsif
|
1195
|
-
fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri
|
1213
|
+
elsif node.option? 'interactive'
|
1214
|
+
fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri node.attr 'fallback'}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>)
|
1196
1215
|
img = %(<object type="image/svg+xml" data="#{node.image_uri target}"#{attrs}>#{fallback}</object>)
|
1216
|
+
else
|
1217
|
+
img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
|
1197
1218
|
end
|
1219
|
+
else
|
1220
|
+
img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
|
1198
1221
|
end
|
1199
|
-
img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
|
1200
1222
|
end
|
1201
1223
|
img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
|
1224
|
+
class_attr_val = type
|
1202
1225
|
if (role = node.role)
|
1203
|
-
|
1204
|
-
class_attr_val = %(#{type} #{node.attr 'float'} #{role})
|
1205
|
-
else
|
1206
|
-
class_attr_val = %(#{type} #{role})
|
1207
|
-
end
|
1226
|
+
class_attr_val = (node.attr? 'float') ? %(#{class_attr_val} #{node.attr 'float'} #{role}) : %(#{class_attr_val} #{role})
|
1208
1227
|
elsif node.attr? 'float'
|
1209
|
-
class_attr_val = %(#{
|
1210
|
-
else
|
1211
|
-
class_attr_val = type
|
1228
|
+
class_attr_val = %(#{class_attr_val} #{node.attr 'float'})
|
1212
1229
|
end
|
1213
1230
|
%(<span class="#{class_attr_val}">#{img}</span>)
|
1214
1231
|
end
|
@@ -1314,9 +1331,20 @@ Your browser does not support the video tag.
|
|
1314
1331
|
</div>)
|
1315
1332
|
end
|
1316
1333
|
|
1334
|
+
def get_root_document node
|
1335
|
+
while (node = node.document).nested?
|
1336
|
+
node = node.parent_document
|
1337
|
+
end
|
1338
|
+
node
|
1339
|
+
end
|
1340
|
+
|
1317
1341
|
# NOTE adapt to older converters that relied on unprefixed method names
|
1318
|
-
def method_missing id, *
|
1319
|
-
!((name = id.to_s).start_with? 'convert_') && (handles? name) ? (send %(convert_#{name}), *
|
1342
|
+
def method_missing id, *args
|
1343
|
+
!((name = id.to_s).start_with? 'convert_') && (handles? name) ? (send %(convert_#{name}), *args) : super
|
1344
|
+
end
|
1345
|
+
|
1346
|
+
def respond_to_missing? id, *options
|
1347
|
+
!((name = id.to_s).start_with? 'convert_') && (handles? name)
|
1320
1348
|
end
|
1321
1349
|
end
|
1322
1350
|
end
|
@@ -5,7 +5,7 @@ module Asciidoctor
|
|
5
5
|
# The output of this converter adheres to the man definition as defined by
|
6
6
|
# groff and uses the manpage output of the DocBook toolchain as a foundation.
|
7
7
|
# That means if you've previously been generating man pages using the a2x tool
|
8
|
-
# from AsciiDoc
|
8
|
+
# from AsciiDoc.py, you should be able to achieve a very similar result
|
9
9
|
# using this converter. Though you'll also get to enjoy some notable
|
10
10
|
# enhancements that have been added since, such as the customizable linkstyle.
|
11
11
|
#
|
@@ -23,7 +23,7 @@ class Converter::ManPageConverter < Converter::Base
|
|
23
23
|
LeadingPeriodRx = /^\./
|
24
24
|
EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) "#{CC_ANY}*?" "#{CC_ANY}*?" )( |[^\s]*)(#{CC_ANY}*?)(?: *#{ESC}\\c)?$/
|
25
25
|
MalformedEscapedMacroRx = /(#{ESC}\\c) (#{ESC}\.(?:URL|MTO) )/
|
26
|
-
MockMacroRx =
|
26
|
+
MockMacroRx = %r(</?(#{ESC}\\[^>]+)>)
|
27
27
|
EmDashCharRefRx = /—(?:​)?/
|
28
28
|
EllipsisCharRefRx = /…(?:​)?/
|
29
29
|
WrappedIndentRx = /#{CG_BLANK}*#{LF}#{CG_BLANK}*/
|
@@ -419,15 +419,7 @@ allbox tab(:);'
|
|
419
419
|
end
|
420
420
|
row_text[row_index] << %(T{#{LF}.sp#{LF})
|
421
421
|
cell_halign = (cell.attr 'halign', 'left').chr
|
422
|
-
if tsec == :
|
423
|
-
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
424
|
-
row_header[row_index][cell_index] << %(#{cell_halign}tB)
|
425
|
-
else
|
426
|
-
row_header[row_index][cell_index + 1] ||= []
|
427
|
-
row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
|
428
|
-
end
|
429
|
-
row_text[row_index] << %(#{manify cell.text, whitespace: :normalize}#{LF})
|
430
|
-
elsif tsec == :body
|
422
|
+
if tsec == :body
|
431
423
|
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
432
424
|
row_header[row_index][cell_index] << %(#{cell_halign}t)
|
433
425
|
else
|
@@ -443,7 +435,7 @@ allbox tab(:);'
|
|
443
435
|
cell_content = manify cell.content.join, whitespace: :normalize
|
444
436
|
end
|
445
437
|
row_text[row_index] << %(#{cell_content}#{LF})
|
446
|
-
|
438
|
+
else # tsec == :head || tsec == :foot
|
447
439
|
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
448
440
|
row_header[row_index][cell_index] << %(#{cell_halign}tB)
|
449
441
|
else
|
@@ -587,8 +579,16 @@ allbox tab(:);'
|
|
587
579
|
%(#{ESC_BS}c#{LF}#{ESC_FS}#{macro} "#{target}" "#{text}" )
|
588
580
|
when :xref
|
589
581
|
unless (text = node.text)
|
590
|
-
refid = node.attributes['refid']
|
591
|
-
|
582
|
+
if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid = node.attributes['refid']] || (refid.nil_or_empty? ? (top = get_root_document node) : nil))
|
583
|
+
if (@resolving_xref ||= (outer = true)) && outer && (text = ref.xreftext node.attr 'xrefstyle', nil, true)
|
584
|
+
text = uppercase_pcdata text if ref.context === :section && ref.level < 2 && text == ref.title
|
585
|
+
else
|
586
|
+
text = top ? '[^top]' : %([#{refid}])
|
587
|
+
end
|
588
|
+
@resolving_xref = nil if outer
|
589
|
+
else
|
590
|
+
text = %([#{refid}])
|
591
|
+
end
|
592
592
|
end
|
593
593
|
text
|
594
594
|
when :ref, :bibref
|
@@ -605,11 +605,11 @@ allbox tab(:);'
|
|
605
605
|
end
|
606
606
|
|
607
607
|
def convert_inline_button node
|
608
|
-
%(
|
608
|
+
%(<#{ESC_BS}fB>[#{ESC_BS}0#{node.text}#{ESC_BS}0]</#{ESC_BS}fP>)
|
609
609
|
end
|
610
610
|
|
611
611
|
def convert_inline_callout node
|
612
|
-
%(
|
612
|
+
%(<#{ESC_BS}fB>(#{node.text})<#{ESC_BS}fP>)
|
613
613
|
end
|
614
614
|
|
615
615
|
def convert_inline_footnote node
|
@@ -629,23 +629,19 @@ allbox tab(:);'
|
|
629
629
|
end
|
630
630
|
|
631
631
|
def convert_inline_kbd node
|
632
|
-
|
633
|
-
keys[0]
|
634
|
-
else
|
635
|
-
keys.join %(#{ESC_BS}0+#{ESC_BS}0)
|
636
|
-
end
|
632
|
+
%[<#{ESC_BS}f(CR>#{(keys = node.attr 'keys').size == 1 ? keys[0] : (keys.join "#{ESC_BS}0+#{ESC_BS}0")}</#{ESC_BS}fP>]
|
637
633
|
end
|
638
634
|
|
639
635
|
def convert_inline_menu node
|
640
636
|
caret = %[#{ESC_BS}0#{ESC_BS}(fc#{ESC_BS}0]
|
641
637
|
menu = node.attr 'menu'
|
642
638
|
if !(submenus = node.attr 'submenus').empty?
|
643
|
-
submenu_path = submenus.map {|item| %(
|
644
|
-
%(
|
639
|
+
submenu_path = submenus.map {|item| %(<#{ESC_BS}fI>#{item}</#{ESC_BS}fP>) }.join caret
|
640
|
+
%(<#{ESC_BS}fI>#{menu}</#{ESC_BS}fP>#{caret}#{submenu_path}#{caret}<#{ESC_BS}fI>#{node.attr 'menuitem'}</#{ESC_BS}fP>)
|
645
641
|
elsif (menuitem = node.attr 'menuitem')
|
646
|
-
%(
|
642
|
+
%(<#{ESC_BS}fI>#{menu}#{caret}#{menuitem}</#{ESC_BS}fP>)
|
647
643
|
else
|
648
|
-
%(
|
644
|
+
%(<#{ESC_BS}fI>#{menu}</#{ESC_BS}fP>)
|
649
645
|
end
|
650
646
|
end
|
651
647
|
|
@@ -668,13 +664,12 @@ allbox tab(:);'
|
|
668
664
|
end
|
669
665
|
|
670
666
|
def self.write_alternate_pages mannames, manvolnum, target
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
end
|
667
|
+
return unless mannames && mannames.size > 1
|
668
|
+
mannames.shift
|
669
|
+
manvolext = %(.#{manvolnum})
|
670
|
+
dir, basename = ::File.split target
|
671
|
+
mannames.each do |manname|
|
672
|
+
::File.write ::File.join(dir, %(#{manname}#{manvolext})), %(.so #{basename}), mode: FILE_WRITE_MODE
|
678
673
|
end
|
679
674
|
end
|
680
675
|
|
@@ -683,7 +678,7 @@ allbox tab(:);'
|
|
683
678
|
def append_footnotes result, node
|
684
679
|
if node.footnotes? && !(node.attr? 'nofootnotes')
|
685
680
|
result << '.SH "NOTES"'
|
686
|
-
node.footnotes.
|
681
|
+
node.footnotes.each do |fn|
|
687
682
|
result << %(.IP [#{fn.index}])
|
688
683
|
# NOTE restore newline in escaped macro that gets removed by normalize_text in substitutor
|
689
684
|
if (text = fn.text).include? %(#{ESC}\\c #{ESC}.)
|
@@ -717,37 +712,40 @@ allbox tab(:);'
|
|
717
712
|
else
|
718
713
|
str = str.tr_s WHITESPACE, ' '
|
719
714
|
end
|
720
|
-
str = str
|
721
|
-
gsub(LiteralBackslashRx) { $1 ? $& : '\\(rs' }
|
722
|
-
gsub(EllipsisCharRefRx, '...')
|
723
|
-
gsub(LeadingPeriodRx, '\\\&.')
|
724
|
-
# drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
|
725
|
-
|
726
|
-
|
727
|
-
gsub('
|
728
|
-
gsub('&
|
729
|
-
gsub('
|
730
|
-
gsub('&#
|
731
|
-
gsub('&#
|
732
|
-
gsub('&#
|
733
|
-
gsub('&#
|
734
|
-
gsub('&#
|
735
|
-
gsub(
|
736
|
-
gsub('&#
|
737
|
-
gsub('&#
|
738
|
-
gsub(
|
739
|
-
gsub('&#
|
740
|
-
gsub('&#
|
741
|
-
gsub('&#
|
742
|
-
gsub('&#
|
743
|
-
gsub('&#
|
744
|
-
gsub('&#
|
745
|
-
gsub('
|
746
|
-
gsub('
|
747
|
-
gsub(
|
748
|
-
gsub(
|
749
|
-
gsub(
|
750
|
-
|
715
|
+
str = str
|
716
|
+
.gsub(LiteralBackslashRx) { $1 ? $& : '\\(rs' } # literal backslash (not a troff escape sequence)
|
717
|
+
.gsub(EllipsisCharRefRx, '...') # horizontal ellipsis
|
718
|
+
.gsub(LeadingPeriodRx, '\\\&.') # leading . is used in troff for macro call or other formatting; replace with \&.
|
719
|
+
.gsub(EscapedMacroRx) do # drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
|
720
|
+
(rest = $3.lstrip).empty? ? %(.#{$1}"#{$2}") : %(.#{$1}"#{$2.rstrip}"#{LF}#{rest})
|
721
|
+
end
|
722
|
+
.gsub('-', '\-')
|
723
|
+
.gsub('<', '<')
|
724
|
+
.gsub('>', '>')
|
725
|
+
.gsub('+', '+') # plus sign; alternately could use \c(pl
|
726
|
+
.gsub(' ', '\~') # non-breaking space
|
727
|
+
.gsub('©', '\(co') # copyright sign
|
728
|
+
.gsub('®', '\(rg') # registered sign
|
729
|
+
.gsub('™', '\(tm') # trademark sign
|
730
|
+
.gsub('°', '\(de') # degree sign
|
731
|
+
.gsub(' ', ' ') # thin space
|
732
|
+
.gsub('–', '\(en') # en dash
|
733
|
+
.gsub(EmDashCharRefRx, '\(em') # em dash
|
734
|
+
.gsub('‘', '\(oq') # left single quotation mark
|
735
|
+
.gsub('’', '\(cq') # right single quotation mark
|
736
|
+
.gsub('“', '\(lq') # left double quotation mark
|
737
|
+
.gsub('”', '\(rq') # right double quotation mark
|
738
|
+
.gsub('←', '\(<-') # leftwards arrow
|
739
|
+
.gsub('→', '\(->') # rightwards arrow
|
740
|
+
.gsub('⇐', '\(lA') # leftwards double arrow
|
741
|
+
.gsub('⇒', '\(rA') # rightwards double arrow
|
742
|
+
.gsub('​', '\:') # zero width space
|
743
|
+
.gsub('&', '&') # literal ampersand (NOTE must take place after any other replacement that includes &)
|
744
|
+
.gsub('\'', '\*(Aq') # apostrophe / neutral single quote
|
745
|
+
.gsub(MockMacroRx, '\1') # mock boundary
|
746
|
+
.gsub(ESC_BS, '\\') # unescape troff backslash (NOTE update if more escapes are added)
|
747
|
+
.gsub(ESC_FS, '.') # unescape full stop in troff commands (NOTE must take place after gsub(LeadingPeriodRx))
|
748
|
+
.rstrip # strip trailing space
|
751
749
|
opts[:append_newline] ? %(#{str}#{LF}) : str
|
752
750
|
end
|
753
751
|
|
@@ -758,5 +756,12 @@ allbox tab(:);'
|
|
758
756
|
def enclose_content node
|
759
757
|
node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content, whitespace: :normalize})
|
760
758
|
end
|
759
|
+
|
760
|
+
def get_root_document node
|
761
|
+
while (node = node.document).nested?
|
762
|
+
node = node.parent_document
|
763
|
+
end
|
764
|
+
node
|
765
|
+
end
|
761
766
|
end
|
762
767
|
end
|