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