asciidoctor 1.5.8 → 2.0.0.rc.1

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.
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