asciidoctor 1.5.5 → 1.5.6
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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +216 -1
- data/CONTRIBUTING.adoc +2 -2
- data/Gemfile +20 -1
- data/LICENSE.adoc +1 -1
- data/README-fr.adoc +4 -3
- data/README-jp.adoc +11 -10
- data/README-zh_CN.adoc +4 -3
- data/README.adoc +17 -202
- data/Rakefile +41 -25
- data/asciidoctor.gemspec +9 -10
- data/data/locale/attributes.adoc +216 -34
- data/data/stylesheets/asciidoctor-default.css +23 -16
- data/features/step_definitions.rb +15 -19
- data/features/xref.feature +584 -20
- data/lib/asciidoctor.rb +292 -278
- data/lib/asciidoctor/abstract_block.rb +155 -94
- data/lib/asciidoctor/abstract_node.rb +108 -94
- data/lib/asciidoctor/attribute_list.rb +30 -22
- data/lib/asciidoctor/block.rb +7 -7
- data/lib/asciidoctor/cli/invoker.rb +47 -34
- data/lib/asciidoctor/cli/options.rb +22 -11
- data/lib/asciidoctor/converter.rb +3 -3
- data/lib/asciidoctor/converter/base.rb +2 -2
- data/lib/asciidoctor/converter/composite.rb +1 -1
- data/lib/asciidoctor/converter/docbook45.rb +2 -2
- data/lib/asciidoctor/converter/docbook5.rb +132 -87
- data/lib/asciidoctor/converter/factory.rb +0 -1
- data/lib/asciidoctor/converter/html5.rb +116 -98
- data/lib/asciidoctor/converter/manpage.rb +51 -52
- data/lib/asciidoctor/converter/template.rb +47 -36
- data/lib/asciidoctor/core_ext.rb +8 -2
- data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +4 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +5 -0
- data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +1 -1
- data/lib/asciidoctor/core_ext/1.8.7/string/{limit.rb → limit_bytesize.rb} +7 -6
- data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +1 -1
- data/lib/asciidoctor/core_ext/nil_or_empty.rb +5 -5
- data/lib/asciidoctor/core_ext/regexp/is_match.rb +3 -0
- data/lib/asciidoctor/core_ext/string/{limit.rb → limit_bytesize.rb} +2 -2
- data/lib/asciidoctor/document.rb +216 -213
- data/lib/asciidoctor/extensions.rb +318 -185
- data/lib/asciidoctor/helpers.rb +35 -35
- data/lib/asciidoctor/inline.rb +32 -1
- data/lib/asciidoctor/list.rb +22 -6
- data/lib/asciidoctor/parser.rb +1008 -1038
- data/lib/asciidoctor/path_resolver.rb +46 -50
- data/lib/asciidoctor/reader.rb +275 -251
- data/lib/asciidoctor/section.rb +86 -58
- data/lib/asciidoctor/stylesheets.rb +6 -6
- data/lib/asciidoctor/substitutors.rb +567 -649
- data/lib/asciidoctor/table.rb +163 -108
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +18 -16
- data/man/asciidoctor.adoc +15 -13
- data/test/attributes_test.rb +138 -22
- data/test/blocks_test.rb +377 -97
- data/test/converter_test.rb +13 -0
- data/test/document_test.rb +244 -34
- data/test/extensions_test.rb +409 -42
- data/test/fixtures/asciidoc_index.txt +521 -0
- data/test/fixtures/basic-docinfo-footer.html +6 -0
- data/test/fixtures/basic-docinfo-footer.xml +8 -0
- data/test/fixtures/basic-docinfo.html +1 -0
- data/test/fixtures/basic-docinfo.xml +4 -0
- data/test/fixtures/basic.asciidoc +5 -0
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/child-include.adoc +5 -0
- data/test/fixtures/circle.svg +9 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
- data/test/fixtures/custom-docinfodir/basic-docinfo.html +1 -0
- data/test/fixtures/custom-docinfodir/docinfo.html +1 -0
- data/test/fixtures/docinfo-footer.html +1 -0
- data/test/fixtures/docinfo-footer.xml +9 -0
- data/test/fixtures/docinfo.html +1 -0
- data/test/fixtures/docinfo.xml +3 -0
- data/test/fixtures/dot.gif +0 -0
- data/test/fixtures/encoding.asciidoc +13 -0
- data/test/fixtures/grandchild-include.adoc +3 -0
- data/test/fixtures/hello-asciidoctor.pdf +69 -0
- data/test/fixtures/include-file.asciidoc +24 -0
- data/test/fixtures/include-file.ml +3 -0
- data/test/fixtures/include-file.xml +5 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/fixtures/mismatched-end-tag.adoc +7 -0
- data/test/fixtures/parent-include-restricted.adoc +5 -0
- data/test/fixtures/parent-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +26 -0
- data/test/fixtures/stylesheets/custom.css +3 -0
- data/test/fixtures/subs-docinfo.html +2 -0
- data/test/fixtures/subs.adoc +7 -0
- data/test/fixtures/tagged-class-enclosed.rb +26 -0
- data/test/fixtures/tagged-class.rb +23 -0
- data/test/fixtures/tip.gif +0 -0
- data/test/invoker_test.rb +82 -4
- data/test/links_test.rb +312 -37
- data/test/lists_test.rb +204 -25
- data/test/manpage_test.rb +191 -4
- data/test/options_test.rb +18 -1
- data/test/paragraphs_test.rb +32 -7
- data/test/parser_test.rb +150 -30
- data/test/paths_test.rb +47 -13
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +366 -126
- data/test/sections_test.rb +203 -56
- data/test/substitutions_test.rb +339 -131
- data/test/tables_test.rb +315 -15
- data/test/test_helper.rb +400 -0
- data/test/text_test.rb +5 -5
- metadata +110 -22
@@ -2,27 +2,28 @@ if RUBY_ENGINE_JRUBY
|
|
2
2
|
class String
|
3
3
|
# Safely truncate the string to the specified number of bytes.
|
4
4
|
# If a multibyte char gets split, the dangling fragment is removed.
|
5
|
-
def
|
5
|
+
def limit_bytesize size
|
6
6
|
return self unless size < bytesize
|
7
7
|
result = (unpack %(a#{size}))[0]
|
8
8
|
begin
|
9
9
|
result.unpack 'U*'
|
10
|
-
rescue ArgumentError
|
10
|
+
rescue ::ArgumentError
|
11
11
|
result.chop!
|
12
12
|
retry
|
13
13
|
end
|
14
14
|
result
|
15
|
-
end unless method_defined? :
|
15
|
+
end unless method_defined? :limit_bytesize
|
16
16
|
end
|
17
17
|
else
|
18
18
|
class String
|
19
|
+
ValidTrailingCharRx = /.$/u
|
19
20
|
# Safely truncate the string to the specified number of bytes.
|
20
21
|
# If a multibyte char gets split, the dangling fragment is removed.
|
21
|
-
def
|
22
|
+
def limit_bytesize size
|
22
23
|
return self unless size < bytesize
|
23
24
|
result = (unpack %(a#{size}))[0]
|
24
|
-
result.chop! until result.empty? ||
|
25
|
+
result.chop! until result.empty? || (ValidTrailingCharRx.match? result)
|
25
26
|
result
|
26
|
-
end unless method_defined? :
|
27
|
+
end unless method_defined? :limit_bytesize
|
27
28
|
end
|
28
29
|
end
|
@@ -3,21 +3,21 @@
|
|
3
3
|
# String, Array, Hash, and Numeric.
|
4
4
|
|
5
5
|
class NilClass
|
6
|
-
alias
|
6
|
+
alias nil_or_empty? nil? unless method_defined? :nil_or_empty?
|
7
7
|
end
|
8
8
|
|
9
9
|
class String
|
10
|
-
alias
|
10
|
+
alias nil_or_empty? empty? unless method_defined? :nil_or_empty?
|
11
11
|
end
|
12
12
|
|
13
13
|
class Array
|
14
|
-
alias
|
14
|
+
alias nil_or_empty? empty? unless method_defined? :nil_or_empty?
|
15
15
|
end
|
16
16
|
|
17
17
|
class Hash
|
18
|
-
alias
|
18
|
+
alias nil_or_empty? empty? unless method_defined? :nil_or_empty?
|
19
19
|
end
|
20
20
|
|
21
21
|
class Numeric
|
22
|
-
alias
|
22
|
+
alias nil_or_empty? nil? unless method_defined? :nil_or_empty?
|
23
23
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
class String
|
2
2
|
# Safely truncate the string to the specified number of bytes.
|
3
3
|
# If a multibyte char gets split, the dangling fragment is removed.
|
4
|
-
def
|
4
|
+
def limit_bytesize size
|
5
5
|
return self unless size < bytesize
|
6
6
|
# NOTE JRuby 1.7 & Rubinius fail to detect invalid encoding unless encoding is forced; impact is marginal.
|
7
7
|
size -= 1 until ((result = byteslice 0, size).force_encoding ::Encoding::UTF_8).valid_encoding?
|
8
8
|
result
|
9
|
-
end unless method_defined? :
|
9
|
+
end unless method_defined? :limit_bytesize
|
10
10
|
end
|
data/lib/asciidoctor/document.rb
CHANGED
@@ -32,20 +32,21 @@ class Document < AbstractBlock
|
|
32
32
|
|
33
33
|
def save_to block_attributes
|
34
34
|
(block_attributes[:attribute_entries] ||= []) << self
|
35
|
+
self
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
38
39
|
# Public Parsed and stores a partitioned title (i.e., title & subtitle).
|
39
40
|
class Title
|
40
41
|
attr_reader :main
|
41
|
-
alias
|
42
|
+
alias title main
|
42
43
|
attr_reader :subtitle
|
43
44
|
attr_reader :combined
|
44
45
|
|
45
46
|
def initialize val, opts = {}
|
46
47
|
# TODO separate sanitization by type (:cdata for HTML/XML, :plain_text for non-SGML, false for none)
|
47
48
|
if (@sanitized = opts[:sanitize]) && val.include?('<')
|
48
|
-
val = val.gsub(XmlSanitizeRx, '').
|
49
|
+
val = val.gsub(XmlSanitizeRx, '').squeeze(' ').strip
|
49
50
|
end
|
50
51
|
if (sep = opts[:separator] || ':').empty? || !val.include?(sep = %(#{sep} ))
|
51
52
|
@main = val
|
@@ -61,7 +62,7 @@ class Document < AbstractBlock
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def subtitle?
|
64
|
-
|
65
|
+
@subtitle ? true : false
|
65
66
|
end
|
66
67
|
|
67
68
|
def to_s
|
@@ -78,7 +79,7 @@ class Document < AbstractBlock
|
|
78
79
|
#
|
79
80
|
# A value of 1 (SAFE) closely parallels safe mode in AsciiDoc. In particular,
|
80
81
|
# it prevents access to files which reside outside of the parent directory
|
81
|
-
# of the source file and disables any macro other than the include
|
82
|
+
# of the source file and disables any macro other than the include directive.
|
82
83
|
#
|
83
84
|
# A value of 10 (SERVER) disallows the document from setting attributes that
|
84
85
|
# would affect the conversion of the document, in addition to all the security
|
@@ -90,7 +91,7 @@ class Document < AbstractBlock
|
|
90
91
|
# A value of 20 (SECURE) disallows the document from attempting to read files
|
91
92
|
# from the file system and including the contents of them into the document,
|
92
93
|
# in addition to all the security features of SafeMode::SECURE. In
|
93
|
-
# particular, it disallows use of the include::[]
|
94
|
+
# particular, it disallows use of the include::[] directive and the embedding of
|
94
95
|
# binary content (data uri), stylesheets and JavaScripts referenced by the
|
95
96
|
# document. (Asciidoctor and trusted extensions may still be allowed to embed
|
96
97
|
# trusted content into the document).
|
@@ -115,11 +116,20 @@ class Document < AbstractBlock
|
|
115
116
|
#
|
116
117
|
attr_reader :compat_mode
|
117
118
|
|
118
|
-
# Public: Get the
|
119
|
-
attr_reader :
|
119
|
+
# Public: Get the cached value of the backend attribute for this document
|
120
|
+
attr_reader :backend
|
121
|
+
|
122
|
+
# Public: Get the cached value of the doctype attribute for this document
|
123
|
+
attr_reader :doctype
|
124
|
+
|
125
|
+
# Public: Get or set the Boolean flag that indicates whether source map information should be tracked by the parser
|
126
|
+
attr_accessor :sourcemap
|
120
127
|
|
121
|
-
# Public: Get the
|
122
|
-
attr_reader :
|
128
|
+
# Public: Get the document catalog Hash
|
129
|
+
attr_reader :catalog
|
130
|
+
|
131
|
+
# Public: Alias catalog property as references for backwards compatiblity
|
132
|
+
alias references catalog
|
123
133
|
|
124
134
|
# Public: Get the Hash of document counters
|
125
135
|
attr_reader :counters
|
@@ -151,7 +161,7 @@ class Document < AbstractBlock
|
|
151
161
|
# Public: Get the Converter associated with this document
|
152
162
|
attr_reader :converter
|
153
163
|
|
154
|
-
# Public: Get the
|
164
|
+
# Public: Get the activated Extensions::Registry associated with this document.
|
155
165
|
attr_reader :extensions
|
156
166
|
|
157
167
|
# Public: Initialize a {Document} object.
|
@@ -173,32 +183,30 @@ class Document < AbstractBlock
|
|
173
183
|
if (parent_doc = options.delete :parent)
|
174
184
|
@parent_document = parent_doc
|
175
185
|
options[:base_dir] ||= parent_doc.base_dir
|
176
|
-
@
|
177
|
-
|
178
|
-
accum[:footnotes] = []
|
179
|
-
else
|
180
|
-
accum[key] = ref
|
181
|
-
end
|
186
|
+
@catalog = parent_doc.catalog.inject({}) do |accum, (key, table)|
|
187
|
+
accum[key] = (key == :footnotes ? [] : table)
|
182
188
|
accum
|
183
189
|
end
|
184
190
|
@callouts = parent_doc.callouts
|
185
191
|
# QUESTION should we support setting attribute in parent document from nested document?
|
186
192
|
# NOTE we must dup or else all the assignments to the overrides clobbers the real attributes
|
187
|
-
attr_overrides = parent_doc.attributes.dup
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
193
|
+
@attribute_overrides = attr_overrides = parent_doc.attributes.dup
|
194
|
+
parent_doctype = attr_overrides.delete 'doctype'
|
195
|
+
attr_overrides.delete 'compat-mode'
|
196
|
+
attr_overrides.delete 'toc'
|
197
|
+
attr_overrides.delete 'toc-placement'
|
198
|
+
attr_overrides.delete 'toc-position'
|
192
199
|
@safe = parent_doc.safe
|
193
|
-
@compat_mode = parent_doc.compat_mode
|
200
|
+
@attributes['compat-mode'] = '' if (@compat_mode = parent_doc.compat_mode)
|
194
201
|
@sourcemap = parent_doc.sourcemap
|
195
202
|
@converter = parent_doc.converter
|
196
203
|
initialize_extensions = false
|
197
204
|
@extensions = parent_doc.extensions
|
198
205
|
else
|
199
206
|
@parent_document = nil
|
200
|
-
@
|
207
|
+
@catalog = {
|
201
208
|
:ids => {},
|
209
|
+
:refs => {},
|
202
210
|
:footnotes => [],
|
203
211
|
:links => [],
|
204
212
|
:images => [],
|
@@ -231,7 +239,7 @@ class Document < AbstractBlock
|
|
231
239
|
else
|
232
240
|
# NOTE: not using infix rescue for performance reasons, see https://github.com/jruby/jruby/issues/1816
|
233
241
|
begin
|
234
|
-
@safe = SafeMode.
|
242
|
+
@safe = SafeMode.value_for_name safe_mode.to_s
|
235
243
|
rescue
|
236
244
|
@safe = SafeMode::SECURE
|
237
245
|
end
|
@@ -247,19 +255,25 @@ class Document < AbstractBlock
|
|
247
255
|
@header = nil
|
248
256
|
@counters = {}
|
249
257
|
@attributes_modified = ::Set.new
|
250
|
-
@options = options
|
251
258
|
@docinfo_processor_extensions = {}
|
252
259
|
header_footer = (options[:header_footer] ||= false)
|
253
|
-
options.freeze
|
260
|
+
(@options = options).freeze
|
254
261
|
|
255
262
|
attrs = @attributes
|
256
263
|
#attrs['encoding'] = 'UTF-8'
|
257
264
|
attrs['sectids'] = ''
|
258
|
-
attrs['notitle'] = '' unless header_footer
|
259
265
|
attrs['toc-placement'] = 'auto'
|
266
|
+
if header_footer
|
267
|
+
attrs['copycss'] = ''
|
268
|
+
# sync embedded attribute with :header_footer option value
|
269
|
+
attr_overrides['embedded'] = nil
|
270
|
+
else
|
271
|
+
attrs['notitle'] = ''
|
272
|
+
# sync embedded attribute with :header_footer option value
|
273
|
+
attr_overrides['embedded'] = ''
|
274
|
+
end
|
260
275
|
attrs['stylesheet'] = ''
|
261
276
|
attrs['webfonts'] = ''
|
262
|
-
attrs['copycss'] = '' if header_footer
|
263
277
|
attrs['prewrap'] = ''
|
264
278
|
attrs['attribute-undefined'] = Compliance.attribute_undefined
|
265
279
|
attrs['attribute-missing'] = Compliance.attribute_missing
|
@@ -272,7 +286,6 @@ class Document < AbstractBlock
|
|
272
286
|
attrs['note-caption'] = 'Note'
|
273
287
|
attrs['tip-caption'] = 'Tip'
|
274
288
|
attrs['warning-caption'] = 'Warning'
|
275
|
-
attrs['appendix-caption'] = 'Appendix'
|
276
289
|
attrs['example-caption'] = 'Example'
|
277
290
|
attrs['figure-caption'] = 'Figure'
|
278
291
|
#attrs['listing-caption'] = 'Listing'
|
@@ -280,6 +293,10 @@ class Document < AbstractBlock
|
|
280
293
|
attrs['toc-title'] = 'Table of Contents'
|
281
294
|
#attrs['preface-title'] = 'Preface'
|
282
295
|
attrs['manname-title'] = 'NAME'
|
296
|
+
attrs['section-refsig'] = 'Section'
|
297
|
+
#attrs['part-refsig'] = 'Part'
|
298
|
+
attrs['chapter-refsig'] = 'Chapter'
|
299
|
+
attrs['appendix-caption'] = attrs['appendix-refsig'] = 'Appendix'
|
283
300
|
attrs['untitled-label'] = 'Untitled'
|
284
301
|
attrs['version-label'] = 'Version'
|
285
302
|
attrs['last-update-label'] = 'Last updated'
|
@@ -287,14 +304,10 @@ class Document < AbstractBlock
|
|
287
304
|
attr_overrides['asciidoctor'] = ''
|
288
305
|
attr_overrides['asciidoctor-version'] = VERSION
|
289
306
|
|
290
|
-
|
291
|
-
attr_overrides['safe-mode-name'] = safe_mode_name
|
307
|
+
attr_overrides['safe-mode-name'] = (safe_mode_name = SafeMode.name_for_value @safe)
|
292
308
|
attr_overrides["safe-mode-#{safe_mode_name}"] = ''
|
293
309
|
attr_overrides['safe-mode-level'] = @safe
|
294
310
|
|
295
|
-
# sync the embedded attribute w/ the value of options...do not allow override
|
296
|
-
attr_overrides['embedded'] = header_footer ? nil : ''
|
297
|
-
|
298
311
|
# the only way to set the max-include-depth attribute is via the API; default to 64 like AsciiDoc Python
|
299
312
|
attr_overrides['max-include-depth'] ||= 64
|
300
313
|
|
@@ -353,7 +366,7 @@ class Document < AbstractBlock
|
|
353
366
|
end
|
354
367
|
|
355
368
|
# the only way to set the max-attribute-value-size attribute is via the API; disabled by default
|
356
|
-
@max_attribute_value_size = (
|
369
|
+
@max_attribute_value_size = (size = (attr_overrides['max-attribute-value-size'] ||= nil)) ? size.to_i.abs : nil
|
357
370
|
|
358
371
|
attr_overrides.delete_if do |key, val|
|
359
372
|
verdict = false
|
@@ -373,8 +386,11 @@ class Document < AbstractBlock
|
|
373
386
|
end
|
374
387
|
|
375
388
|
if parent_doc
|
376
|
-
|
377
|
-
|
389
|
+
@backend = attrs['backend']
|
390
|
+
# reset doctype unless it matches the default value
|
391
|
+
unless (@doctype = attrs['doctype'] = parent_doctype) == DEFAULT_DOCTYPE
|
392
|
+
update_doctype_attributes DEFAULT_DOCTYPE
|
393
|
+
end
|
378
394
|
|
379
395
|
# don't need to do the extra processing within our own document
|
380
396
|
# FIXME line info isn't reported correctly within include files in nested document
|
@@ -389,10 +405,11 @@ class Document < AbstractBlock
|
|
389
405
|
@parsed = true
|
390
406
|
else
|
391
407
|
# setup default backend and doctype
|
408
|
+
@backend = nil
|
392
409
|
if (attrs['backend'] ||= DEFAULT_BACKEND) == 'manpage'
|
393
|
-
attrs['doctype'] = attr_overrides['doctype'] = 'manpage'
|
410
|
+
@doctype = attrs['doctype'] = attr_overrides['doctype'] = 'manpage'
|
394
411
|
else
|
395
|
-
attrs['doctype'] ||= DEFAULT_DOCTYPE
|
412
|
+
@doctype = (attrs['doctype'] ||= DEFAULT_DOCTYPE)
|
396
413
|
end
|
397
414
|
update_backend_attributes attrs['backend'], true
|
398
415
|
|
@@ -402,20 +419,25 @@ class Document < AbstractBlock
|
|
402
419
|
# dynamic intrinstic attribute values
|
403
420
|
|
404
421
|
# See https://reproducible-builds.org/specs/source-date-epoch/
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
end
|
422
|
+
# NOTE Opal can't call key? on ENV
|
423
|
+
now = ::ENV['SOURCE_DATE_EPOCH'] ? ::Time.at(Integer ::ENV['SOURCE_DATE_EPOCH']).utc : ::Time.now
|
424
|
+
if (localdate = attrs['localdate'])
|
425
|
+
localyear = (attrs['localyear'] ||= ((localdate.index '-') == 4 ? (localdate.slice 0, 4) : nil))
|
426
|
+
else
|
427
|
+
localdate = attrs['localdate'] = (now.strftime '%Y-%m-%d')
|
428
|
+
localyear = (attrs['localyear'] ||= now.year.to_s)
|
413
429
|
end
|
430
|
+
localtime = (attrs['localtime'] ||= begin
|
431
|
+
now.strftime '%H:%M:%S %Z'
|
432
|
+
rescue # Asciidoctor.js fails if timezone string has characters outside basic Latin (see asciidoctor.js#23)
|
433
|
+
now.strftime '%H:%M:%S %z'
|
434
|
+
end)
|
414
435
|
attrs['localdatetime'] ||= %(#{localdate} #{localtime})
|
415
436
|
|
416
437
|
# docdate, doctime and docdatetime should default to
|
417
438
|
# localdate, localtime and localdatetime if not otherwise set
|
418
439
|
attrs['docdate'] ||= localdate
|
440
|
+
attrs['docyear'] ||= localyear
|
419
441
|
attrs['doctime'] ||= localtime
|
420
442
|
attrs['docdatetime'] ||= %(#{localdate} #{localtime})
|
421
443
|
|
@@ -424,22 +446,21 @@ class Document < AbstractBlock
|
|
424
446
|
attrs['iconsdir'] ||= ::File.join(attrs.fetch('imagesdir', './images'), 'icons')
|
425
447
|
|
426
448
|
if initialize_extensions
|
427
|
-
if (
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
registry = Extensions::Registry.new
|
449
|
+
if (ext_registry = options[:extension_registry])
|
450
|
+
# QUESTION should we warn the value type of the option is not a registry or boolean?
|
451
|
+
unless Extensions::Registry === ext_registry || (::RUBY_ENGINE_JRUBY &&
|
452
|
+
::AsciidoctorJ::Extensions::ExtensionRegistry === ext_registry)
|
453
|
+
ext_registry = Extensions::Registry.new
|
433
454
|
end
|
434
455
|
elsif ::Proc === (ext_block = options[:extensions])
|
435
|
-
|
456
|
+
ext_registry = Extensions.create(&ext_block)
|
436
457
|
else
|
437
|
-
|
458
|
+
ext_registry = Extensions::Registry.new
|
438
459
|
end
|
439
|
-
@extensions =
|
460
|
+
@extensions = ext_registry.activate self
|
440
461
|
end
|
441
462
|
|
442
|
-
@reader = PreprocessorReader.new self, data, Reader::Cursor.new
|
463
|
+
@reader = PreprocessorReader.new self, data, (Reader::Cursor.new attrs['docfile'], @base_dir), :normalize => true
|
443
464
|
end
|
444
465
|
end
|
445
466
|
|
@@ -460,7 +481,9 @@ class Document < AbstractBlock
|
|
460
481
|
else
|
461
482
|
doc = self
|
462
483
|
# create reader if data is provided (used when data is not known at the time the Document object is created)
|
463
|
-
|
484
|
+
if data
|
485
|
+
@reader = PreprocessorReader.new doc, data, (Reader::Cursor.new @attributes['docfile'], @base_dir), :normalize => true
|
486
|
+
end
|
464
487
|
|
465
488
|
if (exts = @parent_document ? nil : @extensions) && exts.preprocessors?
|
466
489
|
exts.preprocessors.each do |ext|
|
@@ -469,13 +492,13 @@ class Document < AbstractBlock
|
|
469
492
|
end
|
470
493
|
|
471
494
|
# Now parse the lines in the reader into blocks
|
472
|
-
Parser.parse @reader, doc, :header_only =>
|
495
|
+
Parser.parse @reader, doc, :header_only => @options[:parse_header_only]
|
473
496
|
|
474
497
|
# should we call sort of post-parse function?
|
475
498
|
restore_attributes
|
476
499
|
|
477
|
-
if exts && exts.
|
478
|
-
exts.
|
500
|
+
if exts && exts.tree_processors?
|
501
|
+
exts.tree_processors.each do |ext|
|
479
502
|
if (result = ext.process_method[doc]) && Document === result && result != doc
|
480
503
|
doc = result
|
481
504
|
end
|
@@ -493,19 +516,15 @@ class Document < AbstractBlock
|
|
493
516
|
# seed - the initial value as a String or Integer
|
494
517
|
#
|
495
518
|
# returns the next number in the sequence for the specified counter
|
496
|
-
def counter
|
497
|
-
|
498
|
-
|
519
|
+
def counter name, seed = nil
|
520
|
+
return @parent_document.counter name, seed if @parent_document
|
521
|
+
if (attr_seed = !(attr_val = @attributes[name]).nil_or_empty?) && (@counters.key? name)
|
522
|
+
@attributes[name] = @counters[name] = (nextval attr_val)
|
523
|
+
elsif seed
|
524
|
+
@attributes[name] = @counters[name] = (seed == seed.to_i.to_s ? seed.to_i : seed)
|
499
525
|
else
|
500
|
-
|
501
|
-
seed = nextval(attr_is_seed ? attr_val : 0)
|
502
|
-
elsif seed.to_i.to_s == seed
|
503
|
-
seed = seed.to_i
|
504
|
-
end
|
505
|
-
@counters[name] = seed
|
526
|
+
@attributes[name] = @counters[name] = nextval(attr_seed ? attr_val : 0)
|
506
527
|
end
|
507
|
-
|
508
|
-
(@attributes[name] = @counters[name])
|
509
528
|
end
|
510
529
|
|
511
530
|
# Public: Increment the specified counter and store it in the block's attributes
|
@@ -514,11 +533,11 @@ class Document < AbstractBlock
|
|
514
533
|
# block - the Block on which to save the counter
|
515
534
|
#
|
516
535
|
# returns the next number in the sequence for the specified counter
|
517
|
-
def
|
518
|
-
|
519
|
-
AttributeEntry.new(counter_name, val).save_to(block.attributes)
|
520
|
-
val
|
536
|
+
def increment_and_store_counter counter_name, block
|
537
|
+
((AttributeEntry.new counter_name, (counter counter_name)).save_to block.attributes).value
|
521
538
|
end
|
539
|
+
# Deprecated: Map old counter_increment method to increment_counter for backwards compatibility
|
540
|
+
alias counter_increment increment_and_store_counter
|
522
541
|
|
523
542
|
# Internal: Get the next value in the sequence.
|
524
543
|
#
|
@@ -540,44 +559,42 @@ class Document < AbstractBlock
|
|
540
559
|
end
|
541
560
|
end
|
542
561
|
|
543
|
-
def register
|
562
|
+
def register type, value
|
544
563
|
case type
|
545
|
-
when :ids
|
546
|
-
id, reftext =
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
@
|
564
|
+
when :ids # deprecated
|
565
|
+
id, reftext = value
|
566
|
+
@catalog[:ids][id] ||= reftext || ('[' + id + ']')
|
567
|
+
when :refs
|
568
|
+
id, ref, reftext = value
|
569
|
+
unless (refs = @catalog[:refs]).key? id
|
570
|
+
@catalog[:ids][id] = reftext || ('[' + id + ']')
|
571
|
+
refs[id] = ref
|
552
572
|
end
|
553
573
|
when :footnotes, :indexterms
|
554
|
-
@
|
574
|
+
@catalog[type] << value
|
555
575
|
else
|
556
|
-
if @options[:catalog_assets]
|
557
|
-
@references[type] << value
|
558
|
-
end
|
576
|
+
@catalog[type] << value if @options[:catalog_assets]
|
559
577
|
end
|
560
578
|
end
|
561
579
|
|
562
580
|
def footnotes?
|
563
|
-
|
581
|
+
@catalog[:footnotes].empty? ? false : true
|
564
582
|
end
|
565
583
|
|
566
584
|
def footnotes
|
567
|
-
@
|
585
|
+
@catalog[:footnotes]
|
568
586
|
end
|
569
587
|
|
570
588
|
def nested?
|
571
|
-
|
589
|
+
@parent_document ? true : false
|
572
590
|
end
|
573
591
|
|
574
592
|
def embedded?
|
575
|
-
# QUESTION should this be !@options[:header_footer] ?
|
576
593
|
@attributes.key? 'embedded'
|
577
594
|
end
|
578
595
|
|
579
596
|
def extensions?
|
580
|
-
|
597
|
+
@extensions ? true : false
|
581
598
|
end
|
582
599
|
|
583
600
|
# Make the raw source for the Document available.
|
@@ -590,14 +607,6 @@ class Document < AbstractBlock
|
|
590
607
|
@reader.source_lines if @reader
|
591
608
|
end
|
592
609
|
|
593
|
-
def doctype
|
594
|
-
@doctype ||= @attributes['doctype']
|
595
|
-
end
|
596
|
-
|
597
|
-
def backend
|
598
|
-
@backend ||= @attributes['backend']
|
599
|
-
end
|
600
|
-
|
601
610
|
def basebackend? base
|
602
611
|
@attributes['basebackend'] == base
|
603
612
|
end
|
@@ -607,9 +616,11 @@ class Document < AbstractBlock
|
|
607
616
|
@attributes['title']
|
608
617
|
end
|
609
618
|
|
610
|
-
def title=
|
611
|
-
|
612
|
-
|
619
|
+
def title= title
|
620
|
+
unless (sect = @header)
|
621
|
+
(sect = (@header = Section.new self, 0, false)).sectname = 'header'
|
622
|
+
end
|
623
|
+
sect.title = title
|
613
624
|
end
|
614
625
|
|
615
626
|
# Public: Resolves the primary title for the document
|
@@ -634,7 +645,7 @@ class Document < AbstractBlock
|
|
634
645
|
def doctitle opts = {}
|
635
646
|
if !(val = @attributes['title'].nil_or_empty?)
|
636
647
|
val = title
|
637
|
-
elsif (sect = first_section)
|
648
|
+
elsif (sect = first_section)
|
638
649
|
val = sect.title
|
639
650
|
elsif opts[:use_fallback] && (val = @attributes['untitled-label'])
|
640
651
|
# use val set in condition
|
@@ -645,12 +656,12 @@ class Document < AbstractBlock
|
|
645
656
|
if (separator = opts[:partition])
|
646
657
|
Title.new val, opts.merge({ :separator => (separator == true ? @attributes['title-separator'] : separator) })
|
647
658
|
elsif opts[:sanitize] && val.include?('<')
|
648
|
-
val.gsub(XmlSanitizeRx, '').
|
659
|
+
val.gsub(XmlSanitizeRx, '').squeeze(' ').strip
|
649
660
|
else
|
650
661
|
val
|
651
662
|
end
|
652
663
|
end
|
653
|
-
alias
|
664
|
+
alias name doctitle
|
654
665
|
|
655
666
|
# Public: Convenience method to retrieve the document attribute 'author'
|
656
667
|
#
|
@@ -678,15 +689,14 @@ class Document < AbstractBlock
|
|
678
689
|
@attributes.key? 'nofooter'
|
679
690
|
end
|
680
691
|
|
681
|
-
# QUESTION move to AbstractBlock?
|
682
692
|
def first_section
|
683
|
-
|
693
|
+
@header || @blocks.find {|e| e.context == :section }
|
684
694
|
end
|
685
695
|
|
686
696
|
def has_header?
|
687
697
|
@header ? true : false
|
688
698
|
end
|
689
|
-
alias
|
699
|
+
alias header? has_header?
|
690
700
|
|
691
701
|
# Public: Append a content Block to this Document.
|
692
702
|
#
|
@@ -696,14 +706,14 @@ class Document < AbstractBlock
|
|
696
706
|
#
|
697
707
|
# Returns The parent Block
|
698
708
|
def << block
|
699
|
-
|
709
|
+
enumerate_section block if block.context == :section
|
700
710
|
super
|
701
711
|
end
|
702
712
|
|
703
713
|
# Internal: called after the header has been parsed and before the content
|
704
714
|
# will be parsed.
|
705
715
|
#--
|
706
|
-
# QUESTION should we invoke the
|
716
|
+
# QUESTION should we invoke the TreeProcessors here, passing in a phase?
|
707
717
|
# QUESTION is finalize_header the right name?
|
708
718
|
def finalize_header unrooted_attributes, header_valid = true
|
709
719
|
clear_playback_attributes unrooted_attributes
|
@@ -769,11 +779,8 @@ class Document < AbstractBlock
|
|
769
779
|
attrs['toc-class'] ||= default_toc_class if default_toc_class
|
770
780
|
end
|
771
781
|
|
772
|
-
if attrs.key? 'compat-mode'
|
773
|
-
attrs['source-language'] = attrs['language'] if attrs.
|
774
|
-
@compat_mode = true
|
775
|
-
else
|
776
|
-
@compat_mode = false
|
782
|
+
if (@compat_mode = attrs.key? 'compat-mode')
|
783
|
+
attrs['source-language'] = attrs['language'] if attrs.key? 'language'
|
777
784
|
end
|
778
785
|
|
779
786
|
# NOTE pin the outfilesuffix after the header is parsed
|
@@ -825,32 +832,32 @@ class Document < AbstractBlock
|
|
825
832
|
#
|
826
833
|
# If the attribute is locked, false is returned. Otherwise, the value is
|
827
834
|
# assigned to the attribute name after first performing attribute
|
828
|
-
# substitutions on the value. If the attribute name is 'backend'
|
829
|
-
# value of backend-related attributes are updated.
|
835
|
+
# substitutions on the value. If the attribute name is 'backend' or
|
836
|
+
# 'doctype', then the value of backend-related attributes are updated.
|
830
837
|
#
|
831
838
|
# name - the String attribute name
|
832
|
-
# value - the String attribute value
|
839
|
+
# value - the String attribute value; must not be nil (default: '')
|
833
840
|
#
|
834
|
-
#
|
835
|
-
def set_attribute
|
836
|
-
if attribute_locked?
|
841
|
+
# Returns the resolved value if the attribute was set or false if it was not because it's locked.
|
842
|
+
def set_attribute name, value = ''
|
843
|
+
if attribute_locked? name
|
837
844
|
false
|
838
845
|
else
|
839
846
|
if @max_attribute_value_size
|
840
|
-
resolved_value = (apply_attribute_value_subs value).
|
847
|
+
resolved_value = (apply_attribute_value_subs value).limit_bytesize @max_attribute_value_size
|
841
848
|
else
|
842
849
|
resolved_value = apply_attribute_value_subs value
|
843
850
|
end
|
844
851
|
case name
|
845
852
|
when 'backend'
|
846
|
-
update_backend_attributes resolved_value,
|
853
|
+
update_backend_attributes resolved_value, (@attributes_modified.delete? 'htmlsyntax')
|
847
854
|
when 'doctype'
|
848
855
|
update_doctype_attributes resolved_value
|
849
856
|
else
|
850
857
|
@attributes[name] = resolved_value
|
851
858
|
end
|
852
859
|
@attributes_modified << name
|
853
|
-
|
860
|
+
resolved_value
|
854
861
|
end
|
855
862
|
end
|
856
863
|
|
@@ -890,61 +897,60 @@ class Document < AbstractBlock
|
|
890
897
|
# value - The String attribute value on which to perform substitutions
|
891
898
|
#
|
892
899
|
# Returns The String value with substitutions performed
|
893
|
-
def apply_attribute_value_subs
|
894
|
-
if
|
895
|
-
|
896
|
-
subs = resolve_pass_subs m[1]
|
897
|
-
subs.empty? ? m[2] : (apply_subs m[2], subs)
|
898
|
-
else
|
899
|
-
m[2]
|
900
|
-
end
|
900
|
+
def apply_attribute_value_subs value
|
901
|
+
if AttributeEntryPassMacroRx =~ value
|
902
|
+
$1 ? (apply_subs $2, (resolve_pass_subs $1)) : $2
|
901
903
|
else
|
902
904
|
apply_header_subs value
|
903
905
|
end
|
904
906
|
end
|
905
907
|
|
906
|
-
# Public: Update the backend attributes to reflect a change in the
|
908
|
+
# Public: Update the backend attributes to reflect a change in the active backend.
|
907
909
|
#
|
908
910
|
# This method also handles updating the related doctype attributes if the
|
909
911
|
# doctype attribute is assigned at the time this method is called.
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
current_basebackend = attrs['basebackend']
|
915
|
-
current_doctype = attrs['doctype']
|
912
|
+
#
|
913
|
+
# Returns the resolved String backend if updated, nothing otherwise.
|
914
|
+
def update_backend_attributes new_backend, force = nil
|
915
|
+
if force || (new_backend && new_backend != @backend)
|
916
|
+
current_backend, current_basebackend, current_doctype = @backend, (attrs = @attributes)['basebackend'], @doctype
|
916
917
|
if new_backend.start_with? 'xhtml'
|
917
918
|
attrs['htmlsyntax'] = 'xml'
|
918
919
|
new_backend = new_backend[1..-1]
|
919
920
|
elsif new_backend.start_with? 'html'
|
920
921
|
attrs['htmlsyntax'] = 'html' unless attrs['htmlsyntax'] == 'xml'
|
921
922
|
end
|
922
|
-
if (
|
923
|
-
new_backend =
|
923
|
+
if (resolved_backend = BACKEND_ALIASES[new_backend])
|
924
|
+
new_backend = resolved_backend
|
924
925
|
end
|
925
|
-
if
|
926
|
-
|
927
|
-
|
926
|
+
if current_doctype
|
927
|
+
if current_backend
|
928
|
+
attrs.delete %(backend-#{current_backend})
|
928
929
|
attrs.delete %(backend-#{current_backend}-doctype-#{current_doctype})
|
929
930
|
end
|
930
|
-
end
|
931
|
-
if current_doctype
|
932
|
-
attrs[%(doctype-#{current_doctype})] = ''
|
933
931
|
attrs[%(backend-#{new_backend}-doctype-#{current_doctype})] = ''
|
932
|
+
attrs[%(doctype-#{current_doctype})] = ''
|
933
|
+
elsif current_backend
|
934
|
+
attrs.delete %(backend-#{current_backend})
|
934
935
|
end
|
935
|
-
attrs['backend'] = new_backend
|
936
936
|
attrs[%(backend-#{new_backend})] = ''
|
937
|
+
@backend = attrs['backend'] = new_backend
|
937
938
|
# (re)initialize converter
|
938
939
|
if Converter::BackendInfo === (@converter = create_converter)
|
939
940
|
new_basebackend = @converter.basebackend
|
940
941
|
attrs['outfilesuffix'] = @converter.outfilesuffix unless attribute_locked? 'outfilesuffix'
|
941
942
|
new_filetype = @converter.filetype
|
942
|
-
|
943
|
+
elsif @converter
|
943
944
|
new_basebackend = new_backend.sub TrailingDigitsRx, ''
|
944
|
-
|
945
|
-
|
946
|
-
|
945
|
+
if (new_outfilesuffix = DEFAULT_EXTENSIONS[new_basebackend])
|
946
|
+
new_filetype = new_outfilesuffix[1..-1]
|
947
|
+
else
|
948
|
+
new_outfilesuffix, new_basebackend, new_filetype = '.html', 'html', 'html'
|
949
|
+
end
|
947
950
|
attrs['outfilesuffix'] = new_outfilesuffix unless attribute_locked? 'outfilesuffix'
|
951
|
+
else
|
952
|
+
# NOTE ideally we shouldn't need the converter before the converter phase, but we do
|
953
|
+
raise ::NotImplementedError, %(asciidoctor: FAILED: missing converter for backend '#{new_backend}'. Processing aborted.)
|
948
954
|
end
|
949
955
|
if (current_filetype = attrs['filetype'])
|
950
956
|
attrs.delete %(filetype-#{current_filetype})
|
@@ -957,38 +963,44 @@ class Document < AbstractBlock
|
|
957
963
|
attrs.delete 'pagewidth'
|
958
964
|
end
|
959
965
|
if new_basebackend != current_basebackend
|
960
|
-
if
|
961
|
-
|
962
|
-
|
966
|
+
if current_doctype
|
967
|
+
if current_basebackend
|
968
|
+
attrs.delete %(basebackend-#{current_basebackend})
|
963
969
|
attrs.delete %(basebackend-#{current_basebackend}-doctype-#{current_doctype})
|
964
970
|
end
|
971
|
+
attrs[%(basebackend-#{new_basebackend}-doctype-#{current_doctype})] = ''
|
972
|
+
elsif current_basebackend
|
973
|
+
attrs.delete %(basebackend-#{current_basebackend})
|
965
974
|
end
|
966
|
-
attrs['basebackend'] = new_basebackend
|
967
975
|
attrs[%(basebackend-#{new_basebackend})] = ''
|
968
|
-
attrs[
|
976
|
+
attrs['basebackend'] = new_basebackend
|
969
977
|
end
|
970
|
-
|
971
|
-
@backend = nil
|
978
|
+
return new_backend
|
972
979
|
end
|
973
980
|
end
|
974
981
|
|
982
|
+
# TODO document me
|
983
|
+
#
|
984
|
+
# Returns the String doctype if updated, nothing otherwise.
|
975
985
|
def update_doctype_attributes new_doctype
|
976
|
-
if new_doctype && new_doctype != @
|
977
|
-
attrs = @attributes
|
978
|
-
current_doctype = attrs['doctype']
|
979
|
-
current_backend = attrs['backend']
|
980
|
-
current_basebackend = attrs['basebackend']
|
986
|
+
if new_doctype && new_doctype != @doctype
|
987
|
+
current_backend, current_basebackend, current_doctype = @backend, (attrs = @attributes)['basebackend'], @doctype
|
981
988
|
if current_doctype
|
982
989
|
attrs.delete %(doctype-#{current_doctype})
|
983
|
-
|
984
|
-
|
990
|
+
if current_backend
|
991
|
+
attrs.delete %(backend-#{current_backend}-doctype-#{current_doctype})
|
992
|
+
attrs[%(backend-#{current_backend}-doctype-#{new_doctype})] = ''
|
993
|
+
end
|
994
|
+
if current_basebackend
|
995
|
+
attrs.delete %(basebackend-#{current_basebackend}-doctype-#{current_doctype})
|
996
|
+
attrs[%(basebackend-#{current_basebackend}-doctype-#{new_doctype})] = ''
|
997
|
+
end
|
998
|
+
else
|
999
|
+
attrs[%(backend-#{current_backend}-doctype-#{new_doctype})] = '' if current_backend
|
1000
|
+
attrs[%(basebackend-#{current_basebackend}-doctype-#{new_doctype})] = '' if current_basebackend
|
985
1001
|
end
|
986
|
-
attrs['doctype'] = new_doctype
|
987
1002
|
attrs[%(doctype-#{new_doctype})] = ''
|
988
|
-
|
989
|
-
attrs[%(basebackend-#{current_basebackend}-doctype-#{new_doctype})] = '' if current_basebackend
|
990
|
-
# clear cached doctype value
|
991
|
-
@doctype = nil
|
1003
|
+
return @doctype = attrs['doctype'] = new_doctype
|
992
1004
|
end
|
993
1005
|
end
|
994
1006
|
|
@@ -1033,17 +1045,15 @@ class Document < AbstractBlock
|
|
1033
1045
|
@attributes.delete 'outdir' unless (@attributes['outdir'] = opts['outdir'])
|
1034
1046
|
end
|
1035
1047
|
|
1036
|
-
# QUESTION should we add
|
1037
|
-
unless @converter
|
1038
|
-
fail %(asciidoctor: FAILED: missing converter for backend '#{backend}'. Processing aborted.)
|
1039
|
-
end
|
1048
|
+
# QUESTION should we add extensions that execute before conversion begins?
|
1040
1049
|
|
1041
1050
|
if doctype == 'inline'
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1051
|
+
if (block = @blocks[0])
|
1052
|
+
if block.content_model == :compound || block.content_model == :empty
|
1053
|
+
warn %(asciidoctor: WARNING: no inline candidate; use the inline doctype to convert a single paragragh, verbatim, or raw block)
|
1054
|
+
else
|
1055
|
+
output = block.content
|
1056
|
+
end
|
1047
1057
|
end
|
1048
1058
|
else
|
1049
1059
|
transform = ((opts.key? :header_footer) ? opts[:header_footer] : @options[:header_footer]) ? 'document' : 'embedded'
|
@@ -1062,7 +1072,7 @@ class Document < AbstractBlock
|
|
1062
1072
|
end
|
1063
1073
|
|
1064
1074
|
# Alias render to convert to maintain backwards compatibility
|
1065
|
-
alias
|
1075
|
+
alias render convert
|
1066
1076
|
|
1067
1077
|
# Public: Write the output to the specified file
|
1068
1078
|
#
|
@@ -1076,10 +1086,10 @@ class Document < AbstractBlock
|
|
1076
1086
|
unless output.nil_or_empty?
|
1077
1087
|
target.write output.chomp
|
1078
1088
|
# ensure there's a trailing endline
|
1079
|
-
target.write
|
1089
|
+
target.write LF
|
1080
1090
|
end
|
1081
1091
|
else
|
1082
|
-
::
|
1092
|
+
::IO.write target, output
|
1083
1093
|
end
|
1084
1094
|
nil
|
1085
1095
|
end
|
@@ -1126,11 +1136,9 @@ class Document < AbstractBlock
|
|
1126
1136
|
if safe >= SafeMode::SECURE
|
1127
1137
|
''
|
1128
1138
|
else
|
1129
|
-
|
1139
|
+
content = []
|
1140
|
+
qualifier = %(-#{location}) unless location == :head
|
1130
1141
|
suffix = @outfilesuffix unless suffix
|
1131
|
-
docinfodir = @attributes['docinfodir']
|
1132
|
-
|
1133
|
-
content = nil
|
1134
1142
|
|
1135
1143
|
if (docinfo = @attributes['docinfo']).nil_or_empty?
|
1136
1144
|
if @attributes.key? 'docinfo2'
|
@@ -1141,51 +1149,46 @@ class Document < AbstractBlock
|
|
1141
1149
|
docinfo = docinfo ? ['private'] : nil
|
1142
1150
|
end
|
1143
1151
|
else
|
1144
|
-
docinfo = docinfo.split(',').map
|
1152
|
+
docinfo = docinfo.split(',').map {|it| it.strip }
|
1145
1153
|
end
|
1146
1154
|
|
1147
1155
|
if docinfo
|
1148
|
-
|
1156
|
+
docinfo_file, docinfo_dir, docinfo_subs = %(docinfo#{qualifier}#{suffix}), @attributes['docinfodir'], resolve_docinfo_subs
|
1149
1157
|
unless (docinfo & ['shared', %(shared-#{location})]).empty?
|
1150
|
-
docinfo_path = normalize_system_path
|
1158
|
+
docinfo_path = normalize_system_path docinfo_file, docinfo_dir
|
1151
1159
|
# NOTE normalizing the lines is essential if we're performing substitutions
|
1152
|
-
if (
|
1153
|
-
|
1154
|
-
content = (docinfosubs == :attributes) ? sub_attributes(content) : apply_subs(content, docinfosubs)
|
1155
|
-
end
|
1160
|
+
if (shd_content = (read_asset docinfo_path, :normalize => true))
|
1161
|
+
content << (apply_subs shd_content, docinfo_subs)
|
1156
1162
|
end
|
1157
1163
|
end
|
1158
1164
|
|
1159
1165
|
unless @attributes['docname'].nil_or_empty? || (docinfo & ['private', %(private-#{location})]).empty?
|
1160
|
-
docinfo_path = normalize_system_path
|
1166
|
+
docinfo_path = normalize_system_path %(#{@attributes['docname']}-#{docinfo_file}), docinfo_dir
|
1161
1167
|
# NOTE normalizing the lines is essential if we're performing substitutions
|
1162
|
-
if (
|
1163
|
-
|
1164
|
-
content2 = (docinfosubs == :attributes) ? sub_attributes(content2) : apply_subs(content2, docinfosubs)
|
1165
|
-
end
|
1166
|
-
content = content ? %(#{content}#{EOL}#{content2}) : content2
|
1168
|
+
if (pvt_content = (read_asset docinfo_path, :normalize => true))
|
1169
|
+
content << (apply_subs pvt_content, docinfo_subs)
|
1167
1170
|
end
|
1168
1171
|
end
|
1169
1172
|
end
|
1170
1173
|
|
1171
1174
|
# TODO allow document to control whether extension docinfo is contributed
|
1172
|
-
if @extensions && docinfo_processors?
|
1173
|
-
|
1174
|
-
content = content ? %(#{content}#{EOL}#{contentx}) : contentx
|
1175
|
+
if @extensions && (docinfo_processors? location)
|
1176
|
+
content += @docinfo_processor_extensions[location].map {|ext| ext.process_method[self] }.compact
|
1175
1177
|
end
|
1176
1178
|
|
1177
|
-
|
1178
|
-
%(#{content})
|
1179
|
+
content * LF
|
1179
1180
|
end
|
1180
1181
|
end
|
1181
1182
|
|
1183
|
+
# Internal: Resolve the list of comma-delimited subs to apply to docinfo files.
|
1184
|
+
#
|
1185
|
+
# Resolve the list of substitutions from the value of the docinfosubs
|
1186
|
+
# document attribute, if specified. Otherwise, return an Array containing
|
1187
|
+
# the Symbol :attributes.
|
1188
|
+
#
|
1189
|
+
# Returns an [Array] of substitution [Symbol]s
|
1182
1190
|
def resolve_docinfo_subs
|
1183
|
-
|
1184
|
-
subs = resolve_subs @attributes['docinfosubs'], :block, nil, 'docinfo'
|
1185
|
-
subs.empty? ? nil : subs
|
1186
|
-
else
|
1187
|
-
:attributes
|
1188
|
-
end
|
1191
|
+
(@attributes.key? 'docinfosubs') ? (resolve_subs @attributes['docinfosubs'], :block, nil, 'docinfo') : [:attributes]
|
1189
1192
|
end
|
1190
1193
|
|
1191
1194
|
def docinfo_processors?(location = :head)
|