asciidoctor 1.5.8 → 2.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +162 -17
  3. data/LICENSE +1 -1
  4. data/README-de.adoc +12 -13
  5. data/README-fr.adoc +11 -12
  6. data/README-jp.adoc +11 -12
  7. data/README-zh_CN.adoc +12 -13
  8. data/README.adoc +6 -7
  9. data/asciidoctor.gemspec +19 -24
  10. data/bin/asciidoctor +5 -4
  11. data/data/reference/syntax.adoc +283 -0
  12. data/data/stylesheets/asciidoctor-default.css +56 -52
  13. data/data/stylesheets/coderay-asciidoctor.css +7 -9
  14. data/lib/asciidoctor.rb +171 -232
  15. data/lib/asciidoctor/abstract_block.rb +96 -105
  16. data/lib/asciidoctor/abstract_node.rb +118 -139
  17. data/lib/asciidoctor/attribute_list.rb +10 -14
  18. data/lib/asciidoctor/block.rb +20 -19
  19. data/lib/asciidoctor/callouts.rb +4 -2
  20. data/lib/asciidoctor/cli.rb +3 -2
  21. data/lib/asciidoctor/cli/invoker.rb +14 -21
  22. data/lib/asciidoctor/cli/options.rb +64 -54
  23. data/lib/asciidoctor/converter.rb +357 -185
  24. data/lib/asciidoctor/converter/composite.rb +40 -48
  25. data/lib/asciidoctor/converter/docbook5.rb +604 -640
  26. data/lib/asciidoctor/converter/html5.rb +949 -963
  27. data/lib/asciidoctor/converter/manpage.rb +569 -548
  28. data/lib/asciidoctor/converter/template.rb +231 -272
  29. data/lib/asciidoctor/core_ext.rb +5 -18
  30. data/lib/asciidoctor/core_ext/float/truncate.rb +19 -0
  31. data/lib/asciidoctor/core_ext/match_data/names.rb +7 -0
  32. data/lib/asciidoctor/core_ext/nil_or_empty.rb +1 -0
  33. data/lib/asciidoctor/core_ext/regexp/is_match.rb +4 -2
  34. data/lib/asciidoctor/document.rb +399 -377
  35. data/lib/asciidoctor/extensions.rb +72 -140
  36. data/lib/asciidoctor/helpers.rb +122 -83
  37. data/lib/asciidoctor/inline.rb +5 -1
  38. data/lib/asciidoctor/list.rb +13 -11
  39. data/lib/asciidoctor/logging.rb +17 -16
  40. data/lib/asciidoctor/parser.rb +390 -423
  41. data/lib/asciidoctor/path_resolver.rb +10 -5
  42. data/lib/asciidoctor/reader.rb +286 -263
  43. data/lib/asciidoctor/rouge_ext.rb +39 -0
  44. data/lib/asciidoctor/section.rb +9 -8
  45. data/lib/asciidoctor/stylesheets.rb +19 -37
  46. data/lib/asciidoctor/substitutors.rb +364 -509
  47. data/lib/asciidoctor/syntax_highlighter.rb +238 -0
  48. data/lib/asciidoctor/syntax_highlighter/coderay.rb +87 -0
  49. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +26 -0
  50. data/lib/asciidoctor/syntax_highlighter/html_pipeline.rb +10 -0
  51. data/lib/asciidoctor/syntax_highlighter/prettify.rb +27 -0
  52. data/lib/asciidoctor/syntax_highlighter/pygments.rb +149 -0
  53. data/lib/asciidoctor/syntax_highlighter/rouge.rb +129 -0
  54. data/lib/asciidoctor/table.rb +73 -66
  55. data/lib/asciidoctor/timings.rb +4 -2
  56. data/lib/asciidoctor/version.rb +2 -1
  57. data/lib/asciidoctor/writer.rb +30 -0
  58. data/man/asciidoctor.1 +19 -15
  59. data/man/asciidoctor.adoc +14 -12
  60. metadata +69 -216
  61. data/CONTRIBUTING.adoc +0 -185
  62. data/Gemfile +0 -60
  63. data/Rakefile +0 -129
  64. data/bin/asciidoctor-safe +0 -15
  65. data/features/open_block.feature +0 -92
  66. data/features/pass_block.feature +0 -66
  67. data/features/step_definitions.rb +0 -49
  68. data/features/text_formatting.feature +0 -57
  69. data/features/xref.feature +0 -1039
  70. data/lib/asciidoctor/converter/base.rb +0 -59
  71. data/lib/asciidoctor/converter/docbook45.rb +0 -93
  72. data/lib/asciidoctor/converter/factory.rb +0 -226
  73. data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +0 -6
  74. data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +0 -5
  75. data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +0 -4
  76. data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +0 -6
  77. data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +0 -5
  78. data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +0 -6
  79. data/lib/asciidoctor/core_ext/1.8.7/string/limit_bytesize.rb +0 -29
  80. data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +0 -6
  81. data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +0 -6
  82. data/lib/asciidoctor/core_ext/string/limit_bytesize.rb +0 -10
  83. data/test/api_test.rb +0 -1240
  84. data/test/attribute_list_test.rb +0 -242
  85. data/test/attributes_test.rb +0 -1623
  86. data/test/blocks_test.rb +0 -3870
  87. data/test/converter_test.rb +0 -470
  88. data/test/document_test.rb +0 -1853
  89. data/test/extensions_test.rb +0 -1560
  90. data/test/fixtures/asciidoc_index.txt +0 -521
  91. data/test/fixtures/basic-docinfo-footer.html +0 -6
  92. data/test/fixtures/basic-docinfo-footer.xml +0 -8
  93. data/test/fixtures/basic-docinfo.html +0 -1
  94. data/test/fixtures/basic-docinfo.xml +0 -4
  95. data/test/fixtures/basic.asciidoc +0 -5
  96. data/test/fixtures/chapter-a.adoc +0 -3
  97. data/test/fixtures/child-include.adoc +0 -5
  98. data/test/fixtures/circle.svg +0 -9
  99. data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +0 -6
  100. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +0 -6
  101. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +0 -1
  102. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +0 -3
  103. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +0 -5
  104. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +0 -6
  105. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +0 -3
  106. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +0 -5
  107. data/test/fixtures/custom-docinfodir/basic-docinfo.html +0 -1
  108. data/test/fixtures/custom-docinfodir/docinfo.html +0 -1
  109. data/test/fixtures/docinfo-footer.html +0 -1
  110. data/test/fixtures/docinfo-footer.xml +0 -9
  111. data/test/fixtures/docinfo.html +0 -1
  112. data/test/fixtures/docinfo.xml +0 -3
  113. data/test/fixtures/doctime-localtime.adoc +0 -2
  114. data/test/fixtures/dot.gif +0 -0
  115. data/test/fixtures/encoding.asciidoc +0 -13
  116. data/test/fixtures/file-with-missing-include.adoc +0 -1
  117. data/test/fixtures/grandchild-include.adoc +0 -3
  118. data/test/fixtures/hello-asciidoctor.pdf +0 -69
  119. data/test/fixtures/include-file.asciidoc +0 -24
  120. data/test/fixtures/include-file.jsx +0 -8
  121. data/test/fixtures/include-file.ml +0 -3
  122. data/test/fixtures/include-file.xml +0 -5
  123. data/test/fixtures/lists.adoc +0 -96
  124. data/test/fixtures/master.adoc +0 -5
  125. data/test/fixtures/mismatched-end-tag.adoc +0 -7
  126. data/test/fixtures/other-chapters.adoc +0 -11
  127. data/test/fixtures/outer-include.adoc +0 -5
  128. data/test/fixtures/parent-include-restricted.adoc +0 -5
  129. data/test/fixtures/parent-include.adoc +0 -5
  130. data/test/fixtures/sample.asciidoc +0 -30
  131. data/test/fixtures/section-a.adoc +0 -4
  132. data/test/fixtures/stylesheets/custom.css +0 -3
  133. data/test/fixtures/subdir/index.adoc +0 -3
  134. data/test/fixtures/subdir/inner-include.adoc +0 -3
  135. data/test/fixtures/subdir/middle-include.adoc +0 -5
  136. data/test/fixtures/subs-docinfo.html +0 -2
  137. data/test/fixtures/subs.adoc +0 -6
  138. data/test/fixtures/tagged-class-enclosed.rb +0 -25
  139. data/test/fixtures/tagged-class.rb +0 -23
  140. data/test/fixtures/tip.gif +0 -0
  141. data/test/fixtures/unclosed-tag.adoc +0 -3
  142. data/test/fixtures/unexpected-end-tag.adoc +0 -4
  143. data/test/invoker_test.rb +0 -745
  144. data/test/links_test.rb +0 -855
  145. data/test/lists_test.rb +0 -5151
  146. data/test/logger_test.rb +0 -211
  147. data/test/manpage_test.rb +0 -660
  148. data/test/options_test.rb +0 -262
  149. data/test/paragraphs_test.rb +0 -562
  150. data/test/parser_test.rb +0 -742
  151. data/test/paths_test.rb +0 -395
  152. data/test/preamble_test.rb +0 -173
  153. data/test/reader_test.rb +0 -2161
  154. data/test/sections_test.rb +0 -3575
  155. data/test/substitutions_test.rb +0 -2066
  156. data/test/tables_test.rb +0 -2036
  157. data/test/test_helper.rb +0 -447
  158. data/test/text_test.rb +0 -309
