asciidoctor 1.5.3 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of asciidoctor might be problematic. Click here for more details.

Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +67 -5
  3. data/CONTRIBUTING.adoc +171 -0
  4. data/LICENSE.adoc +1 -1
  5. data/README.adoc +62 -30
  6. data/bin/asciidoctor +3 -3
  7. data/bin/asciidoctor-safe +8 -5
  8. data/lib/asciidoctor.rb +10 -21
  9. data/lib/asciidoctor/abstract_block.rb +29 -11
  10. data/lib/asciidoctor/abstract_node.rb +11 -6
  11. data/lib/asciidoctor/callouts.rb +6 -10
  12. data/lib/asciidoctor/cli/options.rb +2 -2
  13. data/lib/asciidoctor/converter.rb +1 -1
  14. data/lib/asciidoctor/converter/docbook5.rb +46 -23
  15. data/lib/asciidoctor/converter/factory.rb +3 -3
  16. data/lib/asciidoctor/converter/html5.rb +27 -24
  17. data/lib/asciidoctor/converter/manpage.rb +72 -61
  18. data/lib/asciidoctor/converter/template.rb +5 -9
  19. data/lib/asciidoctor/document.rb +18 -18
  20. data/lib/asciidoctor/extensions.rb +5 -5
  21. data/lib/asciidoctor/helpers.rb +2 -2
  22. data/lib/asciidoctor/inline.rb +2 -2
  23. data/lib/asciidoctor/parser.rb +59 -59
  24. data/lib/asciidoctor/path_resolver.rb +23 -15
  25. data/lib/asciidoctor/reader.rb +34 -29
  26. data/lib/asciidoctor/section.rb +6 -8
  27. data/lib/asciidoctor/substitutors.rb +2 -2
  28. data/lib/asciidoctor/table.rb +46 -23
  29. data/lib/asciidoctor/version.rb +1 -1
  30. data/man/asciidoctor.1 +11 -11
  31. data/man/asciidoctor.adoc +2 -2
  32. data/test/attributes_test.rb +21 -37
  33. data/test/blocks_test.rb +41 -14
  34. data/test/converter_test.rb +4 -4
  35. data/test/document_test.rb +61 -8
  36. data/test/extensions_test.rb +2 -2
  37. data/test/invoker_test.rb +3 -3
  38. data/test/links_test.rb +13 -3
  39. data/test/lists_test.rb +114 -114
  40. data/test/manpage_test.rb +203 -0
  41. data/test/paragraphs_test.rb +3 -3
  42. data/test/parser_test.rb +4 -4
  43. data/test/preamble_test.rb +1 -1
  44. data/test/reader_test.rb +149 -109
  45. data/test/sections_test.rb +137 -27
  46. data/test/substitutions_test.rb +24 -16
  47. data/test/tables_test.rb +183 -31
  48. data/test/test_helper.rb +10 -22
  49. metadata +9 -6
  50. data/compat/asciidoc.conf +0 -395
  51. data/compat/font-awesome-3-compat.css +0 -397
@@ -140,7 +140,7 @@ module Asciidoctor
140
140
  end
141
141
  nil
142
142
  end
143
-
143
+
144
144
  # Public: Lookup the custom converter registered with this factory to handle
145
145
  # the specified backend.
146
146
  #
@@ -190,7 +190,7 @@ module Asciidoctor
190
190
  return converter
191
191
  end
192
192
  end
193
-
193
+
194
194
  base_converter = case backend
195
195
  when 'html5'
196
196
  unless defined? ::Asciidoctor::Converter::Html5Converter
@@ -215,7 +215,7 @@ module Asciidoctor
215
215
  end
216
216
 
217
217
  return base_converter unless opts.key? :template_dirs
218
-
218
+
219
219
  unless defined? ::Asciidoctor::Converter::TemplateConverter
220
220
  require 'asciidoctor/converter/template'.to_s
221
221
  end
@@ -46,14 +46,13 @@ module Asciidoctor
46
46
  <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"#{slash}><![endif]-->
47
47
  <meta name="viewport" content="width=device-width, initial-scale=1.0"#{slash}>
48
48
  <meta name="generator" content="Asciidoctor #{node.attr 'asciidoctor-version'}"#{slash}>)
