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
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+ module Asciidoctor
3
+ # Public: A pluggable adapter for integrating a syntax (aka code) highlighter into AsciiDoc processing.
4
+ #
5
+ # There are two types of syntax highlighter adapters. The first performs syntax highlighting during the convert phase.
6
+ # This adapter type must define a highlight? method that returns true. The companion highlight method will then be
7
+ # called to handle the :specialcharacters substitution for source blocks. The second assumes syntax highlighting is
8
+ # performed on the client (e.g., when the HTML document is loaded). This adapter type must define a docinfo? method
9
+ # that returns true. The companion docinfo method will then be called to insert markup into the output document. The
10
+ # docinfo functionality is available to both adapter types.
11
+ #
12
+ # Asciidoctor provides several built-in adapters, including coderay, pygments, rouge, highlight.js, html-pipeline, and
13
+ # prettify. Additional adapters can be registered using SyntaxHighlighter.register or by supplying a custom factory.
14
+ module SyntaxHighlighter
15
+ # Public: Returns the String name of this syntax highlighter for referencing it in messages and option names.
16
+ attr_reader :name
17
+
18
+ def initialize name, backend = 'html5', opts = {}
19
+ @name = @pre_class = name
20
+ end
21
+
22
+ # Public: Indicates whether this syntax highlighter has docinfo (i.e., markup) to insert into the output document at
23
+ # the specified location.
24
+ #
25
+ # location - The Symbol representing the location slot (:head or :footer).
26
+ #
27
+ # Returns a [Boolean] indicating whether the docinfo method should be called for this location.
28
+ def docinfo? location; end
29
+
30
+ # Public: Generates docinfo markup to insert in the output document at the specified location.
31
+ #
32
+ # location - The Symbol representing the location slot (:head or :footer).
33
+ #
34
+ # Return the [String] markup to insert.
35
+ def docinfo location
36
+ raise ::NotImplementedError, %(#{SyntaxHighlighter} subclass #{self.class} must implement the ##{__method__} method since #docinfo? returns true)
37
+ end
38
+
39
+ # Public: Indicates whether highlighting is handled by this syntax highlighter or by the client.
40
+ #
41
+ # Returns a [Boolean] indicating whether the highlight method should be used to handle the :specialchars substitution.
42
+ def highlight?; end
43
+
44
+ # Public: Highlights the specified source when this source block is being converted.
45
+ #
46
+ # If the source contains callout marks, the caller assumes the source remains on the same lines and no closing tags
47
+ # are added to the end of each line. If the source gets shifted by one or more lines, this method must return a
48
+ # tuple containing the highlighted source and the number of lines by which the source was shifted.
49
+ #
50
+ # node - The source Block to syntax highlight.
51
+ # source - The raw source text String of this source block (after preprocessing).
52
+ # lang - The source language String specified on this block (e.g., ruby).
53
+ # opts - A Hash of options that configure the syntax highlighting:
54
+ # :callouts - A Hash of callouts extracted from the source, indexed by line number (1-based) (optional).
55
+ # :css_mode - The Symbol CSS mode (:class or :inline).
56
+ # :highlight_lines - A 1-based Array of Integer line numbers to highlight (aka emphasize) (optional).
57
+ # :number_lines - A Symbol indicating whether lines should be numbered (:table or :inline) (optional).
58
+ # :start_line_number - The starting Integer (1-based) line number (optional, default: 1).
59
+ # :style - The String style (aka theme) to use for colorizing the code (optional).
60
+ #
61
+ # Returns the highlighted source String or a tuple of the highlighted source String and an Integer line offset.
62
+ def highlight node, source, lang, opts
63
+ raise ::NotImplementedError, %(#{SyntaxHighlighter} subclass #{self.class} must implement the ##{__method__} method since #highlight? returns true)
64
+ end
65
+
66
+ # Public: Format the highlighted source for inclusion in an HTML document.
67
+ #
68
+ # node - The source Block being processed.
69
+ # lang - The source language String for this Block (e.g., ruby).
70
+ # opts - A Hash of options that control syntax highlighting:
71
+ # :nowrap - A Boolean that indicates whether wrapping should be disabled (optional).
72
+ #
73
+ # Returns the highlighted source [String] wrapped in preformatted tags (e.g., pre and code)
74
+ def format node, lang, opts
75
+ raise ::NotImplementedError, %(#{SyntaxHighlighter} subclass #{self.class} must implement the ##{__method__} method)
76
+ end
77
+
78
+ # Public: Indicates whether this syntax highlighter wants to write a stylesheet to disk. Only called if both the
79
+ # linkcss and copycss attributes are set on the document.
80
+ #
81
+ # doc - The Document in which this syntax highlighter is being used.
82
+ #
83
+ # Returns a [Boolean] indicating whether the write_stylesheet method should be called.
84
+ def write_stylesheet? doc; end
85
+
86
+ # Public: Writes the stylesheet to support the highlighted source(s) to disk.
87
+ #
88
+ # doc - The Document in which this syntax highlighter is being used.
89
+ # to_dir - The absolute String path of the stylesheet output directory.
90
+ #
91
+ # Returns nothing.
92
+ def write_stylesheet doc, to_dir
93
+ raise ::NotImplementedError, %(#{SyntaxHighlighter} subclass #{self.class} must implement the ##{__method__} method since #write_stylesheet? returns true)
94
+ end
95
+
96
+ private_class_method def self.included into
97
+ into.extend Config
98
+ end
99
+
100
+ module Config
101
+ # Public: Statically register the current class in the registry for the specified names.
102
+ #
103
+ # Returns nothing.
104
+ private def register_for *names
105
+ SyntaxHighlighter.register self, *names
106
+ end
107
+ end
108
+
109
+ module Factory
110
+ # Public: Associates the syntax highlighter class or object with the specified names.
111
+ #
112
+ # Returns nothing.
113
+ def register syntax_highlighter, *names
114
+ names.each {|name| registry[name] = syntax_highlighter }
115
+ end
116
+
117
+ # Public: Retrieves the syntax highlighter class or object registered for the specified name.
118
+ #
119
+ # name - The String name of the syntax highlighter to retrieve.
120
+ #
121
+ # Returns the SyntaxHighlighter Class or Object instance registered for this name.
122
+ def for name
123
+ registry[name]
124
+ end
125
+
126
+ # Public: Resolves the name to a syntax highlighter instance, if found in the registry.
127
+ #
128
+ # name - The String name of the syntax highlighter to create.
129
+ # backend - The String name of the backend for which this syntax highlighter is being used (default: 'html5').
130
+ # opts - A Hash of options providing information about the context in which this syntax highlighter is used:
131
+ # :doc - The Document for which this syntax highlighter was created.
132
+ #
133
+ # Returns a [SyntaxHighlighter] instance for the specified name.
134
+ def create name, backend = 'html5', opts = {}
135
+ if (syntax_hl = self.for name)
136
+ syntax_hl = syntax_hl.new name, backend, opts if ::Class === syntax_hl
137
+ raise ::NameError, %(#{syntax_hl.class} must specify a value for `name') unless syntax_hl.name
138
+ syntax_hl
139
+ end
140
+ end
141
+
142
+ private def registry
143
+ raise ::NotImplementedError, %(#{Factory} subclass #{self.class} must implement the ##{__method__} method)
144
+ end
145
+ end
146
+
147
+ class CustomFactory
148
+ include Factory
149
+
150
+ def initialize seed_registry = nil
151
+ @registry = seed_registry || {}
152
+ end
153
+
154
+ private def registry
155
+ @registry
156
+ end
157
+ end
158
+
159
+ module DefaultFactory
160
+ include Factory
161
+
162
+ private
163
+
164
+ @@registry = {}
165
+
166
+ def registry
167
+ @@registry
168
+ end
169
+
170
+ unless RUBY_ENGINE == 'opal'
171
+ public
172
+
173
+ def register syntax_highlighter, *names
174
+ @@mutex.owned? ? names.each {|name| @@registry = @@registry.merge name => syntax_highlighter } :
175
+ @@mutex.synchronize { register syntax_highlighter, *names }
176
+ end
177
+
178
+ # This method will lazy require and register additional built-in implementations, which include coderay,
179
+ # pygments, rouge, and prettify. Refer to {Factory#for} for parameters and return value.
180
+ def for name
181
+ @@registry.fetch name do
182
+ @@mutex.synchronize do
183
+ @@registry.fetch name do
184
+ if (require_path = PROVIDED[name])
185
+ require require_path
186
+ @@registry[name]
187
+ else
188
+ @@registry = @@registry.merge name => nil
189
+ nil
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ PROVIDED = {
197
+ 'coderay' => %(#{__dir__}/syntax_highlighter/coderay),
198
+ 'prettify' => %(#{__dir__}/syntax_highlighter/prettify),
199
+ 'pygments' => %(#{__dir__}/syntax_highlighter/pygments),
200
+ 'rouge' => %(#{__dir__}/syntax_highlighter/rouge),
201
+ }
202
+
203
+ private
204
+
205
+ @@mutex = ::Mutex.new
206
+ end
207
+ end
208
+
209
+ class DefaultFactoryProxy < CustomFactory
210
+ include DefaultFactory # inserts module into ancestors immediately after superclass
211
+
212
+ def for name
213
+ @registry.fetch(name) { super }
214
+ end unless RUBY_ENGINE == 'opal'
215
+ end
216
+
217
+ class Base
218
+ include SyntaxHighlighter
219
+
220
+ def format node, lang, opts
221
+ class_attr_val = opts[:nowrap] ? %(#{@pre_class} highlight nowrap) : %(#{@pre_class} highlight)
222
+ if (transform = opts[:transform])
223
+ pre = { 'class' => class_attr_val }
224
+ code = lang ? { 'data-lang' => lang } : {}
225
+ transform[pre, code]
226
+ %(<pre#{pre.map {|k, v| %[ #{k}="#{v}"] }.join}><code#{code.map {|k, v| %[ #{k}="#{v}"] }.join}>#{node.content}</code></pre>)
227
+ else
228
+ %(<pre class="#{class_attr_val}"><code#{lang ? %[ data-lang="#{lang}"] : ''}>#{node.content}</code></pre>)
229
+ end
230
+ end
231
+ end
232
+
233
+ extend DefaultFactory # exports static methods
234
+ end
235
+ end
236
+
237
+ require_relative 'syntax_highlighter/highlightjs'
238
+ require_relative 'syntax_highlighter/html_pipeline' unless RUBY_ENGINE == 'opal'
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+ module Asciidoctor
3
+ class SyntaxHighlighter::CodeRayAdapter < SyntaxHighlighter::Base
4
+ register_for 'coderay'
5
+
6
+ def initialize *args
7
+ super
8
+ @pre_class = 'CodeRay'
9
+ @requires_stylesheet = nil
10
+ end
11
+
12
+ def highlight?
13
+ library_available?
14
+ end
15
+
16
+ def highlight node, source, lang, opts
17
+ @requires_stylesheet = true if (css_mode = opts[:css_mode]) == :class
18
+ # NOTE CodeRay::Duo gracefully falls back to no highlighting if lang isn't recognized
19
+ highlighted = ::CodeRay::Duo[lang ? lang.to_sym : :text, :html,
20
+ css: css_mode,
21
+ line_numbers: (line_numbers = opts[:number_lines]),
22
+ line_number_start: opts[:start_line_number],
23
+ line_number_anchors: false,
24
+ highlight_lines: opts[:highlight_lines],
25
+ bold_every: false,
26
+ ].highlight source
27
+ if line_numbers == :table && opts[:callouts]
28
+ [highlighted, (idx = highlighted.index CodeCellStartTagCs) ? idx + CodeCellStartTagCs.length : nil]
29
+ else
30
+ highlighted
31
+ end
32
+ end
33
+
34
+ def docinfo? location
35
+ @requires_stylesheet && location == :footer
36
+ end
37
+
38
+ def docinfo location, doc, opts
39
+ if opts[:linkcss]
40
+ %(<link rel="stylesheet" href="#{doc.normalize_web_path stylesheet_basename, (doc.attr 'stylesdir', ''), false}"#{opts[:self_closing_tag_slash]}>)
41
+ else
42
+ %(<style>
43
+ #{read_stylesheet}
44
+ </style>)
45
+ end
46
+ end
47
+
48
+ def write_stylesheet? doc
49
+ @requires_stylesheet
50
+ end
51
+
52
+ def write_stylesheet doc, to_dir
53
+ ::File.write (::File.join to_dir, stylesheet_basename), read_stylesheet, mode: FILE_WRITE_MODE
54
+ end
55
+
56
+ module Loader
57
+ private
58
+
59
+ def library_available?
60
+ (@@library_status ||= load_library) == :loaded ? true : nil
61
+ end
62
+
63
+ def load_library
64
+ (defined? ::CodeRay::Duo) ? :loaded : (Helpers.require_library 'coderay', true, :warn).nil? ? :unavailable : :loaded
65
+ end
66
+ end
67
+
68
+ module Styles
69
+ include Loader
70
+
71
+ def read_stylesheet
72
+ @@stylesheet_cache ||= (::File.read (::File.join Stylesheets::STYLESHEETS_DIR, stylesheet_basename), mode: FILE_READ_MODE).rstrip
73
+ end
74
+
75
+ def stylesheet_basename
76
+ 'coderay-asciidoctor.css'
77
+ end
78
+ end
79
+
80
+ extend Styles # exports static methods
81
+ include Loader, Styles # adds methods to instance
82
+
83
+ CodeCellStartTagCs = '<td class="code"><pre>'
84
+
85
+ private_constant :CodeCellStartTagCs
86
+ end
87
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ module Asciidoctor
3
+ class SyntaxHighlighter::HighlightJsAdapter < SyntaxHighlighter::Base
4
+ register_for 'highlightjs', 'highlight.js'
5
+
6
+ def initialize *args
7
+ super
8
+ @name = @pre_class = 'highlightjs'
9
+ end
10
+
11
+ def format node, lang, opts
12
+ super node, lang, (opts.merge transform: proc {|_, code| code['class'] = %(language-#{lang || 'none'} hljs) } )
13
+ end
14
+
15
+ def docinfo? location
16
+ location == :footer
17
+ end
18
+
19
+ def docinfo location, doc, opts
20
+ base_url = doc.attr 'highlightjsdir', %(#{opts[:cdn_base_url]}/highlight.js/#{HIGHLIGHT_JS_VERSION})
21
+ %(<link rel="stylesheet" href="#{base_url}/styles/#{doc.attr 'highlightjs-theme', 'github'}.min.css"#{opts[:self_closing_tag_slash]}>
22
+ <script src="#{base_url}/highlight.min.js"></script>
23
+ <script>hljs.initHighlighting()</script>)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module Asciidoctor
3
+ class SyntaxHighlighter::HtmlPipelineAdapter < SyntaxHighlighter::Base
4
+ register_for 'html-pipeline'
5
+
6
+ def format node, lang, opts
7
+ %(<pre#{lang ? %[ lang="#{lang}"] : ''}><code>#{node.content}</code></pre>)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module Asciidoctor
3
+ class SyntaxHighlighter::PrettifyAdapter < SyntaxHighlighter::Base
4
+ register_for 'prettify'
5
+
6
+ def initialize *args
7
+ super
8
+ @pre_class = 'prettyprint'
9
+ end
10
+
11
+ def format node, lang, opts
12
+ opts[:transform] = proc {|pre| pre['class'] += %( #{(start = node.attr 'start') ? %[linenums:#{start}] : 'linenums'}) } if node.attr? 'linenums'
13
+ super
14
+ end
15
+
16
+ def docinfo? location
17
+ location == :footer
18
+ end
19
+
20
+ def docinfo location, doc, opts
21
+ base_url = doc.attr 'prettifydir', %(#{opts[:cdn_base_url]}/prettify/r298)
22
+ prettify_theme_url = ((prettify_theme = doc.attr 'prettify-theme', 'prettify').start_with? 'http://', 'https://') ? prettify_theme : %(#{base_url}/#{prettify_theme}.min.css)
23
+ %(<link rel="stylesheet" href="#{prettify_theme_url}"#{opts[:self_closing_tag_slash]}>
24
+ <script src="#{base_url}/run_prettify.min.js"></script>)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+ module Asciidoctor
3
+ class SyntaxHighlighter::PygmentsAdapter < SyntaxHighlighter::Base
4
+ register_for 'pygments'
5
+
6
+ def initialize *args
7
+ super
8
+ @requires_stylesheet = nil
9
+ @style = nil
10
+ end
11
+
12
+ def highlight?
13
+ library_available?
14
+ end
15
+
16
+ def highlight node, source, lang, opts
17
+ lexer = (::Pygments::Lexer.find_by_alias lang) || (::Pygments::Lexer.find_by_mimetype 'text/plain')
18
+ @requires_stylesheet = true unless (noclasses = opts[:css_mode] != :class)
19
+ highlight_opts = {
20
+ classprefix: TOKEN_CLASS_PREFIX,
21
+ cssclass: WRAPPER_CLASS,
22
+ nobackground: true,
23
+ noclasses: noclasses,
24
+ startinline: lexer.name == 'PHP' && !(node.option? 'mixed'),
25
+ stripnl: false,
26
+ style: (@style ||= (style = opts[:style]) && (style_available? style) || DEFAULT_STYLE),
27
+ }
28
+ if (highlight_lines = opts[:highlight_lines])
29
+ highlight_opts[:hl_lines] = highlight_lines.join ' '
30
+ end
31
+ if (linenos = opts[:number_lines]) && (highlight_opts[:linenostart] = opts[:start_line_number]) && (highlight_opts[:linenos] = linenos) == :table
32
+ if (highlighted = lexer.highlight source, options: highlight_opts)
33
+ highlighted = highlighted.sub StyledLinenoColumnStartTagsRx, LinenoColumnStartTagsCs if noclasses
34
+ highlighted = highlighted.sub WrapperTagRx, PreTagCs
35
+ opts[:callouts] ? [highlighted, (idx = highlighted.index CodeCellStartTagCs) ? idx + CodeCellStartTagCs.length : nil] : highlighted
36
+ else
37
+ node.sub_specialchars source # handles nil response from ::Pygments::Lexer#highlight
38
+ end
39
+ elsif (highlighted = lexer.highlight source, options: highlight_opts)
40
+ highlighted = highlighted.gsub StyledLinenoSpanTagRx, LinenoSpanTagCs if linenos && noclasses
41
+ highlighted.sub WrapperTagRx, '\1'
42
+ else
43
+ node.sub_specialchars source # handles nil response from ::Pygments::Lexer#highlight
44
+ end
45
+ end
46
+
47
+ def format node, lang, opts
48
+ if opts[:css_mode] != :class && (@style = (style = opts[:style]) && (style_available? style) || DEFAULT_STYLE) &&
49
+ (pre_style_attr_val = base_style @style)
50
+ opts[:transform] = proc {|pre| pre['style'] = pre_style_attr_val }
51
+ end
52
+ super
53
+ end
54
+
55
+ def docinfo? location
56
+ @requires_stylesheet && location == :footer
57
+ end
58
+
59
+ def docinfo location, doc, opts
60
+ if opts[:linkcss]
61
+ %(<link rel="stylesheet" href="#{doc.normalize_web_path (stylesheet_basename @style), (doc.attr 'stylesdir', ''), false}"#{opts[:self_closing_tag_slash]}>)
62
+ else
63
+ %(<style>
64
+ #{read_stylesheet @style}
65
+ </style>)
66
+ end
67
+ end
68
+
69
+ def write_stylesheet? doc
70
+ @requires_stylesheet
71
+ end
72
+
73
+ def write_stylesheet doc, to_dir
74
+ ::File.write (::File.join to_dir, (stylesheet_basename @style)), (read_stylesheet @style), mode: FILE_WRITE_MODE
75
+ end
76
+
77
+ module Loader
78
+ private
79
+
80
+ def library_available?
81
+ (@@library_status ||= load_library) == :loaded ? true : nil
82
+ end
83
+
84
+ def load_library
85
+ (defined? ::Pygments::Lexer) ? :loaded : (Helpers.require_library 'pygments', 'pygments.rb', :warn).nil? ? :unavailable : :loaded
86
+ end
87
+ end
88
+
89
+ module Styles
90
+ include Loader
91
+
92
+ def read_stylesheet style
93
+ library_available? ? @@stylesheet_cache[style || DEFAULT_STYLE] || '/* Failed to load Pygments CSS. */' : '/* Pygments CSS disabled because Pygments is not available. */'
94
+ end
95
+
96
+ def stylesheet_basename style
97
+ %(pygments-#{style || DEFAULT_STYLE}.css)
98
+ end
99
+
100
+ private
101
+
102
+ def base_style style
103
+ library_available? ? @@base_style_cache[style || DEFAULT_STYLE] : nil
104
+ end
105
+
106
+ def style_available? style
107
+ (((@@available_styles ||= ::Pygments.styles.to_set).include? style) rescue nil) && style
108
+ end
109
+
110
+ @@base_style_cache = ::Hash.new do |cache, key|
111
+ if BaseStyleRx =~ @@stylesheet_cache[key]
112
+ @@base_style_cache = cache.merge key => (style = $1.strip)
113
+ style
114
+ end
115
+ end
116
+ @@stylesheet_cache = ::Hash.new do |cache, key|
117
+ if (stylesheet = ::Pygments.css BASE_SELECTOR, classprefix: TOKEN_CLASS_PREFIX, style: key)
118
+ @@stylesheet_cache = cache.merge key => stylesheet
119
+ stylesheet
120
+ end
121
+ end
122
+
123
+ DEFAULT_STYLE = 'default'
124
+ BASE_SELECTOR = 'pre.pygments'
125
+ TOKEN_CLASS_PREFIX = 'tok-'
126
+
127
+ BaseStyleRx = /^#{BASE_SELECTOR.gsub '.', '\\.'} +\{([^}]+?)\}/
128
+
129
+ private_constant :BASE_SELECTOR, :TOKEN_CLASS_PREFIX, :BaseStyleRx
130
+ end
131
+
132
+ extend Styles # exports static methods
133
+ include Loader, Styles # adds methods to instance
134
+
135
+ CodeCellStartTagCs = '<td class="code">'
136
+ LinenoColumnStartTagsCs = '<td class="linenos"><div class="linenodiv"><pre>'
137
+ LinenoSpanTagCs = '<span class="lineno">\1</span>'
138
+ PreTagCs = '<pre>\1</pre>'
139
+ StyledLinenoColumnStartTagsRx = /<td><div class="linenodiv" style="[^"]+?"><pre style="[^"]+?">/
140
+ StyledLinenoSpanTagRx = %r(<span style="background-color: #f0f0f0; padding: 0 5px 0 5px">( *\d+ )</span>)
141
+ WRAPPER_CLASS = 'lineno' # doesn't appear in output; Pygments appends "table" to this value to make nested table class
142
+ # NOTE <pre> has style attribute when pygments-css=style
143
+ # NOTE <div> has trailing newline when pygments-linenums-mode=table
144
+ # NOTE initial <span></span> preserves leading blank lines
145
+ WrapperTagRx = %r(<div class="#{WRAPPER_CLASS}"><pre\b[^>]*?>(.*)</pre></div>\n*)m
146
+
147
+ private_constant :CodeCellStartTagCs, :LinenoColumnStartTagsCs, :LinenoSpanTagCs, :PreTagCs, :StyledLinenoColumnStartTagsRx, :StyledLinenoSpanTagRx, :WrapperTagRx, :WRAPPER_CLASS
148
+ end
149
+ end