asciidoctor 2.0.10 → 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 +294 -30
- data/LICENSE +1 -1
- data/README-de.adoc +16 -20
- data/README-fr.adoc +15 -22
- data/README-jp.adoc +15 -26
- data/README-zh_CN.adoc +21 -25
- data/README.adoc +161 -138
- data/asciidoctor.gemspec +6 -13
- 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 +8 -7
- 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-th.adoc +23 -0
- data/data/locale/attributes-tr.adoc +4 -3
- data/data/locale/attributes-uk.adoc +6 -5
- data/data/locale/attributes-vi.adoc +23 -0
- 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 +76 -76
- data/data/stylesheets/coderay-asciidoctor.css +9 -9
- data/lib/asciidoctor/abstract_block.rb +20 -13
- data/lib/asciidoctor/abstract_node.rb +23 -12
- data/lib/asciidoctor/attribute_list.rb +64 -72
- data/lib/asciidoctor/block.rb +6 -6
- data/lib/asciidoctor/cli/invoker.rb +3 -2
- data/lib/asciidoctor/cli/options.rb +32 -31
- data/lib/asciidoctor/convert.rb +168 -162
- data/lib/asciidoctor/converter/docbook5.rb +49 -34
- data/lib/asciidoctor/converter/html5.rb +180 -139
- data/lib/asciidoctor/converter/manpage.rb +118 -90
- data/lib/asciidoctor/converter/template.rb +15 -13
- data/lib/asciidoctor/converter.rb +19 -16
- data/lib/asciidoctor/core_ext/hash/merge.rb +1 -1
- data/lib/asciidoctor/document.rb +77 -86
- data/lib/asciidoctor/extensions.rb +22 -16
- data/lib/asciidoctor/helpers.rb +20 -15
- data/lib/asciidoctor/list.rb +2 -6
- data/lib/asciidoctor/load.rb +103 -101
- data/lib/asciidoctor/logging.rb +10 -8
- data/lib/asciidoctor/parser.rb +211 -220
- data/lib/asciidoctor/path_resolver.rb +17 -15
- data/lib/asciidoctor/reader.rb +87 -79
- data/lib/asciidoctor/rx.rb +9 -7
- data/lib/asciidoctor/section.rb +7 -0
- data/lib/asciidoctor/substitutors.rb +167 -148
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +3 -2
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +13 -5
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +19 -11
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +35 -20
- data/lib/asciidoctor/syntax_highlighter.rb +16 -16
- data/lib/asciidoctor/table.rb +70 -43
- data/lib/asciidoctor/timings.rb +3 -3
- data/lib/asciidoctor/version.rb +1 -1
- data/lib/asciidoctor.rb +45 -19
- data/man/asciidoctor.1 +29 -31
- data/man/asciidoctor.adoc +35 -29
- metadata +17 -70
@@ -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 = %r(</?(#{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?
|
@@ -412,15 +419,7 @@ allbox tab(:);'
|
|
412
419
|
end
|
413
420
|
row_text[row_index] << %(T{#{LF}.sp#{LF})
|
414
421
|
cell_halign = (cell.attr 'halign', 'left').chr
|
415
|
-
if tsec == :
|
416
|
-
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
417
|
-
row_header[row_index][cell_index] << %(#{cell_halign}tB)
|
418
|
-
else
|
419
|
-
row_header[row_index][cell_index + 1] ||= []
|
420
|
-
row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
|
421
|
-
end
|
422
|
-
row_text[row_index] << %(#{manify cell.text, whitespace: :normalize}#{LF})
|
423
|
-
elsif tsec == :body
|
422
|
+
if tsec == :body
|
424
423
|
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
425
424
|
row_header[row_index][cell_index] << %(#{cell_halign}t)
|
426
425
|
else
|
@@ -436,7 +435,7 @@ allbox tab(:);'
|
|
436
435
|
cell_content = manify cell.content.join, whitespace: :normalize
|
437
436
|
end
|
438
437
|
row_text[row_index] << %(#{cell_content}#{LF})
|
439
|
-
|
438
|
+
else # tsec == :head || tsec == :foot
|
440
439
|
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
441
440
|
row_header[row_index][cell_index] << %(#{cell_halign}tB)
|
442
441
|
else
|
@@ -526,12 +525,13 @@ allbox tab(:);'
|
|
526
525
|
result.join LF
|
527
526
|
end
|
528
527
|
|
529
|
-
# FIXME git uses [verse] for the synopsis; detect this special case
|
530
528
|
def convert_verse node
|
531
529
|
result = []
|
532
|
-
|
530
|
+
if node.title?
|
531
|
+
result << %(.sp
|
533
532
|
.B #{manify node.title}
|
534
|
-
.br)
|
533
|
+
.br)
|
534
|
+
end
|
535
535
|
attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
|
536
536
|
attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
|
537
537
|
result << %(.sp
|
@@ -579,9 +579,13 @@ allbox tab(:);'
|
|
579
579
|
%(#{ESC_BS}c#{LF}#{ESC_FS}#{macro} "#{target}" "#{text}" )
|
580
580
|
when :xref
|
581
581
|
unless (text = node.text)
|
582
|
-
refid = node.attributes['refid']
|
583
|
-
|
584
|
-
|
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
|
585
589
|
else
|
586
590
|
text = %([#{refid}])
|
587
591
|
end
|
@@ -601,14 +605,13 @@ allbox tab(:);'
|
|
601
605
|
end
|
602
606
|
|
603
607
|
def convert_inline_button node
|
604
|
-
%(
|
608
|
+
%(<#{ESC_BS}fB>[#{ESC_BS}0#{node.text}#{ESC_BS}0]</#{ESC_BS}fP>)
|
605
609
|
end
|
606
610
|
|
607
611
|
def convert_inline_callout node
|
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}])
|
@@ -626,57 +629,68 @@ allbox tab(:);'
|
|
626
629
|
end
|
627
630
|
|
628
631
|
def convert_inline_kbd node
|
629
|
-
|
630
|
-
keys[0]
|
631
|
-
else
|
632
|
-
keys.join %(#{ESC_BS}0+#{ESC_BS}0)
|
633
|
-
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>]
|
634
633
|
end
|
635
634
|
|
636
635
|
def convert_inline_menu node
|
637
636
|
caret = %[#{ESC_BS}0#{ESC_BS}(fc#{ESC_BS}0]
|
638
637
|
menu = node.attr 'menu'
|
639
638
|
if !(submenus = node.attr 'submenus').empty?
|
640
|
-
submenu_path = submenus.map {|item| %(
|
641
|
-
%(
|
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>)
|
642
641
|
elsif (menuitem = node.attr 'menuitem')
|
643
|
-
%(
|
642
|
+
%(<#{ESC_BS}fI>#{menu}#{caret}#{menuitem}</#{ESC_BS}fP>)
|
644
643
|
else
|
645
|
-
%(
|
644
|
+
%(<#{ESC_BS}fI>#{menu}</#{ESC_BS}fP>)
|
646
645
|
end
|
647
646
|
end
|
648
647
|
|
649
|
-
# NOTE use fake
|
648
|
+
# NOTE use fake XML elements to prevent creating artificial word boundaries
|
650
649
|
def convert_inline_quoted node
|
651
650
|
case node.type
|
652
651
|
when :emphasis
|
653
|
-
%(
|
652
|
+
%(<#{ESC_BS}fI>#{node.text}</#{ESC_BS}fP>)
|
654
653
|
when :strong
|
655
|
-
%(
|
654
|
+
%(<#{ESC_BS}fB>#{node.text}</#{ESC_BS}fP>)
|
656
655
|
when :monospaced
|
657
|
-
%[
|
656
|
+
%[<#{ESC_BS}f(CR>#{node.text}</#{ESC_BS}fP>]
|
658
657
|
when :single
|
659
|
-
%[
|
658
|
+
%[<#{ESC_BS}(oq>#{node.text}</#{ESC_BS}(cq>]
|
660
659
|
when :double
|
661
|
-
%[
|
660
|
+
%[<#{ESC_BS}(lq>#{node.text}</#{ESC_BS}(rq>]
|
662
661
|
else
|
663
662
|
node.text
|
664
663
|
end
|
665
664
|
end
|
666
665
|
|
667
666
|
def self.write_alternate_pages mannames, manvolnum, target
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
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
|
675
673
|
end
|
676
674
|
end
|
677
675
|
|
678
676
|
private
|
679
677
|
|
678
|
+
def append_footnotes result, node
|
679
|
+
if node.footnotes? && !(node.attr? 'nofootnotes')
|
680
|
+
result << '.SH "NOTES"'
|
681
|
+
node.footnotes.each do |fn|
|
682
|
+
result << %(.IP [#{fn.index}])
|
683
|
+
# NOTE restore newline in escaped macro that gets removed by normalize_text in substitutor
|
684
|
+
if (text = fn.text).include? %(#{ESC}\\c #{ESC}.)
|
685
|
+
text = (manify %(#{text.gsub MalformedEscapedMacroRx, %(\\1#{LF}\\2)} ), whitespace: :normalize).chomp ' '
|
686
|
+
else
|
687
|
+
text = manify text, whitespace: :normalize
|
688
|
+
end
|
689
|
+
result << text
|
690
|
+
end
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
680
694
|
# Converts HTML entity references back to their original form, escapes
|
681
695
|
# special man characters and strips trailing whitespace.
|
682
696
|
#
|
@@ -698,42 +712,56 @@ allbox tab(:);'
|
|
698
712
|
else
|
699
713
|
str = str.tr_s WHITESPACE, ' '
|
700
714
|
end
|
701
|
-
str = str
|
702
|
-
gsub(LiteralBackslashRx
|
703
|
-
gsub(
|
704
|
-
#
|
705
|
-
gsub(EscapedMacroRx)
|
706
|
-
|
707
|
-
|
708
|
-
gsub('
|
709
|
-
gsub('
|
710
|
-
gsub('
|
711
|
-
gsub('&#
|
712
|
-
gsub('&#
|
713
|
-
gsub('&#
|
714
|
-
gsub('&#
|
715
|
-
gsub(
|
716
|
-
gsub('&#
|
717
|
-
gsub('&#
|
718
|
-
gsub('&#
|
719
|
-
gsub(
|
720
|
-
gsub(
|
721
|
-
gsub('&#
|
722
|
-
gsub('&#
|
723
|
-
gsub('&#
|
724
|
-
gsub('&#
|
725
|
-
gsub('&#
|
726
|
-
gsub('
|
727
|
-
gsub('
|
728
|
-
gsub(
|
729
|
-
gsub(
|
730
|
-
gsub(
|
731
|
-
|
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
|
732
749
|
opts[:append_newline] ? %(#{str}#{LF}) : str
|
733
750
|
end
|
734
751
|
|
752
|
+
def uppercase_pcdata string
|
753
|
+
(XMLMarkupRx.match? string) ? string.gsub(PCDATAFilterRx) { $2 ? $2.upcase : $1 } : string.upcase
|
754
|
+
end
|
755
|
+
|
735
756
|
def enclose_content node
|
736
757
|
node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content, whitespace: :normalize})
|
737
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
|
738
766
|
end
|
739
767
|
end
|
@@ -40,13 +40,13 @@ class Converter::TemplateConverter < Converter::Base
|
|
40
40
|
@caches = { scans: {}, templates: {} }
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
end
|
43
|
+
class << self
|
44
|
+
attr_reader :caches
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
def clear_caches
|
47
|
+
@caches[:scans].clear
|
48
|
+
@caches[:templates].clear
|
49
|
+
end
|
50
50
|
end
|
51
51
|
|
52
52
|
def initialize backend, template_dirs, opts = {}
|
@@ -134,12 +134,10 @@ class Converter::TemplateConverter < Converter::Base
|
|
134
134
|
#
|
135
135
|
# Returns the Tilt template object
|
136
136
|
def register name, template
|
137
|
-
|
137
|
+
if (template_cache = @caches[:templates])
|
138
138
|
template_cache[template.file] = template
|
139
|
-
else
|
140
|
-
template
|
141
139
|
end
|
142
|
-
|
140
|
+
@templates[name] = template
|
143
141
|
end
|
144
142
|
|
145
143
|
private
|
@@ -155,6 +153,7 @@ class Converter::TemplateConverter < Converter::Base
|
|
155
153
|
engine = @engine
|
156
154
|
@template_dirs.each do |template_dir|
|
157
155
|
# FIXME need to think about safe mode restrictions here
|
156
|
+
# Ruby 2.3 requires the extra brackets around the path_resolver.system_path method call
|
158
157
|
next unless ::File.directory?(template_dir = (path_resolver.system_path template_dir))
|
159
158
|
|
160
159
|
if engine
|
@@ -186,8 +185,8 @@ class Converter::TemplateConverter < Converter::Base
|
|
186
185
|
else
|
187
186
|
@templates.update scan_dir(template_dir, pattern, @caches[:templates])
|
188
187
|
end
|
189
|
-
nil
|
190
188
|
end
|
189
|
+
nil
|
191
190
|
end
|
192
191
|
|
193
192
|
# Internal: Scan the specified directory for template files matching pattern and instantiate
|
@@ -197,7 +196,7 @@ class Converter::TemplateConverter < Converter::Base
|
|
197
196
|
def scan_dir template_dir, pattern, template_cache = nil
|
198
197
|
result, helpers = {}, nil
|
199
198
|
# Grab the files in the top level of the directory (do not recurse)
|
200
|
-
::Dir.glob(pattern).
|
199
|
+
::Dir.glob(pattern).keep_if {|match| ::File.file? match }.each do |file|
|
201
200
|
if (basename = ::File.basename file) == 'helpers.rb'
|
202
201
|
helpers = file
|
203
202
|
next
|
@@ -257,9 +256,12 @@ class Converter::TemplateConverter < Converter::Base
|
|
257
256
|
if !name || name == 'erb'
|
258
257
|
require 'erb' unless defined? ::ERB.version
|
259
258
|
[::Tilt::ERBTemplate, {}]
|
259
|
+
elsif name == 'erubi'
|
260
|
+
Helpers.require_library 'erubi' unless defined? ::Erubis::Engine
|
261
|
+
[::Tilt::ErubiTemplate, {}]
|
260
262
|
elsif name == 'erubis'
|
261
263
|
Helpers.require_library 'erubis' unless defined? ::Erubis::FastEruby
|
262
|
-
[::Tilt::ErubisTemplate,
|
264
|
+
[::Tilt::ErubisTemplate, engine_class: ::Erubis::FastEruby]
|
263
265
|
else
|
264
266
|
raise ::ArgumentError, %(Unknown ERB implementation: #{name})
|
265
267
|
end
|
@@ -42,7 +42,7 @@ module Asciidoctor
|
|
42
42
|
# puts Asciidoctor.convert_file 'sample.adoc', safe: :safe
|
43
43
|
module Converter
|
44
44
|
autoload :CompositeConverter, %(#{__dir__}/converter/composite)
|
45
|
-
autoload :TemplateConverter, %(#{__dir__}/converter/template)
|
45
|
+
autoload :TemplateConverter, %(#{__dir__}/converter/template) unless RUBY_ENGINE == 'opal'
|
46
46
|
|
47
47
|
# Public: The String backend name that this converter is handling.
|
48
48
|
attr_reader :backend
|
@@ -85,23 +85,24 @@ module Converter
|
|
85
85
|
# Public: Derive backend traits (basebackend, filetype, outfilesuffix, htmlsyntax) from the given backend.
|
86
86
|
#
|
87
87
|
# backend - the String backend from which to derive the traits
|
88
|
+
# basebackend - the String basebackend to use in favor of deriving one from the backend (optional, default: nil)
|
88
89
|
#
|
89
90
|
# Returns the backend traits for the given backend as a [Hash].
|
90
|
-
def self.derive_backend_traits backend
|
91
|
+
def self.derive_backend_traits backend, basebackend = nil
|
91
92
|
return {} unless backend
|
92
|
-
if (
|
93
|
-
|
93
|
+
if (outfilesuffix = DEFAULT_EXTENSIONS[(basebackend ||= backend.sub TrailingDigitsRx, '')])
|
94
|
+
filetype = outfilesuffix.slice 1, outfilesuffix.length
|
94
95
|
else
|
95
|
-
|
96
|
+
outfilesuffix = %(.#{filetype = basebackend})
|
96
97
|
end
|
97
|
-
|
98
|
-
{ basebackend:
|
99
|
-
{ basebackend:
|
98
|
+
filetype == 'html' ?
|
99
|
+
{ basebackend: basebackend, filetype: filetype, htmlsyntax: 'html', outfilesuffix: outfilesuffix } :
|
100
|
+
{ basebackend: basebackend, filetype: filetype, outfilesuffix: outfilesuffix }
|
100
101
|
end
|
101
102
|
|
102
103
|
module BackendTraits
|
103
104
|
def basebackend value = nil
|
104
|
-
value ? (backend_traits[:basebackend] = value) : backend_traits[:basebackend]
|
105
|
+
value ? ((backend_traits value)[:basebackend] = value) : backend_traits[:basebackend]
|
105
106
|
end
|
106
107
|
|
107
108
|
def filetype value = nil
|
@@ -128,15 +129,15 @@ module Converter
|
|
128
129
|
@backend_traits = value || {}
|
129
130
|
end
|
130
131
|
|
131
|
-
def backend_traits
|
132
|
-
@backend_traits ||= Converter.derive_backend_traits @backend
|
132
|
+
def backend_traits basebackend = nil
|
133
|
+
@backend_traits ||= Converter.derive_backend_traits @backend, basebackend
|
133
134
|
end
|
134
135
|
|
135
136
|
alias backend_info backend_traits
|
136
137
|
|
137
138
|
# Deprecated: Use {Converter.derive_backend_traits} instead.
|
138
|
-
def self.derive_backend_traits backend
|
139
|
-
Converter.derive_backend_traits backend
|
139
|
+
def self.derive_backend_traits backend, basebackend = nil
|
140
|
+
Converter.derive_backend_traits backend, basebackend
|
140
141
|
end
|
141
142
|
end
|
142
143
|
|
@@ -365,15 +366,17 @@ module Converter
|
|
365
366
|
# into - The Class into which the {Converter} module is being included.
|
366
367
|
#
|
367
368
|
# Returns nothing.
|
368
|
-
|
369
|
+
def self.included into
|
369
370
|
into.send :include, BackendTraits
|
370
371
|
into.extend Config
|
371
|
-
end
|
372
|
+
end
|
373
|
+
private_class_method :included # use separate declaration for Ruby 2.0.x
|
372
374
|
|
373
375
|
# An abstract base class for defining converters that can be used to convert {AbstractNode} objects in a parsed
|
374
376
|
# AsciiDoc document to a backend format such as HTML or DocBook.
|
375
377
|
class Base
|
376
|
-
include
|
378
|
+
include Logging
|
379
|
+
include Converter
|
377
380
|
|
378
381
|
# Public: Converts an {AbstractNode} by delegating to a method that matches the transform value.
|
379
382
|
#
|
@@ -3,6 +3,6 @@
|
|
3
3
|
# NOTE use `send :prepend` to be nice to Ruby 2.0
|
4
4
|
Hash.send :prepend, (Module.new do
|
5
5
|
def merge *args
|
6
|
-
(len = args.length) < 1 ?
|
6
|
+
(len = args.length) < 1 ? dup : (len > 1 ? args.inject(self) {|acc, arg| acc.merge arg } : (super args[0]))
|
7
7
|
end
|
8
8
|
end) if (Hash.instance_method :merge).arity == 1
|