49
-
50
49
  result << %(<meta name="application-name" content="#{node.attr 'app-name'}"#{slash}>) if node.attr? 'app-name'
51
50
  result << %(<meta name="description" content="#{node.attr 'description'}"#{slash}>) if node.attr? 'description'
52
51
  result << %(<meta name="keywords" content="#{node.attr 'keywords'}"#{slash}>) if node.attr? 'keywords'
53
52
  result << %(<meta name="author" content="#{node.attr 'authors'}"#{slash}>) if node.attr? 'authors'
54
53
  result << %(<meta name="copyright" content="#{node.attr 'copyright'}"#{slash}>) if node.attr? 'copyright'
55
-
56
54
  result << %(<title>#{node.doctitle :sanitize => true, :use_fallback => true}</title>)
55
+
57
56
  if DEFAULT_STYLESHEET_KEYS.include?(node.attr 'stylesheet')
58
57
  if (webfonts = node.attr 'webfonts')
59
58
  result << %(<link rel="stylesheet" href="#{asset_uri_scheme}//fonts.googleapis.com/css?family=#{webfonts.empty? ? 'Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700' : webfonts}"#{slash}>)
@@ -75,7 +74,7 @@ module Asciidoctor
75
74
 
76
75
  if node.attr? 'icons', 'font'
77
76
  if node.attr? 'iconfont-remote'
78
- result << %(<link rel="stylesheet" href="#{node.attr 'iconfont-cdn', %[#{cdn_base}/font-awesome/4.4.0/css/font-awesome.min.css]}"#{slash}>)
77
+ result << %(<link rel="stylesheet" href="#{node.attr 'iconfont-cdn', %[#{cdn_base}/font-awesome/4.5.0/css/font-awesome.min.css]}"#{slash}>)
79
78
  else
80
79
  iconfont_stylesheet = %(#{node.attr 'iconfont-name', 'font-awesome'}.css)
81
80
  result << %(<link rel="stylesheet" href="#{node.normalize_web_path iconfont_stylesheet, (node.attr 'stylesdir', ''), false}"#{slash}>)
@@ -108,24 +107,20 @@ module Asciidoctor
108
107
 
109
108
  result << '</head>'
110
109
  body_attrs = []
111
- if node.id
112
- body_attrs << %(id="#{node.id}")
113
- end
114
- if (node.attr? 'toc-class') && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
110
+ body_attrs << %(id="#{node.id}") if node.id
111
+ if (sectioned = node.sections?) && (node.attr? 'toc-class') && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
115
112
  body_attrs << %(class="#{node.doctype} #{node.attr 'toc-class'} toc-#{node.attr 'toc-position', 'header'}")
116
113
  else
117
114
  body_attrs << %(class="#{node.doctype}")
118
115
  end
119
- if node.attr? 'max-width'
120
- body_attrs << %(style="max-width: #{node.attr 'max-width'};")
121
- end
116
+ body_attrs << %(style="max-width: #{node.attr 'max-width'};") if node.attr? 'max-width'
122
117
  result << %(<body #{body_attrs * ' '}>)
123
118
 
124
119
  unless node.noheader
125
120
  result << '<div id="header">'
126
121
  if node.doctype == 'manpage'
127
122
  result << %(<h1>#{node.doctitle} Manual Page</h1>)
128
- if (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
123
+ if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
129
124
  result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
130
125
  <div id="toctitle">#{node.attr 'toc-title'}</div>
131
126
  #{outline node}
@@ -170,7 +165,7 @@ module Asciidoctor
170
165
  end
171
166
  end
172
167
 
173
- if (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
168
+ if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
174
169
  result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
175
170
  <div id="toctitle">#{node.attr 'toc-title'}</div>
176
171
  #{outline node}
@@ -199,7 +194,7 @@ module Asciidoctor
199
194
  result << '<div id="footer">'
200
195
  result << '<div id="footer-text">'
201
196
  result << %(#{node.attr 'version-label'} #{node.attr 'revnumber'}#{br}) if node.attr? 'revnumber'
202
- result << %(#{node.attr 'last-update-label'} #{node.attr 'docdatetime'}) if node.attr? 'last-update-label'
197
+ result << %(#{node.attr 'last-update-label'} #{node.attr 'docdatetime'}) if (node.attr? 'last-update-label') && !(node.attr? 'reproducible')
203
198
  result << '</div>'
204
199
  result << '</div>'
205
200
  end
@@ -207,7 +202,7 @@ module Asciidoctor
207
202
  unless (docinfo_content = node.docinfo :footer).empty?
208
203
  result << docinfo_content
209
204
  end
210
-
205
+
211
206
  # Load Javascript at the end of body for performance
212
207
  # See http://www.html5rocks.com/en/tutorials/speed/script-loading/
213
208
  case highlighter
@@ -224,12 +219,13 @@ module Asciidoctor
224
219
  end
225
220
 
226
221
  if node.attr? 'stem'
227
- # IMPORTANT to_s calls on delimiter arrays are intentional for JavaScript compat (emulates JSON.stringify)
228
222
  eqnums_val = node.attr 'eqnums', 'none'
229
223
  eqnums_val = 'AMS' if eqnums_val == ''
230
224
  eqnums_opt = %( equationNumbers: { autoNumber: "#{eqnums_val}" } )
225
+ # IMPORTANT inspect calls on delimiter arrays are intentional for JavaScript compat (emulates JSON.stringify)
231
226
  result << %(<script type="text/x-mathjax-config">
232
227
  MathJax.Hub.Config({
228
+ messageStyle: "none",
233
229
  tex2jax: {
234
230
  inlineMath: [#{INLINE_MATH_DELIMITERS[:latexmath].inspect}],
235
231
  displayMath: [#{BLOCK_MATH_DELIMITERS[:latexmath].inspect}],
@@ -242,7 +238,7 @@ MathJax.Hub.Config({
242
238
  TeX: {#{eqnums_opt}}
243
239
  });
244
240
  </script>
245
- <script src="#{cdn_base}/mathjax/2.5.3/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>)
241
+ <script src="#{cdn_base}/mathjax/2.6.0/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>)
246
242
  end
247
243
 
248
244
  result << '</body>'
@@ -270,7 +266,7 @@ MathJax.Hub.Config({
270
266
  end
271
267
  end
272
268
 
273
- if (node.attr? 'toc') && !['macro', 'preamble'].include?(node.attr 'toc-placement')
269
+ if node.sections? && (node.attr? 'toc') && (toc_p = node.attr 'toc-placement') != 'macro' && toc_p != 'preamble'
274
270
  result << %(<div id="toc" class="toc">
275
271
  <div id="toctitle">#{node.attr 'toc-title'}</div>
276
272
  #{outline node}
@@ -294,10 +290,11 @@ MathJax.Hub.Config({
294
290
  end
295
291
 
296
292
  def outline node, opts = {}
297
- return if (sections = node.sections).empty?
293
+ return unless node.sections?
298
294
  sectnumlevels = opts[:sectnumlevels] || (node.document.attr 'sectnumlevels', 3).to_i
299
295
  toclevels = opts[:toclevels] || (node.document.attr 'toclevels', 2).to_i
300
296
  result = []
297
+ sections = node.sections
301
298
  # FIXME the level for special sections should be set correctly in the model
302
299
  # slevel will only be 0 if we have a book doctype with parts
303
300
  slevel = (first_section = sections[0]).level
@@ -357,7 +354,7 @@ MathJax.Hub.Config({
357
354
  name = node.attr 'name'
358
355
  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : nil
359
356
  caption = if node.document.attr? 'icons'
360
- if node.document.attr? 'icons', 'font'
357
+ if (node.document.attr? 'icons', 'font') && !(node.attr? 'icon')
361
358
  %(<i class="fa icon-#{name}" title="#{node.caption}"></i>)
362
359
  else
363
360
  %(<img src="#{node.icon_uri name}" alt="#{node.caption}"#{@void_element_slash}>)
@@ -711,11 +708,14 @@ Your browser does not support the audio tag.
711
708
  end
712
709
 
713
710
  def preamble node
714
- toc = if (node.attr? 'toc') && (node.attr? 'toc-placement', 'preamble')
715
- %(\n<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
716
- <div id="toctitle">#{node.attr 'toc-title'}</div>
717
- #{outline node.document}
711
+ if (doc = node.document).attr?('toc-placement', 'preamble') && doc.sections? && (doc.attr? 'toc')
712
+ toc = %(
713
+ <div id="toc" class="#{doc.attr 'toc-class', 'toc'}">
714
+ <div id="toctitle">#{doc.attr 'toc-title'}</div>
715
+ #{outline doc}
718
716
  </div>)
717
+ else
718
+ toc = nil
719
719
  end
720
720
 
721
721
  %(<div id="preamble">
@@ -836,7 +836,9 @@ Your browser does not support the audio tag.
836
836
  end
837
837
 
838
838
  def toc node
839
- return '<!-- toc disabled -->' unless (doc = node.document).attr?('toc-placement', 'macro') && doc.attr?('toc')
839
+ unless (doc = node.document).attr?('toc-placement', 'macro') && doc.sections? && (doc.attr? 'toc')
840
+ return '<!-- toc disabled -->'
841
+ end
840
842
 
841
843
  if node.id
842
844
  id_attr = %( id="#{node.id}")
@@ -952,6 +954,7 @@ Your browser does not support the audio tag.
952
954
  asset_uri_scheme = %(#{asset_uri_scheme}:)
953
955
  end
954
956
  rel_param_val = (node.option? 'related') ? 1 : 0
957
+ # NOTE start and end must be seconds (t parameter allows XmYs where X is minutes and Y is seconds)
955
958
  start_param = (node.attr? 'start', nil, false) ? %(&amp;start=#{node.attr 'start'}) : nil
956
959
  end_param = (node.attr? 'end', nil, false) ? %(&amp;end=#{node.attr 'end'}) : nil
957
960
  autoplay_param = (node.option? 'autoplay') ? '&amp;autoplay=1' : nil
@@ -6,48 +6,57 @@ module Asciidoctor
6
6
  #
7
7
  # See http://www.gnu.org/software/groff/manual/html_node/Man-usage.html#Man-usage
8
8
  class Converter::ManPageConverter < Converter::BuiltIn
9
- LF = "\n"
10
- TAB = "\t"
11
- ETAB = ' ' * 8
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)
12
16
 
13
17
  # Converts HTML entity references back to their original form, escapes
14
18
  # special man characters and strips trailing whitespace.
15
19
  #
16
- # Optional features:
17
- # * fold each endline into a single space
18
- # * append a newline
20
+ # It's crucial that text only ever pass through manify once.
21
+ #
22
+ # str - the String to convert
23
+ # opts - an Hash of options to control processing (default: {})
24
+ # * :preserve_space a Boolean that indicates whether to preserve spaces (only expanding tabs) if true
25
+ # or to collapse all adjacent whitespace to a single space if false (default: true)
26
+ # * :append_newline a Boolean that indicates whether to append an endline to the result (default: false)
19
27
  def manify str, opts = {}
20
- append_newline = opts[:append_newline]
21
- preserve_space = opts.fetch :preserve_space, true
22
- str = preserve_space ? str.gsub(TAB, ETAB) : str.tr_s(%(#{LF}#{TAB} ), ' ')
23
- str = str.
24
- gsub(/^\.$/, '\\\&.'). # a lone . is also used in troff to indicate paragraph continuation with visual separator
25
- gsub(/\\$/, '\\(rs'). # a literal backslash at the end of a line
26
- gsub(/^\.((?:URL|MTO) ".*?" ".*?" )( |[^\s]*)(.*?)( *)$/, ".\\1\"\\2\"#{LF}\\3"). # quote last URL argument
27
- gsub(/(?:\A\n|\n *(\n))^\.(URL|MTO) /, "\\1\.\\2 "). # strip blank lines in source that precede a URL
28
- gsub('-', '\\-').
28
+ str = ((opts.fetch :preserve_space, true) ? (str.gsub TAB, ET) : (str.tr_s WHITESPACE, ' ')).
29
+ gsub(/(?:\A|[^#{ESC}])\\/, '\&(rs'). # literal backslash (not a troff escape sequence)
30
+ gsub(/^\./, '\\\&.'). # leading . is used in troff for macro call or other formatting; replace with \&.
31
+ # drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
32
+ gsub(/^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) ".*?" ".*?" )( |[^\s]*)(.*?)(?: *#{ESC}\\c)?$/) {
33
+ (rest = $3.lstrip).empty? ? %(.#$1"#$2") : %(.#$1"#$2"#{LF}#{rest})
34
+ }.
35
+ gsub('-', '\-').
29
36
  gsub('&lt;', '<').
30
37
  gsub('&gt;', '>').
31
- gsub('&#169;', '\\(co'). # copyright sign
32
- gsub('&#174;', '\\(rg'). # registered sign
33
- gsub('&#8482;', '\\(tm'). # trademark sign
38
+ gsub('&#160;', '\~'). # non-breaking space
39
+ gsub('&#169;', '\(co'). # copyright sign
40
+ gsub('&#174;', '\(rg'). # registered sign
41
+ gsub('&#8482;', '\(tm'). # trademark sign
34
42
  gsub('&#8201;', ' '). # thin space
35
- gsub('&#8211;', '\\(en'). # en-dash
36
- gsub(/&#8212(?:;&#8203;)?/, '\\(em'). # em-dash
37
- gsub('&#8216;', '\\(oq'). # left single quotation mark
38
- gsub('&#8217;', '\\(cq'). # right single quotation mark
39
- gsub('&#8220;', '\\(lq'). # left double quotation mark
40
- gsub('&#8221;', '\\(rq'). # right double quotation mark
41
- gsub(/&#8230;(?:&#8203;)?/, '...'). # horizontal ellipsis
42
- gsub('&#8592;', '\\(<-'). # leftwards arrow
43
- gsub('&#8594;', '\\(->'). # rightwards arrow
44
- gsub('&#8656;', '\\(lA'). # leftwards double arrow
45
- gsub('&#8658;', '\\(rA'). # rightwards double arrow
43
+ gsub('&#8211;', '\(en'). # en dash
44
+ gsub(/&#8212(?:;&#8203;)?/, '\(em'). # em dash
45
+ gsub('&#8216;', '\(oq'). # left single quotation mark
46
+ gsub('&#8217;', '\(cq'). # right single quotation mark
47
+ gsub('&#8220;', '\(lq'). # left double quotation mark
48
+ gsub('&#8221;', '\(rq'). # right double quotation mark
49
+ gsub(/&#8230;(?:&#8203;)?/, '...'). # horizontal ellipsis
50
+ gsub('&#8592;', '\(<-'). # leftwards arrow
51
+ gsub('&#8594;', '\(->'). # rightwards arrow
52
+ gsub('&#8656;', '\(lA'). # leftwards double arrow
53
+ gsub('&#8658;', '\(rA'). # rightwards double arrow
46
54
  gsub('&#8203;', '\:'). # zero width space
47
- gsub('\'', '\\(aq'). # apostrophe-quote
55
+ gsub('\'', '\(aq'). # apostrophe-quote
48
56
  gsub(/<\/?BOUNDARY>/, '').# artificial boundary
57
+ gsub(ESC_BS, '\\'). # unescape troff backslash (NOTE update if more escaped are added)
49
58
  rstrip # strip trailing space
50
- append_newline ? %(#{str}#{LF}) : str
59
+ opts[:append_newline] ? %(#{str}#{LF}) : str
51
60
  end
52
61
 
53
62
  def skip_with_warning node, name = nil
@@ -62,17 +71,19 @@ module Asciidoctor
62
71
  mantitle = node.attr 'mantitle'
63
72
  manvolnum = node.attr 'manvolnum', '1'
64
73
  manname = node.attr 'manname', mantitle
74
+ docdate = (node.attr? 'reproducible') ? nil : (node.attr 'docdate')
75
+ # NOTE the first line enables the table (tbl) preprocessor, necessary for non-Linux systems
65
76
  result = [%('\\" t
66
77
  .\\" Title: #{mantitle}
67
78
  .\\" Author: #{(node.attr? 'authors') ? (node.attr 'authors') : '[see the "AUTHORS" section]'}
68
- .\\" Generator: Asciidoctor #{node.attr 'asciidoctor-version'}
69
- .\\" Date: #{docdate = node.attr 'docdate'}
70
- .\\" Manual: #{manual = (node.attr? 'manmanual') ? (node.attr 'manmanual') : '\ \&'}
71
- .\\" Source: #{source = (node.attr? 'mansource') ? (node.attr 'mansource') : '\ \&'}
79
+ .\\" Generator: Asciidoctor #{node.attr 'asciidoctor-version'})]
80
+ result << %(.\\" Date: #{docdate}) if docdate
81
+ result << %(.\\" Manual: #{(manual = node.attr 'manmanual') || '\ \&'}
82
+ .\\" Source: #{(source = node.attr 'mansource') || '\ \&'}
72
83
  .\\" Language: English
73
- .\\")]
84
+ .\\")
74
85
  # TODO add document-level setting to disable capitalization of manname
75
- result << %(.TH "#{manify manname.upcase}" "#{manvolnum}" "#{docdate}" "#{manify source}" "#{manify manual}")
86
+ result << %(.TH "#{manify manname.upcase}" "#{manvolnum}" "#{docdate}" "#{source ? (manify source) : '\ \&'}" "#{manual ? (manify manual) : '\ \&'}")
76
87
  # define portability settings
77
88
  # see http://bugs.debian.org/507673
78
89
  # see http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
@@ -95,10 +106,10 @@ module Asciidoctor
95
106
  # * Third (optional) argument: text that needs to immediately trail
96
107
  # the hyperlink without intervening whitespace
97
108
  result << '.de URL
98
- \\\\$2 \\(laURL: \\\\$1 \\(ra\\\\$3
109
+ \\\\$2 \(laURL: \\\\$1 \(ra\\\\$3
99
110
  ..
100
- .if \n[.g] .mso www.tmac
101
- .LINKSTYLE blue R < >'
111
+ .if \n[.g] .mso www.tmac'
112
+ result << %(.LINKSTYLE #{node.attr 'man-linkstyle', 'blue R < >'})
102
113
 
103
114
  unless node.noheader
104
115
  if node.attr? 'manpurpose'
@@ -187,9 +198,9 @@ Author(s).
187
198
  result << %(.sp
188
199
  .B #{manify node.title}
189
200
  .br) if node.title?
190
- result << %(.TS
191
- tab\(:\);
192
- r lw\(\\n\(.lu*75u/100u\).)
201
+ result << '.TS
202
+ tab(:);
203
+ r lw(\n(.lu*75u/100u).'
193
204
 
194
205
  node.items.each_with_index do |item, index|
195
206
  result << %(\\fB(#{index + 1})\\fP\\h'-2n':T{
@@ -338,7 +349,7 @@ T})
338
349
  .in)
339
350
  end
340
351
  attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
341
- attribution_line = (node.attr? 'attribution') ? %(#{attribution_line}\\\(em #{node.attr 'attribution'}) : nil
352
+ attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
342
353
  result << %(.in +.3i
343
354
  .ll -.3i
344
355
  .nf
@@ -543,7 +554,7 @@ allbox tab(:);'
543
554
  .br)
544
555
  end
545
556
  attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
546
- attribution_line = (node.attr? 'attribution') ? %(#{attribution_line}\\\(em #{node.attr 'attribution'}) : nil
557
+ attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
547
558
  result << %(.sp
548
559
  .nf
549
560
  #{manify node.content}
@@ -573,15 +584,15 @@ allbox tab(:);'
573
584
  if (text = node.text) == target
574
585
  text = nil
575
586
  else
576
- text = text.gsub '"', '\\(dq'
587
+ text = text.gsub '"', %[#{ESC_BS}(dq]
577
588
  end
578
589
  if target.start_with? 'mailto:'
579
590
  macro = 'MTO'
580
- target = target[7..-1].sub('@', '\\(at')
591
+ target = target[7..-1].sub '@', %[#{ESC_BS}(at]
581
592
  else
582
593
  macro = 'URL'
583
594
  end
584
- %(#{LF}.#{macro} "#{target}" "#{text}" )
595
+ %(#{ESC_BS}c#{LF}#{ESC_FS}#{macro} "#{target}" "#{text}" )
585
596
  when :xref
586
597
  refid = (node.attr 'refid') || target
587
598
  node.text || (node.document.references[:ids][refid] || %([#{refid}]))
@@ -599,11 +610,11 @@ allbox tab(:);'
599
610
  end
600
611
 
601
612
  def inline_button node
602
- %(\\fB[\\ #{node.text}\\ ]\\fP)
613
+ %(#{ESC_BS}fB[#{ESC_BS}0#{node.text}#{ESC_BS}0]#{ESC_BS}fP)
603
614
  end
604
615
 
605
616
  def inline_callout node
606
- %[\\fB(#{node.text})\\fP]
617
+ %(#{ESC_BS}fB(#{node.text})#{ESC_BS}fP)
607
618
  end
608
619
 
609
620
  # TODO supposedly groff has footnotes, but we're in search of an example
@@ -629,20 +640,20 @@ allbox tab(:);'
629
640
  if (keys = node.attr 'keys').size == 1
630
641
  keys[0]
631
642
  else
632
- keys.join '\ +\ '
643
+ keys.join %(#{ESC_BS}0+#{ESC_BS}0)
633
644
  end
634
645
  end
635
646
 
636
647
  def inline_menu node
637
- caret = '\ \(fc\ '
648
+ caret = %[#{ESC_BS}0#{ESC_BS}(fc#{ESC_BS}0]
638
649
  menu = node.attr 'menu'
639
650
  if !(submenus = node.attr 'submenus').empty?
640
- submenu_path = submenus.map {|item| %(\\fI#{item}\\fP) }.join caret
641
- %(\\fI#{menu}\\fP#{caret}#{submenu_path}#{caret}\\fI#{node.attr 'menuitem'}\\fP)
651
+ submenu_path = submenus.map {|item| %(#{ESC_BS}fI#{item}#{ESC_BS}fP) }.join caret
652
+ %(#{ESC_BS}fI#{menu}#{ESC_BS}fP#{caret}#{submenu_path}#{caret}#{ESC_BS}fI#{node.attr 'menuitem'}#{ESC_BS}fP)
642
653
  elsif (menuitem = node.attr 'menuitem')
643
- %(\\fI#{menu}#{caret}#{menuitem}\\fP)
654
+ %(#{ESC_BS}fI#{menu}#{caret}#{menuitem}#{ESC_BS}fP)
644
655
  else
645
- %(\\fI#{menu}\\fP)
656
+ %(#{ESC_BS}fI#{menu}#{ESC_BS}fP)
646
657
  end
647
658
  end
648
659
 
@@ -650,15 +661,15 @@ allbox tab(:);'
650
661
  def inline_quoted node
651
662
  case node.type
652
663
  when :emphasis
653
- %[\\fI<BOUNDARY>#{node.text}</BOUNDARY>\\fP]
664
+ %(#{ESC_BS}fI<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP)
654
665
  when :strong
655
- %[\\fB<BOUNDARY>#{node.text}</BOUNDARY>\\fP]
666
+ %(#{ESC_BS}fB<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP)
656
667
  when :monospaced
657
- %[\\f[CR]<BOUNDARY>#{node.text}</BOUNDARY>\\fP]
668
+ %(#{ESC_BS}f[CR]<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP)
658
669
  when :single
659
- %[\\(oq<BOUNDARY>#{node.text}</BOUNDARY>\\(cq]
670
+ %[#{ESC_BS}(oq<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}(cq]
660
671
  when :double
661
- %[\\(lq<BOUNDARY>#{node.text}</BOUNDARY>\\(rq]
672
+ %[#{ESC_BS}(lq<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}(rq]
662
673
  else
663
674
  node.text
664
675
  end
@@ -148,7 +148,7 @@ module Asciidoctor
148
148
  end
149
149
 
150
150
  # Internal: Creates a convert method for the specified name that delegates to the specified template.
151
- #
151
+ #
152
152
  # Returns nothing
153
153
  def create_handler name, template
154
154
  metaclass = class << self; self; end
@@ -250,14 +250,10 @@ module Asciidoctor
250
250
  template_class = ::Tilt
251
251
  extra_engine_options = {}
252
252
  if ext_name == 'slim'
253
- unless defined? ::Slim
254
- # slim doesn't get loaded by Tilt, so we have to load it explicitly
255
- Helpers.require_library 'slim'
256
- if @safe && ::Slim::VERSION >= '3.0'
257
- slim_asciidoc_opts = (@engine_options[:slim][:asciidoc] ||= {})
258
- slim_asciidoc_opts[:safe] ||= @safe
259
- end
260
- end
253
+ # slim doesn't get loaded by Tilt, so we have to load it explicitly
254
+ Helpers.require_library 'slim' unless defined? ::Slim
255
+ # align safe mode of AsciiDoc embedded in Slim template with safe mode of current document
256
+ (@engine_options[:slim][:asciidoc] ||= {})[:safe] ||= @safe if @safe && ::Slim::VERSION >= '3.0'
261
257
  # load include plugin when using Slim >= 2.1
262
258
  require 'slim/include' unless (defined? ::Slim::Include) || ::Slim::VERSION < '2.1'
263
259
  elsif ext_name == 'erb'