asciidoctor 1.5.7.1 → 1.5.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +95 -5
- data/Gemfile +23 -13
- data/README-de.adoc +482 -0
- data/README-fr.adoc +128 -119
- data/README-jp.adoc +2 -3
- data/README-zh_CN.adoc +2 -3
- data/README.adoc +131 -106
- data/asciidoctor.gemspec +9 -7
- data/data/locale/attributes-ar.adoc +1 -1
- data/data/locale/attributes-bg.adoc +1 -1
- data/data/locale/attributes-ca.adoc +1 -1
- data/data/locale/attributes-cs.adoc +1 -1
- data/data/locale/attributes-da.adoc +1 -1
- data/data/locale/attributes-de.adoc +1 -1
- data/data/locale/attributes-en.adoc +1 -1
- data/data/locale/attributes-es.adoc +1 -1
- data/data/locale/attributes-fa.adoc +1 -1
- data/data/locale/attributes-fi.adoc +1 -1
- data/data/locale/attributes-fr.adoc +1 -1
- data/data/locale/attributes-hu.adoc +1 -1
- data/data/locale/attributes-id.adoc +1 -1
- data/data/locale/attributes-it.adoc +1 -1
- data/data/locale/attributes-ja.adoc +1 -1
- data/data/locale/attributes-kr.adoc +1 -1
- data/data/locale/attributes-nb.adoc +1 -1
- data/data/locale/attributes-nl.adoc +1 -1
- data/data/locale/attributes-nn.adoc +1 -1
- data/data/locale/attributes-pl.adoc +1 -1
- data/data/locale/attributes-pt.adoc +1 -1
- data/data/locale/attributes-pt_BR.adoc +1 -1
- data/data/locale/attributes-ro.adoc +1 -1
- data/data/locale/attributes-ru.adoc +1 -1
- data/data/locale/attributes-sr.adoc +5 -4
- data/data/locale/attributes-sr_Latn.adoc +5 -4
- data/data/locale/attributes-sv.adoc +23 -0
- data/data/locale/attributes-tr.adoc +1 -1
- data/data/locale/attributes-uk.adoc +1 -1
- data/data/locale/attributes-zh_CN.adoc +1 -1
- data/data/locale/attributes-zh_TW.adoc +1 -1
- data/data/stylesheets/asciidoctor-default.css +23 -23
- data/lib/asciidoctor.rb +110 -104
- data/lib/asciidoctor/abstract_block.rb +55 -32
- data/lib/asciidoctor/abstract_node.rb +32 -17
- data/lib/asciidoctor/attribute_list.rb +8 -7
- data/lib/asciidoctor/block.rb +5 -7
- data/lib/asciidoctor/cli/options.rb +5 -9
- data/lib/asciidoctor/converter.rb +2 -2
- data/lib/asciidoctor/converter/docbook45.rb +7 -20
- data/lib/asciidoctor/converter/docbook5.rb +36 -37
- data/lib/asciidoctor/converter/factory.rb +10 -8
- data/lib/asciidoctor/converter/html5.rb +90 -65
- data/lib/asciidoctor/converter/manpage.rb +72 -62
- data/lib/asciidoctor/converter/template.rb +8 -6
- data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +5 -0
- data/lib/asciidoctor/document.rb +62 -10
- data/lib/asciidoctor/extensions.rb +74 -16
- data/lib/asciidoctor/helpers.rb +11 -14
- data/lib/asciidoctor/list.rb +2 -2
- data/lib/asciidoctor/parser.rb +223 -195
- data/lib/asciidoctor/path_resolver.rb +15 -7
- data/lib/asciidoctor/reader.rb +65 -36
- data/lib/asciidoctor/section.rb +6 -4
- data/lib/asciidoctor/substitutors.rb +170 -149
- data/lib/asciidoctor/table.rb +16 -8
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +6 -5
- data/man/asciidoctor.adoc +3 -2
- data/test/api_test.rb +236 -0
- data/test/attribute_list_test.rb +242 -0
- data/test/attributes_test.rb +65 -52
- data/test/blocks_test.rb +408 -260
- data/test/converter_test.rb +7 -7
- data/test/document_test.rb +60 -54
- data/test/extensions_test.rb +218 -32
- data/test/fixtures/doctime-localtime.adoc +2 -0
- data/test/fixtures/section-a.adoc +4 -0
- data/test/fixtures/subs.adoc +0 -1
- data/test/invoker_test.rb +56 -18
- data/test/links_test.rb +105 -81
- data/test/lists_test.rb +636 -265
- data/test/logger_test.rb +1 -1
- data/test/manpage_test.rb +140 -3
- data/test/paragraphs_test.rb +42 -42
- data/test/parser_test.rb +63 -183
- data/test/paths_test.rb +21 -4
- data/test/preamble_test.rb +9 -9
- data/test/reader_test.rb +78 -28
- data/test/sections_test.rb +273 -151
- data/test/substitutions_test.rb +53 -19
- data/test/tables_test.rb +286 -163
- data/test/test_helper.rb +4 -3
- data/test/text_test.rb +65 -65
- metadata +16 -21
@@ -159,6 +159,29 @@ module Extensions
|
|
159
159
|
Block.new parent, context, { :source => source, :attributes => attrs }.merge(opts)
|
160
160
|
end
|
161
161
|
|
162
|
+
# Public: Creates a list node and links it to the specified parent.
|
163
|
+
#
|
164
|
+
# parent - The parent Block (Block, Section, or Document) of this new list block.
|
165
|
+
# context - The list context (e.g., :ulist, :olist, :colist, :dlist)
|
166
|
+
# attrs - A Hash of attributes to set on this list block
|
167
|
+
#
|
168
|
+
# Returns a [List] node with all properties properly initialized.
|
169
|
+
def create_list parent, context, attrs = nil
|
170
|
+
list = List.new parent, context
|
171
|
+
list.update_attributes attrs if attrs
|
172
|
+
list
|
173
|
+
end
|
174
|
+
|
175
|
+
# Public: Creates a list item node and links it to the specified parent.
|
176
|
+
#
|
177
|
+
# parent - The parent List of this new list item block.
|
178
|
+
# text - The text of the list item.
|
179
|
+
#
|
180
|
+
# Returns a [ListItem] node with all properties properly initialized.
|
181
|
+
def create_list_item parent, text = nil
|
182
|
+
ListItem.new parent, text
|
183
|
+
end
|
184
|
+
|
162
185
|
# Public: Creates an image block node and links it to the specified parent.
|
163
186
|
#
|
164
187
|
# parent - The parent Block (Block, Section, or Document) of this new image block.
|
@@ -173,7 +196,13 @@ module Extensions
|
|
173
196
|
raise ::ArgumentError, 'Unable to create an image block, target attribute is required'
|
174
197
|
end
|
175
198
|
attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
|
176
|
-
|
199
|
+
title = (attrs.key? 'title') ? (attrs.delete 'title') : nil
|
200
|
+
block = create_block parent, :image, nil, attrs, opts
|
201
|
+
if title
|
202
|
+
block.title = title
|
203
|
+
block.assign_caption((attrs.delete 'caption'), (opts[:caption_context] || 'figure'))
|
204
|
+
end
|
205
|
+
block
|
177
206
|
end
|
178
207
|
|
179
208
|
def create_inline parent, context, text, opts = {}
|
@@ -243,7 +272,15 @@ module Extensions
|
|
243
272
|
end
|
244
273
|
end
|
245
274
|
|
246
|
-
module
|
275
|
+
module DocumentProcessorDsl
|
276
|
+
include ProcessorDsl
|
277
|
+
|
278
|
+
def prefer
|
279
|
+
option :position, :>>
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
module SyntaxProcessorDsl
|
247
284
|
include ProcessorDsl
|
248
285
|
|
249
286
|
def named value
|
@@ -343,7 +380,7 @@ module Extensions
|
|
343
380
|
raise ::NotImplementedError, %(Asciidoctor::Extensions::Preprocessor subclass must implement ##{__method__} method)
|
344
381
|
end
|
345
382
|
end
|
346
|
-
Preprocessor::DSL =
|
383
|
+
Preprocessor::DSL = DocumentProcessorDsl
|
347
384
|
|
348
385
|
# Public: TreeProcessors are run on the Document after the source has been
|
349
386
|
# parsed into an abstract syntax tree (AST), as represented by the Document
|
@@ -360,7 +397,7 @@ module Extensions
|
|
360
397
|
raise ::NotImplementedError, %(Asciidoctor::Extensions::TreeProcessor subclass must implement ##{__method__} method)
|
361
398
|
end
|
362
399
|
end
|
363
|
-
TreeProcessor::DSL =
|
400
|
+
TreeProcessor::DSL = DocumentProcessorDsl
|
364
401
|
|
365
402
|
# Alias deprecated class name for backwards compatibility
|
366
403
|
Treeprocessor = TreeProcessor
|
@@ -385,7 +422,7 @@ module Extensions
|
|
385
422
|
raise ::NotImplementedError, %(Asciidoctor::Extensions::Postprocessor subclass must implement ##{__method__} method)
|
386
423
|
end
|
387
424
|
end
|
388
|
-
Postprocessor::DSL =
|
425
|
+
Postprocessor::DSL = DocumentProcessorDsl
|
389
426
|
|
390
427
|
# Public: IncludeProcessors are used to process `include::<target>[]`
|
391
428
|
# directives in the source document.
|
@@ -409,7 +446,7 @@ module Extensions
|
|
409
446
|
end
|
410
447
|
|
411
448
|
module IncludeProcessorDsl
|
412
|
-
include
|
449
|
+
include DocumentProcessorDsl
|
413
450
|
|
414
451
|
def handles? *args, &block
|
415
452
|
if block_given?
|
@@ -451,7 +488,7 @@ module Extensions
|
|
451
488
|
end
|
452
489
|
|
453
490
|
module DocinfoProcessorDsl
|
454
|
-
include
|
491
|
+
include DocumentProcessorDsl
|
455
492
|
|
456
493
|
def at_location value
|
457
494
|
option :location, value
|
@@ -507,7 +544,7 @@ module Extensions
|
|
507
544
|
end
|
508
545
|
|
509
546
|
module BlockProcessorDsl
|
510
|
-
include
|
547
|
+
include SyntaxProcessorDsl
|
511
548
|
|
512
549
|
def contexts *value
|
513
550
|
option :contexts, value.flatten.to_set
|
@@ -533,7 +570,7 @@ module Extensions
|
|
533
570
|
end
|
534
571
|
|
535
572
|
module MacroProcessorDsl
|
536
|
-
include
|
573
|
+
include SyntaxProcessorDsl
|
537
574
|
|
538
575
|
def resolves_attributes *args
|
539
576
|
if args.size == 1 && !args[0]
|
@@ -552,6 +589,10 @@ module Extensions
|
|
552
589
|
#
|
553
590
|
# BlockMacroProcessor implementations must extend BlockMacroProcessor.
|
554
591
|
class BlockMacroProcessor < MacroProcessor
|
592
|
+
def name
|
593
|
+
raise ::ArgumentError, %(invalid name for block macro: #{@name}) unless MacroNameRx.match? @name.to_s
|
594
|
+
@name
|
595
|
+
end
|
555
596
|
end
|
556
597
|
BlockMacroProcessor::DSL = MacroProcessorDsl
|
557
598
|
|
@@ -1253,6 +1294,26 @@ module Extensions
|
|
1253
1294
|
@inline_macro_extensions.values
|
1254
1295
|
end
|
1255
1296
|
|
1297
|
+
# Public: Inserts the document processor {Extension} instance as the first
|
1298
|
+
# processor of its kind in the extension registry.
|
1299
|
+
#
|
1300
|
+
# Examples
|
1301
|
+
#
|
1302
|
+
# prefer :include_processor do
|
1303
|
+
# process do |document, reader, target, attrs|
|
1304
|
+
# ...
|
1305
|
+
# end
|
1306
|
+
# end
|
1307
|
+
#
|
1308
|
+
# Returns the [Extension] stored in the registry that proxies the instance
|
1309
|
+
# of this processor.
|
1310
|
+
def prefer *args, &block
|
1311
|
+
extension = ProcessorExtension === (arg0 = args.shift) ? arg0 : (send arg0, *args, &block)
|
1312
|
+
extensions_store = instance_variable_get(%(@#{extension.kind}_extensions).to_sym)
|
1313
|
+
extensions_store.unshift extensions_store.delete extension
|
1314
|
+
extension
|
1315
|
+
end
|
1316
|
+
|
1256
1317
|
private
|
1257
1318
|
|
1258
1319
|
def add_document_processor kind, args, &block
|
@@ -1298,11 +1359,8 @@ module Extensions
|
|
1298
1359
|
end
|
1299
1360
|
end
|
1300
1361
|
|
1301
|
-
|
1302
|
-
|
1303
|
-
else
|
1304
|
-
kind_store << extension
|
1305
|
-
end
|
1362
|
+
extension.config[:position] == :>> ? (kind_store.unshift extension) : (kind_store << extension)
|
1363
|
+
extension
|
1306
1364
|
end
|
1307
1365
|
|
1308
1366
|
def add_syntax_processor kind, args, &block
|
@@ -1490,7 +1548,7 @@ module Extensions
|
|
1490
1548
|
# Public: Resolves the Class object for the qualified name.
|
1491
1549
|
#
|
1492
1550
|
# Returns Class
|
1493
|
-
if RUBY_MIN_VERSION_2
|
1551
|
+
if ::RUBY_MIN_VERSION_2
|
1494
1552
|
def class_for_name qualified_name
|
1495
1553
|
resolved = ::Object.const_get qualified_name, false
|
1496
1554
|
raise unless ::Class === resolved
|
@@ -1498,7 +1556,7 @@ module Extensions
|
|
1498
1556
|
rescue
|
1499
1557
|
raise ::NameError, %(Could not resolve class for name: #{qualified_name})
|
1500
1558
|
end
|
1501
|
-
elsif RUBY_MIN_VERSION_1_9
|
1559
|
+
elsif ::RUBY_MIN_VERSION_1_9
|
1502
1560
|
def class_for_name qualified_name
|
1503
1561
|
resolved = (qualified_name.split '::').reduce ::Object do |current, name|
|
1504
1562
|
name.empty? ? current : (current.const_get name, false)
|
data/lib/asciidoctor/helpers.rb
CHANGED
@@ -70,21 +70,20 @@ module Helpers
|
|
70
70
|
if COERCE_ENCODING
|
71
71
|
utf8 = ::Encoding::UTF_8
|
72
72
|
if (leading_2_bytes = leading_bytes.slice 0, 2) == BOM_BYTES_UTF_16LE
|
73
|
-
# HACK Ruby messes up trailing whitespace on UTF-16LE, so
|
74
|
-
|
73
|
+
# HACK Ruby messes up trailing whitespace on UTF-16LE, so reencode whole document first
|
74
|
+
data = data.join
|
75
|
+
return (((data.force_encoding ::Encoding::UTF_16LE).slice 1, data.length).encode utf8).each_line.map {|line| line.rstrip }
|
75
76
|
elsif leading_2_bytes == BOM_BYTES_UTF_16BE
|
76
|
-
data[0] = (first_line.force_encoding ::Encoding::UTF_16BE)
|
77
|
-
return data.map {|line|
|
77
|
+
data[0] = (first_line.force_encoding ::Encoding::UTF_16BE).slice 1, first_line.length
|
78
|
+
return data.map {|line| ((line.force_encoding ::Encoding::UTF_16BE).encode utf8).rstrip }
|
78
79
|
elsif leading_bytes == BOM_BYTES_UTF_8
|
79
|
-
data[0] = (first_line.force_encoding utf8)
|
80
|
+
data[0] = (first_line.force_encoding utf8).slice 1, first_line.length
|
80
81
|
end
|
81
82
|
|
82
83
|
data.map {|line| line.encoding == utf8 ? line.rstrip : (line.force_encoding utf8).rstrip }
|
83
84
|
else
|
84
85
|
# Ruby 1.8 has no built-in re-encoding, so no point in removing the UTF-16 BOMs
|
85
|
-
if leading_bytes == BOM_BYTES_UTF_8
|
86
|
-
data[0] = first_line[3..-1]
|
87
|
-
end
|
86
|
+
data[0] = first_line.slice 3, first_line.length if leading_bytes == BOM_BYTES_UTF_8
|
88
87
|
data.map {|line| line.rstrip }
|
89
88
|
end
|
90
89
|
end
|
@@ -107,19 +106,17 @@ module Helpers
|
|
107
106
|
if COERCE_ENCODING
|
108
107
|
utf8 = ::Encoding::UTF_8
|
109
108
|
if (leading_2_bytes = leading_bytes.slice 0, 2) == BOM_BYTES_UTF_16LE
|
110
|
-
data = (data.force_encoding ::Encoding::UTF_16LE)
|
109
|
+
data = ((data.force_encoding ::Encoding::UTF_16LE).slice 1, data.length).encode utf8
|
111
110
|
elsif leading_2_bytes == BOM_BYTES_UTF_16BE
|
112
|
-
data = (data.force_encoding ::Encoding::UTF_16BE)
|
111
|
+
data = ((data.force_encoding ::Encoding::UTF_16BE).slice 1, data.length).encode utf8
|
113
112
|
elsif leading_bytes == BOM_BYTES_UTF_8
|
114
|
-
data = data.encoding == utf8 ? data
|
113
|
+
data = data.encoding == utf8 ? (data.slice 1, data.length) : ((data.force_encoding utf8).slice 1, data.length)
|
115
114
|
else
|
116
115
|
data = data.force_encoding utf8 unless data.encoding == utf8
|
117
116
|
end
|
118
117
|
else
|
119
118
|
# Ruby 1.8 has no built-in re-encoding, so no point in removing the UTF-16 BOMs
|
120
|
-
if leading_bytes == BOM_BYTES_UTF_8
|
121
|
-
data = data[3..-1]
|
122
|
-
end
|
119
|
+
data = data.slice 3, data.length if leading_bytes == BOM_BYTES_UTF_8
|
123
120
|
end
|
124
121
|
data.each_line.map {|line| line.rstrip }
|
125
122
|
end
|
data/lib/asciidoctor/list.rb
CHANGED
@@ -10,7 +10,7 @@ class List < AbstractBlock
|
|
10
10
|
# Public: Create alias to check if this list has blocks
|
11
11
|
alias items? blocks?
|
12
12
|
|
13
|
-
def initialize parent, context
|
13
|
+
def initialize parent, context, opts = {}
|
14
14
|
super
|
15
15
|
end
|
16
16
|
|
@@ -57,7 +57,7 @@ class ListItem < AbstractBlock
|
|
57
57
|
super parent, :list_item
|
58
58
|
@text = text
|
59
59
|
@level = parent.level
|
60
|
-
@subs = NORMAL_SUBS.
|
60
|
+
@subs = NORMAL_SUBS.drop 0
|
61
61
|
end
|
62
62
|
|
63
63
|
# Public: A convenience method that checks whether the text of this list item
|
data/lib/asciidoctor/parser.rb
CHANGED
@@ -92,13 +92,16 @@ class Parser
|
|
92
92
|
def self.parse(reader, document, options = {})
|
93
93
|
block_attributes = parse_document_header(reader, document)
|
94
94
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
document
|
99
|
-
|
95
|
+
# NOTE don't use a postfix conditional here as it's known to confuse JRuby in certain circumstances
|
96
|
+
unless options[:header_only]
|
97
|
+
while reader.has_more_lines?
|
98
|
+
new_section, block_attributes = next_section(reader, document, block_attributes)
|
99
|
+
if new_section
|
100
|
+
document.assign_numeral new_section
|
101
|
+
document.blocks << new_section
|
102
|
+
end
|
100
103
|
end
|
101
|
-
end
|
104
|
+
end
|
102
105
|
|
103
106
|
document
|
104
107
|
end
|
@@ -169,7 +172,7 @@ class Parser
|
|
169
172
|
doc_attrs['doctitle'] = assigned_doctitle if assigned_doctitle
|
170
173
|
|
171
174
|
# parse title and consume name section of manpage document
|
172
|
-
parse_manpage_header(reader, document) if document.doctype == 'manpage'
|
175
|
+
parse_manpage_header(reader, document, block_attrs) if document.doctype == 'manpage'
|
173
176
|
|
174
177
|
# NOTE block_attrs are the block-level attributes (not document attributes) that
|
175
178
|
# precede the first line of content (document title, first section or first block)
|
@@ -179,47 +182,70 @@ class Parser
|
|
179
182
|
# Public: Parses the manpage header of the AsciiDoc source read from the Reader
|
180
183
|
#
|
181
184
|
# returns Nothing
|
182
|
-
def self.parse_manpage_header(reader, document)
|
183
|
-
if ManpageTitleVolnumRx =~ document.attributes['doctitle']
|
184
|
-
|
185
|
-
|
185
|
+
def self.parse_manpage_header(reader, document, block_attributes)
|
186
|
+
if ManpageTitleVolnumRx =~ (doc_attrs = document.attributes)['doctitle']
|
187
|
+
doc_attrs['manvolnum'] = manvolnum = $2
|
188
|
+
doc_attrs['mantitle'] = (((mantitle = $1).include? ATTR_REF_HEAD) ? (document.sub_attributes mantitle) : mantitle).downcase
|
186
189
|
else
|
187
|
-
logger.error message_with_context '
|
190
|
+
logger.error message_with_context 'non-conforming manpage title', :source_location => (reader.cursor_at_line 1)
|
188
191
|
# provide sensible fallbacks
|
189
|
-
|
190
|
-
|
192
|
+
doc_attrs['mantitle'] = doc_attrs['doctitle'] || doc_attrs['docname'] || 'command'
|
193
|
+
doc_attrs['manvolnum'] = manvolnum = '1'
|
191
194
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
195
|
+
if (manname = doc_attrs['manname']) && doc_attrs['manpurpose']
|
196
|
+
doc_attrs['manname-title'] ||= 'Name'
|
197
|
+
doc_attrs['mannames'] = [manname]
|
198
|
+
if document.backend == 'manpage'
|
199
|
+
doc_attrs['docname'] = manname
|
200
|
+
doc_attrs['outfilesuffix'] = %(.#{manvolnum})
|
201
|
+
end
|
202
|
+
else
|
203
|
+
reader.skip_blank_lines
|
204
|
+
reader.save
|
205
|
+
block_attributes.update parse_block_metadata_lines reader, document
|
206
|
+
if (name_section_level = is_next_line_section? reader, {})
|
207
|
+
if name_section_level == 1
|
208
|
+
name_section = initialize_section reader, document, {}
|
209
|
+
name_section_buffer = (reader.read_lines_until :break_on_blank_lines => true, :skip_line_comments => true).map(&:lstrip).join ' '
|
210
|
+
if ManpageNamePurposeRx =~ name_section_buffer
|
211
|
+
doc_attrs['manname-title'] ||= name_section.title
|
212
|
+
doc_attrs['manname-id'] = name_section.id if name_section.id
|
213
|
+
doc_attrs['manpurpose'] = $2
|
214
|
+
if (manname = $1).include? ATTR_REF_HEAD
|
215
|
+
manname = document.sub_attributes manname
|
216
|
+
end
|
217
|
+
if manname.include? ','
|
218
|
+
manname = (mannames = (manname.split ',').map {|n| n.lstrip })[0]
|
219
|
+
else
|
220
|
+
mannames = [manname]
|
221
|
+
end
|
222
|
+
doc_attrs['manname'] = manname
|
223
|
+
doc_attrs['mannames'] = mannames
|
224
|
+
if document.backend == 'manpage'
|
225
|
+
doc_attrs['docname'] = manname
|
226
|
+
doc_attrs['outfilesuffix'] = %(.#{manvolnum})
|
227
|
+
end
|
205
228
|
else
|
206
|
-
|
207
|
-
end
|
208
|
-
document.attributes['manname'] = manname
|
209
|
-
document.attributes['mannames'] = mannames
|
210
|
-
|
211
|
-
if document.backend == 'manpage'
|
212
|
-
document.attributes['docname'] = manname
|
213
|
-
document.attributes['outfilesuffix'] = %(.#{document.attributes['manvolnum']})
|
229
|
+
error_msg = 'non-conforming name section body'
|
214
230
|
end
|
215
231
|
else
|
216
|
-
|
232
|
+
error_msg = 'name section must be at level 1'
|
217
233
|
end
|
218
234
|
else
|
219
|
-
|
235
|
+
error_msg = 'name section expected'
|
236
|
+
end
|
237
|
+
if error_msg
|
238
|
+
reader.restore_save
|
239
|
+
logger.error message_with_context error_msg, :source_location => reader.cursor
|
240
|
+
doc_attrs['manname'] = (manname = doc_attrs['docname'] || 'command')
|
241
|
+
doc_attrs['mannames'] = [manname]
|
242
|
+
if document.backend == 'manpage'
|
243
|
+
doc_attrs['docname'] = manname
|
244
|
+
doc_attrs['outfilesuffix'] = %(.#{manvolnum})
|
245
|
+
end
|
246
|
+
else
|
247
|
+
reader.discard_save
|
220
248
|
end
|
221
|
-
else
|
222
|
-
logger.error message_with_context 'name section expected', :source_location => reader.cursor_at_prev_line
|
223
249
|
end
|
224
250
|
nil
|
225
251
|
end
|
@@ -489,7 +515,7 @@ class Parser
|
|
489
515
|
end
|
490
516
|
end
|
491
517
|
|
492
|
-
# this loop is used for flow control; it only executes once, and only when delimited_block is set
|
518
|
+
# this loop is used for flow control; it only executes once, and only when delimited_block is not set
|
493
519
|
# break once a block is found or at end of loop
|
494
520
|
# returns nil if the line should be dropped
|
495
521
|
while true
|
@@ -542,7 +568,7 @@ class Parser
|
|
542
568
|
else # :image
|
543
569
|
posattrs = ['alt', 'width', 'height']
|
544
570
|
end
|
545
|
-
block.parse_attributes blk_attrs, posattrs, :sub_input => true, :
|
571
|
+
block.parse_attributes blk_attrs, posattrs, :sub_input => true, :into => attributes
|
546
572
|
end
|
547
573
|
# style doesn't have special meaning for media macros
|
548
574
|
attributes.delete 'style' if attributes.key? 'style'
|
@@ -557,31 +583,36 @@ class Parser
|
|
557
583
|
end
|
558
584
|
end
|
559
585
|
if blk_ctx == :image
|
560
|
-
document.register :images, target
|
586
|
+
document.register :images, [target, (attributes['imagesdir'] = doc_attrs['imagesdir'])]
|
561
587
|
# NOTE style is the value of the first positional attribute in the block attribute line
|
562
588
|
attributes['alt'] ||= style || (attributes['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
|
563
589
|
unless (scaledwidth = attributes.delete 'scaledwidth').nil_or_empty?
|
564
590
|
# NOTE assume % units if not specified
|
565
591
|
attributes['scaledwidth'] = (TrailingDigitsRx.match? scaledwidth) ? %(#{scaledwidth}%) : scaledwidth
|
566
592
|
end
|
567
|
-
|
568
|
-
|
593
|
+
if attributes.key? 'title'
|
594
|
+
block.title = attributes.delete 'title'
|
595
|
+
block.assign_caption((attributes.delete 'caption'), 'figure')
|
596
|
+
end
|
569
597
|
end
|
570
598
|
attributes['target'] = target
|
571
599
|
break
|
572
600
|
|
573
601
|
elsif ch0 == 't' && (this_line.start_with? 'toc:') && BlockTocMacroRx =~ this_line
|
574
602
|
block = Block.new parent, :toc, :content_model => :empty
|
575
|
-
block.parse_attributes
|
603
|
+
block.parse_attributes $1, [], :into => attributes if $1
|
576
604
|
break
|
577
605
|
|
578
606
|
elsif block_macro_extensions && CustomBlockMacroRx =~ this_line &&
|
579
607
|
(extension = extensions.registered_for_block_macro? $1)
|
580
608
|
target, content = $2, $3
|
609
|
+
if (target.include? ATTR_REF_HEAD) && (target = parent.sub_attributes target).empty? &&
|
610
|
+
(doc_attrs['attribute-missing'] || Compliance.attribute_missing) == 'drop-line'
|
611
|
+
attributes.clear
|
612
|
+
return
|
613
|
+
end
|
581
614
|
if extension.config[:content_model] == :attributes
|
582
|
-
if content
|
583
|
-
document.parse_attributes content, extension.config[:pos_attrs] || [], :sub_input => true, :sub_result => false, :into => attributes
|
584
|
-
end
|
615
|
+
document.parse_attributes content, extension.config[:pos_attrs] || [], :sub_input => true, :into => attributes if content
|
585
616
|
else
|
586
617
|
attributes['text'] = content || ''
|
587
618
|
end
|
@@ -601,61 +632,27 @@ class Parser
|
|
601
632
|
end
|
602
633
|
|
603
634
|
# haven't found anything yet, continue
|
604
|
-
if !indented &&
|
605
|
-
(CalloutListSniffRx.match? this_line) && (match = CalloutListRx.match this_line)
|
606
|
-
block = List.new(parent, :colist)
|
607
|
-
attributes['style'] = 'arabic'
|
635
|
+
if !indented && (ch0 ||= this_line.chr) == '<' && CalloutListRx =~ this_line
|
608
636
|
reader.unshift_line this_line
|
609
|
-
|
610
|
-
|
611
|
-
while match || ((match = CalloutListRx.match reader.peek_line) && reader.mark)
|
612
|
-
# might want to move this check to a validate method
|
613
|
-
unless match[1] == next_index.to_s
|
614
|
-
logger.warn message_with_context %(callout list item index: expected #{next_index}, got #{match[1]}), :source_location => reader.cursor_at_mark
|
615
|
-
end
|
616
|
-
if (list_item = next_list_item reader, block, match)
|
617
|
-
block.items << list_item
|
618
|
-
if (coids = document.callouts.callout_ids block.items.size).empty?
|
619
|
-
logger.warn message_with_context %(no callout found for <#{block.items.size}>), :source_location => reader.cursor_at_mark
|
620
|
-
else
|
621
|
-
list_item.attributes['coids'] = coids
|
622
|
-
end
|
623
|
-
end
|
624
|
-
next_index += 1
|
625
|
-
match = nil
|
626
|
-
end
|
627
|
-
|
628
|
-
document.callouts.next_list
|
637
|
+
block = parse_callout_list(reader, $~, parent, document.callouts)
|
638
|
+
attributes['style'] = 'arabic'
|
629
639
|
break
|
630
640
|
|
631
641
|
elsif UnorderedListRx.match? this_line
|
632
642
|
reader.unshift_line this_line
|
633
|
-
if Section === parent && parent.sectname == 'bibliography'
|
634
|
-
|
635
|
-
end unless style
|
636
|
-
block = next_list(reader, :ulist, parent, style)
|
643
|
+
attributes['style'] = (style = 'bibliography') if !style && Section === parent && parent.sectname == 'bibliography'
|
644
|
+
block = parse_list(reader, :ulist, parent, style)
|
637
645
|
break
|
638
646
|
|
639
647
|
elsif (match = OrderedListRx.match(this_line))
|
640
648
|
reader.unshift_line this_line
|
641
|
-
block =
|
642
|
-
|
643
|
-
unless style
|
644
|
-
marker = block.items[0].marker
|
645
|
-
if marker.start_with? '.'
|
646
|
-
# first one makes more sense, but second one is AsciiDoc-compliant
|
647
|
-
# TODO control behavior using a compliance setting
|
648
|
-
#attributes['style'] = (ORDERED_LIST_STYLES[block.level - 1] || 'arabic').to_s
|
649
|
-
attributes['style'] = (ORDERED_LIST_STYLES[marker.length - 1] || 'arabic').to_s
|
650
|
-
else
|
651
|
-
attributes['style'] = (ORDERED_LIST_STYLES.find {|s| OrderedListMarkerRxMap[s].match? marker } || 'arabic').to_s
|
652
|
-
end
|
653
|
-
end
|
649
|
+
block = parse_list(reader, :olist, parent, style)
|
650
|
+
attributes['style'] = block.style if block.style
|
654
651
|
break
|
655
652
|
|
656
653
|
elsif (match = DescriptionListRx.match(this_line))
|
657
654
|
reader.unshift_line this_line
|
658
|
-
block =
|
655
|
+
block = parse_description_list(reader, match, parent)
|
659
656
|
break
|
660
657
|
|
661
658
|
elsif (style == 'float' || style == 'discrete') && (Compliance.underline_style_section_titles ?
|
@@ -725,9 +722,9 @@ class Parser
|
|
725
722
|
attributes['textlabel'] = (attributes.delete 'caption') || doc_attrs[%(#{admonition_name}-caption)]
|
726
723
|
block = Block.new(parent, :admonition, :content_model => :simple, :source => lines, :attributes => attributes)
|
727
724
|
elsif md_syntax && ch0 == '>' && this_line.start_with?('> ')
|
728
|
-
lines.map! {|line| line == '>' ? line
|
725
|
+
lines.map! {|line| line == '>' ? (line.slice 1, line.length) : ((line.start_with? '> ') ? (line.slice 2, line.length) : line) }
|
729
726
|
if lines[-1].start_with? '-- '
|
730
|
-
credit_line = (credit_line = lines.pop
|
727
|
+
credit_line = (credit_line = lines.pop).slice 3, credit_line.length
|
731
728
|
lines.pop while lines[-1].empty?
|
732
729
|
end
|
733
730
|
attributes['style'] = 'quote'
|
@@ -741,10 +738,10 @@ class Parser
|
|
741
738
|
attributes['citetitle'] = citetitle if citetitle
|
742
739
|
end
|
743
740
|
elsif ch0 == '"' && lines.size > 1 && (lines[-1].start_with? '-- ') && (lines[-2].end_with? '"')
|
744
|
-
lines[0] = this_line
|
745
|
-
credit_line = (credit_line = lines.pop).slice
|
741
|
+
lines[0] = this_line.slice 1, this_line.length # strip leading quote
|
742
|
+
credit_line = (credit_line = lines.pop).slice 3, credit_line.length
|
746
743
|
lines.pop while lines[-1].empty?
|
747
|
-
lines
|
744
|
+
lines << lines.pop.chop # strip trailing quote
|
748
745
|
attributes['style'] = 'quote'
|
749
746
|
block = Block.new(parent, :quote, :content_model => :simple, :source => lines, :attributes => attributes)
|
750
747
|
attribution, citetitle = (block.apply_subs credit_line).split ', ', 2
|
@@ -757,7 +754,7 @@ class Parser
|
|
757
754
|
block = Block.new(parent, :paragraph, :content_model => :simple, :source => lines, :attributes => attributes)
|
758
755
|
end
|
759
756
|
|
760
|
-
catalog_inline_anchors
|
757
|
+
catalog_inline_anchors((lines.join LF), block, document, reader)
|
761
758
|
end
|
762
759
|
|
763
760
|
break # forbid loop from executing more than once
|
@@ -848,7 +845,7 @@ class Parser
|
|
848
845
|
# NOTE infer dsv once all other format hint chars are ruled out
|
849
846
|
attributes['format'] ||= (terminator.start_with? ',') ? 'csv' : 'dsv'
|
850
847
|
end
|
851
|
-
block =
|
848
|
+
block = parse_table(block_reader, parent, attributes)
|
852
849
|
|
853
850
|
when :quote, :verse
|
854
851
|
AttributeList.rekey(attributes, [nil, 'attribution', 'citetitle'])
|
@@ -966,7 +963,7 @@ class Parser
|
|
966
963
|
else
|
967
964
|
true
|
968
965
|
end
|
969
|
-
elsif %(#{tip}#{tip
|
966
|
+
elsif %(#{tip}#{tip.slice(-1, 1) * (line_len - tl)}) == line
|
970
967
|
if return_match_data
|
971
968
|
context, masq = DELIMITED_BLOCKS[tip]
|
972
969
|
BlockMatchData.new(context, masq, tip, line)
|
@@ -1091,45 +1088,15 @@ class Parser
|
|
1091
1088
|
# style - The block style assigned to this list (optional, default: nil)
|
1092
1089
|
#
|
1093
1090
|
# Returns the Block encapsulating the parsed unordered or ordered list
|
1094
|
-
def self.
|
1091
|
+
def self.parse_list(reader, list_type, parent, style)
|
1095
1092
|
list_block = List.new(parent, list_type)
|
1096
|
-
list_block.level = parent.context == list_type ? (parent.level + 1) : 1
|
1097
|
-
|
1098
|
-
while reader.has_more_lines? && ListRxMap[list_type] =~ reader.peek_line
|
1099
|
-
match, marker = $~, resolve_list_marker(list_type, $1)
|
1100
|
-
|
1101
|
-
# if we are moving to the next item, and the marker is different
|
1102
|
-
# determine if we are moving up or down in nesting
|
1103
|
-
if list_block.items? && marker != list_block.items[0].marker
|
1104
|
-
# assume list is nested by default, but then check to see if we are
|
1105
|
-
# popping out of a nested list by matching an ancestor's list marker
|
1106
|
-
this_item_level = list_block.level + 1
|
1107
|
-
ancestor = parent
|
1108
|
-
while ancestor.context == list_type
|
1109
|
-
if marker == ancestor.items[0].marker
|
1110
|
-
this_item_level = ancestor.level
|
1111
|
-
break
|
1112
|
-
end
|
1113
|
-
ancestor = ancestor.parent
|
1114
|
-
end
|
1115
|
-
else
|
1116
|
-
this_item_level = list_block.level
|
1117
|
-
end
|
1118
1093
|
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
break
|
1124
|
-
elsif this_item_level > list_block.level
|
1125
|
-
# If this next list level is down one from the
|
1126
|
-
# current Block's, append it to content of the current list item
|
1127
|
-
list_block.items[-1] << next_block(reader, list_block)
|
1094
|
+
while reader.has_more_lines? && (list_rx ||= ListRxMap[list_type]) =~ reader.peek_line
|
1095
|
+
# NOTE parse_list_item will stop at sibling item or end of list; never sees ancestor items
|
1096
|
+
if (list_item = parse_list_item reader, list_block, $~, $1, style)
|
1097
|
+
list_block.items << list_item
|
1128
1098
|
end
|
1129
1099
|
|
1130
|
-
list_block.items << list_item if list_item
|
1131
|
-
list_item = nil
|
1132
|
-
|
1133
1100
|
reader.skip_blank_lines || break
|
1134
1101
|
end
|
1135
1102
|
|
@@ -1144,10 +1111,11 @@ class Parser
|
|
1144
1111
|
# Returns A Boolean indicating whether callouts were found
|
1145
1112
|
def self.catalog_callouts(text, document)
|
1146
1113
|
found = false
|
1114
|
+
autonum = 0
|
1147
1115
|
text.scan(CalloutScanRx) {
|
1148
1116
|
# lead with assignments for Ruby 1.8.7 compat
|
1149
1117
|
captured, num = $&, $2
|
1150
|
-
document.callouts.register num unless captured.start_with? '\\'
|
1118
|
+
document.callouts.register num == '.' ? (autonum += 1).to_s : num unless captured.start_with? '\\'
|
1151
1119
|
# we have to mark as found even if it's escaped so it can be unescaped
|
1152
1120
|
found = true
|
1153
1121
|
} if text.include? '<'
|
@@ -1180,8 +1148,10 @@ class Parser
|
|
1180
1148
|
# document - The current Document on which the references are stored
|
1181
1149
|
#
|
1182
1150
|
# Returns nothing
|
1183
|
-
def self.catalog_inline_anchors text, block, document
|
1151
|
+
def self.catalog_inline_anchors text, block, document, reader
|
1184
1152
|
text.scan(InlineAnchorScanRx) do
|
1153
|
+
# alias match for Ruby 1.8.7 compat
|
1154
|
+
m = $~
|
1185
1155
|
if (id = $1)
|
1186
1156
|
if (reftext = $2)
|
1187
1157
|
next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
@@ -1194,7 +1164,11 @@ class Parser
|
|
1194
1164
|
end
|
1195
1165
|
end
|
1196
1166
|
unless document.register :refs, [id, (Inline.new block, :anchor, reftext, :type => :ref, :id => id), reftext]
|
1197
|
-
|
1167
|
+
location = reader.cursor_at_mark
|
1168
|
+
if (offset = (m.pre_match.count LF) + ((m[0].start_with? LF) ? 1 : 0)) > 0
|
1169
|
+
(location = location.dup).advance offset
|
1170
|
+
end
|
1171
|
+
logger.warn message_with_context %(id assigned to anchor already in use: #{id}), :source_location => location
|
1198
1172
|
end
|
1199
1173
|
end if (text.include? '[[') || (text.include? 'or:')
|
1200
1174
|
nil
|
@@ -1223,7 +1197,7 @@ class Parser
|
|
1223
1197
|
# parent - The parent Block to which this description list belongs
|
1224
1198
|
#
|
1225
1199
|
# Returns the Block encapsulating the parsed description list
|
1226
|
-
def self.
|
1200
|
+
def self.parse_description_list(reader, match, parent)
|
1227
1201
|
list_block = List.new(parent, :dlist)
|
1228
1202
|
previous_pair = nil
|
1229
1203
|
# allows us to capture until we find a description item
|
@@ -1232,7 +1206,7 @@ class Parser
|
|
1232
1206
|
|
1233
1207
|
# NOTE skip the match on the first time through as we've already done it (emulates begin...while)
|
1234
1208
|
while match || (reader.has_more_lines? && (match = sibling_pattern.match(reader.peek_line)))
|
1235
|
-
term, item =
|
1209
|
+
term, item = parse_list_item(reader, list_block, match, sibling_pattern)
|
1236
1210
|
if previous_pair && !previous_pair[1]
|
1237
1211
|
previous_pair[0] << term
|
1238
1212
|
previous_pair[1] = item
|
@@ -1245,25 +1219,63 @@ class Parser
|
|
1245
1219
|
list_block
|
1246
1220
|
end
|
1247
1221
|
|
1248
|
-
# Internal: Parse and construct
|
1249
|
-
#
|
1250
|
-
# ListItem pair for the description list Block.
|
1222
|
+
# Internal: Parse and construct a callout list Block from the current position of the Reader and
|
1223
|
+
# advance the document callouts catalog to the next list.
|
1251
1224
|
#
|
1252
|
-
#
|
1253
|
-
#
|
1254
|
-
#
|
1255
|
-
#
|
1256
|
-
#
|
1225
|
+
# reader - The Reader from which to retrieve the callout list.
|
1226
|
+
# match - The Regexp match containing the head of the list.
|
1227
|
+
# parent - The parent Block to which this callout list belongs.
|
1228
|
+
# callouts - The document callouts catalog.
|
1229
|
+
#
|
1230
|
+
# Returns the Block that represents the parsed callout list.
|
1231
|
+
def self.parse_callout_list reader, match, parent, callouts
|
1232
|
+
list_block = List.new(parent, :colist)
|
1233
|
+
next_index = 1
|
1234
|
+
autonum = 0
|
1235
|
+
# NOTE skip the match on the first time through as we've already done it (emulates begin...while)
|
1236
|
+
while match || ((match = CalloutListRx.match reader.peek_line) && reader.mark)
|
1237
|
+
if (num = match[1]) == '.'
|
1238
|
+
num = (autonum += 1).to_s
|
1239
|
+
end
|
1240
|
+
# might want to move this check to a validate method
|
1241
|
+
unless num == next_index.to_s
|
1242
|
+
logger.warn message_with_context %(callout list item index: expected #{next_index}, got #{num}), :source_location => reader.cursor_at_mark
|
1243
|
+
end
|
1244
|
+
if (list_item = parse_list_item reader, list_block, match, '<1>')
|
1245
|
+
list_block.items << list_item
|
1246
|
+
if (coids = callouts.callout_ids list_block.items.size).empty?
|
1247
|
+
logger.warn message_with_context %(no callout found for <#{list_block.items.size}>), :source_location => reader.cursor_at_mark
|
1248
|
+
else
|
1249
|
+
list_item.attributes['coids'] = coids
|
1250
|
+
end
|
1251
|
+
end
|
1252
|
+
next_index += 1
|
1253
|
+
match = nil
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
callouts.next_list
|
1257
|
+
list_block
|
1258
|
+
end
|
1259
|
+
|
1260
|
+
# Internal: Parse and construct the next ListItem (unordered, ordered, or callout list) or next
|
1261
|
+
# term ListItem and description ListItem pair (description list) for the specified list Block.
|
1262
|
+
#
|
1263
|
+
# First, collect and process all the lines that constitute the next list item for the specified
|
1264
|
+
# list (according to its type). Next, create a ListItem (in the case of a description list, a
|
1265
|
+
# description ListItem), parse the lines into blocks, and associate those blocks with that
|
1266
|
+
# ListItem. Finally, fold the first block into the item's text attribute according to rules
|
1267
|
+
# described in ListItem.
|
1257
1268
|
#
|
1258
1269
|
# reader - The Reader from which to retrieve the next list item
|
1259
|
-
# list_block - The parent list Block
|
1260
|
-
# match - The
|
1261
|
-
# sibling_trait - The
|
1270
|
+
# list_block - The parent list Block for this ListItem. Also provides access to the list type.
|
1271
|
+
# match - The MatchData that contains the list item marker and first line text of the ListItem
|
1272
|
+
# sibling_trait - The trait to match a sibling list item. For ordered and unordered lists, this is
|
1273
|
+
# a String marker (e.g., '**' or 'ii)'). For description lists, this is a Regexp
|
1274
|
+
# marker pattern.
|
1262
1275
|
# style - The block style assigned to this list (optional, default: nil)
|
1263
1276
|
#
|
1264
|
-
# Returns the next ListItem or ListItem pair (
|
1265
|
-
|
1266
|
-
def self.next_list_item(reader, list_block, match, sibling_trait = nil, style = nil)
|
1277
|
+
# Returns the next ListItem or ListItem pair (description list) for the parent list Block.
|
1278
|
+
def self.parse_list_item(reader, list_block, match, sibling_trait, style = nil)
|
1267
1279
|
if (list_type = list_block.context) == :dlist
|
1268
1280
|
dlist = true
|
1269
1281
|
list_term = ListItem.new(list_block, (term_text = match[1]))
|
@@ -1285,7 +1297,7 @@ class Parser
|
|
1285
1297
|
list_item = ListItem.new(list_block, (item_text = match[2]))
|
1286
1298
|
list_item.source_location = reader.cursor if list_block.document.sourcemap
|
1287
1299
|
if list_type == :ulist
|
1288
|
-
list_item.marker =
|
1300
|
+
list_item.marker = sibling_trait
|
1289
1301
|
if item_text.start_with?('[')
|
1290
1302
|
if style && style == 'bibliography'
|
1291
1303
|
if InlineBiblioAnchorRx =~ item_text
|
@@ -1305,9 +1317,18 @@ class Parser
|
|
1305
1317
|
end
|
1306
1318
|
end
|
1307
1319
|
elsif list_type == :olist
|
1308
|
-
|
1320
|
+
sibling_trait, implicit_style = resolve_ordered_list_marker(sibling_trait, (ordinal = list_block.items.size), true, reader)
|
1321
|
+
list_item.marker = sibling_trait
|
1322
|
+
if ordinal == 0 && !style
|
1323
|
+
# using list level makes more sense, but we don't track it
|
1324
|
+
# basing style on marker level is compliant with AsciiDoc Python
|
1325
|
+
list_block.style = implicit_style || ((ORDERED_LIST_STYLES[sibling_trait.length - 1] || 'arabic').to_s)
|
1326
|
+
end
|
1327
|
+
if item_text.start_with?('[[') && LeadingInlineAnchorRx =~ item_text
|
1328
|
+
catalog_inline_anchor $1, $2, list_item, reader
|
1329
|
+
end
|
1309
1330
|
else # :colist
|
1310
|
-
list_item.marker =
|
1331
|
+
list_item.marker = sibling_trait
|
1311
1332
|
end
|
1312
1333
|
end
|
1313
1334
|
|
@@ -1540,7 +1561,7 @@ class Parser
|
|
1540
1561
|
# a blank line would have served the same purpose in the document
|
1541
1562
|
buffer.pop if !buffer.empty? && buffer[-1] == LIST_CONTINUATION
|
1542
1563
|
|
1543
|
-
#warn "BUFFER[#{list_type},#{sibling_trait}]>#{buffer
|
1564
|
+
#warn "BUFFER[#{list_type},#{sibling_trait}]>#{buffer.join LF}<BUFFER"
|
1544
1565
|
#warn "BUFFER[#{list_type},#{sibling_trait}]>#{buffer.inspect}<BUFFER"
|
1545
1566
|
|
1546
1567
|
buffer
|
@@ -1784,23 +1805,25 @@ class Parser
|
|
1784
1805
|
# # => {'author' => 'Author Name', 'firstname' => 'Author', 'lastname' => 'Name', 'email' => 'author@example.org',
|
1785
1806
|
# # 'revnumber' => '1.0', 'revdate' => '2012-12-21', 'revremark' => 'Coincide w/ end of world.'}
|
1786
1807
|
def self.parse_header_metadata(reader, document = nil)
|
1808
|
+
doc_attrs = document && document.attributes
|
1787
1809
|
# NOTE this will discard any comment lines, but not skip blank lines
|
1788
1810
|
process_attribute_entries reader, document
|
1789
1811
|
|
1790
|
-
metadata, implicit_author, implicit_authors = {}, nil, nil
|
1812
|
+
metadata, implicit_author, implicit_authorinitials = implicit_authors = {}, nil, nil
|
1791
1813
|
|
1792
1814
|
if reader.has_more_lines? && !reader.next_line_empty?
|
1793
1815
|
unless (author_metadata = process_authors reader.read_line).empty?
|
1794
1816
|
if document
|
1795
1817
|
# apply header subs and assign to document
|
1796
1818
|
author_metadata.each do |key, val|
|
1797
|
-
unless
|
1798
|
-
|
1819
|
+
unless doc_attrs.key? key
|
1820
|
+
doc_attrs[key] = ::String === val ? (document.apply_header_subs val) : val
|
1799
1821
|
end
|
1800
1822
|
end
|
1801
1823
|
|
1802
|
-
implicit_author =
|
1803
|
-
|
1824
|
+
implicit_author = doc_attrs['author']
|
1825
|
+
implicit_authorinitials = doc_attrs['authorinitials']
|
1826
|
+
implicit_authors = doc_attrs['authors']
|
1804
1827
|
end
|
1805
1828
|
|
1806
1829
|
metadata = author_metadata
|
@@ -1818,7 +1841,7 @@ class Parser
|
|
1818
1841
|
unless (component = match[2].strip).empty?
|
1819
1842
|
# version must begin with 'v' if date is absent
|
1820
1843
|
if !match[1] && (component.start_with? 'v')
|
1821
|
-
rev_metadata['revnumber'] = component
|
1844
|
+
rev_metadata['revnumber'] = component.slice 1, component.length
|
1822
1845
|
else
|
1823
1846
|
rev_metadata['revdate'] = component
|
1824
1847
|
end
|
@@ -1834,8 +1857,8 @@ class Parser
|
|
1834
1857
|
if document
|
1835
1858
|
# apply header subs and assign to document
|
1836
1859
|
rev_metadata.each do |key, val|
|
1837
|
-
unless
|
1838
|
-
|
1860
|
+
unless doc_attrs.key? key
|
1861
|
+
doc_attrs[key] = document.apply_header_subs val
|
1839
1862
|
end
|
1840
1863
|
end
|
1841
1864
|
end
|
@@ -1853,18 +1876,19 @@ class Parser
|
|
1853
1876
|
|
1854
1877
|
# process author attribute entries that override (or stand in for) the implicit author line
|
1855
1878
|
if document
|
1856
|
-
if
|
1879
|
+
if doc_attrs.key?('author') && (author_line = doc_attrs['author']) != implicit_author
|
1857
1880
|
# do not allow multiple, process as names only
|
1858
1881
|
author_metadata = process_authors author_line, true, false
|
1859
|
-
|
1882
|
+
author_metadata.delete 'authorinitials' if doc_attrs['authorinitials'] != implicit_authorinitials
|
1883
|
+
elsif doc_attrs.key?('authors') && (author_line = doc_attrs['authors']) != implicit_authors
|
1860
1884
|
# allow multiple, process as names only
|
1861
1885
|
author_metadata = process_authors author_line, true
|
1862
1886
|
else
|
1863
1887
|
authors, author_idx, author_key, explicit, sparse = [], 1, 'author_1', false, false
|
1864
|
-
while
|
1888
|
+
while doc_attrs.key? author_key
|
1865
1889
|
# only use indexed author attribute if value is different
|
1866
1890
|
# leaves corner case if line matches with underscores converted to spaces; use double space to force
|
1867
|
-
if (author_override =
|
1891
|
+
if (author_override = doc_attrs[author_key]) == author_metadata[author_key]
|
1868
1892
|
authors << nil
|
1869
1893
|
sparse = true
|
1870
1894
|
else
|
@@ -1881,7 +1905,7 @@ class Parser
|
|
1881
1905
|
author_metadata[%(firstname_#{name_idx = idx + 1})],
|
1882
1906
|
author_metadata[%(middlename_#{name_idx})],
|
1883
1907
|
author_metadata[%(lastname_#{name_idx})]
|
1884
|
-
].compact.map {|it| it.tr ' ', '_' }
|
1908
|
+
].compact.map {|it| it.tr ' ', '_' }.join ' '
|
1885
1909
|
end
|
1886
1910
|
end if sparse
|
1887
1911
|
# process as names only
|
@@ -1892,13 +1916,13 @@ class Parser
|
|
1892
1916
|
end
|
1893
1917
|
|
1894
1918
|
if author_metadata.empty?
|
1895
|
-
metadata['authorcount'] ||= (
|
1919
|
+
metadata['authorcount'] ||= (doc_attrs['authorcount'] = 0)
|
1896
1920
|
else
|
1897
|
-
|
1921
|
+
doc_attrs.update author_metadata
|
1898
1922
|
|
1899
1923
|
# special case
|
1900
|
-
if !
|
1901
|
-
|
1924
|
+
if !doc_attrs.key?('email') && doc_attrs.key?('email_1')
|
1925
|
+
doc_attrs['email'] = doc_attrs['email_1']
|
1902
1926
|
end
|
1903
1927
|
end
|
1904
1928
|
end
|
@@ -2045,7 +2069,7 @@ class Parser
|
|
2045
2069
|
elsif (next_line.end_with? ']') && BlockAttributeListRx =~ next_line
|
2046
2070
|
current_style = attributes[1]
|
2047
2071
|
# extract id, role, and options from first positional attribute and remove, if present
|
2048
|
-
if (document.parse_attributes $1, [], :sub_input => true, :into => attributes)[1]
|
2072
|
+
if (document.parse_attributes $1, [], :sub_input => true, :sub_result => true, :into => attributes)[1]
|
2049
2073
|
attributes[1] = (parse_style_attribute attributes, reader) || current_style
|
2050
2074
|
end
|
2051
2075
|
return true
|
@@ -2093,11 +2117,10 @@ class Parser
|
|
2093
2117
|
if (value = match[2]).nil_or_empty?
|
2094
2118
|
value = ''
|
2095
2119
|
elsif value.end_with? LINE_CONTINUATION, LINE_CONTINUATION_LEGACY
|
2096
|
-
con, value = value.slice(-2, 2), value.slice
|
2097
|
-
while reader.advance && !(next_line = reader.peek_line
|
2098
|
-
|
2099
|
-
|
2100
|
-
end
|
2120
|
+
con, value = value.slice(-2, 2), (value.slice 0, value.length - 2).rstrip
|
2121
|
+
while reader.advance && !(next_line = reader.peek_line || '').empty?
|
2122
|
+
next_line = next_line.lstrip
|
2123
|
+
next_line = (next_line.slice 0, next_line.length - 2).rstrip if (keep_open = next_line.end_with? con)
|
2101
2124
|
value = %(#{value}#{(value.end_with? HARD_LINE_BREAK) ? LF : ' '}#{next_line})
|
2102
2125
|
break unless keep_open
|
2103
2126
|
end
|
@@ -2136,9 +2159,9 @@ class Parser
|
|
2136
2159
|
if name == 'leveloffset'
|
2137
2160
|
# support relative leveloffset values
|
2138
2161
|
if value.start_with? '+'
|
2139
|
-
value = ((doc.attr 'leveloffset', 0).to_i + (value
|
2162
|
+
value = ((doc.attr 'leveloffset', 0).to_i + (value.slice 1, value.length).to_i).to_s
|
2140
2163
|
elsif value.start_with? '-'
|
2141
|
-
value = ((doc.attr 'leveloffset', 0).to_i - (value
|
2164
|
+
value = ((doc.attr 'leveloffset', 0).to_i - (value.slice 1, value.length).to_i).to_s
|
2142
2165
|
end
|
2143
2166
|
end
|
2144
2167
|
# QUESTION should we set value to locked value if set_attribute returns false?
|
@@ -2176,7 +2199,7 @@ class Parser
|
|
2176
2199
|
if list_type == :ulist
|
2177
2200
|
marker
|
2178
2201
|
elsif list_type == :olist
|
2179
|
-
resolve_ordered_list_marker(marker, ordinal, validate, reader)
|
2202
|
+
resolve_ordered_list_marker(marker, ordinal, validate, reader)[0]
|
2180
2203
|
else # :colist
|
2181
2204
|
'<1>'
|
2182
2205
|
end
|
@@ -2199,14 +2222,19 @@ class Parser
|
|
2199
2222
|
# Examples
|
2200
2223
|
#
|
2201
2224
|
# marker = 'B.'
|
2202
|
-
# Parser.resolve_ordered_list_marker(marker, 1, true)
|
2203
|
-
# # => 'A.'
|
2225
|
+
# Parser.resolve_ordered_list_marker(marker, 1, true, reader)
|
2226
|
+
# # => ['A.', :upperalpha]
|
2227
|
+
#
|
2228
|
+
# marker = '.'
|
2229
|
+
# Parser.resolve_ordered_list_marker(marker, 1, true, reader)
|
2230
|
+
# # => ['.']
|
2204
2231
|
#
|
2205
|
-
# Returns the String of the first marker in this number
|
2232
|
+
# Returns a tuple that contains the String of the first marker in this number
|
2233
|
+
# series and the implicit list style, if applicable
|
2206
2234
|
def self.resolve_ordered_list_marker(marker, ordinal = 0, validate = false, reader = nil)
|
2207
|
-
return marker if marker.start_with? '.'
|
2208
|
-
|
2209
|
-
case ORDERED_LIST_STYLES.find {|s| OrderedListMarkerRxMap[s].match? marker }
|
2235
|
+
return [marker] if marker.start_with? '.'
|
2236
|
+
# NOTE case statement is guaranteed to match one of the conditions
|
2237
|
+
case (style = ORDERED_LIST_STYLES.find {|s| OrderedListMarkerRxMap[s].match? marker })
|
2210
2238
|
when :arabic
|
2211
2239
|
if validate
|
2212
2240
|
expected = ordinal + 1
|
@@ -2243,7 +2271,7 @@ class Parser
|
|
2243
2271
|
logger.warn message_with_context %(list item index: expected #{expected}, got #{actual}), :source_location => reader.cursor
|
2244
2272
|
end
|
2245
2273
|
|
2246
|
-
marker
|
2274
|
+
[marker, style]
|
2247
2275
|
end
|
2248
2276
|
|
2249
2277
|
# Internal: Determine whether the this line is a sibling list item
|
@@ -2277,7 +2305,7 @@ class Parser
|
|
2277
2305
|
# attributes - attributes captured from above this Block
|
2278
2306
|
#
|
2279
2307
|
# returns an instance of Asciidoctor::Table parsed from the provided reader
|
2280
|
-
def self.
|
2308
|
+
def self.parse_table(table_reader, parent, attributes)
|
2281
2309
|
table = Table.new(parent, attributes)
|
2282
2310
|
if attributes.key? 'title'
|
2283
2311
|
table.title = attributes.delete 'title'
|
@@ -2432,7 +2460,7 @@ class Parser
|
|
2432
2460
|
|
2433
2461
|
specs = []
|
2434
2462
|
# NOTE -1 argument ensures we don't drop empty records
|
2435
|
-
records.split
|
2463
|
+
((records.include? ',') ? (records.split ',', -1) : (records.split ';', -1)).each do |record|
|
2436
2464
|
if record.empty?
|
2437
2465
|
specs << { 'width' => 1 }
|
2438
2466
|
# TODO might want to use scan rather than this mega-regexp
|
@@ -2469,7 +2497,7 @@ class Parser
|
|
2469
2497
|
specs << spec
|
2470
2498
|
end
|
2471
2499
|
end
|
2472
|
-
|
2500
|
+
end
|
2473
2501
|
specs
|
2474
2502
|
end
|
2475
2503
|
|
@@ -2658,7 +2686,7 @@ class Parser
|
|
2658
2686
|
# source.split "\n"
|
2659
2687
|
# # => [" def names", " @names.split", " end"]
|
2660
2688
|
#
|
2661
|
-
# puts Parser.adjust_indentation!(source.split "\n")
|
2689
|
+
# puts Parser.adjust_indentation!(source.split "\n").join "\n"
|
2662
2690
|
# # => def names
|
2663
2691
|
# # => @names.split
|
2664
2692
|
# # => end
|
@@ -2723,12 +2751,12 @@ class Parser
|
|
2723
2751
|
# NOTE gutter_width is > 0 if not nil
|
2724
2752
|
if indent == 0
|
2725
2753
|
if gutter_width
|
2726
|
-
lines.map! {|line| line.empty? ? line : line
|
2754
|
+
lines.map! {|line| line.empty? ? line : (line.slice gutter_width, line.length) }
|
2727
2755
|
end
|
2728
2756
|
else
|
2729
2757
|
padding = ' ' * indent
|
2730
2758
|
if gutter_width
|
2731
|
-
lines.map! {|line| line.empty? ? line : padding + line
|
2759
|
+
lines.map! {|line| line.empty? ? line : padding + (line.slice gutter_width, line.length) }
|
2732
2760
|
else
|
2733
2761
|
lines.map! {|line| line.empty? ? line : padding + line }
|
2734
2762
|
end
|