@@ -1,132 +1,78 @@
1
+ # frozen_string_literal: true
1
2
  module Asciidoctor
2
- # A built-in {Converter} implementation that generates the man page (troff) format.
3
- #
4
- # The output follows the groff man page definition while also trying to be
5
- # consistent with the output produced by the a2x tool from AsciiDoc Python.
6
- #
7
- # See http://www.gnu.org/software/groff/manual/html_node/Man-usage.html#Man-usage
8
- class Converter::ManPageConverter < Converter::BuiltIn
9
- LF = %(\n)
10
- TAB = %(\t)
11
- WHITESPACE = %(#{LF}#{TAB} )
12
- ET = ' ' * 8
13
- ESC = %(\u001b) # troff leader marker
14
- ESC_BS = %(#{ESC}\\) # escaped backslash (indicates troff formatting sequence)
15
- ESC_FS = %(#{ESC}.) # escaped full stop (indicates troff macro)
16
-
17
- LiteralBackslashRx = /(?:\A|[^#{ESC}])\\/
18
- LeadingPeriodRx = /^\./
19
- EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) ".*?" ".*?" )( |[^\s]*)(.*?)(?: *#{ESC}\\c)?$/
20
- MockBoundaryRx = /<\/?BOUNDARY>/
21
- EmDashCharRefRx = /&#8212;(?:&#8203;)?/
22
- EllipsisCharRefRx = /&#8230;(?:&#8203;)?/
23
- WrappedIndentRx = /#{CG_BLANK}*#{LF}#{CG_BLANK}*/
24
-
25
- # Converts HTML entity references back to their original form, escapes
26
- # special man characters and strips trailing whitespace.
27
- #
28
- # It's crucial that text only ever pass through manify once.
29
- #
30
- # str - the String to convert
31
- # opts - an Hash of options to control processing (default: {})
32
- # * :whitespace an enum that indicates how to handle whitespace; supported options are:
33
- # :preserve - preserve spaces (only expanding tabs); :normalize - normalize whitespace
34
- # (remove spaces around newlines); :collapse - collapse adjacent whitespace to a single
35
- # space (default: :collapse)
36
- # * :append_newline a Boolean that indicates whether to append an endline to the result (default: false)
37
- def manify str, opts = {}
38
- case opts.fetch :whitespace, :collapse
39
- when :preserve
40
- str = str.gsub TAB, ET
41
- when :normalize
42
- str = str.gsub WrappedIndentRx, LF
43
- else
44
- str = str.tr_s WHITESPACE, ' '
45
- end
46
- str = str.
47
- gsub(LiteralBackslashRx, '\&(rs'). # literal backslash (not a troff escape sequence)
48
- gsub(LeadingPeriodRx, '\\\&.'). # leading . is used in troff for macro call or other formatting; replace with \&.
49
- # drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
50
- gsub(EscapedMacroRx) { (rest = $3.lstrip).empty? ? %(.#$1"#$2") : %(.#$1"#$2"#{LF}#{rest}) }.
51
- gsub('-', '\-').
52
- gsub('&lt;', '<').
53
- gsub('&gt;', '>').
54
- gsub('&#160;', '\~'). # non-breaking space
55
- gsub('&#169;', '\(co'). # copyright sign
56
- gsub('&#174;', '\(rg'). # registered sign
57
- gsub('&#8482;', '\(tm'). # trademark sign
58
- gsub('&#8201;', ' '). # thin space
59
- gsub('&#8211;', '\(en'). # en dash
60
- gsub(EmDashCharRefRx, '\(em'). # em dash
61
- gsub('&#8216;', '\(oq'). # left single quotation mark
62
- gsub('&#8217;', '\(cq'). # right single quotation mark
63
- gsub('&#8220;', '\(lq'). # left double quotation mark
64
- gsub('&#8221;', '\(rq'). # right double quotation mark
65
- gsub(EllipsisCharRefRx, '...'). # horizontal ellipsis
66
- gsub('&#8592;', '\(<-'). # leftwards arrow
67
- gsub('&#8594;', '\(->'). # rightwards arrow
68
- gsub('&#8656;', '\(lA'). # leftwards double arrow
69
- gsub('&#8658;', '\(rA'). # rightwards double arrow
70
- gsub('&#8203;', '\:'). # zero width space
71
- gsub('&amp;','&'). # literal ampersand (NOTE must take place after any other replacement that includes &)
72
- gsub('\'', '\(aq'). # apostrophe-quote
73
- gsub(MockBoundaryRx, ''). # mock boundary
74
- gsub(ESC_BS, '\\'). # unescape troff backslash (NOTE update if more escapes are added)
75
- gsub(ESC_FS, '.'). # unescape full stop in troff commands (NOTE must take place after gsub(LeadingPeriodRx))
76
- rstrip # strip trailing space
77
- opts[:append_newline] ? %(#{str}#{LF}) : str
78
- end
79
-
80
- def skip_with_warning node, name = nil
81
- logger.warn %(converter missing for #{name || node.node_name} node in manpage backend)
82
- nil
83
- end
3
+ # A built-in {Converter} implementation that generates the man page (troff) format.
4
+ #
5
+ # The output follows the groff man page definition while also trying to be
6
+ # consistent with the output produced by the a2x tool from AsciiDoc Python.
7
+ #
8
+ # See http://www.gnu.org/software/groff/manual/html_node/Man-usage.html#Man-usage
9
+ class Converter::ManPageConverter < Converter::Base
10
+ register_for 'manpage'
11
+
12
+ WHITESPACE = %(#{LF}#{TAB} )
13
+ ET = ' ' * 8
14
+ ESC = ?\u001b # troff leader marker
15
+ ESC_BS = %(#{ESC}\\) # escaped backslash (indicates troff formatting sequence)
16
+ ESC_FS = %(#{ESC}.) # escaped full stop (indicates troff macro)
17
+
18
+ LiteralBackslashRx = /(?:\A|[^#{ESC}])\\/
19
+ LeadingPeriodRx = /^\./
20
+ EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) ".*?" ".*?" )( |[^\s]*)(.*?)(?: *#{ESC}\\c)?$/
21
+ MockBoundaryRx = /<\/?BOUNDARY>/
22
+ EmDashCharRefRx = /&#8212;(?:&#8203;)?/
23
+ EllipsisCharRefRx = /&#8230;(?:&#8203;)?/
24
+ WrappedIndentRx = /#{CG_BLANK}*#{LF}#{CG_BLANK}*/
25
+
26
+ def initialize backend, opts = {}
27
+ @backend = backend
28
+ init_backend_traits basebackend: 'manpage', filetype: 'man', outfilesuffix: '.man', supports_templates: true
29
+ end
84
30
 
85
- def document node
86
- unless node.attr? 'mantitle'
87
- raise 'asciidoctor: ERROR: doctype must be set to manpage when using manpage backend'
88
- end
89
- mantitle = node.attr 'mantitle'
90
- manvolnum = node.attr 'manvolnum', '1'
91
- manname = node.attr 'manname', mantitle
92
- manmanual = node.attr 'manmanual'
93
- mansource = node.attr 'mansource'
94
- docdate = (node.attr? 'reproducible') ? nil : (node.attr 'docdate')
95
- # NOTE the first line enables the table (tbl) preprocessor, necessary for non-Linux systems
96
- result = [%('\\" t
31
+ def document node
32
+ unless node.attr? 'mantitle'
33
+ raise 'asciidoctor: ERROR: doctype must be set to manpage when using manpage backend'
34
+ end
35
+ mantitle = node.attr 'mantitle'
36
+ manvolnum = node.attr 'manvolnum', '1'
37
+ manname = node.attr 'manname', mantitle
38
+ manmanual = node.attr 'manmanual'
39
+ mansource = node.attr 'mansource'
40
+ docdate = (node.attr? 'reproducible') ? nil : (node.attr 'docdate')
41
+ # NOTE the first line enables the table (tbl) preprocessor, necessary for non-Linux systems
42
+ result = [%('\\" t
97
43
  .\\" Title: #{mantitle}
98
44
  .\\" Author: #{(node.attr? 'authors') ? (node.attr 'authors') : '[see the "AUTHOR(S)" section]'}
99
45
  .\\" Generator: Asciidoctor #{node.attr 'asciidoctor-version'})]
100
- result << %(.\\" Date: #{docdate}) if docdate
101
- result << %(.\\" Manual: #{manmanual ? (manmanual.tr_s WHITESPACE, ' ') : '\ \&'}
46
+ result << %(.\\" Date: #{docdate}) if docdate
47
+ result << %(.\\" Manual: #{manmanual ? (manmanual.tr_s WHITESPACE, ' ') : '\ \&'}
102
48
  .\\" Source: #{mansource ? (mansource.tr_s WHITESPACE, ' ') : '\ \&'}
103
49
  .\\" Language: English
104
50
  .\\")
105
- # TODO add document-level setting to disable capitalization of manname
106
- result << %(.TH "#{manify manname.upcase}" "#{manvolnum}" "#{docdate}" "#{mansource ? (manify mansource) : '\ \&'}" "#{manmanual ? (manify manmanual) : '\ \&'}")
107
- # define portability settings
108
- # see http://bugs.debian.org/507673
109
- # see http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
110
- result << '.ie \n(.g .ds Aq \(aq'
111
- result << '.el .ds Aq \''
112
- # set sentence_space_size to 0 to prevent extra space between sentences separated by a newline
113
- # the alternative is to add \& at the end of the line
114
- result << '.ss \n[.ss] 0'
115
- # disable hyphenation
116
- result << '.nh'
117
- # disable justification (adjust text to left margin only)
118
- result << '.ad l'
119
- # define URL macro for portability
120
- # see http://web.archive.org/web/20060102165607/http://people.debian.org/~branden/talks/wtfm/wtfm.pdf
121
- #
122
- # Usage
123
- #
124
- # .URL "http://www.debian.org" "Debian" "."
125
- #
126
- # * First argument: the URL
127
- # * Second argument: text to be hyperlinked
128
- # * Third (optional) argument: text that needs to immediately trail the hyperlink without intervening whitespace
129
- result << '.de URL
51
+ # TODO add document-level setting to disable capitalization of manname
52
+ result << %(.TH "#{_manify manname.upcase}" "#{manvolnum}" "#{docdate}" "#{mansource ? (_manify mansource) : '\ \&'}" "#{manmanual ? (_manify manmanual) : '\ \&'}")
53
+ # define portability settings
54
+ # see http://bugs.debian.org/507673
55
+ # see http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
56
+ result << '.ie \n(.g .ds Aq \(aq'
57
+ result << '.el .ds Aq \''
58
+ # set sentence_space_size to 0 to prevent extra space between sentences separated by a newline
59
+ # the alternative is to add \& at the end of the line
60
+ result << '.ss \n[.ss] 0'
61
+ # disable hyphenation
62
+ result << '.nh'
63
+ # disable justification (adjust text to left margin only)
64
+ result << '.ad l'
65
+ # define URL macro for portability
66
+ # see http://web.archive.org/web/20060102165607/http://people.debian.org/~branden/talks/wtfm/wtfm.pdf
67
+ #
68
+ # Usage
69
+ #
70
+ # .URL "http://www.debian.org" "Debian" "."
71
+ #
72
+ # * First argument: the URL
73
+ # * Second argument: text to be hyperlinked
74
+ # * Third (optional) argument: text that needs to immediately trail the hyperlink without intervening whitespace
75
+ result << '.de URL
130
76
  \\fI\\\\$2\\fP <\\\\$1>\\\\$3
131
77
  ..
132
78
  .als MTO URL
@@ -138,192 +84,208 @@ module Asciidoctor
138
84
  . am MTO
139
85
  . ad l
140
86
  . .'
141
- result << %(. LINKSTYLE #{node.attr 'man-linkstyle', 'blue R < >'})
142
- result << '.\}'
143
-
144
- unless node.noheader
145
- if node.attr? 'manpurpose'
146
- mannames = node.attr 'mannames', [manname]
147
- result << %(.SH "#{(node.attr 'manname-title', 'NAME').upcase}"
148
- #{mannames.map {|n| manify n }.join ', '} \\- #{manify node.attr('manpurpose'), :whitespace => :normalize})
149
- end
87
+ result << %(. LINKSTYLE #{node.attr 'man-linkstyle', 'blue R < >'})
88
+ result << '.\}'
89
+
90
+ unless node.noheader
91
+ if node.attr? 'manpurpose'
92
+ mannames = node.attr 'mannames', [manname]
93
+ result << %(.SH "#{(node.attr 'manname-title', 'NAME').upcase}"
94
+ #{mannames.map {|n| _manify n }.join ', '} \\- #{_manify node.attr('manpurpose'), whitespace: :normalize})
150
95
  end
96
+ end
151
97
 
152
- result << node.content
98
+ result << node.content
153
99
 
154
- # QUESTION should NOTES come after AUTHOR(S)?
155
- if node.footnotes? && !(node.attr? 'nofootnotes')
156
- result << '.SH "NOTES"'
157
- result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
158
- end
100
+ # QUESTION should NOTES come after AUTHOR(S)?
101
+ if node.footnotes? && !(node.attr? 'nofootnotes')
102
+ result << '.SH "NOTES"'
103
+ result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
104
+ end
159
105
 
160
- unless (authors = node.authors).empty?
161
- if authors.size > 1
162
- result << '.SH "AUTHORS"'
163
- authors.each do |author|
164
- result << %(.sp
106
+ unless (authors = node.authors).empty?
107
+ if authors.size > 1
108
+ result << '.SH "AUTHORS"'
109
+ authors.each do |author|
110
+ result << %(.sp
165
111
  #{author.name})
166
- end
167
- else
168
- result << %(.SH "AUTHOR"
112
+ end
113
+ else
114
+ result << %(.SH "AUTHOR"
169
115
  .sp
170
116
  #{authors[0].name})
171
- end
172
117
  end
173
-
174
- result.join LF
175
118
  end
176
119
 
177
- # NOTE embedded doesn't really make sense in the manpage backend
178
- def embedded node
179
- result = [node.content]
180
-
181
- if node.footnotes? && !(node.attr? 'nofootnotes')
182
- result << '.SH "NOTES"'
183
- result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
184
- end
120
+ result.join LF
121
+ end
185
122
 
186
- # QUESTION should we add an AUTHOR(S) section?
123
+ # NOTE embedded doesn't really make sense in the manpage backend
124
+ def embedded node
125
+ result = [node.content]
187
126
 
188
- result.join LF
127
+ if node.footnotes? && !(node.attr? 'nofootnotes')
128
+ result << '.SH "NOTES"'
129
+ result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
189
130
  end
190
131
 
191
- def section node
192
- result = []
193
- if node.level > 1
194
- macro = 'SS'
195
- # QUESTION why captioned title? why not when level == 1?
196
- stitle = node.captioned_title
197
- else
198
- macro = 'SH'
199
- stitle = node.title.upcase
200
- end
201
- result << %(.#{macro} "#{manify stitle}"
132
+ # QUESTION should we add an AUTHOR(S) section?
133
+
134
+ result.join LF
135
+ end
136
+
137
+ def section node
138
+ result = []
139
+ if node.level > 1
140
+ macro = 'SS'
141
+ # QUESTION why captioned title? why not when level == 1?
142
+ stitle = node.captioned_title
143
+ else
144
+ macro = 'SH'
145
+ stitle = node.title.upcase
146
+ end
147
+ result << %(.#{macro} "#{_manify stitle}"
202
148
  #{node.content})
203
- result.join LF
204
- end
149
+ result.join LF
150
+ end
205
151
 
206
- def admonition node
207
- result = []
208
- result << %(.if n .sp
152
+ def admonition node
153
+ result = []
154
+ result << %(.if n .sp
209
155
  .RS 4
210
156
  .it 1 an-trap
211
157
  .nr an-no-space-flag 1
212
158
  .nr an-break-flag 1
213
159
  .br
214
160
  .ps +1
215
- .B #{node.attr 'textlabel'}#{node.title? ? "\\fP: #{manify node.title}" : ''}
161
+ .B #{node.attr 'textlabel'}#{node.title? ? "\\fP: #{_manify node.title}" : ''}
216
162
  .ps -1
217
163
  .br
218
- #{resolve_content node}
164
+ #{_enclose_content node}
219
165
  .sp .5v
220
166
  .RE)
221
- result.join LF
222
- end
223
-
224
- alias audio skip_with_warning
167
+ result.join LF
168
+ end
225
169
 
226
- def colist node
227
- result = []
228
- result << %(.sp
229
- .B #{manify node.title}
170
+ def colist node
171
+ result = []
172
+ result << %(.sp
173
+ .B #{_manify node.title}
230
174
  .br) if node.title?
231
- result << '.TS
175
+ result << '.TS
232
176
  tab(:);
233
177
  r lw(\n(.lu*75u/100u).'
234
178
 
235
- num = 0
236
- node.items.each do |item|
237
- result << %(\\fB(#{num += 1})\\fP\\h'-2n':T{)
238
- result << (manify item.text, :whitespace => :normalize)
239
- result << item.content if item.blocks?
240
- result << 'T}'
241
- end
242
- result << '.TE'
243
- result.join LF
179
+ num = 0
180
+ node.items.each do |item|
181
+ result << %(\\fB(#{num += 1})\\fP\\h'-2n':T{)
182
+ result << (_manify item.text, whitespace: :normalize)
183
+ result << item.content if item.blocks?
184
+ result << 'T}'
244
185
  end
186
+ result << '.TE'
187
+ result.join LF
188
+ end
245
189
 
246
- # TODO implement horizontal (if it makes sense)
247
- def dlist node
248
- result = []
249
- result << %(.sp
250
- .B #{manify node.title}
190
+ # TODO implement horizontal (if it makes sense)
191
+ def dlist node
192
+ result = []
193
+ result << %(.sp
194
+ .B #{_manify node.title}
251
195
  .br) if node.title?
252
- counter = 0
253
- node.items.each do |terms, dd|
254
- counter += 1
255
- case node.style
256
- when 'qanda'
257
- result << %(.sp
258
- #{counter}. #{manify [*terms].map {|dt| dt.text }.join ' '}
196
+ counter = 0
197
+ node.items.each do |terms, dd|
198
+ counter += 1
199
+ case node.style
200
+ when 'qanda'
201
+ result << %(.sp
202
+ #{counter}. #{_manify terms.map {|dt| dt.text }.join ' '}
259
203
  .RS 4)
260
- else
261
- result << %(.sp
262
- #{manify [*terms].map {|dt| dt.text }.join(', '), :whitespace => :normalize}
204
+ else
205
+ result << %(.sp
206
+ #{_manify terms.map {|dt| dt.text }.join(', '), whitespace: :normalize}
263
207
  .RS 4)
264
- end
265
- if dd
266
- result << (manify dd.text, :whitespace => :normalize) if dd.text?
267
- result << dd.content if dd.blocks?
268
- end
269
- result << '.RE'
270
208
  end
271
- result.join LF
209
+ if dd
210
+ result << (_manify dd.text, whitespace: :normalize) if dd.text?
211
+ result << dd.content if dd.blocks?
212
+ end
213
+ result << '.RE'
272
214
  end
215
+ result.join LF
216
+ end
273
217
 
274
- def example node
275
- result = []
276
- result << %(.sp
277
- .B #{manify node.captioned_title}
278
- .br) if node.title?
279
- result << %(.RS 4
280
- #{resolve_content node}
218
+ def example node
219
+ result = []
220
+ result << (node.title? ? %(.sp
221
+ .B #{_manify node.captioned_title}
222
+ .br) : '.sp')
223
+ result << %(.RS 4
224
+ #{_enclose_content node}
281
225
  .RE)
282
- result.join LF
283
- end
226
+ result.join LF
227
+ end
284
228
 
285
- def floating_title node
286
- %(.SS "#{manify node.title}")
287
- end
229
+ def floating_title node
230
+ %(.SS "#{_manify node.title}")
231
+ end
288
232
 
289
- alias image skip_with_warning
233
+ def image node
234
+ result = []
235
+ result << (node.title? ? %(.sp
236
+ .B #{_manify node.captioned_title}
237
+ .br) : '.sp')
238
+ result << %([#{node.alt}])
239
+ result.join LF
240
+ end
290
241
 
291
- def listing node
292
- result = []
293
- result << %(.sp
294
- .B #{manify node.captioned_title}
242
+ def listing node
243
+ result = []
244
+ result << %(.sp
245
+ .B #{_manify node.captioned_title}
295
246
  .br) if node.title?
296
- result << %(.sp
247
+ result << %(.sp
297
248
  .if n .RS 4
298
249
  .nf
299
- #{manify node.content, :whitespace => :preserve}
250
+ #{_manify node.content, whitespace: :preserve}
300
251
  .fi
301
252
  .if n .RE)
302
- result.join LF
303
- end
253
+ result.join LF
254
+ end
304
255
 
305
- def literal node
306
- result = []
307
- result << %(.sp
308
- .B #{manify node.title}
256
+ def literal node
257
+ result = []
258
+ result << %(.sp
259
+ .B #{_manify node.title}
309
260
  .br) if node.title?
310
- result << %(.sp
261
+ result << %(.sp
311
262
  .if n .RS 4
312
263
  .nf
313
- #{manify node.content, :whitespace => :preserve}
264
+ #{_manify node.content, whitespace: :preserve}
314
265
  .fi
315
266
  .if n .RE)
316
- result.join LF
317
- end
267
+ result.join LF
268
+ end
318
269
 
319
- def olist node
320
- result = []
321
- result << %(.sp
322
- .B #{manify node.title}
270
+ def sidebar node
271
+ result = []
272
+ result << (node.title? ? %(.sp
273
+ .B #{_manify node.title}
274
+ .br) : '.sp')
275
+ result << %(.RS 4
276
+ #{_enclose_content node}
277
+ .RE)
278
+ result.join LF
279
+ end
280
+
281
+ def olist node
282
+ result = []
283
+ result << %(.sp
284
+ .B #{_manify node.title}
323
285
  .br) if node.title?
324
286
 
325
- node.items.each_with_index do |item, idx|
326
- result << %(.sp
287
+ node.items.each_with_index do |item, idx|
288
+ result << %(.sp
327
289
  .RS 4
328
290
  .ie n \\{\\
329
291
  \\h'-04' #{idx + 1}.\\h'+01'\\c
@@ -332,226 +294,223 @@ r lw(\n(.lu*75u/100u).'
332
294
  . sp -1
333
295
  . IP " #{idx + 1}." 4.2
334
296
  .\\}
335
- #{manify item.text, :whitespace => :normalize})
336
- result << item.content if item.blocks?
337
- result << '.RE'
338
- end
339
- result.join LF
297
+ #{_manify item.text, whitespace: :normalize})
298
+ result << item.content if item.blocks?
299
+ result << '.RE'
340
300
  end
301
+ result.join LF
302
+ end
341
303
 
342
- def open node
343
- case node.style
344
- when 'abstract', 'partintro'
345
- resolve_content node
346
- else
347
- node.content
348
- end
304
+ def open node
305
+ case node.style
306
+ when 'abstract', 'partintro'
307
+ _enclose_content node
308
+ else
309
+ node.content
349
310
  end
311
+ end
350
312
 
351
- # TODO use Page Control https://www.gnu.org/software/groff/manual/html_node/Page-Control.html#Page-Control
352
- alias page_break skip
313
+ # TODO use Page Control https://www.gnu.org/software/groff/manual/html_node/Page-Control.html#Page-Control
314
+ alias page_break _skip
353
315
 
354
- def paragraph node
355
- if node.title?
356
- %(.sp
357
- .B #{manify node.title}
316
+ def paragraph node
317
+ if node.title?
318
+ %(.sp
319
+ .B #{_manify node.title}
358
320
  .br
359
- #{manify node.content, :whitespace => :normalize})
360
- else
361
- %(.sp
362
- #{manify node.content, :whitespace => :normalize})
363
- end
321
+ #{_manify node.content, whitespace: :normalize})
322
+ else
323
+ %(.sp
324
+ #{_manify node.content, whitespace: :normalize})
364
325
  end
326
+ end
365
327
 
366
- alias preamble content
328
+ alias pass _content_only
329
+ alias preamble _content_only
367
330
 
368
- def quote node
369
- result = []
370
- if node.title?
371
- result << %(.sp
331
+ def quote node
332
+ result = []
333
+ if node.title?
334
+ result << %(.sp
372
335
  .RS 3
373
- .B #{manify node.title}
336
+ .B #{_manify node.title}
374
337
  .br
375
338
  .RE)
376
- end
377
- attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
378
- attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
379
- result << %(.RS 3
339
+ end
340
+ attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
341
+ attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
342
+ result << %(.RS 3
380
343
  .ll -.6i
381
- #{resolve_content node}
344
+ #{_enclose_content node}
382
345
  .br
383
346
  .RE
384
347
  .ll)
385
- if attribution_line
386
- result << %(.RS 5
348
+ if attribution_line
349
+ result << %(.RS 5
387
350
  .ll -.10i
388
351
  #{attribution_line}
389
352
  .RE
390
353
  .ll)
391
- end
392
- result.join LF
393
354
  end
355
+ result.join LF
356
+ end
394
357
 
395
- alias sidebar skip_with_warning
396
-
397
- def stem node
398
- title_element = node.title? ? %(.sp
399
- .B #{manify node.title}
400
- .br) : ''
401
- open, close = BLOCK_MATH_DELIMITERS[node.style.to_sym]
402
-
403
- unless ((equation = node.content).start_with? open) && (equation.end_with? close)
404
- equation = %(#{open}#{equation}#{close})
405
- end
406
-
407
- %(#{title_element}#{equation})
408
- end
358
+ def stem node
359
+ result = []
360
+ result << (node.title? ? %(.sp
361
+ .B #{_manify node.title}
362
+ .br) : '.sp')
363
+ open, close = BLOCK_MATH_DELIMITERS[node.style.to_sym]
364
+ if ((equation = node.content).start_with? open) && (equation.end_with? close)
365
+ equation = equation.slice open.length, equation.length - open.length - close.length
366
+ end
367
+ result << %(#{_manify equation, whitespace: :preserve} (#{node.style}))
368
+ result.join LF
369
+ end
409
370
 
410
- # FIXME: The reason this method is so complicated is because we are not
411
- # receiving empty(marked) cells when there are colspans or rowspans. This
412
- # method has to create a map of all cells and in the case of rowspans
413
- # create empty cells as placeholders of the span.
414
- # To fix this, asciidoctor needs to provide an API to tell the user if a
415
- # given cell is being used as a colspan or rowspan.
416
- def table node
417
- result = []
418
- if node.title?
419
- result << %(.sp
371
+ # FIXME: The reason this method is so complicated is because we are not
372
+ # receiving empty(marked) cells when there are colspans or rowspans. This
373
+ # method has to create a map of all cells and in the case of rowspans
374
+ # create empty cells as placeholders of the span.
375
+ # To fix this, asciidoctor needs to provide an API to tell the user if a
376
+ # given cell is being used as a colspan or rowspan.
377
+ def table node
378
+ result = []
379
+ if node.title?
380
+ result << %(.sp
420
381
  .it 1 an-trap
421
382
  .nr an-no-space-flag 1
422
383
  .nr an-break-flag 1
423
384
  .br
424
- .B #{manify node.captioned_title}
385
+ .B #{_manify node.captioned_title}
425
386
  )
426
- end
427
- result << '.TS
387
+ end
388
+ result << '.TS
428
389
  allbox tab(:);'
429
- row_header = []
430
- row_text = []
431
- row_index = 0
432
- node.rows.by_section.each do |tsec, rows|
433
- rows.each do |row|
434
- row_header[row_index] ||= []
435
- row_text[row_index] ||= []
436
- # result << LF
437
- # l left-adjusted
438
- # r right-adjusted
439
- # c centered-adjusted
440
- # n numerical align
441
- # a alphabetic align
442
- # s spanned
443
- # ^ vertically spanned
444
- remaining_cells = row.size
445
- row.each_with_index do |cell, cell_index|
446
- remaining_cells -= 1
447
- row_header[row_index][cell_index] ||= []
448
- # Add an empty cell if this is a rowspan cell
449
- if row_header[row_index][cell_index] == ['^t']
450
- row_text[row_index] << %(T{#{LF}.sp#{LF}T}:)
390
+ row_header = []
391
+ row_text = []
392
+ row_index = 0
393
+ node.rows.to_h.each do |tsec, rows|
394
+ rows.each do |row|
395
+ row_header[row_index] ||= []
396
+ row_text[row_index] ||= []
397
+ # result << LF
398
+ # l left-adjusted
399
+ # r right-adjusted
400
+ # c centered-adjusted
401
+ # n numerical align
402
+ # a alphabetic align
403
+ # s spanned
404
+ # ^ vertically spanned
405
+ remaining_cells = row.size
406
+ row.each_with_index do |cell, cell_index|
407
+ remaining_cells -= 1
408
+ row_header[row_index][cell_index] ||= []
409
+ # Add an empty cell if this is a rowspan cell
410
+ if row_header[row_index][cell_index] == ['^t']
411
+ row_text[row_index] << %(T{#{LF}.sp#{LF}T}:)
412
+ end
413
+ row_text[row_index] << %(T{#{LF}.sp#{LF})
414
+ cell_halign = (cell.attr 'halign', 'left').chr
415
+ if tsec == :head
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)
451
421
  end
452
- row_text[row_index] << %(T{#{LF}.sp#{LF})
453
- cell_halign = (cell.attr 'halign', 'left').chr
454
- if tsec == :head
455
- if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
456
- row_header[row_index][cell_index] << %(#{cell_halign}tB)
457
- else
458
- row_header[row_index][cell_index + 1] ||= []
459
- row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
460
- end
461
- row_text[row_index] << %(#{manify cell.text, :whitespace => :normalize}#{LF})
462
- elsif tsec == :body
463
- if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
464
- row_header[row_index][cell_index] << %(#{cell_halign}t)
465
- else
466
- row_header[row_index][cell_index + 1] ||= []
467
- row_header[row_index][cell_index + 1] << %(#{cell_halign}t)
468
- end
469
- case cell.style
470
- when :asciidoc
471
- cell_content = cell.content
472
- when :literal
473
- cell_content = %(.nf#{LF}#{manify cell.text, :whitespace => :preserve}#{LF}.fi)
474
- when :verse
475
- cell_content = %(.nf#{LF}#{manify cell.text, :whitespace => :preserve}#{LF}.fi)
476
- else
477
- cell_content = manify cell.content.join, :whitespace => :normalize
478
- end
479
- row_text[row_index] << %(#{cell_content}#{LF})
480
- elsif tsec == :foot
422
+ row_text[row_index] << %(#{_manify cell.text, whitespace: :normalize}#{LF})
423
+ elsif tsec == :body
424
+ if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
425
+ row_header[row_index][cell_index] << %(#{cell_halign}t)
426
+ else
427
+ row_header[row_index][cell_index + 1] ||= []
428
+ row_header[row_index][cell_index + 1] << %(#{cell_halign}t)
429
+ end
430
+ case cell.style
431
+ when :asciidoc
432
+ cell_content = cell.content
433
+ when :literal
434
+ cell_content = %(.nf#{LF}#{_manify cell.text, whitespace: :preserve}#{LF}.fi)
435
+ else
436
+ cell_content = _manify cell.content.join, whitespace: :normalize
437
+ end
438
+ row_text[row_index] << %(#{cell_content}#{LF})
439
+ elsif tsec == :foot
440
+ if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
441
+ row_header[row_index][cell_index] << %(#{cell_halign}tB)
442
+ else
443
+ row_header[row_index][cell_index + 1] ||= []
444
+ row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
445
+ end
446
+ row_text[row_index] << %(#{_manify cell.text, whitespace: :normalize}#{LF})
447
+ end
448
+ if cell.colspan && cell.colspan > 1
449
+ (cell.colspan - 1).times do |i|
481
450
  if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
482
- row_header[row_index][cell_index] << %(#{cell_halign}tB)
451
+ row_header[row_index][cell_index + i] << 'st'
483
452
  else
484
- row_header[row_index][cell_index + 1] ||= []
485
- row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
486
- end
487
- row_text[row_index] << %(#{manify cell.text, :whitespace => :normalize}#{LF})
488
- end
489
- if cell.colspan && cell.colspan > 1
490
- (cell.colspan - 1).times do |i|
491
- if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
492
- row_header[row_index][cell_index + i] << 'st'
493
- else
494
- row_header[row_index][cell_index + 1 + i] ||= []
495
- row_header[row_index][cell_index + 1 + i] << 'st'
496
- end
453
+ row_header[row_index][cell_index + 1 + i] ||= []
454
+ row_header[row_index][cell_index + 1 + i] << 'st'
497
455
  end
498
456
  end
499
- if cell.rowspan && cell.rowspan > 1
500
- (cell.rowspan - 1).times do |i|
501
- row_header[row_index + 1 + i] ||= []
502
- if row_header[row_index + 1 + i].empty? || row_header[row_index + 1 + i][cell_index].empty?
503
- row_header[row_index + 1 + i][cell_index] ||= []
504
- row_header[row_index + 1 + i][cell_index] << '^t'
505
- else
506
- row_header[row_index + 1 + i][cell_index + 1] ||= []
507
- row_header[row_index + 1 + i][cell_index + 1] << '^t'
508
- end
457
+ end
458
+ if cell.rowspan && cell.rowspan > 1
459
+ (cell.rowspan - 1).times do |i|
460
+ row_header[row_index + 1 + i] ||= []
461
+ if row_header[row_index + 1 + i].empty? || row_header[row_index + 1 + i][cell_index].empty?
462
+ row_header[row_index + 1 + i][cell_index] ||= []
463
+ row_header[row_index + 1 + i][cell_index] << '^t'
464
+ else
465
+ row_header[row_index + 1 + i][cell_index + 1] ||= []
466
+ row_header[row_index + 1 + i][cell_index + 1] << '^t'
509
467
  end
510
468
  end
511
- if remaining_cells >= 1
512
- row_text[row_index] << 'T}:'
513
- else
514
- row_text[row_index] << %(T}#{LF})
515
- end
516
469
  end
517
- row_index += 1
518
- end unless rows.empty?
519
- end
520
-
521
- #row_header.each do |row|
522
- # result << LF
523
- # row.each_with_index do |cell, i|
524
- # result << (cell.join ' ')
525
- # result << ' ' if row.size > i + 1
526
- # end
527
- #end
528
- # FIXME temporary fix to get basic table to display
529
- result << LF
530
- result << ('lt ' * row_header[0].size).chop
531
-
532
- result << %(.#{LF})
533
- row_text.each do |row|
534
- result << row.join
535
- end
536
- result << %(.TE#{LF}.sp)
537
- result.join
538
- end
470
+ if remaining_cells >= 1
471
+ row_text[row_index] << 'T}:'
472
+ else
473
+ row_text[row_index] << %(T}#{LF})
474
+ end
475
+ end
476
+ row_index += 1
477
+ end unless rows.empty?
478
+ end
479
+
480
+ #row_header.each do |row|
481
+ # result << LF
482
+ # row.each_with_index do |cell, i|
483
+ # result << (cell.join ' ')
484
+ # result << ' ' if row.size > i + 1
485
+ # end
486
+ #end
487
+ # FIXME temporary fix to get basic table to display
488
+ result << LF
489
+ result << ('lt ' * row_header[0].size).chop
490
+
491
+ result << %(.#{LF})
492
+ row_text.each do |row|
493
+ result << row.join
494
+ end
495
+ result << %(.TE#{LF}.sp)
496
+ result.join
497
+ end
539
498
 
540
- def thematic_break node
541
- '.sp
499
+ def thematic_break node
500
+ '.sp
542
501
  .ce
543
502
  \l\'\n(.lu*25u/100u\(ap\''
544
- end
503
+ end
545
504
 
546
- alias toc skip
505
+ alias toc _skip
547
506
 
548
- def ulist node
549
- result = []
550
- result << %(.sp
551
- .B #{manify node.title}
507
+ def ulist node
508
+ result = []
509
+ result << %(.sp
510
+ .B #{_manify node.title}
552
511
  .br) if node.title?
553
- node.items.map {|item|
554
- result << %[.sp
512
+ node.items.map do |item|
513
+ result << %[.sp
555
514
  .RS 4
556
515
  .ie n \\{\\
557
516
  \\h'-04'\\(bu\\h'+03'\\c
@@ -560,159 +519,221 @@ allbox tab(:);'
560
519
  . sp -1
561
520
  . IP \\(bu 2.3
562
521
  .\\}
563
- #{manify item.text, :whitespace => :normalize}]
564
- result << item.content if item.blocks?
565
- result << '.RE'
566
- }
567
- result.join LF
522
+ #{_manify item.text, whitespace: :normalize}]
523
+ result << item.content if item.blocks?
524
+ result << '.RE'
568
525
  end
526
+ result.join LF
527
+ end
569
528
 
570
- # FIXME git uses [verse] for the synopsis; detect this special case
571
- def verse node
572
- result = []
573
- if node.title?
574
- result << %(.sp
575
- .B #{manify node.title}
576
- .br)
577
- end
578
- attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
579
- attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
580
- result << %(.sp
529
+ # FIXME git uses [verse] for the synopsis; detect this special case
530
+ def verse node
531
+ result = []
532
+ result << (node.title? ? %(.sp
533
+ .B #{_manify node.title}
534
+ .br) : '.sp')
535
+ attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
536
+ attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
537
+ result << %(.sp
581
538
  .nf
582
- #{manify node.content, :whitespace => :preserve}
539
+ #{_manify node.content, whitespace: :preserve}
583
540
  .fi
584
541
  .br)
585
- if attribution_line
586
- result << %(.in +.5i
542
+ if attribution_line
543
+ result << %(.in +.5i
587
544
  .ll -.5i
588
545
  #{attribution_line}
589
546
  .in
590
547
  .ll)
591
- end
592
- result.join LF
593
548
  end
549
+ result.join LF
550
+ end
594
551
 
595
- def video node
596
- start_param = (node.attr? 'start', nil, false) ? %(&start=#{node.attr 'start'}) : ''
597
- end_param = (node.attr? 'end', nil, false) ? %(&end=#{node.attr 'end'}) : ''
598
- result = []
599
- result << %(.sp
600
- .B #{manify node.title}
601
- .br) if node.title?
602
- result << %(<#{node.media_uri(node.attr 'target')}#{start_param}#{end_param}> (video))
603
- result.join LF
604
- end
552
+ def video node
553
+ start_param = (node.attr? 'start') ? %(&start=#{node.attr 'start'}) : ''
554
+ end_param = (node.attr? 'end') ? %(&end=#{node.attr 'end'}) : ''
555
+ result = []
556
+ result << (node.title? ? %(.sp
557
+ .B #{_manify node.title}
558
+ .br) : '.sp')
559
+ result << %(<#{node.media_uri(node.attr 'target')}#{start_param}#{end_param}> (video))
560
+ result.join LF
561
+ end
605
562
 
606
- def inline_anchor node
607
- target = node.target
608
- case node.type
609
- when :link
610
- if target.start_with? 'mailto:'
611
- macro = 'MTO'
612
- target = target.slice 7, target.length
613
- else
614
- macro = 'URL'
615
- end
616
- if (text = node.text) == target
617
- text = ''
563
+ def inline_anchor node
564
+ target = node.target
565
+ case node.type
566
+ when :link
567
+ if target.start_with? 'mailto:'
568
+ macro = 'MTO'
569
+ target = target.slice 7, target.length
570
+ else
571
+ macro = 'URL'
572
+ end
573
+ if (text = node.text) == target
574
+ text = ''
575
+ else
576
+ text = text.gsub '"', %[#{ESC_BS}(dq]
577
+ end
578
+ target = target.sub '@', %[#{ESC_BS}(at] if macro == 'MTO'
579
+ %(#{ESC_BS}c#{LF}#{ESC_FS}#{macro} "#{target}" "#{text}" )
580
+ when :xref
581
+ unless (text = node.text)
582
+ refid = node.attributes['refid']
583
+ if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid])
584
+ text = (ref.xreftext node.attr('xrefstyle', nil, true)) || %([#{refid}])
618
585
  else
619
- text = text.gsub '"', %[#{ESC_BS}(dq]
586
+ text = %([#{refid}])
620
587
  end
621
- target = target.sub '@', %[#{ESC_BS}(at] if macro == 'MTO'
622
- %(#{ESC_BS}c#{LF}#{ESC_FS}#{macro} "#{target}" "#{text}" )
623
- when :xref
624
- refid = (node.attr 'refid') || target
625
- node.text || (node.document.catalog[:ids][refid] || %([#{refid}]))
626
- when :ref, :bibref
627
- # These are anchor points, which shouldn't be visible
628
- ''
629
- else
630
- logger.warn %(unknown anchor type: #{node.type.inspect})
631
- nil
632
588
  end
589
+ text
590
+ when :ref, :bibref
591
+ # These are anchor points, which shouldn't be visible
592
+ ''
593
+ else
594
+ logger.warn %(unknown anchor type: #{node.type.inspect})
595
+ nil
633
596
  end
597
+ end
634
598
 
635
- def inline_break node
636
- %(#{node.text}#{LF}#{ESC_FS}br)
637
- end
599
+ def inline_break node
600
+ %(#{node.text}#{LF}#{ESC_FS}br)
601
+ end
638
602
 
639
- def inline_button node
640
- %(#{ESC_BS}fB[#{ESC_BS}0#{node.text}#{ESC_BS}0]#{ESC_BS}fP)
641
- end
603
+ def inline_button node
604
+ %(#{ESC_BS}fB[#{ESC_BS}0#{node.text}#{ESC_BS}0]#{ESC_BS}fP)
605
+ end
642
606
 
643
- def inline_callout node
644
- %(#{ESC_BS}fB(#{node.text})#{ESC_BS}fP)
645
- end
607
+ def inline_callout node
608
+ %(#{ESC_BS}fB(#{node.text})#{ESC_BS}fP)
609
+ end
646
610
 
647
- # TODO supposedly groff has footnotes, but we're in search of an example
648
- def inline_footnote node
649
- if (index = node.attr 'index')
650
- %([#{index}])
651
- elsif node.type == :xref
652
- %([#{node.text}])
653
- end
611
+ # TODO supposedly groff has footnotes, but we're in search of an example
612
+ def inline_footnote node
613
+ if (index = node.attr 'index')
614
+ %([#{index}])
615
+ elsif node.type == :xref
616
+ %([#{node.text}])
654
617
  end
618
+ end
655
619
 
656
- def inline_image node
657
- (node.attr? 'link') ? %([#{node.alt}] <#{node.attr 'link'}>) : %([#{node.alt}])
658
- end
620
+ def inline_image node
621
+ (node.attr? 'link') ? %([#{node.alt}] <#{node.attr 'link'}>) : %([#{node.alt}])
622
+ end
623
+
624
+ def inline_indexterm node
625
+ node.type == :visible ? node.text : ''
626
+ end
659
627
 
660
- def inline_indexterm node
661
- node.type == :visible ? node.text : ''
628
+ def inline_kbd node
629
+ if (keys = node.attr 'keys').size == 1
630
+ keys[0]
631
+ else
632
+ keys.join %(#{ESC_BS}0+#{ESC_BS}0)
662
633
  end
634
+ end
663
635
 
664
- def inline_kbd node
665
- if (keys = node.attr 'keys').size == 1
666
- keys[0]
667
- else
668
- keys.join %(#{ESC_BS}0+#{ESC_BS}0)
669
- end
636
+ def inline_menu node
637
+ caret = %[#{ESC_BS}0#{ESC_BS}(fc#{ESC_BS}0]
638
+ menu = node.attr 'menu'
639
+ if !(submenus = node.attr 'submenus').empty?
640
+ submenu_path = submenus.map {|item| %(#{ESC_BS}fI#{item}#{ESC_BS}fP) }.join caret
641
+ %(#{ESC_BS}fI#{menu}#{ESC_BS}fP#{caret}#{submenu_path}#{caret}#{ESC_BS}fI#{node.attr 'menuitem'}#{ESC_BS}fP)
642
+ elsif (menuitem = node.attr 'menuitem')
643
+ %(#{ESC_BS}fI#{menu}#{caret}#{menuitem}#{ESC_BS}fP)
644
+ else
645
+ %(#{ESC_BS}fI#{menu}#{ESC_BS}fP)
670
646
  end
647
+ end
671
648
 
672
- def inline_menu node
673
- caret = %[#{ESC_BS}0#{ESC_BS}(fc#{ESC_BS}0]
674
- menu = node.attr 'menu'
675
- if !(submenus = node.attr 'submenus').empty?
676
- submenu_path = submenus.map {|item| %(#{ESC_BS}fI#{item}#{ESC_BS}fP) }.join caret
677
- %(#{ESC_BS}fI#{menu}#{ESC_BS}fP#{caret}#{submenu_path}#{caret}#{ESC_BS}fI#{node.attr 'menuitem'}#{ESC_BS}fP)
678
- elsif (menuitem = node.attr 'menuitem')
679
- %(#{ESC_BS}fI#{menu}#{caret}#{menuitem}#{ESC_BS}fP)
680
- else
681
- %(#{ESC_BS}fI#{menu}#{ESC_BS}fP)
682
- end
649
+ # NOTE use fake <BOUNDARY> element to prevent creating artificial word boundaries
650
+ def inline_quoted node
651
+ case node.type
652
+ when :emphasis
653
+ %(#{ESC_BS}fI<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP)
654
+ when :strong
655
+ %(#{ESC_BS}fB<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP)
656
+ when :monospaced
657
+ %[#{ESC_BS}f(CR<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP]
658
+ when :single
659
+ %[#{ESC_BS}(oq<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}(cq]
660
+ when :double
661
+ %[#{ESC_BS}(lq<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}(rq]
662
+ else
663
+ node.text
683
664
  end
665
+ end
684
666
 
685
- # NOTE use fake <BOUNDARY> element to prevent creating artificial word boundaries
686
- def inline_quoted node
687
- case node.type
688
- when :emphasis
689
- %(#{ESC_BS}fI<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP)
690
- when :strong
691
- %(#{ESC_BS}fB<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP)
692
- when :monospaced
693
- %[#{ESC_BS}f(CR<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP]
694
- when :single
695
- %[#{ESC_BS}(oq<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}(cq]
696
- when :double
697
- %[#{ESC_BS}(lq<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}(rq]
698
- else
699
- node.text
667
+ def self.write_alternate_pages mannames, manvolnum, target
668
+ if mannames && mannames.size > 1
669
+ mannames.shift
670
+ manvolext = %(.#{manvolnum})
671
+ dir, basename = ::File.split target
672
+ mannames.each do |manname|
673
+ ::File.write ::File.join(dir, %(#{manname}#{manvolext})), %(.so #{basename}), mode: FILE_WRITE_MODE
700
674
  end
701
675
  end
676
+ end
702
677
 
703
- def resolve_content node
704
- node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content, :whitespace => :normalize})
705
- end
678
+ private
706
679
 
707
- def write_alternate_pages mannames, manvolnum, target
708
- if mannames && mannames.size > 1
709
- mannames.shift
710
- manvolext = %(.#{manvolnum})
711
- dir, basename = ::File.split target
712
- mannames.each do |manname|
713
- ::IO.write ::File.join(dir, %(#{manname}#{manvolext})), %(.so #{basename})
714
- end
715
- end
716
- end
680
+ # Converts HTML entity references back to their original form, escapes
681
+ # special man characters and strips trailing whitespace.
682
+ #
683
+ # It's crucial that text only ever pass through _manify once.
684
+ #
685
+ # str - the String to convert
686
+ # opts - an Hash of options to control processing (default: {})
687
+ # * :whitespace an enum that indicates how to handle whitespace; supported options are:
688
+ # :preserve - preserve spaces (only expanding tabs); :normalize - normalize whitespace
689
+ # (remove spaces around newlines); :collapse - collapse adjacent whitespace to a single
690
+ # space (default: :collapse)
691
+ # * :append_newline a Boolean that indicates whether to append a newline to the result (default: false)
692
+ def _manify str, opts = {}
693
+ case opts.fetch :whitespace, :collapse
694
+ when :preserve
695
+ str = str.gsub TAB, ET
696
+ when :normalize
697
+ str = str.gsub WrappedIndentRx, LF
698
+ else
699
+ str = str.tr_s WHITESPACE, ' '
700
+ end
701
+ str = str.
702
+ gsub(LiteralBackslashRx, '\&(rs'). # literal backslash (not a troff escape sequence)
703
+ gsub(LeadingPeriodRx, '\\\&.'). # leading . is used in troff for macro call or other formatting; replace with \&.
704
+ # drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
705
+ gsub(EscapedMacroRx) { (rest = $3.lstrip).empty? ? %(.#$1"#$2") : %(.#$1"#$2"#{LF}#{rest}) }.
706
+ gsub('-', '\-').
707
+ gsub('&lt;', '<').
708
+ gsub('&gt;', '>').
709
+ gsub('&#160;', '\~'). # non-breaking space
710
+ gsub('&#169;', '\(co'). # copyright sign
711
+ gsub('&#174;', '\(rg'). # registered sign
712
+ gsub('&#8482;', '\(tm'). # trademark sign
713
+ gsub('&#8201;', ' '). # thin space
714
+ gsub('&#8211;', '\(en'). # en dash
715
+ gsub(EmDashCharRefRx, '\(em'). # em dash
716
+ gsub('&#8216;', '\(oq'). # left single quotation mark
717
+ gsub('&#8217;', '\(cq'). # right single quotation mark
718
+ gsub('&#8220;', '\(lq'). # left double quotation mark
719
+ gsub('&#8221;', '\(rq'). # right double quotation mark
720
+ gsub(EllipsisCharRefRx, '...'). # horizontal ellipsis
721
+ gsub('&#8592;', '\(<-'). # leftwards arrow
722
+ gsub('&#8594;', '\(->'). # rightwards arrow
723
+ gsub('&#8656;', '\(lA'). # leftwards double arrow
724
+ gsub('&#8658;', '\(rA'). # rightwards double arrow
725
+ gsub('&#8203;', '\:'). # zero width space
726
+ gsub('&amp;','&'). # literal ampersand (NOTE must take place after any other replacement that includes &)
727
+ gsub('\'', '\(aq'). # apostrophe-quote
728
+ gsub(MockBoundaryRx, ''). # mock boundary
729
+ gsub(ESC_BS, '\\'). # unescape troff backslash (NOTE update if more escapes are added)
730
+ gsub(ESC_FS, '.'). # unescape full stop in troff commands (NOTE must take place after gsub(LeadingPeriodRx))
731
+ rstrip # strip trailing space
732
+ opts[:append_newline] ? %(#{str}#{LF}) : str
717
733
  end
734
+
735
+ def _enclose_content node
736
+ node.content_model == :compound ? node.content : %(.sp#{LF}#{_manify node.content, whitespace: :normalize})
737
+ end
738
+ end
718
739
  end