asciidoctor 1.5.6.2 → 1.5.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +330 -143
- data/README-fr.adoc +441 -0
- data/README-jp.adoc +418 -0
- data/README-zh_CN.adoc +430 -0
- data/README.adoc +454 -0
- data/Rakefile +57 -0
- data/asciidoctor.gemspec +7 -1
- data/data/locale/attributes-ar.adoc +22 -0
- data/data/locale/attributes-bg.adoc +22 -0
- data/data/locale/attributes-ca.adoc +22 -0
- data/data/locale/attributes-cs.adoc +22 -0
- data/data/locale/attributes-da.adoc +22 -0
- data/data/locale/attributes-de.adoc +22 -0
- data/data/locale/attributes-en.adoc +23 -0
- data/data/locale/attributes-es.adoc +22 -0
- data/data/locale/attributes-fa.adoc +22 -0
- data/data/locale/attributes-fi.adoc +22 -0
- data/data/locale/attributes-fr.adoc +22 -0
- data/data/locale/attributes-hu.adoc +22 -0
- data/data/locale/attributes-id.adoc +22 -0
- data/data/locale/attributes-it.adoc +22 -0
- data/data/locale/attributes-ja.adoc +22 -0
- data/data/locale/attributes-kr.adoc +22 -0
- data/data/locale/attributes-nb.adoc +22 -0
- data/data/locale/attributes-nl.adoc +22 -0
- data/data/locale/attributes-nn.adoc +22 -0
- data/data/locale/attributes-pl.adoc +22 -0
- data/data/locale/attributes-pt.adoc +22 -0
- data/data/locale/attributes-pt_BR.adoc +22 -0
- data/data/locale/attributes-ro.adoc +22 -0
- data/data/locale/attributes-ru.adoc +22 -0
- data/data/locale/attributes-sr.adoc +22 -0
- data/data/locale/attributes-sr_Latn.adoc +22 -0
- data/data/locale/attributes-tr.adoc +22 -0
- data/data/locale/attributes-uk.adoc +22 -0
- data/data/locale/attributes-zh_CN.adoc +22 -0
- data/data/locale/attributes-zh_TW.adoc +22 -0
- data/data/locale/attributes.adoc +8 -649
- data/data/stylesheets/asciidoctor-default.css +77 -72
- data/features/xref.feature +366 -7
- data/lib/asciidoctor.rb +107 -93
- data/lib/asciidoctor/abstract_block.rb +247 -239
- data/lib/asciidoctor/abstract_node.rb +56 -58
- data/lib/asciidoctor/block.rb +3 -3
- data/lib/asciidoctor/callouts.rb +1 -1
- data/lib/asciidoctor/cli/invoker.rb +36 -9
- data/lib/asciidoctor/cli/options.rb +63 -25
- data/lib/asciidoctor/converter.rb +23 -13
- data/lib/asciidoctor/converter/base.rb +4 -0
- data/lib/asciidoctor/converter/docbook45.rb +16 -9
- data/lib/asciidoctor/converter/docbook5.rb +115 -97
- data/lib/asciidoctor/converter/factory.rb +29 -31
- data/lib/asciidoctor/converter/html5.rb +229 -192
- data/lib/asciidoctor/converter/manpage.rb +72 -50
- data/lib/asciidoctor/converter/template.rb +12 -12
- data/lib/asciidoctor/core_ext.rb +5 -1
- data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +6 -0
- data/lib/asciidoctor/document.rb +168 -77
- data/lib/asciidoctor/extensions.rb +79 -47
- data/lib/asciidoctor/helpers.rb +33 -11
- data/lib/asciidoctor/inline.rb +3 -2
- data/lib/asciidoctor/list.rb +2 -1
- data/lib/asciidoctor/logging.rb +122 -0
- data/lib/asciidoctor/parser.rb +406 -382
- data/lib/asciidoctor/path_resolver.rb +169 -162
- data/lib/asciidoctor/reader.rb +166 -121
- data/lib/asciidoctor/section.rb +45 -28
- data/lib/asciidoctor/stylesheets.rb +13 -5
- data/lib/asciidoctor/substitutors.rb +328 -254
- data/lib/asciidoctor/table.rb +105 -48
- data/lib/asciidoctor/timings.rb +34 -6
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +41 -23
- data/man/asciidoctor.adoc +14 -8
- data/test/api_test.rb +1004 -0
- data/test/attributes_test.rb +241 -50
- data/test/blocks_test.rb +549 -124
- data/test/converter_test.rb +170 -78
- data/test/document_test.rb +208 -767
- data/test/extensions_test.rb +188 -53
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +1 -1
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +1 -1
- data/test/fixtures/file-with-missing-include.adoc +1 -0
- data/test/fixtures/include-file.jsx +8 -0
- data/test/fixtures/lists.adoc +96 -0
- data/test/fixtures/other-chapters.adoc +11 -0
- data/test/fixtures/outer-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +5 -1
- data/test/fixtures/subdir/index.adoc +3 -0
- data/test/fixtures/subdir/inner-include.adoc +3 -0
- data/test/fixtures/subdir/middle-include.adoc +5 -0
- data/test/fixtures/tagged-class-enclosed.rb +0 -1
- data/test/fixtures/unclosed-tag.adoc +3 -0
- data/test/fixtures/unexpected-end-tag.adoc +4 -0
- data/test/invoker_test.rb +101 -40
- data/test/links_test.rb +266 -72
- data/test/lists_test.rb +243 -45
- data/test/logger_test.rb +211 -0
- data/test/manpage_test.rb +124 -6
- data/test/options_test.rb +46 -1
- data/test/paragraphs_test.rb +23 -10
- data/test/parser_test.rb +30 -1
- data/test/paths_test.rb +115 -33
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +337 -81
- data/test/sections_test.rb +656 -72
- data/test/substitutions_test.rb +182 -57
- data/test/tables_test.rb +324 -57
- data/test/test_helper.rb +77 -32
- data/test/text_test.rb +7 -7
- metadata +67 -3
data/lib/asciidoctor/section.rb
CHANGED
@@ -38,14 +38,19 @@ class Section < AbstractBlock
|
|
38
38
|
|
39
39
|
# Public: Initialize an Asciidoctor::Section object.
|
40
40
|
#
|
41
|
-
# parent
|
42
|
-
|
41
|
+
# parent - The parent AbstractBlock. If set, must be a Document or Section object (default: nil)
|
42
|
+
# level - The Integer level of this section (default: 1 more than parent level or 1 if parent not defined)
|
43
|
+
# numbered - A Boolean indicating whether numbering is enabled for this Section (default: false)
|
44
|
+
# opts - An optional Hash of options (default: {})
|
45
|
+
def initialize parent = nil, level = nil, numbered = false, opts = {}
|
43
46
|
super parent, :section, opts
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
if Section === parent
|
48
|
+
@level, @special = level || (parent.level + 1), parent.special
|
49
|
+
else
|
50
|
+
@level, @special = level || 1, false
|
51
|
+
end
|
52
|
+
@numbered = numbered
|
47
53
|
@index = 0
|
48
|
-
@number = 1
|
49
54
|
end
|
50
55
|
|
51
56
|
# Public: The name of this section, an alias of the section title
|
@@ -60,10 +65,12 @@ class Section < AbstractBlock
|
|
60
65
|
|
61
66
|
# Public: Get the section number for the current Section
|
62
67
|
#
|
63
|
-
# The section number is a
|
64
|
-
#
|
65
|
-
# the
|
66
|
-
#
|
68
|
+
# The section number is a dot-separated String that uniquely describes the position of this
|
69
|
+
# Section in the document. Each entry represents a level of nesting. The value of each entry is
|
70
|
+
# the 1-based outline number of the Section amongst its numbered sibling Sections.
|
71
|
+
#
|
72
|
+
# This method assumes that both the @level and @parent instance variables have been assigned.
|
73
|
+
# The method also assumes that the value of @parent is either a Document or Section.
|
67
74
|
#
|
68
75
|
# delimiter - the delimiter to separate the number for each level
|
69
76
|
# append - the String to append at the end of the section number
|
@@ -103,10 +110,12 @@ class Section < AbstractBlock
|
|
103
110
|
# Returns the section number as a String
|
104
111
|
def sectnum(delimiter = '.', append = nil)
|
105
112
|
append ||= (append == false ? '' : delimiter)
|
106
|
-
if @level
|
107
|
-
%(#{@parent.sectnum(delimiter)}#{@number}#{append})
|
108
|
-
else
|
113
|
+
if @level == 1
|
109
114
|
%(#{@number}#{append})
|
115
|
+
elsif @level > 1
|
116
|
+
Section === @parent ? %(#{@parent.sectnum(delimiter)}#{@number}#{append}) : %(#{@number}#{append})
|
117
|
+
else # @level == 0
|
118
|
+
%(#{Helpers.int_to_roman @number}#{append})
|
110
119
|
end
|
111
120
|
end
|
112
121
|
|
@@ -153,7 +162,7 @@ class Section < AbstractBlock
|
|
153
162
|
#
|
154
163
|
# Returns The parent Block
|
155
164
|
def << block
|
156
|
-
|
165
|
+
assign_numeral block if block.context == :section
|
157
166
|
super
|
158
167
|
end
|
159
168
|
|
@@ -169,32 +178,40 @@ class Section < AbstractBlock
|
|
169
178
|
# Public: Generate a String ID from the given section title.
|
170
179
|
#
|
171
180
|
# The generated ID is prefixed with value of the 'idprefix' attribute, which
|
172
|
-
# is an underscore by default. Invalid characters are
|
173
|
-
# value of the 'idseparator' attribute, which is
|
181
|
+
# is an underscore (_) by default. Invalid characters are then removed and
|
182
|
+
# spaces are replaced with the value of the 'idseparator' attribute, which is
|
183
|
+
# an underscore (_) by default.
|
174
184
|
#
|
175
|
-
# If the generated ID is already in use in the document, a count is appended
|
176
|
-
# until a unique
|
185
|
+
# If the generated ID is already in use in the document, a count is appended,
|
186
|
+
# offset by the separator, until a unique ID is found.
|
177
187
|
#
|
178
|
-
# Section ID generation can be disabled by
|
188
|
+
# Section ID generation can be disabled by unsetting the 'sectids' document attribute.
|
179
189
|
#
|
180
190
|
# Examples
|
181
191
|
#
|
182
192
|
# Section.generate_id 'Foo', document
|
183
193
|
# => "_foo"
|
184
194
|
#
|
195
|
+
# Returns the generated [String] ID.
|
185
196
|
def self.generate_id title, document
|
186
197
|
attrs = document.attributes
|
187
|
-
sep = attrs['idseparator'] || '_'
|
188
198
|
pre = attrs['idprefix'] || '_'
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
gen_id = gen_id.tr_s sep, sep
|
193
|
-
gen_id = gen_id.chop if gen_id.end_with? sep
|
194
|
-
# ensure id doesn't begin with idseparator if idprefix is empty and idseparator is not empty
|
195
|
-
if pre.empty?
|
196
|
-
gen_id = gen_id.slice 1, gen_id.length while gen_id.start_with? sep
|
199
|
+
if (sep = attrs['idseparator'])
|
200
|
+
if sep.length == 1 || (!(no_sep = sep.empty?) && (sep = attrs['idseparator'] = sep.chr))
|
201
|
+
sep_sub = sep == '-' || sep == '.' ? ' .-' : %( #{sep}.-)
|
197
202
|
end
|
203
|
+
else
|
204
|
+
sep, sep_sub = '_', ' _.-'
|
205
|
+
end
|
206
|
+
gen_id = %(#{pre}#{title.downcase.gsub InvalidSectionIdCharsRx, ''})
|
207
|
+
if no_sep
|
208
|
+
gen_id = gen_id.delete ' '
|
209
|
+
else
|
210
|
+
# replace space with separator and remove repeating and trailing separator characters
|
211
|
+
gen_id = gen_id.tr_s sep_sub, sep
|
212
|
+
gen_id = gen_id.chop if gen_id.end_with? sep
|
213
|
+
# ensure id doesn't begin with idseparator if idprefix is empty (assuming idseparator is not empty)
|
214
|
+
gen_id = gen_id.slice 1, gen_id.length if pre.empty? && (gen_id.start_with? sep)
|
198
215
|
end
|
199
216
|
if document.catalog[:ids].key? gen_id
|
200
217
|
ids, cnt = document.catalog[:ids], Compliance.unique_id_start_index
|
@@ -8,6 +8,7 @@ class Stylesheets
|
|
8
8
|
DEFAULT_STYLESHEET_NAME = 'asciidoctor.css'
|
9
9
|
DEFAULT_PYGMENTS_STYLE = 'default'
|
10
10
|
STYLESHEETS_DATA_PATH = ::File.join DATA_PATH, 'stylesheets'
|
11
|
+
PygmentsBgColorRx = /^\.pygments +\{ *background: *([^;]+);/
|
11
12
|
|
12
13
|
@__instance__ = new
|
13
14
|
|
@@ -23,7 +24,7 @@ class Stylesheets
|
|
23
24
|
#
|
24
25
|
# returns the [String] Asciidoctor stylesheet data
|
25
26
|
def primary_stylesheet_data
|
26
|
-
@primary_stylesheet_data ||= ::IO.read(::File.join(STYLESHEETS_DATA_PATH, 'asciidoctor-default.css')).
|
27
|
+
@primary_stylesheet_data ||= ::IO.read(::File.join(STYLESHEETS_DATA_PATH, 'asciidoctor-default.css')).rstrip
|
27
28
|
end
|
28
29
|
|
29
30
|
def embed_primary_stylesheet
|
@@ -48,7 +49,7 @@ class Stylesheets
|
|
48
49
|
# unless load_coderay.nil?
|
49
50
|
# ::CodeRay::Encoders[:html]::CSS.new(:default).stylesheet
|
50
51
|
# end
|
51
|
-
@coderay_stylesheet_data ||= ::IO.read(::File.join(STYLESHEETS_DATA_PATH, 'coderay-asciidoctor.css')).
|
52
|
+
@coderay_stylesheet_data ||= ::IO.read(::File.join(STYLESHEETS_DATA_PATH, 'coderay-asciidoctor.css')).rstrip
|
52
53
|
end
|
53
54
|
|
54
55
|
def embed_coderay_stylesheet
|
@@ -65,16 +66,23 @@ class Stylesheets
|
|
65
66
|
%(pygments-#{style || DEFAULT_PYGMENTS_STYLE}.css)
|
66
67
|
end
|
67
68
|
|
69
|
+
def pygments_background style = nil
|
70
|
+
if load_pygments && PygmentsBgColorRx =~ (::Pygments.css '.pygments', :style => style || DEFAULT_PYGMENTS_STYLE)
|
71
|
+
$1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
68
75
|
# Public: Generate the Pygments stylesheet with the specified style.
|
69
76
|
#
|
70
77
|
# returns the [String] Pygments stylesheet data
|
71
78
|
def pygments_stylesheet_data style = nil
|
72
79
|
if load_pygments
|
73
|
-
|
74
|
-
|
80
|
+
style ||= DEFAULT_PYGMENTS_STYLE
|
81
|
+
(@pygments_stylesheet_data ||= {})[style] ||=
|
82
|
+
((::Pygments.css '.listingblock .pygments', :classprefix => 'tok-', :style => style) || '/* Failed to load Pygments CSS. */').
|
75
83
|
sub('.listingblock .pygments {', '.listingblock .pygments, .listingblock .pygments code {')
|
76
84
|
else
|
77
|
-
'/* Pygments
|
85
|
+
'/* Pygments CSS disabled. Pygments is not available. */'
|
78
86
|
end
|
79
87
|
end
|
80
88
|
|
@@ -43,14 +43,24 @@ module Substitutors
|
|
43
43
|
|
44
44
|
SUB_HIGHLIGHT = ['coderay', 'pygments']
|
45
45
|
|
46
|
-
|
47
|
-
|
46
|
+
if ::RUBY_MIN_VERSION_1_9
|
47
|
+
CAN = %(\u0018)
|
48
|
+
DEL = %(\u007f)
|
48
49
|
|
49
|
-
|
50
|
-
|
50
|
+
# Delimiters and matchers for the passthrough placeholder
|
51
|
+
# See http://www.aivosto.com/vbtips/control-characters.html#listabout for characters to use
|
51
52
|
|
52
|
-
|
53
|
-
|
53
|
+
# SPA, start of guarded protected area (\u0096)
|
54
|
+
PASS_START = %(\u0096)
|
55
|
+
|
56
|
+
# EPA, end of guarded protected area (\u0097)
|
57
|
+
PASS_END = %(\u0097)
|
58
|
+
else
|
59
|
+
CAN = 24.chr
|
60
|
+
DEL = 127.chr
|
61
|
+
PASS_START = 150.chr
|
62
|
+
PASS_END = 151.chr
|
63
|
+
end
|
54
64
|
|
55
65
|
# match passthrough slot
|
56
66
|
PassSlotRx = /#{PASS_START}(\d+)#{PASS_END}/
|
@@ -74,39 +84,20 @@ module Substitutors
|
|
74
84
|
# Internal: A String Array of passthough (unprocessed) text captured from this block
|
75
85
|
attr_reader :passthroughs
|
76
86
|
|
77
|
-
# Public: Apply the specified substitutions to the
|
87
|
+
# Public: Apply the specified substitutions to the text.
|
78
88
|
#
|
79
|
-
#
|
80
|
-
# subs
|
81
|
-
# expand - A Boolean (or nil) to control whether substitution aliases are expanded (default: nil).
|
89
|
+
# text - The String or String Array of text to process; must not be nil.
|
90
|
+
# subs - The substitutions to perform; must be a Symbol Array or nil (default: NORMAL_SUBS).
|
82
91
|
#
|
83
|
-
# Returns a String or String Array
|
84
|
-
def apply_subs
|
85
|
-
if
|
86
|
-
return source
|
87
|
-
elsif expand
|
88
|
-
if ::Symbol === subs
|
89
|
-
subs = SUB_GROUPS[subs] || [subs]
|
90
|
-
else
|
91
|
-
effective_subs = []
|
92
|
-
subs.each do |key|
|
93
|
-
if (sub_group = SUB_GROUPS[key])
|
94
|
-
effective_subs += sub_group unless sub_group.empty?
|
95
|
-
else
|
96
|
-
effective_subs << key
|
97
|
-
end
|
98
|
-
end
|
92
|
+
# Returns a String or String Array to match the type of the text argument with substitutions applied.
|
93
|
+
def apply_subs text, subs = NORMAL_SUBS
|
94
|
+
return text if text.empty? || !subs
|
99
95
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
104
|
-
elsif subs.empty?
|
105
|
-
return source
|
96
|
+
if (multiline = ::Array === text)
|
97
|
+
#text = text.size > 1 ? (text.join LF) : text[0]
|
98
|
+
text = text[1] ? (text.join LF) : text[0]
|
106
99
|
end
|
107
100
|
|
108
|
-
text = (multiline = ::Array === source) ? source * LF : source
|
109
|
-
|
110
101
|
if (has_passthroughs = subs.include? :macros)
|
111
102
|
text = extract_passthroughs text
|
112
103
|
has_passthroughs = false if @passthroughs.empty?
|
@@ -119,7 +110,7 @@ module Substitutors
|
|
119
110
|
when :quotes
|
120
111
|
text = sub_quotes text
|
121
112
|
when :attributes
|
122
|
-
text = sub_attributes
|
113
|
+
text = sub_attributes text if text.include? ATTR_REF_HEAD
|
123
114
|
when :replacements
|
124
115
|
text = sub_replacements text
|
125
116
|
when :macros
|
@@ -131,7 +122,7 @@ module Substitutors
|
|
131
122
|
when :post_replacements
|
132
123
|
text = sub_post_replacements text
|
133
124
|
else
|
134
|
-
warn %(
|
125
|
+
logger.warn %(unknown substitution type #{type})
|
135
126
|
end
|
136
127
|
end
|
137
128
|
text = restore_passthroughs text if has_passthroughs
|
@@ -238,13 +229,13 @@ module Substitutors
|
|
238
229
|
next m[0][1..-1]
|
239
230
|
end
|
240
231
|
|
241
|
-
@passthroughs[pass_key = @passthroughs.size] = {:text => (unescape_brackets m[8]), :subs => (m[7] ? (resolve_pass_subs m[7]) :
|
232
|
+
@passthroughs[pass_key = @passthroughs.size] = {:text => (unescape_brackets m[8]), :subs => (m[7] ? (resolve_pass_subs m[7]) : nil)}
|
242
233
|
end
|
243
234
|
|
244
235
|
%(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
|
245
236
|
} if (text.include? '++') || (text.include? '$$') || (text.include? 'ss:')
|
246
237
|
|
247
|
-
pass_inline_char1, pass_inline_char2, pass_inline_rx =
|
238
|
+
pass_inline_char1, pass_inline_char2, pass_inline_rx = InlinePassRx[compat_mode]
|
248
239
|
text = text.gsub(pass_inline_rx) {
|
249
240
|
# alias match for Ruby 1.8.7 compat
|
250
241
|
m = $~
|
@@ -264,7 +255,8 @@ module Substitutors
|
|
264
255
|
|
265
256
|
if attributes
|
266
257
|
if format_mark == '`' && !old_behavior
|
267
|
-
|
258
|
+
# extract nested single-plus passthrough; otherwise return unprocessed
|
259
|
+
next (extract_inner_passthrough content, %(#{preceding}[#{attributes}]#{escape_mark}), attributes)
|
268
260
|
end
|
269
261
|
|
270
262
|
if escape_mark
|
@@ -278,7 +270,8 @@ module Substitutors
|
|
278
270
|
attributes = parse_attributes attributes
|
279
271
|
end
|
280
272
|
elsif format_mark == '`' && !old_behavior
|
281
|
-
|
273
|
+
# extract nested single-plus passthrough; otherwise return unprocessed
|
274
|
+
next (extract_inner_passthrough content, %(#{preceding}#{escape_mark}))
|
282
275
|
elsif escape_mark
|
283
276
|
# honor the escape of the formatting mark
|
284
277
|
next %(#{preceding}#{m[3][1..-1]})
|
@@ -311,10 +304,10 @@ module Substitutors
|
|
311
304
|
end
|
312
305
|
|
313
306
|
if (type = m[1].to_sym) == :stem
|
314
|
-
type =
|
307
|
+
type = STEM_TYPE_ALIASES[@document.attributes['stem']].to_sym
|
315
308
|
end
|
316
309
|
content = unescape_brackets m[3]
|
317
|
-
subs = m[2] ? (resolve_pass_subs m[2]) : ((@document.basebackend? 'html') ? BASIC_SUBS :
|
310
|
+
subs = m[2] ? (resolve_pass_subs m[2]) : ((@document.basebackend? 'html') ? BASIC_SUBS : nil)
|
318
311
|
@passthroughs[pass_key = @passthroughs.size] = {:text => content, :subs => subs, :type => type}
|
319
312
|
%(#{PASS_START}#{pass_key}#{PASS_END})
|
320
313
|
} if (text.include? ':') && ((text.include? 'stem:') || (text.include? 'math:'))
|
@@ -322,6 +315,21 @@ module Substitutors
|
|
322
315
|
text
|
323
316
|
end
|
324
317
|
|
318
|
+
def extract_inner_passthrough text, pre, attributes = nil
|
319
|
+
if (text.end_with? '+') && (text.start_with? '+', '\+') && SinglePlusInlinePassRx =~ text
|
320
|
+
if $1
|
321
|
+
%(#{pre}`+#{$2}+`)
|
322
|
+
else
|
323
|
+
@passthroughs[pass_key = @passthroughs.size] = attributes ?
|
324
|
+
{ :text => $2, :subs => BASIC_SUBS, :attributes => attributes, :type => :unquoted } :
|
325
|
+
{ :text => $2, :subs => BASIC_SUBS }
|
326
|
+
%(#{pre}`#{PASS_START}#{pass_key}#{PASS_END}`)
|
327
|
+
end
|
328
|
+
else
|
329
|
+
%(#{pre}`#{text}`)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
325
333
|
# Internal: Restore the passthrough text by reinserting into the placeholder positions
|
326
334
|
#
|
327
335
|
# text - The String text into which to restore the passthrough text
|
@@ -366,10 +374,6 @@ module Substitutors
|
|
366
374
|
end
|
367
375
|
text
|
368
376
|
end
|
369
|
-
|
370
|
-
def sub_specialchars text
|
371
|
-
(text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub SpecialCharsRx, SpecialCharsTr) : text
|
372
|
-
end
|
373
377
|
else
|
374
378
|
# Public: Substitute quoted text (includes emphasis, strong, monospaced, etc)
|
375
379
|
#
|
@@ -404,23 +408,23 @@ module Substitutors
|
|
404
408
|
end
|
405
409
|
text
|
406
410
|
end
|
411
|
+
end
|
407
412
|
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
end
|
413
|
+
# Public: Substitute special characters (i.e., encode XML)
|
414
|
+
#
|
415
|
+
# The special characters <, &, and > get replaced with <,
|
416
|
+
# &, and >, respectively.
|
417
|
+
#
|
418
|
+
# text - The String text to process.
|
419
|
+
#
|
420
|
+
# returns The String text with special characters replaced.
|
421
|
+
if ::RUBY_MIN_VERSION_1_9
|
422
|
+
def sub_specialchars text
|
423
|
+
(text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub SpecialCharsRx, SpecialCharsTr) : text
|
424
|
+
end
|
425
|
+
else
|
426
|
+
def sub_specialchars text
|
427
|
+
(text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub(SpecialCharsRx) { SpecialCharsTr[$&] }) : text
|
424
428
|
end
|
425
429
|
end
|
426
430
|
alias sub_specialcharacters sub_specialchars
|
@@ -444,74 +448,78 @@ module Substitutors
|
|
444
448
|
end
|
445
449
|
end
|
446
450
|
|
447
|
-
# Public:
|
451
|
+
# Public: Substitutes attribute references in the specified text
|
448
452
|
#
|
449
453
|
# Attribute references are in the format +{name}+.
|
450
454
|
#
|
451
|
-
# If an attribute referenced in the line is missing, the line
|
455
|
+
# If an attribute referenced in the line is missing or undefined, the line may be dropped
|
456
|
+
# based on the attribute-missing or attribute-undefined setting, respectively.
|
452
457
|
#
|
453
|
-
# text
|
458
|
+
# text - The String text to process
|
459
|
+
# opts - A Hash of options to control processing: (default: {})
|
460
|
+
# * :attribute_missing controls how to handle a missing attribute
|
454
461
|
#
|
455
|
-
#
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
when 'set'
|
473
|
-
_, value = Parser.store_attribute args[0], args[1] || '', @document
|
474
|
-
# since this is an assignment, only drop-line applies here (skip and drop imply the same result)
|
475
|
-
if (doc_attrs.fetch 'attribute-undefined', Compliance.attribute_undefined) == 'drop-line'
|
476
|
-
reject = true
|
477
|
-
break ''
|
478
|
-
end unless value
|
479
|
-
reject_if_empty = true
|
480
|
-
''
|
481
|
-
when 'counter2'
|
482
|
-
@document.counter(*args)
|
483
|
-
reject_if_empty = true
|
484
|
-
''
|
485
|
-
else # 'counter'
|
486
|
-
@document.counter(*args)
|
487
|
-
end
|
488
|
-
elsif doc_attrs.key?(key = $2.downcase)
|
489
|
-
doc_attrs[key]
|
490
|
-
elsif INTRINSIC_ATTRIBUTES.key? key
|
491
|
-
INTRINSIC_ATTRIBUTES[key]
|
492
|
-
else
|
493
|
-
case (attribute_missing ||= opts[:attribute_missing] || (doc_attrs.fetch 'attribute-missing', Compliance.attribute_missing))
|
494
|
-
when 'drop'
|
495
|
-
# QUESTION should we warn in this case?
|
496
|
-
reject_if_empty = true
|
497
|
-
''
|
498
|
-
when 'drop-line'
|
499
|
-
warn %(asciidoctor: WARNING: dropping line containing reference to missing attribute: #{key})
|
500
|
-
reject = true
|
501
|
-
break ''
|
502
|
-
when 'warn'
|
503
|
-
warn %(asciidoctor: WARNING: skipping reference to missing attribute: #{key})
|
504
|
-
$&
|
505
|
-
else # 'skip'
|
506
|
-
$&
|
462
|
+
# Returns the [String] text with the attribute references replaced with resolved values
|
463
|
+
def sub_attributes text, opts = {}
|
464
|
+
doc_attrs = @document.attributes
|
465
|
+
drop = drop_line = drop_empty_line = attribute_undefined = attribute_missing = nil
|
466
|
+
result = text.gsub AttributeReferenceRx do
|
467
|
+
# escaped attribute, return unescaped
|
468
|
+
if $1 == RS || $4 == RS
|
469
|
+
%({#{$2}})
|
470
|
+
elsif $3
|
471
|
+
case (args = $2.split ':', 3).shift
|
472
|
+
when 'set'
|
473
|
+
_, value = Parser.store_attribute args[0], args[1] || '', @document
|
474
|
+
# NOTE since this is an assignment, only drop-line applies here (skip and drop imply the same result)
|
475
|
+
if value || (attribute_undefined ||= doc_attrs['attribute-undefined'] || Compliance.attribute_undefined) != 'drop-line'
|
476
|
+
drop = drop_empty_line = DEL
|
477
|
+
else
|
478
|
+
drop = drop_line = CAN
|
507
479
|
end
|
480
|
+
when 'counter2'
|
481
|
+
@document.counter(*args)
|
482
|
+
drop = drop_empty_line = DEL
|
483
|
+
else # 'counter'
|
484
|
+
@document.counter(*args)
|
508
485
|
end
|
509
|
-
|
510
|
-
|
511
|
-
|
486
|
+
elsif doc_attrs.key?(key = $2.downcase)
|
487
|
+
doc_attrs[key]
|
488
|
+
elsif (value = INTRINSIC_ATTRIBUTES[key])
|
489
|
+
value
|
490
|
+
else
|
491
|
+
case (attribute_missing ||= opts[:attribute_missing] || doc_attrs['attribute-missing'] || Compliance.attribute_missing)
|
492
|
+
when 'drop'
|
493
|
+
drop = drop_empty_line = DEL
|
494
|
+
when 'drop-line'
|
495
|
+
logger.warn %(dropping line containing reference to missing attribute: #{key})
|
496
|
+
drop = drop_line = CAN
|
497
|
+
when 'warn'
|
498
|
+
logger.warn %(skipping reference to missing attribute: #{key})
|
499
|
+
$&
|
500
|
+
else # 'skip'
|
501
|
+
$&
|
502
|
+
end
|
503
|
+
end
|
512
504
|
end
|
513
505
|
|
514
|
-
|
506
|
+
if drop
|
507
|
+
# drop lines from result
|
508
|
+
if drop_empty_line
|
509
|
+
lines = (result.tr_s DEL, DEL).split LF, -1
|
510
|
+
if drop_line
|
511
|
+
(lines.reject {|line| line == DEL || line == CAN || (line.start_with? CAN) || (line.include? CAN) }.join LF).delete DEL
|
512
|
+
else
|
513
|
+
(lines.reject {|line| line == DEL }.join LF).delete DEL
|
514
|
+
end
|
515
|
+
elsif result.include? LF
|
516
|
+
(result.split LF, -1).reject {|line| line == CAN || (line.start_with? CAN) || (line.include? CAN) }.join LF
|
517
|
+
else
|
518
|
+
''
|
519
|
+
end
|
520
|
+
else
|
521
|
+
result
|
522
|
+
end
|
515
523
|
end
|
516
524
|
|
517
525
|
# Public: Substitute inline macros (e.g., links, images, etc)
|
@@ -529,8 +537,7 @@ module Substitutors
|
|
529
537
|
found_colon = source.include? ':'
|
530
538
|
found_macroish = found[:macroish] = found_square_bracket && found_colon
|
531
539
|
found_macroish_short = found_macroish && (source.include? ':[')
|
532
|
-
doc_attrs = @document.attributes
|
533
|
-
use_link_attrs = doc_attrs.key? 'linkattrs'
|
540
|
+
doc_attrs = (doc = @document).attributes
|
534
541
|
result = source
|
535
542
|
|
536
543
|
if doc_attrs.key? 'experimental'
|
@@ -591,7 +598,7 @@ module Substitutors
|
|
591
598
|
end
|
592
599
|
|
593
600
|
if (result.include? '"') && (result.include? '>')
|
594
|
-
result = result.gsub(
|
601
|
+
result = result.gsub(InlineMenuRx) {
|
595
602
|
# alias match for Ruby 1.8.7 compat
|
596
603
|
m = $~
|
597
604
|
# honor the escape
|
@@ -610,7 +617,7 @@ module Substitutors
|
|
610
617
|
|
611
618
|
# FIXME this location is somewhat arbitrary, probably need to be able to control ordering
|
612
619
|
# TODO this handling needs some cleanup
|
613
|
-
if (extensions =
|
620
|
+
if (extensions = doc.extensions) && extensions.inline_macros? # && found_macroish
|
614
621
|
extensions.inline_macros.each do |extension|
|
615
622
|
result = result.gsub(extension.instance.regexp) {
|
616
623
|
# alias match for Ruby 1.8.7 compat
|
@@ -664,63 +671,72 @@ module Substitutors
|
|
664
671
|
# TODO remove this special case once titles use normal substitution order
|
665
672
|
target = sub_attributes target
|
666
673
|
end
|
667
|
-
|
674
|
+
doc.register(:images, target) unless type == 'icon'
|
668
675
|
attrs = parse_attributes(m[2], posattrs, :unescape_input => true)
|
669
676
|
attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
|
670
677
|
Inline.new(self, :image, nil, :type => type, :target => target, :attributes => attrs).convert
|
671
678
|
}
|
672
679
|
end
|
673
680
|
|
674
|
-
if ((result.include? '((') && (result.include? '))')) ||
|
675
|
-
(found_macroish_short && (result.include? 'indexterm'))
|
681
|
+
if ((result.include? '((') && (result.include? '))')) || (found_macroish_short && (result.include? 'dexterm'))
|
676
682
|
# (((Tigers,Big cats)))
|
677
683
|
# indexterm:[Tigers,Big cats]
|
678
684
|
# ((Tigers))
|
679
685
|
# indexterm2:[Tigers]
|
680
686
|
result = result.gsub(InlineIndextermMacroRx) {
|
681
|
-
|
682
|
-
m = $~
|
683
|
-
|
684
|
-
# honor the escape
|
685
|
-
if m[0].start_with? RS
|
686
|
-
next m[0][1..-1]
|
687
|
-
end
|
688
|
-
|
689
|
-
case m[1]
|
687
|
+
case $1
|
690
688
|
when 'indexterm'
|
689
|
+
text = $2
|
690
|
+
# honor the escape
|
691
|
+
if (m0 = $&).start_with? RS
|
692
|
+
next m0.slice 1, m0.length
|
693
|
+
end
|
691
694
|
# indexterm:[Tigers,Big cats]
|
692
|
-
terms = split_simple_csv
|
693
|
-
|
695
|
+
terms = split_simple_csv normalize_string text, true
|
696
|
+
doc.register :indexterms, terms
|
694
697
|
(Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
|
695
698
|
when 'indexterm2'
|
699
|
+
text = $2
|
700
|
+
# honor the escape
|
701
|
+
if (m0 = $&).start_with? RS
|
702
|
+
next m0.slice 1, m0.length
|
703
|
+
end
|
696
704
|
# indexterm2:[Tigers]
|
697
|
-
term = normalize_string
|
698
|
-
|
705
|
+
term = normalize_string text, true
|
706
|
+
doc.register :indexterms, [term]
|
699
707
|
(Inline.new self, :indexterm, term, :type => :visible).convert
|
700
708
|
else
|
701
|
-
text
|
702
|
-
|
703
|
-
|
704
|
-
|
709
|
+
text = $3
|
710
|
+
# honor the escape
|
711
|
+
if (m0 = $&).start_with? RS
|
712
|
+
# escape concealed index term, but process nested flow index term
|
713
|
+
if (text.start_with? '(') && (text.end_with? ')')
|
714
|
+
text = text.slice 1, text.length - 2
|
715
|
+
visible, before, after = true, '(', ')'
|
705
716
|
else
|
706
|
-
|
717
|
+
next m0.slice 1, m0.length
|
707
718
|
end
|
708
|
-
|
719
|
+
else
|
720
|
+
visible = true
|
709
721
|
if text.start_with? '('
|
710
|
-
|
711
|
-
|
722
|
+
if text.end_with? ')'
|
723
|
+
text, visible = (text.slice 1, text.length - 2), false
|
724
|
+
else
|
725
|
+
text, before, after = (text.slice 1, text.length), '(', ''
|
726
|
+
end
|
727
|
+
elsif text.end_with? ')'
|
712
728
|
text, before, after = (text.slice 0, text.length - 1), '', ')'
|
713
729
|
end
|
714
730
|
end
|
715
731
|
if visible
|
716
732
|
# ((Tigers))
|
717
733
|
term = normalize_string text
|
718
|
-
|
734
|
+
doc.register :indexterms, [term]
|
719
735
|
result = (Inline.new self, :indexterm, term, :type => :visible).convert
|
720
736
|
else
|
721
737
|
# (((Tigers,Big cats)))
|
722
738
|
terms = split_simple_csv(normalize_string text)
|
723
|
-
|
739
|
+
doc.register :indexterms, terms
|
724
740
|
result = (Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
|
725
741
|
end
|
726
742
|
before ? %(#{before}#{result}#{after}) : result
|
@@ -730,7 +746,7 @@ module Substitutors
|
|
730
746
|
|
731
747
|
if found_colon && (result.include? '://')
|
732
748
|
# inline urls, target[text] (optionally prefixed with link: and optionally surrounded by <>)
|
733
|
-
result = result.gsub(
|
749
|
+
result = result.gsub(InlineLinkRx) {
|
734
750
|
# alias match for Ruby 1.8.7 compat
|
735
751
|
m = $~
|
736
752
|
# honor the escape
|
@@ -759,15 +775,13 @@ module Substitutors
|
|
759
775
|
if prefix.start_with?('<') && target.end_with?('>')
|
760
776
|
prefix = prefix[4..-1]
|
761
777
|
target = target[0...-4]
|
778
|
+
# strip trailing ;
|
779
|
+
# check for trailing );
|
780
|
+
elsif (target = target.chop).end_with?(')')
|
781
|
+
target = target.chop
|
782
|
+
suffix = ');'
|
762
783
|
else
|
763
|
-
|
764
|
-
# check for trailing );
|
765
|
-
if (target = target.chop).end_with?(')')
|
766
|
-
target = target.chop
|
767
|
-
suffix = ');'
|
768
|
-
else
|
769
|
-
suffix = ';'
|
770
|
-
end
|
784
|
+
suffix = ';'
|
771
785
|
end
|
772
786
|
when ':'
|
773
787
|
# strip trailing :
|
@@ -779,15 +793,16 @@ module Substitutors
|
|
779
793
|
suffix = ':'
|
780
794
|
end
|
781
795
|
end
|
796
|
+
# NOTE handle case when remaining target is a URI scheme (e.g., http://)
|
797
|
+
return m[0] if target.end_with? '://'
|
782
798
|
end
|
783
799
|
|
784
800
|
attrs, link_opts = nil, { :type => :link }
|
785
801
|
unless text.empty?
|
786
802
|
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
|
787
|
-
if
|
788
|
-
attrs =
|
803
|
+
if !doc.compat_mode && (text.include? '=')
|
804
|
+
text = (attrs = (AttributeList.new text, self).parse)[1] || ''
|
789
805
|
link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
|
790
|
-
text = attrs[1] || ''
|
791
806
|
end
|
792
807
|
|
793
808
|
# TODO enable in Asciidoctor 1.6.x
|
@@ -818,7 +833,7 @@ module Substitutors
|
|
818
833
|
end
|
819
834
|
end
|
820
835
|
|
821
|
-
|
836
|
+
doc.register :links, (link_opts[:target] = target)
|
822
837
|
link_opts[:attributes] = attrs if attrs
|
823
838
|
%(#{prefix}#{Inline.new(self, :anchor, text, link_opts).convert}#{suffix})
|
824
839
|
}
|
@@ -837,10 +852,10 @@ module Substitutors
|
|
837
852
|
attrs, link_opts = nil, { :type => :link }
|
838
853
|
unless (text = m[3]).empty?
|
839
854
|
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
|
840
|
-
if
|
841
|
-
|
842
|
-
|
843
|
-
|
855
|
+
if mailto
|
856
|
+
if !doc.compat_mode && (text.include? ',')
|
857
|
+
text = (attrs = (AttributeList.new text, self).parse)[1] || ''
|
858
|
+
link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
|
844
859
|
if attrs.key? 2
|
845
860
|
if attrs.key? 3
|
846
861
|
target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]}&body=#{Helpers.uri_encode attrs[3]})
|
@@ -849,7 +864,9 @@ module Substitutors
|
|
849
864
|
end
|
850
865
|
end
|
851
866
|
end
|
852
|
-
|
867
|
+
elsif !doc.compat_mode && (text.include? '=')
|
868
|
+
text = (attrs = (AttributeList.new text, self).parse)[1] || ''
|
869
|
+
link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
|
853
870
|
end
|
854
871
|
|
855
872
|
# TODO enable in Asciidoctor 1.6.x
|
@@ -886,14 +903,14 @@ module Substitutors
|
|
886
903
|
end
|
887
904
|
|
888
905
|
# QUESTION should a mailto be registered as an e-mail address?
|
889
|
-
|
906
|
+
doc.register :links, (link_opts[:target] = target)
|
890
907
|
link_opts[:attributes] = attrs if attrs
|
891
908
|
Inline.new(self, :anchor, text, link_opts).convert
|
892
909
|
}
|
893
910
|
end
|
894
911
|
|
895
912
|
if result.include? '@'
|
896
|
-
result = result.gsub(
|
913
|
+
result = result.gsub(InlineEmailRx) {
|
897
914
|
address, tip = $&, $1
|
898
915
|
if tip
|
899
916
|
next (tip == RS ? address[1..-1] : address)
|
@@ -901,13 +918,13 @@ module Substitutors
|
|
901
918
|
|
902
919
|
target = %(mailto:#{address})
|
903
920
|
# QUESTION should this be registered as an e-mail address?
|
904
|
-
|
921
|
+
doc.register(:links, target)
|
905
922
|
|
906
923
|
Inline.new(self, :anchor, address, :type => :link, :target => target).convert
|
907
924
|
}
|
908
925
|
end
|
909
926
|
|
910
|
-
if
|
927
|
+
if found_macroish && (result.include? 'tnote')
|
911
928
|
result = result.gsub(InlineFootnoteMacroRx) {
|
912
929
|
# alias match for Ruby 1.8.7 compat
|
913
930
|
m = $~
|
@@ -915,36 +932,35 @@ module Substitutors
|
|
915
932
|
if m[0].start_with? RS
|
916
933
|
next m[0][1..-1]
|
917
934
|
end
|
918
|
-
if m[1]
|
919
|
-
id =
|
920
|
-
# REVIEW it's a dirty job, but somebody's gotta do it
|
921
|
-
text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string m[2], true)), false)
|
922
|
-
index = @document.counter('footnote-number')
|
923
|
-
@document.register(:footnotes, Document::Footnote.new(index, id, text))
|
924
|
-
type = nil
|
925
|
-
target = nil
|
935
|
+
if m[1] # footnoteref (legacy)
|
936
|
+
id, text = (m[3] || '').split(',', 2)
|
926
937
|
else
|
927
|
-
id, text = m[2]
|
928
|
-
|
938
|
+
id, text = m[2], m[3]
|
939
|
+
end
|
940
|
+
if id
|
929
941
|
if text
|
930
942
|
# REVIEW it's a dirty job, but somebody's gotta do it
|
931
943
|
text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
|
932
|
-
index =
|
933
|
-
|
934
|
-
type = :ref
|
935
|
-
target = nil
|
944
|
+
index = doc.counter('footnote-number')
|
945
|
+
doc.register(:footnotes, Document::Footnote.new(index, id, text))
|
946
|
+
type, target = :ref, nil
|
936
947
|
else
|
937
|
-
if (footnote =
|
938
|
-
index = footnote.index
|
939
|
-
text = footnote.text
|
948
|
+
if (footnote = doc.footnotes.find {|candidate| candidate.id == id })
|
949
|
+
index, text = footnote.index, footnote.text
|
940
950
|
else
|
941
|
-
|
942
|
-
text = id
|
951
|
+
logger.warn %(invalid footnote reference: #{id})
|
952
|
+
index, text = nil, id
|
943
953
|
end
|
944
|
-
target = id
|
945
|
-
id = nil
|
946
|
-
type = :xref
|
954
|
+
type, target, id = :xref, id, nil
|
947
955
|
end
|
956
|
+
elsif text
|
957
|
+
# REVIEW it's a dirty job, but somebody's gotta do it
|
958
|
+
text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
|
959
|
+
index = doc.counter('footnote-number')
|
960
|
+
doc.register(:footnotes, Document::Footnote.new(index, id, text))
|
961
|
+
type = target = nil
|
962
|
+
else
|
963
|
+
next m[0]
|
948
964
|
end
|
949
965
|
Inline.new(self, :footnote, text, :attributes => {'index' => index}, :id => id, :target => target, :type => type).convert
|
950
966
|
}
|
@@ -985,74 +1001,95 @@ module Substitutors
|
|
985
1001
|
end
|
986
1002
|
|
987
1003
|
# Internal: Substitute cross reference links
|
988
|
-
def sub_inline_xrefs(
|
989
|
-
if ((found ? found[:macroish] : (
|
990
|
-
|
991
|
-
text = text.gsub(InlineXrefMacroRx) {
|
1004
|
+
def sub_inline_xrefs(content, found = nil)
|
1005
|
+
if ((found ? found[:macroish] : (content.include? '[')) && (content.include? 'xref:')) || ((content.include? '&') && (content.include? 'lt;&'))
|
1006
|
+
content = content.gsub(InlineXrefMacroRx) {
|
992
1007
|
# alias match for Ruby 1.8.7 compat
|
993
1008
|
m = $~
|
994
1009
|
# honor the escape
|
995
1010
|
if m[0].start_with? RS
|
996
1011
|
next m[0][1..-1]
|
997
1012
|
end
|
998
|
-
|
999
|
-
|
1000
|
-
|
1013
|
+
attrs, doc = {}, @document
|
1014
|
+
if (refid = m[1])
|
1015
|
+
refid, text = refid.split ',', 2
|
1016
|
+
text = text.lstrip if text
|
1001
1017
|
else
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1018
|
+
macro = true
|
1019
|
+
refid = m[2]
|
1020
|
+
if (text = m[3])
|
1021
|
+
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
|
1022
|
+
# NOTE if an equal sign (=) is present, parse text as attributes
|
1023
|
+
text = ((AttributeList.new text, self).parse_into attrs)[1] if !doc.compat_mode && (text.include? '=')
|
1005
1024
|
end
|
1006
1025
|
end
|
1007
1026
|
|
1008
|
-
if
|
1027
|
+
if doc.compat_mode
|
1028
|
+
fragment = refid
|
1029
|
+
elsif (hash_idx = refid.index '#')
|
1009
1030
|
if hash_idx > 0
|
1010
|
-
if (fragment_len =
|
1011
|
-
path, fragment = (
|
1031
|
+
if (fragment_len = refid.length - hash_idx - 1) > 0
|
1032
|
+
path, fragment = (refid.slice 0, hash_idx), (refid.slice hash_idx + 1, fragment_len)
|
1012
1033
|
else
|
1013
|
-
path
|
1034
|
+
path = refid.slice 0, hash_idx
|
1035
|
+
end
|
1036
|
+
if (ext = ::File.extname path).empty?
|
1037
|
+
src2src = path
|
1038
|
+
elsif ASCIIDOC_EXTENSIONS[ext]
|
1039
|
+
src2src = (path = path.slice 0, path.length - ext.length)
|
1014
1040
|
end
|
1015
1041
|
else
|
1016
|
-
target,
|
1042
|
+
target, fragment = refid, (refid.slice 1, refid.length)
|
1017
1043
|
end
|
1044
|
+
elsif macro && (refid.end_with? '.adoc')
|
1045
|
+
src2src = (path = refid.slice 0, refid.length - 5)
|
1018
1046
|
else
|
1019
|
-
|
1047
|
+
fragment = refid
|
1020
1048
|
end
|
1021
1049
|
|
1022
1050
|
# handles: #id
|
1023
1051
|
if target
|
1024
1052
|
refid = fragment
|
1025
|
-
|
1053
|
+
logger.warn %(invalid reference: #{refid}) if $VERBOSE && !(doc.catalog[:ids].key? refid)
|
1026
1054
|
elsif path
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1055
|
+
# handles: path#, path#id, path.adoc#, path.adoc#id, or path.adoc (xref macro only)
|
1056
|
+
# the referenced path is the current document, or its contents have been included in the current document
|
1057
|
+
if src2src && (doc.attributes['docname'] == path || doc.catalog[:includes][path])
|
1058
|
+
if fragment
|
1059
|
+
refid, path, target = fragment, nil, %(##{fragment})
|
1060
|
+
logger.warn %(invalid reference: #{refid}) if $VERBOSE && !(doc.catalog[:ids].key? refid)
|
1061
|
+
else
|
1062
|
+
refid, path, target = nil, nil, '#'
|
1063
|
+
end
|
1033
1064
|
else
|
1034
|
-
refid =
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
else
|
1040
|
-
# resolve fragment as reftext if it's not a known ID and resembles reftext (includes space or has uppercase char)
|
1041
|
-
unless @document.catalog[:ids].key? fragment
|
1042
|
-
if ((fragment.include? ' ') || fragment.downcase != fragment) &&
|
1043
|
-
(resolved_id = @document.catalog[:ids].key fragment)
|
1044
|
-
fragment = resolved_id
|
1045
|
-
elsif $VERBOSE
|
1046
|
-
warn %(asciidoctor: WARNING: invalid reference: #{fragment})
|
1065
|
+
refid, path = path, %(#{doc.attributes['relfileprefix']}#{path}#{src2src ? (doc.attributes.fetch 'relfilesuffix', doc.outfilesuffix) : ''})
|
1066
|
+
if fragment
|
1067
|
+
refid, target = %(#{refid}##{fragment}), %(#{path}##{fragment})
|
1068
|
+
else
|
1069
|
+
target = path
|
1047
1070
|
end
|
1048
1071
|
end
|
1072
|
+
# handles: id (in compat mode or when natural xrefs are disabled)
|
1073
|
+
elsif doc.compat_mode || !Compliance.natural_xrefs
|
1074
|
+
refid, target = fragment, %(##{fragment})
|
1075
|
+
logger.warn %(invalid reference: #{refid}) if $VERBOSE && !(doc.catalog[:ids].key? refid)
|
1076
|
+
# handles: id
|
1077
|
+
elsif doc.catalog[:ids].key? fragment
|
1078
|
+
refid, target = fragment, %(##{fragment})
|
1079
|
+
# handles: Node Title or Reference Text
|
1080
|
+
# do reverse lookup on fragment if not a known ID and resembles reftext (contains a space or uppercase char)
|
1081
|
+
elsif (refid = doc.catalog[:ids].key fragment) && ((fragment.include? ' ') || fragment.downcase != fragment)
|
1082
|
+
fragment, target = refid, %(##{refid})
|
1083
|
+
else
|
1049
1084
|
refid, target = fragment, %(##{fragment})
|
1085
|
+
logger.warn %(invalid reference: #{refid}) if $VERBOSE
|
1050
1086
|
end
|
1051
|
-
|
1087
|
+
attrs['path'], attrs['fragment'], attrs['refid'] = path, fragment, refid
|
1088
|
+
Inline.new(self, :anchor, text, :type => :xref, :target => target, :attributes => attrs).convert
|
1052
1089
|
}
|
1053
1090
|
end
|
1054
1091
|
|
1055
|
-
|
1092
|
+
content
|
1056
1093
|
end
|
1057
1094
|
|
1058
1095
|
# Public: Substitute callout source references
|
@@ -1135,7 +1172,7 @@ module Substitutors
|
|
1135
1172
|
# Returns a Hash of attributes (role and id only)
|
1136
1173
|
def parse_quoted_text_attributes str
|
1137
1174
|
# NOTE attributes are typically resolved after quoted text, so substitute eagerly
|
1138
|
-
str = sub_attributes str if str.include? ATTR_REF_HEAD
|
1175
|
+
str = sub_attributes str, :multiline => true if str.include? ATTR_REF_HEAD
|
1139
1176
|
# for compliance, only consider first positional attribute
|
1140
1177
|
str = str.slice 0, (str.index ',') if str.include? ','
|
1141
1178
|
|
@@ -1178,8 +1215,8 @@ module Substitutors
|
|
1178
1215
|
def parse_attributes(attrline, posattrs = ['role'], opts = {})
|
1179
1216
|
return unless attrline
|
1180
1217
|
return {} if attrline.empty?
|
1181
|
-
attrline = @document.sub_attributes
|
1182
|
-
attrline = unescape_bracketed_text
|
1218
|
+
attrline = @document.sub_attributes attrline if opts[:sub_input] && (attrline.include? ATTR_REF_HEAD)
|
1219
|
+
attrline = unescape_bracketed_text attrline if opts[:unescape_input]
|
1183
1220
|
# substitutions are only performed on attribute values if block is not nil
|
1184
1221
|
block = opts.fetch(:sub_result, true) ? self : nil
|
1185
1222
|
if (into = opts[:into])
|
@@ -1189,7 +1226,33 @@ module Substitutors
|
|
1189
1226
|
end
|
1190
1227
|
end
|
1191
1228
|
|
1192
|
-
#
|
1229
|
+
# Expand all groups in the subs list and return. If no subs are resolve, return nil.
|
1230
|
+
#
|
1231
|
+
# subs - The substitutions to expand; can be a Symbol, Symbol Array or nil
|
1232
|
+
#
|
1233
|
+
# Returns a Symbol Array of substitutions to pass to apply_subs or nil if no substitutions were resolved.
|
1234
|
+
def expand_subs subs
|
1235
|
+
if ::Symbol === subs
|
1236
|
+
unless subs == :none
|
1237
|
+
SUB_GROUPS[subs] || [subs]
|
1238
|
+
end
|
1239
|
+
else
|
1240
|
+
expanded_subs = []
|
1241
|
+
subs.each do |key|
|
1242
|
+
unless key == :none
|
1243
|
+
if (sub_group = SUB_GROUPS[key])
|
1244
|
+
expanded_subs += sub_group
|
1245
|
+
else
|
1246
|
+
expanded_subs << key
|
1247
|
+
end
|
1248
|
+
end
|
1249
|
+
end
|
1250
|
+
|
1251
|
+
expanded_subs.empty? ? nil : expanded_subs
|
1252
|
+
end
|
1253
|
+
end
|
1254
|
+
|
1255
|
+
# Internal: Strip bounding whitespace, fold endlines and unescape closing
|
1193
1256
|
# square brackets from text extracted from brackets
|
1194
1257
|
def unescape_bracketed_text text
|
1195
1258
|
if (text = text.strip.tr LF, ' ').include? R_SB
|
@@ -1253,9 +1316,9 @@ module Substitutors
|
|
1253
1316
|
#
|
1254
1317
|
# subs - A comma-delimited String of substitution aliases
|
1255
1318
|
#
|
1256
|
-
# returns An Array of Symbols representing the substitution operation
|
1319
|
+
# returns An Array of Symbols representing the substitution operation or nothing if no subs are found.
|
1257
1320
|
def resolve_subs subs, type = :block, defaults = nil, subject = nil
|
1258
|
-
return
|
1321
|
+
return if subs.nil_or_empty?
|
1259
1322
|
# QUESTION should we store candidates as a Set instead of an Array?
|
1260
1323
|
candidates = nil
|
1261
1324
|
subs = subs.delete ' ' if subs.include? ' '
|
@@ -1306,12 +1369,12 @@ module Substitutors
|
|
1306
1369
|
candidates += resolved_keys
|
1307
1370
|
end
|
1308
1371
|
end
|
1309
|
-
return
|
1372
|
+
return unless candidates
|
1310
1373
|
# weed out invalid options and remove duplicates (order is preserved; first occurence wins)
|
1311
1374
|
resolved = candidates & SUB_OPTIONS[type]
|
1312
1375
|
unless (candidates - resolved).empty?
|
1313
1376
|
invalid = candidates - resolved
|
1314
|
-
warn %(
|
1377
|
+
logger.warn %(invalid substitution type#{invalid.size > 1 ? 's' : ''}#{subject ? ' for ' : ''}#{subject}: #{invalid * ', '})
|
1315
1378
|
end
|
1316
1379
|
resolved
|
1317
1380
|
end
|
@@ -1339,19 +1402,21 @@ module Substitutors
|
|
1339
1402
|
def highlight_source source, process_callouts, highlighter = nil
|
1340
1403
|
case (highlighter ||= @document.attributes['source-highlighter'])
|
1341
1404
|
when 'coderay'
|
1342
|
-
unless (highlighter_loaded = defined? ::CodeRay) ||
|
1405
|
+
unless (highlighter_loaded = defined? ::CodeRay) ||
|
1406
|
+
(defined? @@coderay_unavailable) || @document.attributes['coderay-unavailable']
|
1343
1407
|
if (Helpers.require_library 'coderay', true, :warn).nil?
|
1344
|
-
# prevent further attempts to load CodeRay
|
1345
|
-
|
1408
|
+
# prevent further attempts to load CodeRay in this process
|
1409
|
+
@@coderay_unavailable = true
|
1346
1410
|
else
|
1347
1411
|
highlighter_loaded = true
|
1348
1412
|
end
|
1349
1413
|
end
|
1350
1414
|
when 'pygments'
|
1351
|
-
unless (highlighter_loaded = defined? ::Pygments) ||
|
1415
|
+
unless (highlighter_loaded = defined? ::Pygments) ||
|
1416
|
+
(defined? @@pygments_unavailable) || @document.attributes['pygments-unavailable']
|
1352
1417
|
if (Helpers.require_library 'pygments', 'pygments.rb', :warn).nil?
|
1353
|
-
# prevent further attempts to load Pygments
|
1354
|
-
|
1418
|
+
# prevent further attempts to load Pygments in this process
|
1419
|
+
@@pygments_unavailable = true
|
1355
1420
|
else
|
1356
1421
|
highlighter_loaded = true
|
1357
1422
|
end
|
@@ -1422,21 +1487,26 @@ module Substitutors
|
|
1422
1487
|
opts[:hl_lines] = highlight_lines * ' '
|
1423
1488
|
end
|
1424
1489
|
end
|
1490
|
+
# NOTE highlight can return nil if something goes wrong; fallback to source if this happens
|
1425
1491
|
# TODO we could add the line numbers in ourselves instead of having to strip out the junk
|
1426
1492
|
if (attr? 'linenums', nil, false) && (opts[:linenos] = @document.attributes['pygments-linenums-mode'] || 'table') == 'table'
|
1427
1493
|
linenums_mode = :table
|
1428
|
-
result = lexer.highlight
|
1429
|
-
|
1430
|
-
|
1494
|
+
if (result = lexer.highlight source, :options => opts)
|
1495
|
+
result = (result.sub PygmentsWrapperDivRx, '\1').gsub PygmentsWrapperPreRx, '\1'
|
1496
|
+
else
|
1497
|
+
result = sub_specialchars source
|
1498
|
+
end
|
1499
|
+
elsif (result = lexer.highlight source, :options => opts)
|
1500
|
+
if PygmentsWrapperPreRx =~ result
|
1431
1501
|
result = $1
|
1432
1502
|
end
|
1503
|
+
else
|
1504
|
+
result = sub_specialchars source
|
1433
1505
|
end
|
1434
1506
|
end
|
1435
1507
|
|
1436
1508
|
# fix passthrough placeholders that got caught up in syntax highlighting
|
1437
|
-
unless @passthroughs.empty?
|
1438
|
-
result = result.gsub HighlightedPassSlotRx, %(#{PASS_START}\\1#{PASS_END})
|
1439
|
-
end
|
1509
|
+
result = result.gsub HighlightedPassSlotRx, %(#{PASS_START}\\1#{PASS_END}) unless @passthroughs.empty?
|
1440
1510
|
|
1441
1511
|
if process_callouts && callout_marks
|
1442
1512
|
lineno = 0
|
@@ -1539,7 +1609,11 @@ module Substitutors
|
|
1539
1609
|
end
|
1540
1610
|
end
|
1541
1611
|
|
1542
|
-
|
1612
|
+
if (custom_subs = @attributes['subs'])
|
1613
|
+
@subs = (resolve_block_subs custom_subs, default_subs, @context) || []
|
1614
|
+
else
|
1615
|
+
@subs = default_subs.dup
|
1616
|
+
end
|
1543
1617
|
|
1544
1618
|
# QUESION delegate this logic to a method?
|
1545
1619
|
if @context == :listing && @style == 'source' && (@attributes.key? 'language') && (@document.basebackend? 'html') &&
|