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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +162 -17
- data/LICENSE +1 -1
- data/README-de.adoc +12 -13
- data/README-fr.adoc +11 -12
- data/README-jp.adoc +11 -12
- data/README-zh_CN.adoc +12 -13
- data/README.adoc +6 -7
- data/asciidoctor.gemspec +19 -24
- data/bin/asciidoctor +5 -4
- data/data/reference/syntax.adoc +283 -0
- data/data/stylesheets/asciidoctor-default.css +56 -52
- data/data/stylesheets/coderay-asciidoctor.css +7 -9
- data/lib/asciidoctor.rb +171 -232
- data/lib/asciidoctor/abstract_block.rb +96 -105
- data/lib/asciidoctor/abstract_node.rb +118 -139
- data/lib/asciidoctor/attribute_list.rb +10 -14
- data/lib/asciidoctor/block.rb +20 -19
- data/lib/asciidoctor/callouts.rb +4 -2
- data/lib/asciidoctor/cli.rb +3 -2
- data/lib/asciidoctor/cli/invoker.rb +14 -21
- data/lib/asciidoctor/cli/options.rb +64 -54
- data/lib/asciidoctor/converter.rb +357 -185
- data/lib/asciidoctor/converter/composite.rb +40 -48
- data/lib/asciidoctor/converter/docbook5.rb +604 -640
- data/lib/asciidoctor/converter/html5.rb +949 -963
- data/lib/asciidoctor/converter/manpage.rb +569 -548
- data/lib/asciidoctor/converter/template.rb +231 -272
- data/lib/asciidoctor/core_ext.rb +5 -18
- data/lib/asciidoctor/core_ext/float/truncate.rb +19 -0
- data/lib/asciidoctor/core_ext/match_data/names.rb +7 -0
- data/lib/asciidoctor/core_ext/nil_or_empty.rb +1 -0
- data/lib/asciidoctor/core_ext/regexp/is_match.rb +4 -2
- data/lib/asciidoctor/document.rb +399 -377
- data/lib/asciidoctor/extensions.rb +72 -140
- data/lib/asciidoctor/helpers.rb +122 -83
- data/lib/asciidoctor/inline.rb +5 -1
- data/lib/asciidoctor/list.rb +13 -11
- data/lib/asciidoctor/logging.rb +17 -16
- data/lib/asciidoctor/parser.rb +390 -423
- data/lib/asciidoctor/path_resolver.rb +10 -5
- data/lib/asciidoctor/reader.rb +286 -263
- data/lib/asciidoctor/rouge_ext.rb +39 -0
- data/lib/asciidoctor/section.rb +9 -8
- data/lib/asciidoctor/stylesheets.rb +19 -37
- data/lib/asciidoctor/substitutors.rb +364 -509
- data/lib/asciidoctor/syntax_highlighter.rb +238 -0
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +87 -0
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +26 -0
- data/lib/asciidoctor/syntax_highlighter/html_pipeline.rb +10 -0
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +27 -0
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +149 -0
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +129 -0
- data/lib/asciidoctor/table.rb +73 -66
- data/lib/asciidoctor/timings.rb +4 -2
- data/lib/asciidoctor/version.rb +2 -1
- data/lib/asciidoctor/writer.rb +30 -0
- data/man/asciidoctor.1 +19 -15
- data/man/asciidoctor.adoc +14 -12
- metadata +69 -216
- data/CONTRIBUTING.adoc +0 -185
- data/Gemfile +0 -60
- data/Rakefile +0 -129
- data/bin/asciidoctor-safe +0 -15
- data/features/open_block.feature +0 -92
- data/features/pass_block.feature +0 -66
- data/features/step_definitions.rb +0 -49
- data/features/text_formatting.feature +0 -57
- data/features/xref.feature +0 -1039
- data/lib/asciidoctor/converter/base.rb +0 -59
- data/lib/asciidoctor/converter/docbook45.rb +0 -93
- data/lib/asciidoctor/converter/factory.rb +0 -226
- data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +0 -5
- data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +0 -4
- data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +0 -5
- data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/string/limit_bytesize.rb +0 -29
- data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +0 -6
- data/lib/asciidoctor/core_ext/string/limit_bytesize.rb +0 -10
- data/test/api_test.rb +0 -1240
- data/test/attribute_list_test.rb +0 -242
- data/test/attributes_test.rb +0 -1623
- data/test/blocks_test.rb +0 -3870
- data/test/converter_test.rb +0 -470
- data/test/document_test.rb +0 -1853
- data/test/extensions_test.rb +0 -1560
- data/test/fixtures/asciidoc_index.txt +0 -521
- data/test/fixtures/basic-docinfo-footer.html +0 -6
- data/test/fixtures/basic-docinfo-footer.xml +0 -8
- data/test/fixtures/basic-docinfo.html +0 -1
- data/test/fixtures/basic-docinfo.xml +0 -4
- data/test/fixtures/basic.asciidoc +0 -5
- data/test/fixtures/chapter-a.adoc +0 -3
- data/test/fixtures/child-include.adoc +0 -5
- data/test/fixtures/circle.svg +0 -9
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +0 -6
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +0 -6
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +0 -1
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +0 -3
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +0 -5
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +0 -6
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +0 -3
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +0 -5
- data/test/fixtures/custom-docinfodir/basic-docinfo.html +0 -1
- data/test/fixtures/custom-docinfodir/docinfo.html +0 -1
- data/test/fixtures/docinfo-footer.html +0 -1
- data/test/fixtures/docinfo-footer.xml +0 -9
- data/test/fixtures/docinfo.html +0 -1
- data/test/fixtures/docinfo.xml +0 -3
- data/test/fixtures/doctime-localtime.adoc +0 -2
- data/test/fixtures/dot.gif +0 -0
- data/test/fixtures/encoding.asciidoc +0 -13
- data/test/fixtures/file-with-missing-include.adoc +0 -1
- data/test/fixtures/grandchild-include.adoc +0 -3
- data/test/fixtures/hello-asciidoctor.pdf +0 -69
- data/test/fixtures/include-file.asciidoc +0 -24
- data/test/fixtures/include-file.jsx +0 -8
- data/test/fixtures/include-file.ml +0 -3
- data/test/fixtures/include-file.xml +0 -5
- data/test/fixtures/lists.adoc +0 -96
- data/test/fixtures/master.adoc +0 -5
- data/test/fixtures/mismatched-end-tag.adoc +0 -7
- data/test/fixtures/other-chapters.adoc +0 -11
- data/test/fixtures/outer-include.adoc +0 -5
- data/test/fixtures/parent-include-restricted.adoc +0 -5
- data/test/fixtures/parent-include.adoc +0 -5
- data/test/fixtures/sample.asciidoc +0 -30
- data/test/fixtures/section-a.adoc +0 -4
- data/test/fixtures/stylesheets/custom.css +0 -3
- data/test/fixtures/subdir/index.adoc +0 -3
- data/test/fixtures/subdir/inner-include.adoc +0 -3
- data/test/fixtures/subdir/middle-include.adoc +0 -5
- data/test/fixtures/subs-docinfo.html +0 -2
- data/test/fixtures/subs.adoc +0 -6
- data/test/fixtures/tagged-class-enclosed.rb +0 -25
- data/test/fixtures/tagged-class.rb +0 -23
- data/test/fixtures/tip.gif +0 -0
- data/test/fixtures/unclosed-tag.adoc +0 -3
- data/test/fixtures/unexpected-end-tag.adoc +0 -4
- data/test/invoker_test.rb +0 -745
- data/test/links_test.rb +0 -855
- data/test/lists_test.rb +0 -5151
- data/test/logger_test.rb +0 -211
- data/test/manpage_test.rb +0 -660
- data/test/options_test.rb +0 -262
- data/test/paragraphs_test.rb +0 -562
- data/test/parser_test.rb +0 -742
- data/test/paths_test.rb +0 -395
- data/test/preamble_test.rb +0 -173
- data/test/reader_test.rb +0 -2161
- data/test/sections_test.rb +0 -3575
- data/test/substitutions_test.rb +0 -2066
- data/test/tables_test.rb +0 -2036
- data/test/test_helper.rb +0 -447
- data/test/text_test.rb +0 -309
data/lib/asciidoctor/core_ext.rb
CHANGED
@@ -1,18 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
if
|
4
|
-
|
5
|
-
|
6
|
-
require 'asciidoctor/core_ext/1.8.7/io/binread'
|
7
|
-
require 'asciidoctor/core_ext/1.8.7/io/write'
|
8
|
-
end
|
9
|
-
elsif RUBY_ENGINE != 'opal'
|
10
|
-
require 'asciidoctor/core_ext/1.8.7/base64/strict_encode64'
|
11
|
-
require 'asciidoctor/core_ext/1.8.7/hash/key'
|
12
|
-
require 'asciidoctor/core_ext/1.8.7/io/binread'
|
13
|
-
require 'asciidoctor/core_ext/1.8.7/io/write'
|
14
|
-
require 'asciidoctor/core_ext/1.8.7/string/chr'
|
15
|
-
require 'asciidoctor/core_ext/1.8.7/string/limit_bytesize'
|
16
|
-
require 'asciidoctor/core_ext/1.8.7/symbol/empty'
|
17
|
-
require 'asciidoctor/core_ext/1.8.7/symbol/length'
|
18
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'core_ext/float/truncate'
|
3
|
+
require_relative 'core_ext/match_data/names' if RUBY_ENGINE == 'opal'
|
4
|
+
require_relative 'core_ext/nil_or_empty'
|
5
|
+
require_relative 'core_ext/regexp/is_match'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# NOTE remove once minimum required Ruby version is at least 2.4
|
3
|
+
Float.prepend(Module.new do
|
4
|
+
def truncate *args
|
5
|
+
if args.length == 1
|
6
|
+
if (precision = Integer args.shift) == 0
|
7
|
+
super
|
8
|
+
elsif precision > 0
|
9
|
+
precision_factor = 10.0 ** precision
|
10
|
+
(self * precision_factor).to_i / precision_factor
|
11
|
+
else
|
12
|
+
precision_factor = 10 ** precision.abs
|
13
|
+
(self / precision_factor).to_i * precision_factor
|
14
|
+
end
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end) if (Float.instance_method :truncate).arity == 0
|
data/lib/asciidoctor/document.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
3
|
# Public: The Document class represents a parsed AsciiDoc document.
|
4
4
|
#
|
@@ -153,9 +153,9 @@ class Document < AbstractBlock
|
|
153
153
|
#
|
154
154
|
# A value of 10 (SERVER) disallows the document from setting attributes that
|
155
155
|
# would affect the conversion of the document, in addition to all the security
|
156
|
-
# features of SafeMode::SAFE. For instance, this
|
157
|
-
# backend or
|
158
|
-
# document. This is the most fundamental level of security for server
|
156
|
+
# features of SafeMode::SAFE. For instance, this level forbids changing the
|
157
|
+
# backend or source-highlighter using an attribute defined in the source
|
158
|
+
# document header. This is the most fundamental level of security for server
|
159
159
|
# deployments (hence the name).
|
160
160
|
#
|
161
161
|
# A value of 20 (SECURE) disallows the document from attempting to read files
|
@@ -167,7 +167,7 @@ class Document < AbstractBlock
|
|
167
167
|
# trusted content into the document).
|
168
168
|
#
|
169
169
|
# Since Asciidoctor is aiming for wide adoption, 20 (SECURE) is the default
|
170
|
-
# value and is recommended for server
|
170
|
+
# value and is recommended for server deployments.
|
171
171
|
#
|
172
172
|
# A value of 100 (PARANOID) is planned to disallow the use of passthrough
|
173
173
|
# macros and prevents the document from setting any known attributes in
|
@@ -204,7 +204,7 @@ class Document < AbstractBlock
|
|
204
204
|
# Public: Get the Hash of document counters
|
205
205
|
attr_reader :counters
|
206
206
|
|
207
|
-
# Public: Get the level-0 Section
|
207
|
+
# Public: Get the level-0 Section (i.e., doctitle). (Only stores the title, not the header attributes).
|
208
208
|
attr_reader :header
|
209
209
|
|
210
210
|
# Public: Get the String base directory for converting this document.
|
@@ -231,6 +231,9 @@ class Document < AbstractBlock
|
|
231
231
|
# Public: Get the Converter associated with this document
|
232
232
|
attr_reader :converter
|
233
233
|
|
234
|
+
# Public: Get the SyntaxHighlighter associated with this document
|
235
|
+
attr_reader :syntax_highlighter
|
236
|
+
|
234
237
|
# Public: Get the activated Extensions::Registry associated with this document.
|
235
238
|
attr_reader :extensions
|
236
239
|
|
@@ -254,10 +257,7 @@ class Document < AbstractBlock
|
|
254
257
|
@parent_document = parent_doc
|
255
258
|
options[:base_dir] ||= parent_doc.base_dir
|
256
259
|
options[:catalog_assets] = true if parent_doc.options[:catalog_assets]
|
257
|
-
@catalog = parent_doc.catalog.
|
258
|
-
accum[key] = (key == :footnotes ? [] : table)
|
259
|
-
accum
|
260
|
-
end
|
260
|
+
@catalog = parent_doc.catalog.dup.tap {|catalog| catalog[:footnotes] = [] }
|
261
261
|
# QUESTION should we support setting attribute in parent document from nested document?
|
262
262
|
# NOTE we must dup or else all the assignments to the overrides clobbers the real attributes
|
263
263
|
@attribute_overrides = attr_overrides = parent_doc.attributes.dup
|
@@ -268,23 +268,25 @@ class Document < AbstractBlock
|
|
268
268
|
attr_overrides.delete 'toc-position'
|
269
269
|
@safe = parent_doc.safe
|
270
270
|
@attributes['compat-mode'] = '' if (@compat_mode = parent_doc.compat_mode)
|
271
|
+
@outfilesuffix = parent_doc.outfilesuffix
|
271
272
|
@sourcemap = parent_doc.sourcemap
|
272
273
|
@timings = nil
|
273
274
|
@path_resolver = parent_doc.path_resolver
|
274
275
|
@converter = parent_doc.converter
|
275
|
-
initialize_extensions =
|
276
|
+
initialize_extensions = nil
|
276
277
|
@extensions = parent_doc.extensions
|
278
|
+
@syntax_highlighter = parent_doc.syntax_highlighter
|
277
279
|
else
|
278
280
|
@parent_document = nil
|
279
281
|
@catalog = {
|
280
|
-
:
|
281
|
-
:
|
282
|
-
:
|
283
|
-
:
|
284
|
-
:
|
285
|
-
:
|
286
|
-
:
|
287
|
-
:
|
282
|
+
ids: {}, # deprecated; kept for backwards compatibility with converters
|
283
|
+
refs: {},
|
284
|
+
footnotes: [],
|
285
|
+
links: [],
|
286
|
+
images: [],
|
287
|
+
indexterms: [],
|
288
|
+
callouts: Callouts.new,
|
289
|
+
includes: {},
|
288
290
|
}
|
289
291
|
# copy attributes map and normalize keys
|
290
292
|
# attribute overrides are attributes that can only be set from the commandline
|
@@ -317,20 +319,15 @@ class Document < AbstractBlock
|
|
317
319
|
# be permissive in case API user wants to define new levels
|
318
320
|
@safe = safe_mode
|
319
321
|
else
|
320
|
-
|
321
|
-
begin
|
322
|
-
@safe = SafeMode.value_for_name safe_mode.to_s
|
323
|
-
rescue
|
324
|
-
@safe = SafeMode::SECURE
|
325
|
-
end
|
322
|
+
@safe = (SafeMode.value_for_name safe_mode) rescue SafeMode::SECURE
|
326
323
|
end
|
324
|
+
input_mtime = options.delete :input_mtime
|
327
325
|
@compat_mode = attr_overrides.key? 'compat-mode'
|
328
326
|
@sourcemap = options[:sourcemap]
|
329
327
|
@timings = options.delete :timings
|
330
328
|
@path_resolver = PathResolver.new
|
331
|
-
|
332
|
-
|
333
|
-
@extensions = nil # initialize furthur down
|
329
|
+
initialize_extensions = (defined? ::Asciidoctor::Extensions) ? true : nil
|
330
|
+
@extensions = nil # initialize furthur down if initialize_extensions is true
|
334
331
|
end
|
335
332
|
|
336
333
|
@parsed = false
|
@@ -383,7 +380,7 @@ class Document < AbstractBlock
|
|
383
380
|
attrs['last-update-label'] = 'Last updated'
|
384
381
|
|
385
382
|
attr_overrides['asciidoctor'] = ''
|
386
|
-
attr_overrides['asciidoctor-version'] = VERSION
|
383
|
+
attr_overrides['asciidoctor-version'] = ::Asciidoctor::VERSION
|
387
384
|
|
388
385
|
attr_overrides['safe-mode-name'] = (safe_mode_name = SafeMode.name_for_value @safe)
|
389
386
|
attr_overrides["safe-mode-#{safe_mode_name}"] = ''
|
@@ -397,8 +394,9 @@ class Document < AbstractBlock
|
|
397
394
|
|
398
395
|
attr_overrides['user-home'] = USER_HOME
|
399
396
|
|
400
|
-
# legacy
|
397
|
+
# remap legacy attribute names
|
401
398
|
attr_overrides['sectnums'] = attr_overrides.delete 'numbered' if attr_overrides.key? 'numbered'
|
399
|
+
attr_overrides['hardbreaks-option'] = attr_overrides.delete 'hardbreaks' if attr_overrides.key? 'hardbreaks'
|
402
400
|
|
403
401
|
# If the base_dir option is specified, it overrides docdir and is used as the root for relative
|
404
402
|
# paths. Otherwise, the base_dir is the directory of the source file (docdir), if set, otherwise
|
@@ -482,47 +480,28 @@ class Document < AbstractBlock
|
|
482
480
|
else
|
483
481
|
# setup default backend and doctype
|
484
482
|
@backend = nil
|
485
|
-
if (attrs['backend']
|
483
|
+
if (initial_backend = attrs['backend'] || DEFAULT_BACKEND) == 'manpage'
|
486
484
|
@doctype = attrs['doctype'] = attr_overrides['doctype'] = 'manpage'
|
487
485
|
else
|
488
486
|
@doctype = (attrs['doctype'] ||= DEFAULT_DOCTYPE)
|
489
487
|
end
|
490
|
-
update_backend_attributes
|
491
|
-
|
492
|
-
#attrs['indir'] = attrs['docdir']
|
493
|
-
#attrs['infile'] = attrs['docfile']
|
488
|
+
update_backend_attributes initial_backend, true
|
494
489
|
|
495
490
|
# dynamic intrinstic attribute values
|
496
491
|
|
497
|
-
#
|
498
|
-
#
|
499
|
-
now = ::ENV['SOURCE_DATE_EPOCH'] ? ::Time.at(Integer ::ENV['SOURCE_DATE_EPOCH']).utc : ::Time.now
|
500
|
-
if (localdate = attrs['localdate'])
|
501
|
-
localyear = (attrs['localyear'] ||= ((localdate.index '-') == 4 ? (localdate.slice 0, 4) : nil))
|
502
|
-
else
|
503
|
-
localdate = attrs['localdate'] = (now.strftime '%F')
|
504
|
-
localyear = (attrs['localyear'] ||= now.year.to_s)
|
505
|
-
end
|
506
|
-
# %Z is OS dependent and may contain characters that aren't UTF-8 encoded (see asciidoctor#2770 and asciidoctor.js#23)
|
507
|
-
# Ruby 1.8 doesn't support %:z
|
508
|
-
localtime = (attrs['localtime'] ||= now.strftime %(%T #{now.utc_offset == 0 ? 'UTC' : '%z'}))
|
509
|
-
attrs['localdatetime'] ||= %(#{localdate} #{localtime})
|
510
|
-
|
511
|
-
# docdate, doctime and docdatetime should default to
|
512
|
-
# localdate, localtime and localdatetime if not otherwise set
|
513
|
-
attrs['docdate'] ||= localdate
|
514
|
-
attrs['docyear'] ||= localyear
|
515
|
-
attrs['doctime'] ||= localtime
|
516
|
-
attrs['docdatetime'] ||= %(#{localdate} #{localtime})
|
492
|
+
#attrs['indir'] = attrs['docdir']
|
493
|
+
#attrs['infile'] = attrs['docfile']
|
517
494
|
|
518
495
|
# fallback directories
|
519
496
|
attrs['stylesdir'] ||= '.'
|
520
497
|
attrs['iconsdir'] ||= %(#{attrs.fetch 'imagesdir', './images'}/icons)
|
521
498
|
|
499
|
+
fill_datetime_attributes attrs, input_mtime
|
500
|
+
|
522
501
|
if initialize_extensions
|
523
502
|
if (ext_registry = options[:extension_registry])
|
524
503
|
# QUESTION should we warn if the value type of this option is not a registry
|
525
|
-
if Extensions::Registry === ext_registry || (::
|
504
|
+
if Extensions::Registry === ext_registry || ((defined? ::AsciidoctorJ::Extensions::ExtensionRegistry) &&
|
526
505
|
::AsciidoctorJ::Extensions::ExtensionRegistry === ext_registry)
|
527
506
|
@extensions = ext_registry.activate self
|
528
507
|
end
|
@@ -533,7 +512,7 @@ class Document < AbstractBlock
|
|
533
512
|
end
|
534
513
|
end
|
535
514
|
|
536
|
-
@reader = PreprocessorReader.new self, data, (Reader::Cursor.new attrs['docfile'], @base_dir), :
|
515
|
+
@reader = PreprocessorReader.new self, data, (Reader::Cursor.new attrs['docfile'], @base_dir), normalize: true
|
537
516
|
@source_location = @reader.cursor if @sourcemap
|
538
517
|
end
|
539
518
|
end
|
@@ -556,7 +535,7 @@ class Document < AbstractBlock
|
|
556
535
|
doc = self
|
557
536
|
# create reader if data is provided (used when data is not known at the time the Document object is created)
|
558
537
|
if data
|
559
|
-
@reader = PreprocessorReader.new doc, data, (Reader::Cursor.new @attributes['docfile'], @base_dir), :
|
538
|
+
@reader = PreprocessorReader.new doc, data, (Reader::Cursor.new @attributes['docfile'], @base_dir), normalize: true
|
560
539
|
@source_location = @reader.cursor if @sourcemap
|
561
540
|
end
|
562
541
|
|
@@ -567,7 +546,7 @@ class Document < AbstractBlock
|
|
567
546
|
end
|
568
547
|
|
569
548
|
# Now parse the lines in the reader into blocks
|
570
|
-
Parser.parse @reader, doc, :
|
549
|
+
Parser.parse @reader, doc, header_only: @options[:parse_header_only]
|
571
550
|
|
572
551
|
# should we call sort of post-parse function?
|
573
552
|
restore_attributes
|
@@ -585,6 +564,11 @@ class Document < AbstractBlock
|
|
585
564
|
end
|
586
565
|
end
|
587
566
|
|
567
|
+
# Public: Returns whether the source lines of the document have been parsed.
|
568
|
+
def parsed?
|
569
|
+
@parsed
|
570
|
+
end
|
571
|
+
|
588
572
|
# Public: Get the named counter and take the next number in the sequence.
|
589
573
|
#
|
590
574
|
# name - the String name of the counter
|
@@ -594,11 +578,11 @@ class Document < AbstractBlock
|
|
594
578
|
def counter name, seed = nil
|
595
579
|
return @parent_document.counter name, seed if @parent_document
|
596
580
|
if (attr_seed = !(attr_val = @attributes[name]).nil_or_empty?) && (@counters.key? name)
|
597
|
-
@attributes[name] = @counters[name] =
|
581
|
+
@attributes[name] = @counters[name] = Helpers.nextval attr_val
|
598
582
|
elsif seed
|
599
|
-
@attributes[name] = @counters[name] =
|
583
|
+
@attributes[name] = @counters[name] = seed == seed.to_i.to_s ? seed.to_i : seed
|
600
584
|
else
|
601
|
-
@attributes[name] = @counters[name] = nextval
|
585
|
+
@attributes[name] = @counters[name] = Helpers.nextval attr_seed ? attr_val : 0
|
602
586
|
end
|
603
587
|
end
|
604
588
|
|
@@ -614,46 +598,26 @@ class Document < AbstractBlock
|
|
614
598
|
# Deprecated: Map old counter_increment method to increment_counter for backwards compatibility
|
615
599
|
alias counter_increment increment_and_store_counter
|
616
600
|
|
617
|
-
#
|
618
|
-
#
|
619
|
-
# Handles both integer and character sequences.
|
620
|
-
#
|
621
|
-
# current - the value to increment as a String or Integer
|
622
|
-
#
|
623
|
-
# returns the next value in the sequence according to the current value's type
|
624
|
-
def nextval(current)
|
625
|
-
if ::Integer === current
|
626
|
-
current + 1
|
627
|
-
else
|
628
|
-
intval = current.to_i
|
629
|
-
if intval.to_s != current.to_s
|
630
|
-
(current[0].ord + 1).chr
|
631
|
-
else
|
632
|
-
intval + 1
|
633
|
-
end
|
634
|
-
end
|
635
|
-
end
|
636
|
-
|
601
|
+
# Public: Register a reference in the document catalog
|
637
602
|
def register type, value
|
638
603
|
case type
|
639
604
|
when :ids # deprecated
|
640
|
-
|
641
|
-
@catalog[:ids][id] ||= reftext || ('[' + id + ']')
|
605
|
+
register :refs, [(id = value[0]), (Inline.new self, :anchor, value[1], type: :ref, id: id)]
|
642
606
|
when :refs
|
643
|
-
|
644
|
-
|
645
|
-
@catalog[:ids][id] = reftext || ('[' + id + ']')
|
646
|
-
refs[id] = ref
|
647
|
-
end
|
607
|
+
@catalog[:refs][value[0]] ||= (ref = value[1])
|
608
|
+
ref
|
648
609
|
when :footnotes, :indexterms
|
649
610
|
@catalog[type] << value
|
650
611
|
else
|
651
|
-
if @options[:catalog_assets]
|
652
|
-
@catalog[type] << (type == :images ? (ImageReference.new value[0], value[1]) : value)
|
653
|
-
end
|
612
|
+
@catalog[type] << (type == :images ? (ImageReference.new value[0], value[1]) : value) if @options[:catalog_assets]
|
654
613
|
end
|
655
614
|
end
|
656
615
|
|
616
|
+
def resolve_id text
|
617
|
+
((@reftexts ||= @parsed ? {}.tap {|accum| @catalog[:refs].each {|id, ref| accum[ref.xreftext] = id } } : nil) ||
|
618
|
+
{}.tap {|accum| @catalog[:refs].find {|id, ref| ref.xreftext == text ? accum[text] = id : nil } })[text]
|
619
|
+
end
|
620
|
+
|
657
621
|
def footnotes?
|
658
622
|
@catalog[:footnotes].empty? ? false : true
|
659
623
|
end
|
@@ -743,7 +707,7 @@ class Document < AbstractBlock
|
|
743
707
|
end
|
744
708
|
|
745
709
|
if (separator = opts[:partition])
|
746
|
-
Title.new val, opts.merge({ :
|
710
|
+
Title.new val, opts.merge({ separator: (separator == true ? @attributes['title-separator'] : separator) })
|
747
711
|
elsif opts[:sanitize] && val.include?('<')
|
748
712
|
val.gsub(XmlSanitizeRx, '').squeeze(' ').strip
|
749
713
|
else
|
@@ -803,10 +767,10 @@ class Document < AbstractBlock
|
|
803
767
|
@header || @blocks.find {|e| e.context == :section }
|
804
768
|
end
|
805
769
|
|
806
|
-
def
|
770
|
+
def header?
|
807
771
|
@header ? true : false
|
808
772
|
end
|
809
|
-
alias
|
773
|
+
alias has_header? header?
|
810
774
|
|
811
775
|
# Public: Append a content Block to this Document.
|
812
776
|
#
|
@@ -820,8 +784,8 @@ class Document < AbstractBlock
|
|
820
784
|
super
|
821
785
|
end
|
822
786
|
|
823
|
-
# Internal:
|
824
|
-
#
|
787
|
+
# Internal: Called by the parser after parsing the header and before parsing
|
788
|
+
# the body, even if no header is found.
|
825
789
|
#--
|
826
790
|
# QUESTION should we invoke the TreeProcessors here, passing in a phase?
|
827
791
|
# QUESTION is finalize_header the right name?
|
@@ -832,96 +796,7 @@ class Document < AbstractBlock
|
|
832
796
|
unrooted_attributes
|
833
797
|
end
|
834
798
|
|
835
|
-
#
|
836
|
-
# at a future time.
|
837
|
-
def save_attributes
|
838
|
-
# enable toc and sectnums (i.e., numbered) by default in DocBook backend
|
839
|
-
# NOTE the attributes_modified should go away once we have a proper attribute storage & tracking facility
|
840
|
-
if (attrs = @attributes)['basebackend'] == 'docbook'
|
841
|
-
attrs['toc'] = '' unless attribute_locked?('toc') || @attributes_modified.include?('toc')
|
842
|
-
attrs['sectnums'] = '' unless attribute_locked?('sectnums') || @attributes_modified.include?('sectnums')
|
843
|
-
end
|
844
|
-
|
845
|
-
unless attrs.key?('doctitle') || !(val = doctitle)
|
846
|
-
attrs['doctitle'] = val
|
847
|
-
end
|
848
|
-
|
849
|
-
# css-signature cannot be updated after header attributes are processed
|
850
|
-
@id = attrs['css-signature'] unless @id
|
851
|
-
|
852
|
-
toc_position_val = if (toc_val = (attrs.delete('toc2') ? 'left' : attrs['toc']))
|
853
|
-
# toc-placement allows us to separate position from using fitted slot vs macro
|
854
|
-
(toc_placement = attrs.fetch('toc-placement', 'macro')) && toc_placement != 'auto' ? toc_placement : attrs['toc-position']
|
855
|
-
else
|
856
|
-
nil
|
857
|
-
end
|
858
|
-
|
859
|
-
if toc_val && (!toc_val.empty? || !toc_position_val.nil_or_empty?)
|
860
|
-
default_toc_position = 'left'
|
861
|
-
# TODO rename toc2 to aside-toc
|
862
|
-
default_toc_class = 'toc2'
|
863
|
-
if !toc_position_val.nil_or_empty?
|
864
|
-
position = toc_position_val
|
865
|
-
elsif !toc_val.empty?
|
866
|
-
position = toc_val
|
867
|
-
else
|
868
|
-
position = default_toc_position
|
869
|
-
end
|
870
|
-
attrs['toc'] = ''
|
871
|
-
attrs['toc-placement'] = 'auto'
|
872
|
-
case position
|
873
|
-
when 'left', '<', '<'
|
874
|
-
attrs['toc-position'] = 'left'
|
875
|
-
when 'right', '>', '>'
|
876
|
-
attrs['toc-position'] = 'right'
|
877
|
-
when 'top', '^'
|
878
|
-
attrs['toc-position'] = 'top'
|
879
|
-
when 'bottom', 'v'
|
880
|
-
attrs['toc-position'] = 'bottom'
|
881
|
-
when 'preamble', 'macro'
|
882
|
-
attrs['toc-position'] = 'content'
|
883
|
-
attrs['toc-placement'] = position
|
884
|
-
default_toc_class = nil
|
885
|
-
else
|
886
|
-
attrs.delete 'toc-position'
|
887
|
-
default_toc_class = nil
|
888
|
-
end
|
889
|
-
attrs['toc-class'] ||= default_toc_class if default_toc_class
|
890
|
-
end
|
891
|
-
|
892
|
-
if (@compat_mode = attrs.key? 'compat-mode')
|
893
|
-
attrs['source-language'] = attrs['language'] if attrs.key? 'language'
|
894
|
-
end
|
895
|
-
|
896
|
-
# NOTE pin the outfilesuffix after the header is parsed
|
897
|
-
@outfilesuffix = attrs['outfilesuffix']
|
898
|
-
|
899
|
-
@header_attributes = attrs.dup
|
900
|
-
|
901
|
-
# unfreeze "flexible" attributes
|
902
|
-
unless @parent_document
|
903
|
-
FLEXIBLE_ATTRIBUTES.each do |name|
|
904
|
-
# turning a flexible attribute off should be permanent
|
905
|
-
# (we may need more config if that's not always the case)
|
906
|
-
if @attribute_overrides.key?(name) && @attribute_overrides[name]
|
907
|
-
@attribute_overrides.delete(name)
|
908
|
-
end
|
909
|
-
end
|
910
|
-
end
|
911
|
-
end
|
912
|
-
|
913
|
-
# Internal: Restore the attributes to the previously saved state (attributes in header)
|
914
|
-
def restore_attributes
|
915
|
-
@catalog[:callouts].rewind unless @parent_document
|
916
|
-
@attributes.replace @header_attributes
|
917
|
-
end
|
918
|
-
|
919
|
-
# Internal: Delete any attributes stored for playback
|
920
|
-
def clear_playback_attributes(attributes)
|
921
|
-
attributes.delete(:attribute_entries)
|
922
|
-
end
|
923
|
-
|
924
|
-
# Internal: Replay attribute assignments at the block level
|
799
|
+
# Public: Replay attribute assignments at the block level
|
925
800
|
def playback_attributes(block_attributes)
|
926
801
|
if block_attributes.key? :attribute_entries
|
927
802
|
block_attributes[:attribute_entries].each do |entry|
|
@@ -937,6 +812,12 @@ class Document < AbstractBlock
|
|
937
812
|
end
|
938
813
|
end
|
939
814
|
|
815
|
+
# Public: Restore the attributes to the previously saved state (attributes in header)
|
816
|
+
def restore_attributes
|
817
|
+
@catalog[:callouts].rewind unless @parent_document
|
818
|
+
@attributes.replace @header_attributes
|
819
|
+
end
|
820
|
+
|
940
821
|
# Public: Set the specified attribute on the document if the name is not locked
|
941
822
|
#
|
942
823
|
# If the attribute is locked, false is returned. Otherwise, the value is
|
@@ -945,28 +826,27 @@ class Document < AbstractBlock
|
|
945
826
|
# 'doctype', then the value of backend-related attributes are updated.
|
946
827
|
#
|
947
828
|
# name - the String attribute name
|
948
|
-
# value - the String attribute value; must not be nil (default: '')
|
829
|
+
# value - the String attribute value; must not be nil (optional, default: '')
|
949
830
|
#
|
950
|
-
# Returns the
|
831
|
+
# Returns the substituted value if the attribute was set or nil if it was not because it's locked.
|
951
832
|
def set_attribute name, value = ''
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
if @
|
956
|
-
|
833
|
+
unless attribute_locked? name
|
834
|
+
value = apply_attribute_value_subs value unless value.empty?
|
835
|
+
# NOTE if @header_attributes is set, we're beyond the document header
|
836
|
+
if @header_attributes
|
837
|
+
@attributes[name] = value
|
957
838
|
else
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
@
|
839
|
+
case name
|
840
|
+
when 'backend'
|
841
|
+
update_backend_attributes value, (@attributes_modified.delete? 'htmlsyntax') && value == @backend
|
842
|
+
when 'doctype'
|
843
|
+
update_doctype_attributes value
|
844
|
+
else
|
845
|
+
@attributes[name] = value
|
846
|
+
end
|
847
|
+
@attributes_modified << name
|
967
848
|
end
|
968
|
-
|
969
|
-
resolved_value
|
849
|
+
value
|
970
850
|
end
|
971
851
|
end
|
972
852
|
|
@@ -1017,151 +897,6 @@ class Document < AbstractBlock
|
|
1017
897
|
end
|
1018
898
|
end
|
1019
899
|
|
1020
|
-
# Internal: Apply substitutions to the attribute value
|
1021
|
-
#
|
1022
|
-
# If the value is an inline passthrough macro (e.g., pass:<subs>[value]),
|
1023
|
-
# apply the substitutions defined in <subs> to the value, or leave the value
|
1024
|
-
# unmodified if no substitutions are specified. If the value is not an
|
1025
|
-
# inline passthrough macro, apply header substitutions to the value.
|
1026
|
-
#
|
1027
|
-
# value - The String attribute value on which to perform substitutions
|
1028
|
-
#
|
1029
|
-
# Returns The String value with substitutions performed
|
1030
|
-
def apply_attribute_value_subs value
|
1031
|
-
if AttributeEntryPassMacroRx =~ value
|
1032
|
-
$1 ? (apply_subs $2, (resolve_pass_subs $1)) : $2
|
1033
|
-
else
|
1034
|
-
apply_header_subs value
|
1035
|
-
end
|
1036
|
-
end
|
1037
|
-
|
1038
|
-
# Public: Update the backend attributes to reflect a change in the active backend.
|
1039
|
-
#
|
1040
|
-
# This method also handles updating the related doctype attributes if the
|
1041
|
-
# doctype attribute is assigned at the time this method is called.
|
1042
|
-
#
|
1043
|
-
# Returns the resolved String backend if updated, nothing otherwise.
|
1044
|
-
def update_backend_attributes new_backend, force = nil
|
1045
|
-
if force || (new_backend && new_backend != @backend)
|
1046
|
-
current_backend, current_basebackend, current_doctype = @backend, (attrs = @attributes)['basebackend'], @doctype
|
1047
|
-
if new_backend.start_with? 'xhtml'
|
1048
|
-
attrs['htmlsyntax'] = 'xml'
|
1049
|
-
new_backend = new_backend.slice 1, new_backend.length
|
1050
|
-
elsif new_backend.start_with? 'html'
|
1051
|
-
attrs['htmlsyntax'] = 'html' unless attrs['htmlsyntax'] == 'xml'
|
1052
|
-
end
|
1053
|
-
if (resolved_backend = BACKEND_ALIASES[new_backend])
|
1054
|
-
new_backend = resolved_backend
|
1055
|
-
end
|
1056
|
-
if current_doctype
|
1057
|
-
if current_backend
|
1058
|
-
attrs.delete %(backend-#{current_backend})
|
1059
|
-
attrs.delete %(backend-#{current_backend}-doctype-#{current_doctype})
|
1060
|
-
end
|
1061
|
-
attrs[%(backend-#{new_backend}-doctype-#{current_doctype})] = ''
|
1062
|
-
attrs[%(doctype-#{current_doctype})] = ''
|
1063
|
-
elsif current_backend
|
1064
|
-
attrs.delete %(backend-#{current_backend})
|
1065
|
-
end
|
1066
|
-
attrs[%(backend-#{new_backend})] = ''
|
1067
|
-
@backend = attrs['backend'] = new_backend
|
1068
|
-
# (re)initialize converter
|
1069
|
-
if Converter::BackendInfo === (@converter = create_converter)
|
1070
|
-
new_basebackend = @converter.basebackend
|
1071
|
-
attrs['outfilesuffix'] = @converter.outfilesuffix unless attribute_locked? 'outfilesuffix'
|
1072
|
-
new_filetype = @converter.filetype
|
1073
|
-
elsif @converter
|
1074
|
-
new_basebackend = new_backend.sub TrailingDigitsRx, ''
|
1075
|
-
if (new_outfilesuffix = DEFAULT_EXTENSIONS[new_basebackend])
|
1076
|
-
new_filetype = new_outfilesuffix.slice 1, new_outfilesuffix.length
|
1077
|
-
else
|
1078
|
-
new_outfilesuffix, new_basebackend, new_filetype = '.html', 'html', 'html'
|
1079
|
-
end
|
1080
|
-
attrs['outfilesuffix'] = new_outfilesuffix unless attribute_locked? 'outfilesuffix'
|
1081
|
-
else
|
1082
|
-
# NOTE ideally we shouldn't need the converter before the converter phase, but we do
|
1083
|
-
raise ::NotImplementedError, %(asciidoctor: FAILED: missing converter for backend '#{new_backend}'. Processing aborted.)
|
1084
|
-
end
|
1085
|
-
if (current_filetype = attrs['filetype'])
|
1086
|
-
attrs.delete %(filetype-#{current_filetype})
|
1087
|
-
end
|
1088
|
-
attrs['filetype'] = new_filetype
|
1089
|
-
attrs[%(filetype-#{new_filetype})] = ''
|
1090
|
-
if (page_width = DEFAULT_PAGE_WIDTHS[new_basebackend])
|
1091
|
-
attrs['pagewidth'] = page_width
|
1092
|
-
else
|
1093
|
-
attrs.delete 'pagewidth'
|
1094
|
-
end
|
1095
|
-
if new_basebackend != current_basebackend
|
1096
|
-
if current_doctype
|
1097
|
-
if current_basebackend
|
1098
|
-
attrs.delete %(basebackend-#{current_basebackend})
|
1099
|
-
attrs.delete %(basebackend-#{current_basebackend}-doctype-#{current_doctype})
|
1100
|
-
end
|
1101
|
-
attrs[%(basebackend-#{new_basebackend}-doctype-#{current_doctype})] = ''
|
1102
|
-
elsif current_basebackend
|
1103
|
-
attrs.delete %(basebackend-#{current_basebackend})
|
1104
|
-
end
|
1105
|
-
attrs[%(basebackend-#{new_basebackend})] = ''
|
1106
|
-
attrs['basebackend'] = new_basebackend
|
1107
|
-
end
|
1108
|
-
return new_backend
|
1109
|
-
end
|
1110
|
-
end
|
1111
|
-
|
1112
|
-
# TODO document me
|
1113
|
-
#
|
1114
|
-
# Returns the String doctype if updated, nothing otherwise.
|
1115
|
-
def update_doctype_attributes new_doctype
|
1116
|
-
if new_doctype && new_doctype != @doctype
|
1117
|
-
current_backend, current_basebackend, current_doctype = @backend, (attrs = @attributes)['basebackend'], @doctype
|
1118
|
-
if current_doctype
|
1119
|
-
attrs.delete %(doctype-#{current_doctype})
|
1120
|
-
if current_backend
|
1121
|
-
attrs.delete %(backend-#{current_backend}-doctype-#{current_doctype})
|
1122
|
-
attrs[%(backend-#{current_backend}-doctype-#{new_doctype})] = ''
|
1123
|
-
end
|
1124
|
-
if current_basebackend
|
1125
|
-
attrs.delete %(basebackend-#{current_basebackend}-doctype-#{current_doctype})
|
1126
|
-
attrs[%(basebackend-#{current_basebackend}-doctype-#{new_doctype})] = ''
|
1127
|
-
end
|
1128
|
-
else
|
1129
|
-
attrs[%(backend-#{current_backend}-doctype-#{new_doctype})] = '' if current_backend
|
1130
|
-
attrs[%(basebackend-#{current_basebackend}-doctype-#{new_doctype})] = '' if current_basebackend
|
1131
|
-
end
|
1132
|
-
attrs[%(doctype-#{new_doctype})] = ''
|
1133
|
-
return @doctype = attrs['doctype'] = new_doctype
|
1134
|
-
end
|
1135
|
-
end
|
1136
|
-
|
1137
|
-
# TODO document me
|
1138
|
-
def create_converter
|
1139
|
-
converter_opts = {}
|
1140
|
-
converter_opts[:htmlsyntax] = @attributes['htmlsyntax']
|
1141
|
-
if (template_dir = @options[:template_dir])
|
1142
|
-
template_dirs = [template_dir]
|
1143
|
-
elsif (template_dirs = @options[:template_dirs])
|
1144
|
-
template_dirs = Array template_dirs
|
1145
|
-
end
|
1146
|
-
if template_dirs
|
1147
|
-
converter_opts[:template_dirs] = template_dirs
|
1148
|
-
converter_opts[:template_cache] = @options.fetch :template_cache, true
|
1149
|
-
converter_opts[:template_engine] = @options[:template_engine]
|
1150
|
-
converter_opts[:template_engine_options] = @options[:template_engine_options]
|
1151
|
-
converter_opts[:eruby] = @options[:eruby]
|
1152
|
-
converter_opts[:safe] = @safe
|
1153
|
-
end
|
1154
|
-
if (converter = @options[:converter])
|
1155
|
-
converter_factory = Converter::Factory.new ::Hash[backend, converter]
|
1156
|
-
else
|
1157
|
-
converter_factory = Converter::Factory.default false
|
1158
|
-
end
|
1159
|
-
# QUESTION should we honor the convert_opts?
|
1160
|
-
# QUESTION should we pass through all options and attributes too?
|
1161
|
-
#converter_opts.update opts
|
1162
|
-
converter_factory.create backend, converter_opts
|
1163
|
-
end
|
1164
|
-
|
1165
900
|
# Public: Convert the AsciiDoc document using the templates
|
1166
901
|
# loaded by the Converter. If a :template_dir is not specified,
|
1167
902
|
# or a template is missing, the converter will fall back to
|
@@ -1223,13 +958,11 @@ class Document < AbstractBlock
|
|
1223
958
|
# ensure there's a trailing endline
|
1224
959
|
target.write LF
|
1225
960
|
end
|
1226
|
-
elsif COERCE_ENCODING
|
1227
|
-
::IO.write target, output, :encoding => ::Encoding::UTF_8
|
1228
961
|
else
|
1229
|
-
::
|
962
|
+
::File.write target, output, mode: FILE_WRITE_MODE
|
1230
963
|
end
|
1231
|
-
if @backend == 'manpage' && ::String === target && (@converter.respond_to? :write_alternate_pages)
|
1232
|
-
@converter.write_alternate_pages @attributes['mannames'], @attributes['manvolnum'], target
|
964
|
+
if @backend == 'manpage' && ::String === target && (@converter.class.respond_to? :write_alternate_pages)
|
965
|
+
@converter.class.write_alternate_pages @attributes['mannames'], @attributes['manvolnum'], target
|
1233
966
|
end
|
1234
967
|
end
|
1235
968
|
@timings.record :write if @timings
|
@@ -1274,10 +1007,7 @@ class Document < AbstractBlock
|
|
1274
1007
|
# returns The contents of the docinfo file(s) or empty string if no files are
|
1275
1008
|
# found or the safe mode is secure or greater.
|
1276
1009
|
def docinfo location = :head, suffix = nil
|
1277
|
-
if safe
|
1278
|
-
''
|
1279
|
-
else
|
1280
|
-
content = []
|
1010
|
+
if safe < SafeMode::SECURE
|
1281
1011
|
qualifier = %(-#{location}) unless location == :head
|
1282
1012
|
suffix = @outfilesuffix unless suffix
|
1283
1013
|
|
@@ -1294,33 +1024,85 @@ class Document < AbstractBlock
|
|
1294
1024
|
end
|
1295
1025
|
|
1296
1026
|
if docinfo
|
1027
|
+
content = []
|
1297
1028
|
docinfo_file, docinfo_dir, docinfo_subs = %(docinfo#{qualifier}#{suffix}), @attributes['docinfodir'], resolve_docinfo_subs
|
1298
1029
|
unless (docinfo & ['shared', %(shared-#{location})]).empty?
|
1299
1030
|
docinfo_path = normalize_system_path docinfo_file, docinfo_dir
|
1300
1031
|
# NOTE normalizing the lines is essential if we're performing substitutions
|
1301
|
-
if (
|
1302
|
-
content << (apply_subs
|
1032
|
+
if (shared_docinfo = read_asset docinfo_path, normalize: true)
|
1033
|
+
content << (apply_subs shared_docinfo, docinfo_subs)
|
1303
1034
|
end
|
1304
1035
|
end
|
1305
1036
|
|
1306
1037
|
unless @attributes['docname'].nil_or_empty? || (docinfo & ['private', %(private-#{location})]).empty?
|
1307
1038
|
docinfo_path = normalize_system_path %(#{@attributes['docname']}-#{docinfo_file}), docinfo_dir
|
1308
1039
|
# NOTE normalizing the lines is essential if we're performing substitutions
|
1309
|
-
if (
|
1310
|
-
content << (apply_subs
|
1040
|
+
if (private_docinfo = read_asset docinfo_path, normalize: true)
|
1041
|
+
content << (apply_subs private_docinfo, docinfo_subs)
|
1311
1042
|
end
|
1312
1043
|
end
|
1313
1044
|
end
|
1045
|
+
end
|
1314
1046
|
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1047
|
+
# TODO allow document to control whether extension docinfo is contributed
|
1048
|
+
if @extensions && (docinfo_processors? location)
|
1049
|
+
(content ||= []).concat @docinfo_processor_extensions[location].map {|ext| ext.process_method[self] }.compact
|
1050
|
+
end
|
1319
1051
|
|
1320
|
-
|
1052
|
+
content ? (content.join LF) : ''
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
def docinfo_processors?(location = :head)
|
1056
|
+
if @docinfo_processor_extensions.key?(location)
|
1057
|
+
# false means we already performed a lookup and didn't find any
|
1058
|
+
@docinfo_processor_extensions[location] != false
|
1059
|
+
elsif @extensions && @document.extensions.docinfo_processors?(location)
|
1060
|
+
!!(@docinfo_processor_extensions[location] = @document.extensions.docinfo_processors(location))
|
1061
|
+
else
|
1062
|
+
@docinfo_processor_extensions[location] = false
|
1321
1063
|
end
|
1322
1064
|
end
|
1323
1065
|
|
1066
|
+
def to_s
|
1067
|
+
%(#<#{self.class}@#{object_id} {doctype: #{doctype.inspect}, doctitle: #{(@header != nil ? @header.title : nil).inspect}, blocks: #{@blocks.size}}>)
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
private
|
1071
|
+
|
1072
|
+
# Internal: Apply substitutions to the attribute value
|
1073
|
+
#
|
1074
|
+
# If the value is an inline passthrough macro (e.g., pass:<subs>[value]),
|
1075
|
+
# apply the substitutions defined in <subs> to the value, or leave the value
|
1076
|
+
# unmodified if no substitutions are specified. If the value is not an
|
1077
|
+
# inline passthrough macro, apply header substitutions to the value.
|
1078
|
+
#
|
1079
|
+
# value - The String attribute value on which to perform substitutions
|
1080
|
+
#
|
1081
|
+
# Returns The String value with substitutions performed
|
1082
|
+
def apply_attribute_value_subs value
|
1083
|
+
if AttributeEntryPassMacroRx =~ value
|
1084
|
+
value = $1 ? (apply_subs $2, (resolve_pass_subs $1)) : $2
|
1085
|
+
else
|
1086
|
+
value = apply_header_subs value
|
1087
|
+
end
|
1088
|
+
@max_attribute_value_size ? (limit_bytesize value, @max_attribute_value_size) : value
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
# Internal: Safely truncates a string to the specified number of bytes.
|
1092
|
+
#
|
1093
|
+
# If a multibyte char gets split, the dangling fragment is dropped.
|
1094
|
+
#
|
1095
|
+
# str - The String the truncate.
|
1096
|
+
# max - The maximum allowable size of the String, in bytes.
|
1097
|
+
#
|
1098
|
+
# Returns the String truncated to the specified bytesize.
|
1099
|
+
def limit_bytesize str, max
|
1100
|
+
if str.bytesize > max
|
1101
|
+
max -= 1 until (str = str.byteslice 0, max).valid_encoding?
|
1102
|
+
end
|
1103
|
+
str
|
1104
|
+
end
|
1105
|
+
|
1324
1106
|
# Internal: Resolve the list of comma-delimited subs to apply to docinfo files.
|
1325
1107
|
#
|
1326
1108
|
# Resolve the list of substitutions from the value of the docinfosubs
|
@@ -1332,20 +1114,260 @@ class Document < AbstractBlock
|
|
1332
1114
|
(@attributes.key? 'docinfosubs') ? (resolve_subs @attributes['docinfosubs'], :block, nil, 'docinfo') : [:attributes]
|
1333
1115
|
end
|
1334
1116
|
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1117
|
+
# Internal: Create and initialize an instance of the converter for this document
|
1118
|
+
#--
|
1119
|
+
# QUESTION is there any additional information we should be passing to the converter?
|
1120
|
+
def create_converter backend, delegate_backend
|
1121
|
+
converter_opts = { document: self, htmlsyntax: @attributes['htmlsyntax'] }
|
1122
|
+
if (template_dirs = (opts = @options)[:template_dirs] || opts[:template_dir])
|
1123
|
+
converter_opts[:template_dirs] = [*template_dirs]
|
1124
|
+
converter_opts[:template_cache] = opts.fetch :template_cache, true
|
1125
|
+
converter_opts[:template_engine] = opts[:template_engine]
|
1126
|
+
converter_opts[:template_engine_options] = opts[:template_engine_options]
|
1127
|
+
converter_opts[:eruby] = opts[:eruby]
|
1128
|
+
converter_opts[:safe] = @safe
|
1129
|
+
converter_opts[:delegate_backend] = delegate_backend if delegate_backend
|
1130
|
+
end
|
1131
|
+
if (converter = opts[:converter])
|
1132
|
+
(Converter::CustomFactory.new backend => converter).create backend, converter_opts
|
1341
1133
|
else
|
1342
|
-
|
1134
|
+
(opts.fetch :converter_factory, Converter).create backend, converter_opts
|
1343
1135
|
end
|
1344
1136
|
end
|
1345
1137
|
|
1346
|
-
|
1347
|
-
|
1138
|
+
# Internal: Delete any attributes stored for playback
|
1139
|
+
def clear_playback_attributes(attributes)
|
1140
|
+
attributes.delete(:attribute_entries)
|
1348
1141
|
end
|
1349
1142
|
|
1143
|
+
# Internal: Branch the attributes so that the original state can be restored
|
1144
|
+
# at a future time.
|
1145
|
+
#
|
1146
|
+
# Returns the duplicated attributes, which will later be restored
|
1147
|
+
def save_attributes
|
1148
|
+
unless ((attrs = @attributes).key? 'doctitle') || !(doctitle_val = doctitle)
|
1149
|
+
attrs['doctitle'] = doctitle_val
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
# css-signature cannot be updated after header attributes are processed
|
1153
|
+
@id ||= attrs['css-signature']
|
1154
|
+
|
1155
|
+
if (toc_val = (attrs.delete 'toc2') ? 'left' : attrs['toc'])
|
1156
|
+
# toc-placement allows us to separate position from using fitted slot vs macro
|
1157
|
+
toc_position_val = (toc_placement_val = attrs.fetch 'toc-placement', 'macro') && toc_placement_val != 'auto' ? toc_placement_val : attrs['toc-position']
|
1158
|
+
unless toc_val.empty? && toc_position_val.nil_or_empty?
|
1159
|
+
default_toc_position = 'left'
|
1160
|
+
# TODO rename toc2 to aside-toc
|
1161
|
+
default_toc_class = 'toc2'
|
1162
|
+
position = toc_position_val.nil_or_empty? ? (toc_val.empty? ? default_toc_position : toc_val) : toc_position_val
|
1163
|
+
attrs['toc'] = ''
|
1164
|
+
attrs['toc-placement'] = 'auto'
|
1165
|
+
case position
|
1166
|
+
when 'left', '<', '<'
|
1167
|
+
attrs['toc-position'] = 'left'
|
1168
|
+
when 'right', '>', '>'
|
1169
|
+
attrs['toc-position'] = 'right'
|
1170
|
+
when 'top', '^'
|
1171
|
+
attrs['toc-position'] = 'top'
|
1172
|
+
when 'bottom', 'v'
|
1173
|
+
attrs['toc-position'] = 'bottom'
|
1174
|
+
when 'preamble', 'macro'
|
1175
|
+
attrs['toc-position'] = 'content'
|
1176
|
+
attrs['toc-placement'] = position
|
1177
|
+
default_toc_class = nil
|
1178
|
+
else
|
1179
|
+
attrs.delete 'toc-position'
|
1180
|
+
default_toc_class = nil
|
1181
|
+
end
|
1182
|
+
attrs['toc-class'] ||= default_toc_class if default_toc_class
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
if (icons_val = attrs['icons']) && !(attrs.key? 'icontype')
|
1187
|
+
case icons_val
|
1188
|
+
when '', 'font'
|
1189
|
+
else
|
1190
|
+
attrs['icons'] = ''
|
1191
|
+
attrs['icontype'] = icons_val
|
1192
|
+
end
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
if (@compat_mode = attrs.key? 'compat-mode')
|
1196
|
+
attrs['source-language'] = attrs['language'] if attrs.key? 'language'
|
1197
|
+
end
|
1198
|
+
|
1199
|
+
unless @parent_document
|
1200
|
+
if (basebackend = attrs['basebackend']) == 'html'
|
1201
|
+
# QUESTION should we allow source-highlighter to be disabled in AsciiDoc table cell?
|
1202
|
+
if (syntax_hl_name = attrs['source-highlighter']) && !attrs[%(#{syntax_hl_name}-unavailable)]
|
1203
|
+
if (syntax_hl_factory = @options[:syntax_highlighter_factory])
|
1204
|
+
@syntax_highlighter = syntax_hl_factory.create syntax_hl_name, @backend, document: self
|
1205
|
+
elsif (syntax_hls = @options[:syntax_highlighters])
|
1206
|
+
@syntax_highlighter = (SyntaxHighlighter::DefaultFactoryProxy.new syntax_hls).create syntax_hl_name, @backend, document: self
|
1207
|
+
else
|
1208
|
+
@syntax_highlighter = SyntaxHighlighter.create syntax_hl_name, @backend, document: self
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
# enable toc and sectnums (i.e., numbered) by default in DocBook backend
|
1212
|
+
elsif basebackend == 'docbook'
|
1213
|
+
# NOTE the attributes_modified should go away once we have a proper attribute storage & tracking facility
|
1214
|
+
attrs['toc'] = '' unless (attribute_locked? 'toc') || (@attributes_modified.include? 'toc')
|
1215
|
+
attrs['sectnums'] = '' unless (attribute_locked? 'sectnums') || (@attributes_modified.include? 'sectnums')
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
# NOTE pin the outfilesuffix after the header is parsed
|
1219
|
+
@outfilesuffix = attrs['outfilesuffix']
|
1220
|
+
|
1221
|
+
# unfreeze "flexible" attributes
|
1222
|
+
FLEXIBLE_ATTRIBUTES.each do |name|
|
1223
|
+
# turning a flexible attribute off should be permanent
|
1224
|
+
# (we may need more config if that's not always the case)
|
1225
|
+
if @attribute_overrides.key?(name) && @attribute_overrides[name]
|
1226
|
+
@attribute_overrides.delete(name)
|
1227
|
+
end
|
1228
|
+
end
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
@header_attributes = attrs.dup
|
1232
|
+
end
|
1233
|
+
|
1234
|
+
# Internal: Assign the local and document datetime attributes, which includes localdate, localyear, localtime,
|
1235
|
+
# localdatetime, docdate, docyear, doctime, and docdatetime. Honor the SOURCE_DATE_EPOCH environment variable, if set.
|
1236
|
+
def fill_datetime_attributes attrs, input_mtime
|
1237
|
+
# See https://reproducible-builds.org/specs/source-date-epoch/
|
1238
|
+
now = (::ENV.key? 'SOURCE_DATE_EPOCH') ? (source_date_epoch = (::Time.at Integer ::ENV['SOURCE_DATE_EPOCH']).utc) : ::Time.now
|
1239
|
+
if (localdate = attrs['localdate'])
|
1240
|
+
attrs['localyear'] ||= (localdate.index '-') == 4 ? (localdate.slice 0, 4) : nil
|
1241
|
+
else
|
1242
|
+
localdate = attrs['localdate'] = now.strftime '%F'
|
1243
|
+
attrs['localyear'] ||= now.year.to_s
|
1244
|
+
end
|
1245
|
+
# %Z is OS dependent and may contain characters that aren't UTF-8 encoded (see asciidoctor#2770 and asciidoctor.js#23)
|
1246
|
+
localtime = (attrs['localtime'] ||= now.strftime %(%T #{now.utc_offset == 0 ? 'UTC' : '%z'}))
|
1247
|
+
attrs['localdatetime'] ||= %(#{localdate} #{localtime})
|
1248
|
+
# docdate, doctime and docdatetime should default to localdate, localtime and localdatetime if not otherwise set
|
1249
|
+
input_mtime = source_date_epoch || input_mtime || now
|
1250
|
+
if (docdate = attrs['docdate'])
|
1251
|
+
attrs['docyear'] ||= ((docdate.index '-') == 4 ? (docdate.slice 0, 4) : nil)
|
1252
|
+
else
|
1253
|
+
docdate = attrs['docdate'] = input_mtime.strftime '%F'
|
1254
|
+
attrs['docyear'] ||= input_mtime.year.to_s
|
1255
|
+
end
|
1256
|
+
# %Z is OS dependent and may contain characters that aren't UTF-8 encoded (see asciidoctor#2770 and asciidoctor.js#23)
|
1257
|
+
doctime = (attrs['doctime'] ||= input_mtime.strftime %(%T #{input_mtime.utc_offset == 0 ? 'UTC' : '%z'}))
|
1258
|
+
attrs['docdatetime'] ||= %(#{docdate} #{doctime})
|
1259
|
+
nil
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
# Internal: Update the backend attributes to reflect a change in the active backend.
|
1263
|
+
#
|
1264
|
+
# This method also handles updating the related doctype attributes if the
|
1265
|
+
# doctype attribute is assigned at the time this method is called.
|
1266
|
+
#
|
1267
|
+
# Returns the resolved String backend if updated, nothing otherwise.
|
1268
|
+
def update_backend_attributes new_backend, init = nil
|
1269
|
+
if init || new_backend != @backend
|
1270
|
+
current_backend = @backend
|
1271
|
+
current_basebackend = (attrs = @attributes)['basebackend']
|
1272
|
+
current_doctype = @doctype
|
1273
|
+
actual_backend, _, new_backend = new_backend.partition ':' if new_backend.include? ':'
|
1274
|
+
if new_backend.start_with? 'xhtml'
|
1275
|
+
attrs['htmlsyntax'] = 'xml'
|
1276
|
+
new_backend = new_backend.slice 1, new_backend.length
|
1277
|
+
elsif new_backend.start_with? 'html'
|
1278
|
+
attrs['htmlsyntax'] ||= 'html'
|
1279
|
+
end
|
1280
|
+
new_backend = BACKEND_ALIASES[new_backend] || new_backend
|
1281
|
+
new_backend, delegate_backend = actual_backend, new_backend if actual_backend
|
1282
|
+
if current_doctype
|
1283
|
+
if current_backend
|
1284
|
+
attrs.delete %(backend-#{current_backend})
|
1285
|
+
attrs.delete %(backend-#{current_backend}-doctype-#{current_doctype})
|
1286
|
+
end
|
1287
|
+
attrs[%(backend-#{new_backend}-doctype-#{current_doctype})] = ''
|
1288
|
+
attrs[%(doctype-#{current_doctype})] = ''
|
1289
|
+
elsif current_backend
|
1290
|
+
attrs.delete %(backend-#{current_backend})
|
1291
|
+
end
|
1292
|
+
attrs[%(backend-#{new_backend})] = ''
|
1293
|
+
# QUESTION should we defer the @backend assignment until after the converter is created?
|
1294
|
+
@backend = attrs['backend'] = new_backend
|
1295
|
+
# (re)initialize converter
|
1296
|
+
if Converter::BackendTraits === (converter = create_converter new_backend, delegate_backend)
|
1297
|
+
new_basebackend = converter.basebackend
|
1298
|
+
new_filetype = converter.filetype
|
1299
|
+
if (htmlsyntax = converter.htmlsyntax)
|
1300
|
+
attrs['htmlsyntax'] = htmlsyntax
|
1301
|
+
end
|
1302
|
+
if init
|
1303
|
+
attrs['outfilesuffix'] ||= converter.outfilesuffix
|
1304
|
+
else
|
1305
|
+
attrs['outfilesuffix'] = converter.outfilesuffix unless attribute_locked? 'outfilesuffix'
|
1306
|
+
end
|
1307
|
+
elsif converter
|
1308
|
+
backend_traits = Converter::BackendTraits.derive_backend_traits new_backend
|
1309
|
+
new_basebackend = backend_traits[:basebackend]
|
1310
|
+
new_filetype = backend_traits[:filetype]
|
1311
|
+
if init
|
1312
|
+
attrs['outfilesuffix'] ||= backend_traits[:outfilesuffix]
|
1313
|
+
else
|
1314
|
+
attrs['outfilesuffix'] = backend_traits[:outfilesuffix] unless attribute_locked? 'outfilesuffix'
|
1315
|
+
end
|
1316
|
+
else
|
1317
|
+
# NOTE ideally we shouldn't need the converter before the converter phase, but we do
|
1318
|
+
raise ::NotImplementedError, %(asciidoctor: FAILED: missing converter for backend '#{new_backend}'. Processing aborted.)
|
1319
|
+
end
|
1320
|
+
@converter = converter
|
1321
|
+
if (current_filetype = attrs['filetype'])
|
1322
|
+
attrs.delete %(filetype-#{current_filetype})
|
1323
|
+
end
|
1324
|
+
attrs['filetype'] = new_filetype
|
1325
|
+
attrs[%(filetype-#{new_filetype})] = ''
|
1326
|
+
if (page_width = DEFAULT_PAGE_WIDTHS[new_basebackend])
|
1327
|
+
attrs['pagewidth'] = page_width
|
1328
|
+
else
|
1329
|
+
attrs.delete 'pagewidth'
|
1330
|
+
end
|
1331
|
+
if new_basebackend != current_basebackend
|
1332
|
+
if current_doctype
|
1333
|
+
if current_basebackend
|
1334
|
+
attrs.delete %(basebackend-#{current_basebackend})
|
1335
|
+
attrs.delete %(basebackend-#{current_basebackend}-doctype-#{current_doctype})
|
1336
|
+
end
|
1337
|
+
attrs[%(basebackend-#{new_basebackend}-doctype-#{current_doctype})] = ''
|
1338
|
+
elsif current_basebackend
|
1339
|
+
attrs.delete %(basebackend-#{current_basebackend})
|
1340
|
+
end
|
1341
|
+
attrs[%(basebackend-#{new_basebackend})] = ''
|
1342
|
+
attrs['basebackend'] = new_basebackend
|
1343
|
+
end
|
1344
|
+
new_backend
|
1345
|
+
end
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
# Internal: Update the doctype and backend attributes to reflect a change in the active doctype.
|
1349
|
+
#
|
1350
|
+
# Returns the String doctype if updated, nothing otherwise.
|
1351
|
+
def update_doctype_attributes new_doctype
|
1352
|
+
if new_doctype && new_doctype != @doctype
|
1353
|
+
current_backend, current_basebackend, current_doctype = @backend, (attrs = @attributes)['basebackend'], @doctype
|
1354
|
+
if current_doctype
|
1355
|
+
attrs.delete %(doctype-#{current_doctype})
|
1356
|
+
if current_backend
|
1357
|
+
attrs.delete %(backend-#{current_backend}-doctype-#{current_doctype})
|
1358
|
+
attrs[%(backend-#{current_backend}-doctype-#{new_doctype})] = ''
|
1359
|
+
end
|
1360
|
+
if current_basebackend
|
1361
|
+
attrs.delete %(basebackend-#{current_basebackend}-doctype-#{current_doctype})
|
1362
|
+
attrs[%(basebackend-#{current_basebackend}-doctype-#{new_doctype})] = ''
|
1363
|
+
end
|
1364
|
+
else
|
1365
|
+
attrs[%(backend-#{current_backend}-doctype-#{new_doctype})] = '' if current_backend
|
1366
|
+
attrs[%(basebackend-#{current_basebackend}-doctype-#{new_doctype})] = '' if current_basebackend
|
1367
|
+
end
|
1368
|
+
attrs[%(doctype-#{new_doctype})] = ''
|
1369
|
+
return @doctype = attrs['doctype'] = new_doctype
|
1370
|
+
end
|
1371
|
+
end
|
1350
1372
|
end
|
1351
1373
|
end
|