asciidoctor 1.5.5 → 1.5.6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +216 -1
- data/CONTRIBUTING.adoc +2 -2
- data/Gemfile +20 -1
- data/LICENSE.adoc +1 -1
- data/README-fr.adoc +4 -3
- data/README-jp.adoc +11 -10
- data/README-zh_CN.adoc +4 -3
- data/README.adoc +17 -202
- data/Rakefile +41 -25
- data/asciidoctor.gemspec +9 -10
- data/data/locale/attributes.adoc +216 -34
- data/data/stylesheets/asciidoctor-default.css +23 -16
- data/features/step_definitions.rb +15 -19
- data/features/xref.feature +584 -20
- data/lib/asciidoctor.rb +292 -278
- data/lib/asciidoctor/abstract_block.rb +155 -94
- data/lib/asciidoctor/abstract_node.rb +108 -94
- data/lib/asciidoctor/attribute_list.rb +30 -22
- data/lib/asciidoctor/block.rb +7 -7
- data/lib/asciidoctor/cli/invoker.rb +47 -34
- data/lib/asciidoctor/cli/options.rb +22 -11
- data/lib/asciidoctor/converter.rb +3 -3
- data/lib/asciidoctor/converter/base.rb +2 -2
- data/lib/asciidoctor/converter/composite.rb +1 -1
- data/lib/asciidoctor/converter/docbook45.rb +2 -2
- data/lib/asciidoctor/converter/docbook5.rb +132 -87
- data/lib/asciidoctor/converter/factory.rb +0 -1
- data/lib/asciidoctor/converter/html5.rb +116 -98
- data/lib/asciidoctor/converter/manpage.rb +51 -52
- data/lib/asciidoctor/converter/template.rb +47 -36
- data/lib/asciidoctor/core_ext.rb +8 -2
- data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +4 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +5 -0
- data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +1 -1
- data/lib/asciidoctor/core_ext/1.8.7/string/{limit.rb → limit_bytesize.rb} +7 -6
- data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +1 -1
- data/lib/asciidoctor/core_ext/nil_or_empty.rb +5 -5
- data/lib/asciidoctor/core_ext/regexp/is_match.rb +3 -0
- data/lib/asciidoctor/core_ext/string/{limit.rb → limit_bytesize.rb} +2 -2
- data/lib/asciidoctor/document.rb +216 -213
- data/lib/asciidoctor/extensions.rb +318 -185
- data/lib/asciidoctor/helpers.rb +35 -35
- data/lib/asciidoctor/inline.rb +32 -1
- data/lib/asciidoctor/list.rb +22 -6
- data/lib/asciidoctor/parser.rb +1008 -1038
- data/lib/asciidoctor/path_resolver.rb +46 -50
- data/lib/asciidoctor/reader.rb +275 -251
- data/lib/asciidoctor/section.rb +86 -58
- data/lib/asciidoctor/stylesheets.rb +6 -6
- data/lib/asciidoctor/substitutors.rb +567 -649
- data/lib/asciidoctor/table.rb +163 -108
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +18 -16
- data/man/asciidoctor.adoc +15 -13
- data/test/attributes_test.rb +138 -22
- data/test/blocks_test.rb +377 -97
- data/test/converter_test.rb +13 -0
- data/test/document_test.rb +244 -34
- data/test/extensions_test.rb +409 -42
- data/test/fixtures/asciidoc_index.txt +521 -0
- data/test/fixtures/basic-docinfo-footer.html +6 -0
- data/test/fixtures/basic-docinfo-footer.xml +8 -0
- data/test/fixtures/basic-docinfo.html +1 -0
- data/test/fixtures/basic-docinfo.xml +4 -0
- data/test/fixtures/basic.asciidoc +5 -0
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/child-include.adoc +5 -0
- data/test/fixtures/circle.svg +9 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
- data/test/fixtures/custom-docinfodir/basic-docinfo.html +1 -0
- data/test/fixtures/custom-docinfodir/docinfo.html +1 -0
- data/test/fixtures/docinfo-footer.html +1 -0
- data/test/fixtures/docinfo-footer.xml +9 -0
- data/test/fixtures/docinfo.html +1 -0
- data/test/fixtures/docinfo.xml +3 -0
- data/test/fixtures/dot.gif +0 -0
- data/test/fixtures/encoding.asciidoc +13 -0
- data/test/fixtures/grandchild-include.adoc +3 -0
- data/test/fixtures/hello-asciidoctor.pdf +69 -0
- data/test/fixtures/include-file.asciidoc +24 -0
- data/test/fixtures/include-file.ml +3 -0
- data/test/fixtures/include-file.xml +5 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/fixtures/mismatched-end-tag.adoc +7 -0
- data/test/fixtures/parent-include-restricted.adoc +5 -0
- data/test/fixtures/parent-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +26 -0
- data/test/fixtures/stylesheets/custom.css +3 -0
- data/test/fixtures/subs-docinfo.html +2 -0
- data/test/fixtures/subs.adoc +7 -0
- data/test/fixtures/tagged-class-enclosed.rb +26 -0
- data/test/fixtures/tagged-class.rb +23 -0
- data/test/fixtures/tip.gif +0 -0
- data/test/invoker_test.rb +82 -4
- data/test/links_test.rb +312 -37
- data/test/lists_test.rb +204 -25
- data/test/manpage_test.rb +191 -4
- data/test/options_test.rb +18 -1
- data/test/paragraphs_test.rb +32 -7
- data/test/parser_test.rb +150 -30
- data/test/paths_test.rb +47 -13
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +366 -126
- data/test/sections_test.rb +203 -56
- data/test/substitutions_test.rb +339 -131
- data/test/tables_test.rb +315 -15
- data/test/test_helper.rb +400 -0
- data/test/text_test.rb +5 -5
- metadata +110 -22
data/lib/asciidoctor/section.rb
CHANGED
@@ -24,10 +24,6 @@ class Section < AbstractBlock
|
|
24
24
|
# Public: Get/Set the 0-based index order of this section within the parent block
|
25
25
|
attr_accessor :index
|
26
26
|
|
27
|
-
# Public: Get/Set the number of this section within the parent block
|
28
|
-
# Only relevant if the attribute numbered is true
|
29
|
-
attr_accessor :number
|
30
|
-
|
31
27
|
# Public: Get/Set the section name of this section
|
32
28
|
attr_accessor :sectname
|
33
29
|
|
@@ -37,16 +33,15 @@ class Section < AbstractBlock
|
|
37
33
|
# Public: Get the state of the numbered attribute at this section (need to preserve for creating TOC)
|
38
34
|
attr_accessor :numbered
|
39
35
|
|
36
|
+
# Public: Get the caption for this section (only relevant for appendices)
|
37
|
+
attr_reader :caption
|
38
|
+
|
40
39
|
# Public: Initialize an Asciidoctor::Section object.
|
41
40
|
#
|
42
41
|
# parent - The parent Asciidoc Object.
|
43
42
|
def initialize parent = nil, level = nil, numbered = true, opts = {}
|
44
43
|
super parent, :section, opts
|
45
|
-
|
46
|
-
@level = level
|
47
|
-
else
|
48
|
-
@level = parent ? (parent.level + 1) : 1
|
49
|
-
end
|
44
|
+
@level = level ? level : (parent ? (parent.level + 1) : 1)
|
50
45
|
@numbered = numbered && @level > 0
|
51
46
|
@special = parent && parent.context == :section && parent.special
|
52
47
|
@index = 0
|
@@ -54,54 +49,13 @@ class Section < AbstractBlock
|
|
54
49
|
end
|
55
50
|
|
56
51
|
# Public: The name of this section, an alias of the section title
|
57
|
-
alias
|
52
|
+
alias name title
|
58
53
|
|
59
|
-
# Public: Generate a String
|
60
|
-
#
|
61
|
-
# The generated id is prefixed with value of the 'idprefix' attribute, which
|
62
|
-
# is an underscore by default.
|
63
|
-
#
|
64
|
-
# Section id synthesis can be disabled by undefining the 'sectids' attribute.
|
54
|
+
# Public: Generate a String ID from the title of this section.
|
65
55
|
#
|
66
|
-
#
|
67
|
-
# until a unique id is found.
|
68
|
-
#
|
69
|
-
# Examples
|
70
|
-
#
|
71
|
-
# section = Section.new(parent)
|
72
|
-
# section.title = "Foo"
|
73
|
-
# section.generate_id
|
74
|
-
# => "_foo"
|
75
|
-
#
|
76
|
-
# another_section = Section.new(parent)
|
77
|
-
# another_section.title = "Foo"
|
78
|
-
# another_section.generate_id
|
79
|
-
# => "_foo_1"
|
80
|
-
#
|
81
|
-
# yet_another_section = Section.new(parent)
|
82
|
-
# yet_another_section.title = "Ben & Jerry"
|
83
|
-
# yet_another_section.generate_id
|
84
|
-
# => "_ben_jerry"
|
56
|
+
# See Section.generate_id for details.
|
85
57
|
def generate_id
|
86
|
-
|
87
|
-
sep = @document.attributes['idseparator'] || '_'
|
88
|
-
pre = @document.attributes['idprefix'] || '_'
|
89
|
-
base_id = %(#{pre}#{title.downcase.gsub(InvalidSectionIdCharsRx, sep).tr_s(sep, sep).chomp(sep)})
|
90
|
-
# ensure id doesn't begin with idseparator if idprefix is empty and idseparator is not empty
|
91
|
-
if pre.empty? && !sep.empty? && base_id.start_with?(sep)
|
92
|
-
base_id = base_id[1..-1]
|
93
|
-
base_id = base_id[1..-1] while base_id.start_with?(sep)
|
94
|
-
end
|
95
|
-
gen_id = base_id
|
96
|
-
cnt = Compliance.unique_id_start_index
|
97
|
-
while @document.references[:ids].has_key? gen_id
|
98
|
-
gen_id = %(#{base_id}#{sep}#{cnt})
|
99
|
-
cnt += 1
|
100
|
-
end
|
101
|
-
gen_id
|
102
|
-
else
|
103
|
-
nil
|
104
|
-
end
|
58
|
+
Section.generate_id title, @document
|
105
59
|
end
|
106
60
|
|
107
61
|
# Public: Get the section number for the current Section
|
@@ -156,6 +110,41 @@ class Section < AbstractBlock
|
|
156
110
|
end
|
157
111
|
end
|
158
112
|
|
113
|
+
# (see AbstractBlock#xreftext)
|
114
|
+
def xreftext xrefstyle = nil
|
115
|
+
if (val = reftext) && !val.empty?
|
116
|
+
val
|
117
|
+
elsif xrefstyle
|
118
|
+
if @numbered
|
119
|
+
case xrefstyle
|
120
|
+
when 'full'
|
121
|
+
if (type = @sectname) == 'chapter' || type == 'appendix'
|
122
|
+
quoted_title = sprintf sub_quotes('_%s_'), title
|
123
|
+
else
|
124
|
+
quoted_title = sprintf sub_quotes(@document.compat_mode ? %q(``%s'') : '"`%s`"'), title
|
125
|
+
end
|
126
|
+
if (signifier = @document.attributes[%(#{type}-refsig)])
|
127
|
+
%(#{signifier} #{sectnum '.', ','} #{quoted_title})
|
128
|
+
else
|
129
|
+
%(#{sectnum '.', ','} #{quoted_title})
|
130
|
+
end
|
131
|
+
when 'short'
|
132
|
+
if (signifier = @document.attributes[%(#{@sectname}-refsig)])
|
133
|
+
%(#{signifier} #{sectnum '.', ''})
|
134
|
+
else
|
135
|
+
sectnum '.', ''
|
136
|
+
end
|
137
|
+
else # 'basic'
|
138
|
+
(type = @sectname) == 'chapter' || type == 'appendix' ? (sprintf sub_quotes('_%s_'), title) : title
|
139
|
+
end
|
140
|
+
else # apply basic styling
|
141
|
+
(type = @sectname) == 'chapter' || type == 'appendix' ? (sprintf sub_quotes('_%s_'), title) : title
|
142
|
+
end
|
143
|
+
else
|
144
|
+
title
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
159
148
|
# Public: Append a content block to this block's list of blocks.
|
160
149
|
#
|
161
150
|
# If the child block is a Section, assign an index to it.
|
@@ -164,17 +153,56 @@ class Section < AbstractBlock
|
|
164
153
|
#
|
165
154
|
# Returns The parent Block
|
166
155
|
def << block
|
167
|
-
|
156
|
+
enumerate_section block if block.context == :section
|
168
157
|
super
|
169
158
|
end
|
170
159
|
|
171
160
|
def to_s
|
172
|
-
if @title
|
173
|
-
|
174
|
-
%(#<#{self.class}@#{object_id} {level: #{@level}, title: #{
|
161
|
+
if @title
|
162
|
+
formal_title = @numbered ? %(#{sectnum} #{@title}) : @title
|
163
|
+
%(#<#{self.class}@#{object_id} {level: #{@level}, title: #{formal_title.inspect}, blocks: #{@blocks.size}}>)
|
175
164
|
else
|
176
165
|
super
|
177
166
|
end
|
178
167
|
end
|
168
|
+
|
169
|
+
# Public: Generate a String ID from the given section title.
|
170
|
+
#
|
171
|
+
# The generated ID is prefixed with value of the 'idprefix' attribute, which
|
172
|
+
# is an underscore by default. Invalid characters are replaced with the
|
173
|
+
# value of the 'idseparator' attribute, which is an underscore by default.
|
174
|
+
#
|
175
|
+
# If the generated ID is already in use in the document, a count is appended
|
176
|
+
# until a unique id is found.
|
177
|
+
#
|
178
|
+
# Section ID generation can be disabled by undefining the 'sectids' attribute.
|
179
|
+
#
|
180
|
+
# Examples
|
181
|
+
#
|
182
|
+
# Section.generate_id 'Foo', document
|
183
|
+
# => "_foo"
|
184
|
+
#
|
185
|
+
def self.generate_id title, document
|
186
|
+
attrs = document.attributes
|
187
|
+
sep = attrs['idseparator'] || '_'
|
188
|
+
pre = attrs['idprefix'] || '_'
|
189
|
+
gen_id = %(#{pre}#{title.downcase.gsub InvalidSectionIdCharsRx, sep})
|
190
|
+
unless sep.empty?
|
191
|
+
# remove repeat and trailing separator characters
|
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
|
197
|
+
end
|
198
|
+
end
|
199
|
+
if document.catalog[:ids].key? gen_id
|
200
|
+
ids, cnt = document.catalog[:ids], Compliance.unique_id_start_index
|
201
|
+
cnt += 1 while ids.key?(candidate_id = %(#{gen_id}#{sep}#{cnt}))
|
202
|
+
candidate_id
|
203
|
+
else
|
204
|
+
gen_id
|
205
|
+
end
|
206
|
+
end
|
179
207
|
end
|
180
208
|
end
|
@@ -32,8 +32,8 @@ class Stylesheets
|
|
32
32
|
</style>)
|
33
33
|
end
|
34
34
|
|
35
|
-
def write_primary_stylesheet target_dir
|
36
|
-
::
|
35
|
+
def write_primary_stylesheet target_dir = '.'
|
36
|
+
::IO.write(::File.join(target_dir, primary_stylesheet_name), primary_stylesheet_data)
|
37
37
|
end
|
38
38
|
|
39
39
|
def coderay_stylesheet_name
|
@@ -57,8 +57,8 @@ class Stylesheets
|
|
57
57
|
</style>)
|
58
58
|
end
|
59
59
|
|
60
|
-
def write_coderay_stylesheet target_dir
|
61
|
-
::
|
60
|
+
def write_coderay_stylesheet target_dir = '.'
|
61
|
+
::IO.write(::File.join(target_dir, coderay_stylesheet_name), coderay_stylesheet_data)
|
62
62
|
end
|
63
63
|
|
64
64
|
def pygments_stylesheet_name style = nil
|
@@ -84,8 +84,8 @@ class Stylesheets
|
|
84
84
|
</style>)
|
85
85
|
end
|
86
86
|
|
87
|
-
def write_pygments_stylesheet target_dir, style = nil
|
88
|
-
::
|
87
|
+
def write_pygments_stylesheet target_dir = '.', style = nil
|
88
|
+
::IO.write(::File.join(target_dir, pygments_stylesheet_name(style)), pygments_stylesheet_data(style))
|
89
89
|
end
|
90
90
|
|
91
91
|
#def load_coderay
|
@@ -4,34 +4,28 @@ module Asciidoctor
|
|
4
4
|
# is intented to be mixed-in to Section and Block to provide operations for performing
|
5
5
|
# the necessary substitutions.
|
6
6
|
module Substitutors
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
:
|
7
|
+
SpecialCharsRx = /[<&>]/
|
8
|
+
SpecialCharsTr = { '>' => '>', '<' => '<', '&' => '&' }
|
9
|
+
|
10
|
+
# Detects if text is a possible candidate for the quotes substitution.
|
11
|
+
QuotedTextSniffRx = { false => /[*_`#^~]/, true => /[*'_+#^~]/ }
|
12
|
+
|
13
|
+
(BASIC_SUBS = [:specialcharacters]).freeze
|
14
|
+
(HEADER_SUBS = [:specialcharacters, :attributes]).freeze
|
15
|
+
(NORMAL_SUBS = [:specialcharacters, :quotes, :attributes, :replacements, :macros, :post_replacements]).freeze
|
16
|
+
(NONE_SUBS = []).freeze
|
17
|
+
(TITLE_SUBS = [:specialcharacters, :quotes, :replacements, :macros, :attributes, :post_replacements]).freeze
|
18
|
+
(REFTEXT_SUBS = [:specialcharacters, :quotes, :replacements]).freeze
|
19
|
+
(VERBATIM_SUBS = [:specialcharacters, :callouts]).freeze
|
20
|
+
|
21
|
+
SUB_GROUPS = {
|
22
|
+
:none => NONE_SUBS,
|
23
|
+
:normal => NORMAL_SUBS,
|
24
|
+
:verbatim => VERBATIM_SUBS,
|
25
|
+
:specialchars => BASIC_SUBS
|
25
26
|
}
|
26
27
|
|
27
|
-
|
28
|
-
:none => [],
|
29
|
-
:normal => SUBS[:normal],
|
30
|
-
:verbatim => SUBS[:verbatim],
|
31
|
-
:specialchars => [:specialcharacters]
|
32
|
-
}
|
33
|
-
|
34
|
-
SUB_SYMBOLS = {
|
28
|
+
SUB_HINTS = {
|
35
29
|
:a => :attributes,
|
36
30
|
:m => :macros,
|
37
31
|
:n => :normal,
|
@@ -43,8 +37,8 @@ module Substitutors
|
|
43
37
|
}
|
44
38
|
|
45
39
|
SUB_OPTIONS = {
|
46
|
-
:block =>
|
47
|
-
:inline =>
|
40
|
+
:block => SUB_GROUPS.keys + NORMAL_SUBS + [:callouts],
|
41
|
+
:inline => SUB_GROUPS.keys + NORMAL_SUBS
|
48
42
|
}
|
49
43
|
|
50
44
|
SUB_HIGHLIGHT = ['coderay', 'pygments']
|
@@ -53,52 +47,63 @@ module Substitutors
|
|
53
47
|
# See http://www.aivosto.com/vbtips/control-characters.html#listabout for characters to use
|
54
48
|
|
55
49
|
# SPA, start of guarded protected area (\u0096)
|
56
|
-
PASS_START =
|
50
|
+
PASS_START = %(\u0096)
|
57
51
|
|
58
52
|
# EPA, end of guarded protected area (\u0097)
|
59
|
-
PASS_END =
|
53
|
+
PASS_END = %(\u0097)
|
54
|
+
|
55
|
+
# match passthrough slot
|
56
|
+
PassSlotRx = /#{PASS_START}(\d+)#{PASS_END}/
|
57
|
+
|
58
|
+
# fix passthrough slot after syntax highlighting
|
59
|
+
HighlightedPassSlotRx = %r(<span\b[^>]*>#{PASS_START}</span>[^\d]*(\d+)[^\d]*<span\b[^>]*>#{PASS_END}</span>)
|
60
60
|
|
61
|
-
|
62
|
-
PASS_MATCH = /\u0096(\d+)\u0097/
|
61
|
+
RS = '\\'
|
63
62
|
|
64
|
-
|
65
|
-
|
63
|
+
R_SB = ']'
|
64
|
+
|
65
|
+
ESC_R_SB = '\]'
|
66
|
+
|
67
|
+
PLUS = '+'
|
68
|
+
|
69
|
+
PygmentsWrapperDivRx = %r(<div class="pyhl">(.*)</div>)m
|
70
|
+
# NOTE handles all permutations of <pre> wrapper
|
71
|
+
# NOTE trailing whitespace appears when pygments-linenums-mode=table; <pre> has style attribute when pygments-css=inline
|
72
|
+
PygmentsWrapperPreRx = %r(<pre\b[^>]*?>(.*?)</pre>\s*)m
|
66
73
|
|
67
74
|
# Internal: A String Array of passthough (unprocessed) text captured from this block
|
68
75
|
attr_reader :passthroughs
|
69
76
|
|
70
|
-
# Public: Apply the specified substitutions to the
|
77
|
+
# Public: Apply the specified substitutions to the source.
|
71
78
|
#
|
72
|
-
# source - The String or String Array of text to process
|
73
|
-
# subs - The substitutions to perform
|
74
|
-
# expand
|
79
|
+
# source - The String or String Array of text to process; must not be nil.
|
80
|
+
# subs - The substitutions to perform; can be a Symbol or Symbol Array; must not be nil (default: :normal).
|
81
|
+
# expand - A Boolean to control whether substitution aliases are expanded (default: false).
|
75
82
|
#
|
76
|
-
#
|
77
|
-
def apply_subs source, subs =
|
78
|
-
if
|
83
|
+
# Returns a String or String Array with substitutions applied, matching the type of source argument.
|
84
|
+
def apply_subs source, subs = NORMAL_SUBS, expand = false
|
85
|
+
if source.empty? || subs.empty?
|
79
86
|
return source
|
80
|
-
elsif subs == :normal
|
81
|
-
subs = SUBS[:normal]
|
82
87
|
elsif expand
|
83
88
|
if ::Symbol === subs
|
84
|
-
subs =
|
89
|
+
subs = SUB_GROUPS[subs] || [subs]
|
85
90
|
else
|
86
91
|
effective_subs = []
|
87
92
|
subs.each do |key|
|
88
|
-
if
|
89
|
-
effective_subs +=
|
93
|
+
if (sub_group = SUB_GROUPS[key])
|
94
|
+
effective_subs += sub_group unless sub_group.empty?
|
90
95
|
else
|
91
96
|
effective_subs << key
|
92
97
|
end
|
93
98
|
end
|
94
99
|
|
95
|
-
subs = effective_subs
|
100
|
+
if (subs = effective_subs).empty?
|
101
|
+
return source
|
102
|
+
end
|
96
103
|
end
|
97
104
|
end
|
98
105
|
|
99
|
-
|
100
|
-
|
101
|
-
text = (multiline = ::Array === source) ? source * EOL : source
|
106
|
+
text = (multiline = ::Array === source) ? source * LF : source
|
102
107
|
|
103
108
|
if (has_passthroughs = subs.include? :macros)
|
104
109
|
text = extract_passthroughs text
|
@@ -112,7 +117,7 @@ module Substitutors
|
|
112
117
|
when :quotes
|
113
118
|
text = sub_quotes text
|
114
119
|
when :attributes
|
115
|
-
text = sub_attributes(text.split
|
120
|
+
text = sub_attributes(text.split LF, -1) * LF if text.include? '{'
|
116
121
|
when :replacements
|
117
122
|
text = sub_replacements text
|
118
123
|
when :macros
|
@@ -129,16 +134,18 @@ module Substitutors
|
|
129
134
|
end
|
130
135
|
text = restore_passthroughs text if has_passthroughs
|
131
136
|
|
132
|
-
multiline ? (text.split
|
137
|
+
multiline ? (text.split LF, -1) : text
|
133
138
|
end
|
134
139
|
|
135
140
|
# Public: Apply normal substitutions.
|
136
141
|
#
|
137
|
-
#
|
142
|
+
# An alias for apply_subs with default remaining arguments.
|
143
|
+
#
|
144
|
+
# text - The String text to which to apply normal substitutions
|
138
145
|
#
|
139
|
-
#
|
140
|
-
def apply_normal_subs
|
141
|
-
apply_subs
|
146
|
+
# Returns the String with normal substitutions applied.
|
147
|
+
def apply_normal_subs text
|
148
|
+
apply_subs text
|
142
149
|
end
|
143
150
|
|
144
151
|
# Public: Apply substitutions for titles.
|
@@ -147,7 +154,16 @@ module Substitutors
|
|
147
154
|
#
|
148
155
|
# returns - A String with title substitutions performed
|
149
156
|
def apply_title_subs(title)
|
150
|
-
apply_subs title,
|
157
|
+
apply_subs title, TITLE_SUBS
|
158
|
+
end
|
159
|
+
|
160
|
+
# Public: Apply substitutions for reftext.
|
161
|
+
#
|
162
|
+
# text - The String to process
|
163
|
+
#
|
164
|
+
# Returns a String with all substitutions from the reftext substitution group applied
|
165
|
+
def apply_reftext_subs text
|
166
|
+
apply_subs text, REFTEXT_SUBS
|
151
167
|
end
|
152
168
|
|
153
169
|
# Public: Apply substitutions for header metadata and attribute assignments
|
@@ -156,7 +172,7 @@ module Substitutors
|
|
156
172
|
#
|
157
173
|
# returns - A String with header substitutions performed
|
158
174
|
def apply_header_subs(text)
|
159
|
-
apply_subs text,
|
175
|
+
apply_subs text, HEADER_SUBS
|
160
176
|
end
|
161
177
|
|
162
178
|
# Internal: Extract the passthrough text from the document for reinsertion after processing.
|
@@ -166,19 +182,12 @@ module Substitutors
|
|
166
182
|
# returns - The text with the passthrough region substituted with placeholders
|
167
183
|
def extract_passthroughs(text)
|
168
184
|
compat_mode = @document.compat_mode
|
169
|
-
text = text.gsub(
|
185
|
+
text = text.gsub(InlinePassMacroRx) {
|
170
186
|
# alias match for Ruby 1.8.7 compat
|
171
187
|
m = $~
|
172
188
|
preceding = nil
|
173
189
|
|
174
|
-
if (boundary = m[4])
|
175
|
-
if m[6] == '\\'
|
176
|
-
# NOTE we don't look for nested pass:[] macros
|
177
|
-
next m[0][1..-1]
|
178
|
-
end
|
179
|
-
|
180
|
-
@passthroughs[pass_key = @passthroughs.size] = {:text => (unescape_brackets m[8]), :subs => (m[7].nil_or_empty? ? [] : (resolve_pass_subs m[7]))}
|
181
|
-
else # $$, ++ or +++
|
190
|
+
if (boundary = m[4]) # $$, ++, or +++
|
182
191
|
# skip ++ in compat mode, handled as normal quoted text
|
183
192
|
if compat_mode && boundary == '++'
|
184
193
|
next m[2].nil_or_empty? ?
|
@@ -187,22 +196,15 @@ module Substitutors
|
|
187
196
|
end
|
188
197
|
|
189
198
|
attributes = m[2]
|
190
|
-
|
191
|
-
# fix non-matching group results in Opal under Firefox
|
192
|
-
if ::RUBY_ENGINE_OPAL
|
193
|
-
attributes = nil if attributes == ''
|
194
|
-
end
|
195
|
-
|
196
|
-
escape_count = m[3].size
|
199
|
+
escape_count = m[3].length
|
197
200
|
content = m[5]
|
198
201
|
old_behavior = false
|
199
202
|
|
200
203
|
if attributes
|
201
204
|
if escape_count > 0
|
202
205
|
# NOTE we don't look for nested unconstrained pass macros
|
203
|
-
#
|
204
|
-
|
205
|
-
elsif m[1] == '\\'
|
206
|
+
next %(#{m[1]}[#{attributes}]#{RS * (escape_count - 1)}#{boundary}#{m[5]}#{boundary})
|
207
|
+
elsif m[1] == RS
|
206
208
|
preceding = %([#{attributes}])
|
207
209
|
attributes = nil
|
208
210
|
else
|
@@ -214,21 +216,27 @@ module Substitutors
|
|
214
216
|
end
|
215
217
|
elsif escape_count > 0
|
216
218
|
# NOTE we don't look for nested unconstrained pass macros
|
217
|
-
#
|
218
|
-
next "#{m[1]}[#{attributes}]#{'\\' * (escape_count - 1)}#{boundary}#{m[5]}#{boundary}"
|
219
|
+
next %(#{RS * (escape_count - 1)}#{boundary}#{m[5]}#{boundary})
|
219
220
|
end
|
220
|
-
subs = (boundary == '+++' ? [] :
|
221
|
+
subs = (boundary == '+++' ? [] : BASIC_SUBS)
|
221
222
|
|
222
223
|
pass_key = @passthroughs.size
|
223
224
|
if attributes
|
224
225
|
if old_behavior
|
225
|
-
@passthroughs[pass_key] = {:text => content, :subs =>
|
226
|
+
@passthroughs[pass_key] = {:text => content, :subs => NORMAL_SUBS, :type => :monospaced, :attributes => attributes}
|
226
227
|
else
|
227
228
|
@passthroughs[pass_key] = {:text => content, :subs => subs, :type => :unquoted, :attributes => attributes}
|
228
229
|
end
|
229
230
|
else
|
230
231
|
@passthroughs[pass_key] = {:text => content, :subs => subs}
|
231
232
|
end
|
233
|
+
else # pass:[]
|
234
|
+
if m[6] == RS
|
235
|
+
# NOTE we don't look for nested pass:[] macros
|
236
|
+
next m[0][1..-1]
|
237
|
+
end
|
238
|
+
|
239
|
+
@passthroughs[pass_key = @passthroughs.size] = {:text => (unescape_brackets m[8]), :subs => (m[7] ? (resolve_pass_subs m[7]) : [])}
|
232
240
|
end
|
233
241
|
|
234
242
|
%(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
|
@@ -240,15 +248,10 @@ module Substitutors
|
|
240
248
|
m = $~
|
241
249
|
preceding = m[1]
|
242
250
|
attributes = m[2]
|
243
|
-
escape_mark =
|
251
|
+
escape_mark = RS if m[3].start_with? RS
|
244
252
|
format_mark = m[4]
|
245
253
|
content = m[5]
|
246
254
|
|
247
|
-
# fix non-matching group results in Opal under Firefox
|
248
|
-
if ::RUBY_ENGINE_OPAL
|
249
|
-
attributes = nil if attributes == ''
|
250
|
-
end
|
251
|
-
|
252
255
|
if compat_mode
|
253
256
|
old_behavior = true
|
254
257
|
else
|
@@ -259,14 +262,13 @@ module Substitutors
|
|
259
262
|
|
260
263
|
if attributes
|
261
264
|
if format_mark == '`' && !old_behavior
|
262
|
-
|
263
|
-
next "#{preceding}[#{attributes}]#{escape_mark}`#{extract_passthroughs content}`"
|
265
|
+
next %(#{preceding}[#{attributes}]#{escape_mark}`#{extract_passthroughs content}`)
|
264
266
|
end
|
265
267
|
|
266
268
|
if escape_mark
|
267
|
-
# honor the escape of the formatting mark
|
268
|
-
next
|
269
|
-
elsif preceding ==
|
269
|
+
# honor the escape of the formatting mark
|
270
|
+
next %(#{preceding}[#{attributes}]#{m[3][1..-1]})
|
271
|
+
elsif preceding == RS
|
270
272
|
# honor the escape of the attributes
|
271
273
|
preceding = %([#{attributes}])
|
272
274
|
attributes = nil
|
@@ -274,49 +276,43 @@ module Substitutors
|
|
274
276
|
attributes = parse_attributes attributes
|
275
277
|
end
|
276
278
|
elsif format_mark == '`' && !old_behavior
|
277
|
-
|
278
|
-
next "#{preceding}#{escape_mark}`#{extract_passthroughs content}`"
|
279
|
+
next %(#{preceding}#{escape_mark}`#{extract_passthroughs content}`)
|
279
280
|
elsif escape_mark
|
280
|
-
# honor the escape of the formatting mark
|
281
|
-
next
|
281
|
+
# honor the escape of the formatting mark
|
282
|
+
next %(#{preceding}#{m[3][1..-1]})
|
282
283
|
end
|
283
284
|
|
284
285
|
pass_key = @passthroughs.size
|
285
286
|
if compat_mode
|
286
|
-
@passthroughs[pass_key] = {:text => content, :subs =>
|
287
|
+
@passthroughs[pass_key] = {:text => content, :subs => BASIC_SUBS, :attributes => attributes, :type => :monospaced}
|
287
288
|
elsif attributes
|
288
289
|
if old_behavior
|
289
|
-
subs = (format_mark == '`' ?
|
290
|
+
subs = (format_mark == '`' ? BASIC_SUBS : NORMAL_SUBS)
|
290
291
|
@passthroughs[pass_key] = {:text => content, :subs => subs, :attributes => attributes, :type => :monospaced}
|
291
292
|
else
|
292
|
-
@passthroughs[pass_key] = {:text => content, :subs =>
|
293
|
+
@passthroughs[pass_key] = {:text => content, :subs => BASIC_SUBS, :attributes => attributes, :type => :unquoted}
|
293
294
|
end
|
294
295
|
else
|
295
|
-
@passthroughs[pass_key] = {:text => content, :subs =>
|
296
|
+
@passthroughs[pass_key] = {:text => content, :subs => BASIC_SUBS}
|
296
297
|
end
|
297
298
|
|
298
299
|
%(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
|
299
300
|
} if (text.include? pass_inline_char1) || (pass_inline_char2 && (text.include? pass_inline_char2))
|
300
301
|
|
301
302
|
# NOTE we need to do the stem in a subsequent step to allow it to be escaped by the former
|
302
|
-
text = text.gsub(
|
303
|
+
text = text.gsub(InlineStemMacroRx) {
|
303
304
|
# alias match for Ruby 1.8.7 compat
|
304
305
|
m = $~
|
305
306
|
# honor the escape
|
306
|
-
if m[0].start_with?
|
307
|
+
if m[0].start_with? RS
|
307
308
|
next m[0][1..-1]
|
308
309
|
end
|
309
310
|
|
310
311
|
if (type = m[1].to_sym) == :stem
|
311
|
-
type = ((default_stem_type = document.attributes['stem']).nil_or_empty? ? 'asciimath' : default_stem_type).to_sym
|
312
|
+
type = ((default_stem_type = @document.attributes['stem']).nil_or_empty? ? 'asciimath' : default_stem_type).to_sym
|
312
313
|
end
|
313
314
|
content = unescape_brackets m[3]
|
314
|
-
|
315
|
-
subs = (@document.basebackend? 'html') ? [:specialcharacters] : []
|
316
|
-
else
|
317
|
-
subs = resolve_pass_subs m[2]
|
318
|
-
end
|
319
|
-
|
315
|
+
subs = m[2] ? (resolve_pass_subs m[2]) : ((@document.basebackend? 'html') ? BASIC_SUBS : [])
|
320
316
|
@passthroughs[pass_key = @passthroughs.size] = {:text => content, :subs => subs, :type => type}
|
321
317
|
%(#{PASS_START}#{pass_key}#{PASS_END})
|
322
318
|
} if (text.include? ':') && ((text.include? 'stem:') || (text.include? 'math:'))
|
@@ -335,10 +331,10 @@ module Substitutors
|
|
335
331
|
return text
|
336
332
|
end
|
337
333
|
|
338
|
-
text.gsub(
|
334
|
+
text.gsub(PassSlotRx) {
|
339
335
|
# NOTE we can't remove entry from map because placeholder may have been duplicated by other substitutions
|
340
|
-
pass = @passthroughs[
|
341
|
-
subbed_text =
|
336
|
+
pass = @passthroughs[$1.to_i]
|
337
|
+
subbed_text = apply_subs(pass[:text], pass[:subs])
|
342
338
|
if (type = pass[:type])
|
343
339
|
subbed_text = Inline.new(self, :quoted, subbed_text, :type => type, :attributes => pass[:attributes]).convert
|
344
340
|
end
|
@@ -349,34 +345,29 @@ module Substitutors
|
|
349
345
|
@passthroughs.clear if outer
|
350
346
|
end
|
351
347
|
|
352
|
-
# Public: Substitute special characters (i.e., encode XML)
|
353
|
-
#
|
354
|
-
# Special characters are defined in the Asciidoctor::SPECIAL_CHARS Array constant
|
355
|
-
#
|
356
|
-
# text - The String text to process
|
357
|
-
#
|
358
|
-
# returns The String text with special characters replaced
|
359
|
-
def sub_specialchars(text)
|
360
|
-
SUPPORTS_GSUB_RESULT_HASH ?
|
361
|
-
text.gsub(SPECIAL_CHARS_PATTERN, SPECIAL_CHARS) :
|
362
|
-
text.gsub(SPECIAL_CHARS_PATTERN) { SPECIAL_CHARS[$&] }
|
363
|
-
end
|
364
|
-
alias :sub_specialcharacters :sub_specialchars
|
365
348
|
|
366
349
|
if RUBY_ENGINE == 'opal'
|
367
350
|
def sub_quotes text
|
368
|
-
|
369
|
-
|
351
|
+
if QuotedTextSniffRx[compat = @document.compat_mode].match? text
|
352
|
+
QUOTE_SUBS[compat].each do |type, scope, pattern|
|
353
|
+
text = text.gsub(pattern) { convert_quoted_text $~, type, scope }
|
354
|
+
end
|
370
355
|
end
|
371
356
|
text
|
372
357
|
end
|
373
358
|
|
374
359
|
def sub_replacements text
|
375
|
-
|
376
|
-
|
360
|
+
if ReplaceableTextRx.match? text
|
361
|
+
REPLACEMENTS.each do |pattern, replacement, restore|
|
362
|
+
text = text.gsub(pattern) { do_replacement $~, replacement, restore }
|
363
|
+
end
|
377
364
|
end
|
378
365
|
text
|
379
366
|
end
|
367
|
+
|
368
|
+
def sub_specialchars text
|
369
|
+
(text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub SpecialCharsRx, SpecialCharsTr) : text
|
370
|
+
end
|
380
371
|
else
|
381
372
|
# Public: Substitute quoted text (includes emphasis, strong, monospaced, etc)
|
382
373
|
#
|
@@ -384,11 +375,13 @@ module Substitutors
|
|
384
375
|
#
|
385
376
|
# returns The converted String text
|
386
377
|
def sub_quotes text
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
378
|
+
if QuotedTextSniffRx[compat = @document.compat_mode].match? text
|
379
|
+
# NOTE interpolation is faster than String#dup
|
380
|
+
text = %(#{text})
|
381
|
+
QUOTE_SUBS[compat].each do |type, scope, pattern|
|
382
|
+
# NOTE using gsub! here as an MRI Ruby optimization
|
383
|
+
text.gsub!(pattern) { convert_quoted_text $~, type, scope }
|
384
|
+
end
|
392
385
|
end
|
393
386
|
text
|
394
387
|
end
|
@@ -399,30 +392,52 @@ module Substitutors
|
|
399
392
|
#
|
400
393
|
# returns The String text with the replacement characters substituted
|
401
394
|
def sub_replacements text
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
395
|
+
if ReplaceableTextRx.match? text
|
396
|
+
# NOTE interpolation is faster than String#dup
|
397
|
+
text = %(#{text})
|
398
|
+
REPLACEMENTS.each do |pattern, replacement, restore|
|
399
|
+
# NOTE Using gsub! as optimization
|
400
|
+
text.gsub!(pattern) { do_replacement $~, replacement, restore }
|
401
|
+
end
|
407
402
|
end
|
408
403
|
text
|
409
404
|
end
|
405
|
+
|
406
|
+
# Public: Substitute special characters (i.e., encode XML)
|
407
|
+
#
|
408
|
+
# The special characters are <, &, and >, which get replaced with <,
|
409
|
+
# &, and >, respectively.
|
410
|
+
#
|
411
|
+
# text - The String text to process
|
412
|
+
#
|
413
|
+
# returns The String text with special characters replaced
|
414
|
+
if ::RUBY_MIN_VERSION_1_9
|
415
|
+
def sub_specialchars text
|
416
|
+
(text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub! SpecialCharsRx, SpecialCharsTr) : text
|
417
|
+
end
|
418
|
+
else
|
419
|
+
def sub_specialchars text
|
420
|
+
(text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub!(SpecialCharsRx) { SpecialCharsTr[$&] }) : text
|
421
|
+
end
|
422
|
+
end
|
410
423
|
end
|
424
|
+
alias sub_specialcharacters sub_specialchars
|
411
425
|
|
412
426
|
# Internal: Substitute replacement text for matched location
|
413
427
|
#
|
414
428
|
# returns The String text with the replacement characters substituted
|
415
429
|
def do_replacement m, replacement, restore
|
416
|
-
if (
|
417
|
-
|
430
|
+
if (captured = m[0]).include? RS
|
431
|
+
# we have to use sub since we aren't sure it's the first char
|
432
|
+
captured.sub RS, ''
|
418
433
|
else
|
419
434
|
case restore
|
420
435
|
when :none
|
421
436
|
replacement
|
422
|
-
when :leading
|
423
|
-
%(#{m[1]}#{replacement})
|
424
437
|
when :bounding
|
425
438
|
%(#{m[1]}#{replacement}#{m[2]})
|
439
|
+
else # :leading
|
440
|
+
%(#{m[1]}#{replacement})
|
426
441
|
end
|
427
442
|
end
|
428
443
|
end
|
@@ -441,74 +456,52 @@ module Substitutors
|
|
441
456
|
# so that a missing key doesn't wipe out the whole block of data
|
442
457
|
# when attribute-undefined and/or attribute-missing is drop-line
|
443
458
|
def sub_attributes data, opts = {}
|
444
|
-
return data if data.nil_or_empty?
|
445
|
-
|
446
459
|
# normalizes data type to an array (string becomes single-element array)
|
447
|
-
if (
|
448
|
-
|
449
|
-
end
|
450
|
-
|
451
|
-
doc_attrs = @document.attributes
|
452
|
-
attribute_missing = nil
|
453
|
-
result = []
|
460
|
+
data = [data] if (input_is_string = ::String === data)
|
461
|
+
doc_attrs, result = @document.attributes, []
|
454
462
|
data.each do |line|
|
455
|
-
reject = false
|
456
|
-
reject_if_empty = false
|
463
|
+
reject = reject_if_empty = false
|
457
464
|
line = line.gsub(AttributeReferenceRx) {
|
458
|
-
# alias match for Ruby 1.8.7 compat
|
459
|
-
m = $~
|
460
465
|
# escaped attribute, return unescaped
|
461
|
-
if
|
462
|
-
%({#{
|
463
|
-
elsif
|
464
|
-
|
465
|
-
expr = m[2][offset..-1]
|
466
|
-
case directive
|
466
|
+
if $1 == RS || $4 == RS
|
467
|
+
%({#{$2}})
|
468
|
+
elsif $3
|
469
|
+
case (args = $2.split ':', 3).shift
|
467
470
|
when 'set'
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
break ''
|
475
|
-
end
|
476
|
-
end
|
471
|
+
_, value = Parser.store_attribute args[0], args[1] || '', @document
|
472
|
+
# since this is an assignment, only drop-line applies here (skip and drop imply the same result)
|
473
|
+
if (doc_attrs.fetch 'attribute-undefined', Compliance.attribute_undefined) == 'drop-line'
|
474
|
+
reject = true
|
475
|
+
break ''
|
476
|
+
end unless value
|
477
477
|
reject_if_empty = true
|
478
478
|
''
|
479
|
-
when '
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
else
|
486
|
-
val
|
487
|
-
end
|
488
|
-
else
|
489
|
-
# if we get here, our AttributeReference regex is too loose
|
490
|
-
warn %(asciidoctor: WARNING: illegal attribute directive: #{m[3]})
|
491
|
-
m[0]
|
479
|
+
when 'counter2'
|
480
|
+
@document.counter(*args)
|
481
|
+
reject_if_empty = true
|
482
|
+
''
|
483
|
+
else # 'counter'
|
484
|
+
@document.counter(*args)
|
492
485
|
end
|
493
|
-
elsif doc_attrs.key?(key =
|
486
|
+
elsif doc_attrs.key?(key = $2.downcase)
|
494
487
|
doc_attrs[key]
|
495
488
|
elsif INTRINSIC_ATTRIBUTES.key? key
|
496
489
|
INTRINSIC_ATTRIBUTES[key]
|
497
490
|
else
|
498
|
-
case (attribute_missing ||=
|
499
|
-
when '
|
500
|
-
|
491
|
+
case (attribute_missing ||= opts[:attribute_missing] || (doc_attrs.fetch 'attribute-missing', Compliance.attribute_missing))
|
492
|
+
when 'drop'
|
493
|
+
# QUESTION should we warn in this case?
|
494
|
+
reject_if_empty = true
|
495
|
+
''
|
501
496
|
when 'drop-line'
|
502
497
|
warn %(asciidoctor: WARNING: dropping line containing reference to missing attribute: #{key})
|
503
498
|
reject = true
|
504
499
|
break ''
|
505
500
|
when 'warn'
|
506
501
|
warn %(asciidoctor: WARNING: skipping reference to missing attribute: #{key})
|
507
|
-
|
508
|
-
else # '
|
509
|
-
|
510
|
-
reject_if_empty = true
|
511
|
-
''
|
502
|
+
$&
|
503
|
+
else # 'skip'
|
504
|
+
$&
|
512
505
|
end
|
513
506
|
end
|
514
507
|
} if line.include? '{'
|
@@ -516,7 +509,7 @@ module Substitutors
|
|
516
509
|
result << line unless reject || (reject_if_empty && line.empty?)
|
517
510
|
end
|
518
511
|
|
519
|
-
|
512
|
+
input_is_string ? result * LF : result
|
520
513
|
end
|
521
514
|
|
522
515
|
# Public: Substitute inline macros (e.g., links, images, etc)
|
@@ -527,91 +520,80 @@ module Substitutors
|
|
527
520
|
#
|
528
521
|
# returns The converted String text
|
529
522
|
def sub_macros(source)
|
530
|
-
return source if source.nil_or_empty?
|
531
|
-
|
523
|
+
#return source if source.nil_or_empty?
|
532
524
|
# some look ahead assertions to cut unnecessary regex calls
|
533
525
|
found = {}
|
534
|
-
found[:square_bracket] = source.include?
|
535
|
-
|
536
|
-
found[:
|
537
|
-
|
538
|
-
|
539
|
-
use_link_attrs =
|
540
|
-
|
541
|
-
|
542
|
-
# NOTE interpolation is faster than String#dup
|
543
|
-
result = %(#{source})
|
544
|
-
|
545
|
-
if experimental
|
546
|
-
if found[:macroish_short_form] && (result.include?('kbd:') || result.include?('btn:'))
|
547
|
-
result = result.gsub(KbdBtnInlineMacroRx) {
|
548
|
-
# alias match for Ruby 1.8.7 compat
|
549
|
-
m = $~
|
550
|
-
# honor the escape
|
551
|
-
if (captured = m[0]).start_with? '\\'
|
552
|
-
next captured[1..-1]
|
553
|
-
end
|
554
|
-
|
555
|
-
if captured.start_with?('kbd')
|
556
|
-
keys = unescape_bracketed_text m[1]
|
526
|
+
found_square_bracket = found[:square_bracket] = (source.include? '[')
|
527
|
+
found_colon = source.include? ':'
|
528
|
+
found_macroish = found[:macroish] = found_square_bracket && found_colon
|
529
|
+
found_macroish_short = found_macroish && (source.include? ':[')
|
530
|
+
doc_attrs = @document.attributes
|
531
|
+
use_link_attrs = doc_attrs.key? 'linkattrs'
|
532
|
+
result = source
|
557
533
|
|
558
|
-
|
559
|
-
|
534
|
+
if doc_attrs.key? 'experimental'
|
535
|
+
if found_macroish_short && ((result.include? 'kbd:') || (result.include? 'btn:'))
|
536
|
+
result = result.gsub(InlineKbdBtnMacroRx) {
|
537
|
+
# honor the escape
|
538
|
+
if $1
|
539
|
+
$&.slice 1, $&.length
|
540
|
+
elsif $2 == 'kbd'
|
541
|
+
if (keys = $3.strip).include? R_SB
|
542
|
+
keys = keys.gsub ESC_R_SB, R_SB
|
543
|
+
end
|
544
|
+
if keys.length > 1 && (delim_idx = (delim_idx = keys.index ',', 1) ?
|
545
|
+
[delim_idx, (keys.index '+', 1)].compact.min : (keys.index '+', 1))
|
546
|
+
delim = keys.slice delim_idx, 1
|
547
|
+
# NOTE handle special case where keys ends with delimiter (e.g., Ctrl++ or Ctrl,,)
|
548
|
+
if keys.end_with? delim
|
549
|
+
keys = (keys.chop.split delim, -1).map {|key| key.strip }
|
550
|
+
keys[-1] = %(#{keys[-1]}#{delim})
|
551
|
+
else
|
552
|
+
keys = keys.split(delim).map {|key| key.strip }
|
553
|
+
end
|
560
554
|
else
|
561
|
-
|
562
|
-
keys = keys.split(KbdDelimiterRx).inject([]) {|c, key|
|
563
|
-
if key.end_with?('++')
|
564
|
-
c << key[0..-3].strip
|
565
|
-
c << '+'
|
566
|
-
else
|
567
|
-
c << key.strip
|
568
|
-
end
|
569
|
-
c
|
570
|
-
}
|
555
|
+
keys = [keys]
|
571
556
|
end
|
572
|
-
Inline.new
|
573
|
-
|
574
|
-
|
575
|
-
Inline.new(self, :button, label).convert
|
557
|
+
(Inline.new self, :kbd, nil, :attributes => { 'keys' => keys }).convert
|
558
|
+
else # $2 == 'btn'
|
559
|
+
(Inline.new self, :button, (unescape_bracketed_text $3)).convert
|
576
560
|
end
|
577
561
|
}
|
578
562
|
end
|
579
563
|
|
580
|
-
if
|
581
|
-
result = result.gsub(
|
564
|
+
if found_macroish && (result.include? 'menu:')
|
565
|
+
result = result.gsub(InlineMenuMacroRx) {
|
582
566
|
# alias match for Ruby 1.8.7 compat
|
583
567
|
m = $~
|
584
568
|
# honor the escape
|
585
|
-
if (captured = m[0]).start_with?
|
569
|
+
if (captured = m[0]).start_with? RS
|
586
570
|
next captured[1..-1]
|
587
571
|
end
|
588
572
|
|
589
|
-
menu = m[1]
|
590
|
-
items = m[2]
|
573
|
+
menu, items = m[1], m[2]
|
591
574
|
|
592
|
-
if
|
593
|
-
|
594
|
-
menuitem = nil
|
595
|
-
else
|
575
|
+
if items
|
576
|
+
items = items.gsub ESC_R_SB, R_SB if items.include? R_SB
|
596
577
|
if (delim = items.include?('>') ? '>' : (items.include?(',') ? ',' : nil))
|
597
578
|
submenus = items.split(delim).map {|it| it.strip }
|
598
579
|
menuitem = submenus.pop
|
599
580
|
else
|
600
|
-
submenus = []
|
601
|
-
menuitem = items.rstrip
|
581
|
+
submenus, menuitem = [], items.rstrip
|
602
582
|
end
|
583
|
+
else
|
584
|
+
submenus, menuitem = [], nil
|
603
585
|
end
|
604
586
|
|
605
587
|
Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).convert
|
606
588
|
}
|
607
589
|
end
|
608
590
|
|
609
|
-
if result.include?
|
591
|
+
if (result.include? '"') && (result.include? '>')
|
610
592
|
result = result.gsub(MenuInlineRx) {
|
611
593
|
# alias match for Ruby 1.8.7 compat
|
612
594
|
m = $~
|
613
595
|
# honor the escape
|
614
|
-
if (captured = m[0]).start_with?
|
596
|
+
if (captured = m[0]).start_with? RS
|
615
597
|
next captured[1..-1]
|
616
598
|
end
|
617
599
|
|
@@ -626,114 +608,117 @@ module Substitutors
|
|
626
608
|
|
627
609
|
# FIXME this location is somewhat arbitrary, probably need to be able to control ordering
|
628
610
|
# TODO this handling needs some cleanup
|
629
|
-
if (extensions = @document.extensions) && extensions.inline_macros? # &&
|
611
|
+
if (extensions = @document.extensions) && extensions.inline_macros? # && found_macroish
|
630
612
|
extensions.inline_macros.each do |extension|
|
631
613
|
result = result.gsub(extension.instance.regexp) {
|
632
614
|
# alias match for Ruby 1.8.7 compat
|
633
615
|
m = $~
|
634
616
|
# honor the escape
|
635
|
-
if m[0].start_with?
|
617
|
+
if m[0].start_with? RS
|
636
618
|
next m[0][1..-1]
|
637
619
|
end
|
638
620
|
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
621
|
+
if (m.names rescue []).empty?
|
622
|
+
target, content, extconf = m[1], m[2], extension.config
|
623
|
+
else
|
624
|
+
target, content, extconf = (m[:target] rescue nil), (m[:content] rescue nil), extension.config
|
625
|
+
end
|
626
|
+
attributes = (attributes = extconf[:default_attrs]) ? attributes.dup : {}
|
627
|
+
if content.nil_or_empty?
|
628
|
+
attributes['text'] = content if content && extconf[:content_model] != :attributes
|
644
629
|
else
|
645
|
-
|
646
|
-
|
630
|
+
content = unescape_bracketed_text content
|
631
|
+
if extconf[:content_model] == :attributes
|
632
|
+
# QUESTION should we store the text in the _text key?
|
633
|
+
# QUESTION why is the sub_result option false? why isn't the unescape_input option true?
|
634
|
+
parse_attributes content, extconf[:pos_attrs] || [], :sub_result => false, :into => attributes
|
647
635
|
else
|
648
|
-
|
636
|
+
attributes['text'] = content
|
649
637
|
end
|
650
638
|
end
|
651
|
-
|
639
|
+
# NOTE use content if target is not set (short form only); deprecated - remove in 1.6.0
|
640
|
+
replacement = extension.process_method[self, target || content, attributes]
|
641
|
+
Inline === replacement ? replacement.convert : replacement
|
652
642
|
}
|
653
643
|
end
|
654
644
|
end
|
655
645
|
|
656
|
-
if
|
646
|
+
if found_macroish && ((result.include? 'image:') || (result.include? 'icon:'))
|
657
647
|
# image:filename.png[Alt Text]
|
658
|
-
result = result.gsub(
|
648
|
+
result = result.gsub(InlineImageMacroRx) {
|
659
649
|
# alias match for Ruby 1.8.7 compat
|
660
650
|
m = $~
|
661
651
|
# honor the escape
|
662
|
-
if m[0].start_with?
|
663
|
-
next
|
652
|
+
if (captured = m[0]).start_with? RS
|
653
|
+
next captured[1..-1]
|
664
654
|
end
|
665
655
|
|
666
|
-
|
667
|
-
|
668
|
-
type = 'icon'
|
669
|
-
posattrs = ['size']
|
656
|
+
if captured.start_with? 'icon:'
|
657
|
+
type, posattrs = 'icon', ['size']
|
670
658
|
else
|
671
|
-
type = 'image'
|
672
|
-
posattrs = ['alt', 'width', 'height']
|
659
|
+
type, posattrs = 'image', ['alt', 'width', 'height']
|
673
660
|
end
|
674
|
-
target =
|
675
|
-
unless type == 'icon'
|
676
|
-
|
677
|
-
|
678
|
-
attrs = parse_attributes(raw_attrs, posattrs)
|
679
|
-
attrs['alt'] ||= Helpers.basename(target, true).tr('_-', ' ')
|
661
|
+
target = m[1]
|
662
|
+
@document.register(:images, target) unless type == 'icon'
|
663
|
+
attrs = parse_attributes(m[2], posattrs, :unescape_input => true)
|
664
|
+
attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
|
680
665
|
Inline.new(self, :image, nil, :type => type, :target => target, :attributes => attrs).convert
|
681
666
|
}
|
682
667
|
end
|
683
668
|
|
684
|
-
if
|
685
|
-
|
669
|
+
if ((result.include? '((') && (result.include? '))')) ||
|
670
|
+
(found_macroish_short && (result.include? 'indexterm'))
|
686
671
|
# (((Tigers,Big cats)))
|
687
|
-
#
|
672
|
+
# indexterm:[Tigers,Big cats]
|
688
673
|
# ((Tigers))
|
689
|
-
|
674
|
+
# indexterm2:[Tigers]
|
675
|
+
result = result.gsub(InlineIndextermMacroRx) {
|
690
676
|
# alias match for Ruby 1.8.7 compat
|
691
677
|
m = $~
|
692
678
|
|
693
679
|
# honor the escape
|
694
|
-
if m[0].start_with?
|
680
|
+
if m[0].start_with? RS
|
695
681
|
next m[0][1..-1]
|
696
682
|
end
|
697
683
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
num_brackets = 3
|
710
|
-
else
|
711
|
-
num_brackets = 2
|
712
|
-
end
|
713
|
-
end
|
714
|
-
|
715
|
-
# non-visible
|
716
|
-
if macro_name == 'indexterm' || num_brackets == 3
|
717
|
-
if !macro_name
|
718
|
-
# (((Tigers,Big cats)))
|
719
|
-
terms = split_simple_csv normalize_string(text_in_brackets)
|
720
|
-
else
|
721
|
-
# indexterm:[Tigers,Big cats]
|
722
|
-
terms = split_simple_csv normalize_string(m[2], true)
|
723
|
-
end
|
724
|
-
@document.register(:indexterms, [*terms])
|
725
|
-
Inline.new(self, :indexterm, nil, :attributes => {'terms' => terms}).convert
|
726
|
-
# visible
|
684
|
+
case m[1]
|
685
|
+
when 'indexterm'
|
686
|
+
# indexterm:[Tigers,Big cats]
|
687
|
+
terms = split_simple_csv(normalize_string m[2], true)
|
688
|
+
@document.register :indexterms, terms
|
689
|
+
(Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
|
690
|
+
when 'indexterm2'
|
691
|
+
# indexterm2:[Tigers]
|
692
|
+
term = normalize_string m[2], true
|
693
|
+
@document.register :indexterms, [term]
|
694
|
+
(Inline.new self, :indexterm, term, :type => :visible).convert
|
727
695
|
else
|
728
|
-
|
696
|
+
text, visible, before, after = m[3], true, nil, nil
|
697
|
+
if text.start_with? '('
|
698
|
+
if text.end_with? ')'
|
699
|
+
text, visible = (text.slice 1, text.length - 2), false
|
700
|
+
else
|
701
|
+
text, before, after = (text.slice 1, text.length - 1), '(', ''
|
702
|
+
end
|
703
|
+
elsif text.end_with? ')'
|
704
|
+
if text.start_with? '('
|
705
|
+
text, visible = (text.slice 1, text.length - 2), false
|
706
|
+
else
|
707
|
+
text, before, after = (text.slice 0, text.length - 1), '', ')'
|
708
|
+
end
|
709
|
+
end
|
710
|
+
if visible
|
729
711
|
# ((Tigers))
|
730
|
-
|
712
|
+
term = normalize_string text
|
713
|
+
@document.register :indexterms, [term]
|
714
|
+
result = (Inline.new self, :indexterm, term, :type => :visible).convert
|
731
715
|
else
|
732
|
-
#
|
733
|
-
|
716
|
+
# (((Tigers,Big cats)))
|
717
|
+
terms = split_simple_csv(normalize_string text)
|
718
|
+
@document.register :indexterms, terms
|
719
|
+
result = (Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
|
734
720
|
end
|
735
|
-
|
736
|
-
Inline.new(self, :indexterm, text, :type => :visible).convert
|
721
|
+
before ? %(#{before}#{result}#{after}) : result
|
737
722
|
end
|
738
723
|
}
|
739
724
|
end
|
@@ -744,71 +729,65 @@ module Substitutors
|
|
744
729
|
# alias match for Ruby 1.8.7 compat
|
745
730
|
m = $~
|
746
731
|
# honor the escape
|
747
|
-
if m[2].start_with?
|
748
|
-
|
749
|
-
next "#{m[1]}#{m[2][1..-1]}#{m[3]}"
|
732
|
+
if m[2].start_with? RS
|
733
|
+
next %(#{m[1]}#{m[2][1..-1]}#{m[3]})
|
750
734
|
end
|
751
|
-
#
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
735
|
+
# NOTE if text is non-nil, then we've matched a formal macro (i.e., trailing square brackets)
|
736
|
+
prefix, target, text, suffix = m[1], m[2], (macro = m[3]) || '', ''
|
737
|
+
if prefix == 'link:'
|
738
|
+
if macro
|
739
|
+
prefix = ''
|
740
|
+
else
|
741
|
+
# invalid macro syntax (link: prefix w/o trailing square brackets)
|
742
|
+
# we probably shouldn't even get here...our regex is doing too much
|
743
|
+
next m[0]
|
744
|
+
end
|
759
745
|
end
|
760
|
-
|
761
|
-
|
762
|
-
suffix = ''
|
763
|
-
unless m[3] || target !~ UriTerminator
|
764
|
-
case $~[0]
|
746
|
+
unless macro || UriTerminatorRx !~ target
|
747
|
+
case $&
|
765
748
|
when ')'
|
766
|
-
# strip
|
767
|
-
target = target
|
749
|
+
# strip trailing )
|
750
|
+
target = target.chop
|
768
751
|
suffix = ')'
|
769
752
|
when ';'
|
770
|
-
# strip
|
753
|
+
# strip <> around URI
|
771
754
|
if prefix.start_with?('<') && target.end_with?('>')
|
772
755
|
prefix = prefix[4..-1]
|
773
|
-
target = target[0
|
774
|
-
# strip the ); from the end of the link
|
775
|
-
elsif target.end_with?(');')
|
776
|
-
target = target[0..-3]
|
777
|
-
suffix = ');'
|
756
|
+
target = target[0...-4]
|
778
757
|
else
|
779
|
-
|
780
|
-
|
758
|
+
# strip trailing ;
|
759
|
+
# check for trailing );
|
760
|
+
if (target = target.chop).end_with?(')')
|
761
|
+
target = target.chop
|
762
|
+
suffix = ');'
|
763
|
+
else
|
764
|
+
suffix = ';'
|
765
|
+
end
|
781
766
|
end
|
782
767
|
when ':'
|
783
|
-
# strip
|
784
|
-
|
785
|
-
|
768
|
+
# strip trailing :
|
769
|
+
# check for trailing ):
|
770
|
+
if (target = target.chop).end_with?(')')
|
771
|
+
target = target.chop
|
786
772
|
suffix = '):'
|
787
773
|
else
|
788
|
-
target = target[0..-2]
|
789
774
|
suffix = ':'
|
790
775
|
end
|
791
776
|
end
|
792
777
|
end
|
793
|
-
@document.register(:links, target)
|
794
778
|
|
795
|
-
link_opts = { :type => :link
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
if use_link_attrs && (m[3].start_with?('"') || (m[3].include?(',') && m[3].include?('=')))
|
802
|
-
attrs = parse_attributes(sub_attributes(m[3].gsub('\]', ']')), [])
|
803
|
-
link_opts[:id] = (attrs.delete 'id') if attrs.has_key? 'id'
|
779
|
+
attrs, link_opts = nil, { :type => :link }
|
780
|
+
unless text.empty?
|
781
|
+
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
|
782
|
+
if use_link_attrs && ((text.start_with? '"') || ((text.include? ',') && (text.include? '=')))
|
783
|
+
attrs = parse_attributes text, []
|
784
|
+
link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
|
804
785
|
text = attrs[1] || ''
|
805
|
-
else
|
806
|
-
text = sub_attributes(m[3].gsub('\]', ']'))
|
807
786
|
end
|
808
787
|
|
809
|
-
# TODO enable in Asciidoctor 1.
|
788
|
+
# TODO enable in Asciidoctor 1.6.x
|
810
789
|
# support pipe-separated text and title
|
811
|
-
#unless attrs && (attrs.
|
790
|
+
#unless attrs && (attrs.key? 'title')
|
812
791
|
# if text.include? '|'
|
813
792
|
# attrs ||= {}
|
814
793
|
# text, attrs['title'] = text.split '|', 2
|
@@ -820,120 +799,99 @@ module Substitutors
|
|
820
799
|
if attrs
|
821
800
|
attrs['window'] ||= '_blank'
|
822
801
|
else
|
823
|
-
attrs = {'window' => '_blank'}
|
802
|
+
attrs = { 'window' => '_blank' }
|
824
803
|
end
|
825
804
|
end
|
826
805
|
end
|
827
806
|
|
828
807
|
if text.empty?
|
829
|
-
|
830
|
-
text = target.sub UriSniffRx, ''
|
831
|
-
else
|
832
|
-
text = target
|
833
|
-
end
|
834
|
-
|
808
|
+
text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
|
835
809
|
if attrs
|
836
|
-
attrs['role'] = %(bare #{attrs['role']})
|
810
|
+
attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
|
837
811
|
else
|
838
|
-
attrs = {'role' => 'bare'}
|
812
|
+
attrs = { 'role' => 'bare' }
|
839
813
|
end
|
840
814
|
end
|
841
815
|
|
816
|
+
@document.register :links, (link_opts[:target] = target)
|
842
817
|
link_opts[:attributes] = attrs if attrs
|
843
818
|
%(#{prefix}#{Inline.new(self, :anchor, text, link_opts).convert}#{suffix})
|
844
819
|
}
|
845
820
|
end
|
846
821
|
|
847
|
-
if
|
822
|
+
if found_macroish && ((result.include? 'link:') || (result.include? 'mailto:'))
|
848
823
|
# inline link macros, link:target[text]
|
849
|
-
result = result.gsub(
|
824
|
+
result = result.gsub(InlineLinkMacroRx) {
|
850
825
|
# alias match for Ruby 1.8.7 compat
|
851
826
|
m = $~
|
852
827
|
# honor the escape
|
853
|
-
if m[0].start_with?
|
828
|
+
if m[0].start_with? RS
|
854
829
|
next m[0][1..-1]
|
855
830
|
end
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
if attrs.key? 3
|
871
|
-
target = link_opts[:target] = "#{target}&body=#{Helpers.encode_uri(attrs[3])}"
|
831
|
+
target = (mailto = m[1]) ? %(mailto:#{m[2]}) : m[2]
|
832
|
+
attrs, link_opts = nil, { :type => :link }
|
833
|
+
unless (text = m[3]).empty?
|
834
|
+
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
|
835
|
+
if use_link_attrs && ((text.start_with? '"') || ((text.include? ',') && (mailto || (text.include? '='))))
|
836
|
+
attrs = parse_attributes text, []
|
837
|
+
link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
|
838
|
+
if mailto
|
839
|
+
if attrs.key? 2
|
840
|
+
if attrs.key? 3
|
841
|
+
target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]}&body=#{Helpers.uri_encode attrs[3]})
|
842
|
+
else
|
843
|
+
target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]})
|
844
|
+
end
|
872
845
|
end
|
873
846
|
end
|
847
|
+
text = attrs[1] || ''
|
874
848
|
end
|
875
|
-
attrs[1]
|
876
|
-
else
|
877
|
-
sub_attributes(m[2].gsub('\]', ']'))
|
878
|
-
end
|
879
849
|
|
880
|
-
|
881
|
-
|
850
|
+
# TODO enable in Asciidoctor 1.6.x
|
851
|
+
# support pipe-separated text and title
|
852
|
+
#unless attrs && (attrs.key? 'title')
|
853
|
+
# if text.include? '|'
|
854
|
+
# attrs ||= {}
|
855
|
+
# text, attrs['title'] = text.split '|', 2
|
856
|
+
# end
|
857
|
+
#end
|
882
858
|
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
#end
|
891
|
-
|
892
|
-
if text.end_with? '^'
|
893
|
-
text = text.chop
|
894
|
-
if attrs
|
895
|
-
attrs['window'] ||= '_blank'
|
896
|
-
else
|
897
|
-
attrs = {'window' => '_blank'}
|
859
|
+
if text.end_with? '^'
|
860
|
+
text = text.chop
|
861
|
+
if attrs
|
862
|
+
attrs['window'] ||= '_blank'
|
863
|
+
else
|
864
|
+
attrs = { 'window' => '_blank' }
|
865
|
+
end
|
898
866
|
end
|
899
867
|
end
|
900
868
|
|
901
869
|
if text.empty?
|
902
870
|
# mailto is a special case, already processed
|
903
871
|
if mailto
|
904
|
-
text =
|
872
|
+
text = m[2]
|
905
873
|
else
|
906
|
-
|
907
|
-
text = raw_target.sub UriSniffRx, ''
|
908
|
-
else
|
909
|
-
text = raw_target
|
910
|
-
end
|
911
|
-
|
874
|
+
text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
|
912
875
|
if attrs
|
913
|
-
attrs['role'] = %(bare #{attrs['role']})
|
876
|
+
attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
|
914
877
|
else
|
915
|
-
attrs = {'role' => 'bare'}
|
878
|
+
attrs = { 'role' => 'bare' }
|
916
879
|
end
|
917
880
|
end
|
918
881
|
end
|
919
882
|
|
883
|
+
# QUESTION should a mailto be registered as an e-mail address?
|
884
|
+
@document.register :links, (link_opts[:target] = target)
|
920
885
|
link_opts[:attributes] = attrs if attrs
|
921
886
|
Inline.new(self, :anchor, text, link_opts).convert
|
922
887
|
}
|
923
888
|
end
|
924
889
|
|
925
890
|
if result.include? '@'
|
926
|
-
result = result.gsub(
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
if (lead = m[1])
|
931
|
-
case lead
|
932
|
-
when '\\'
|
933
|
-
next address[1..-1]
|
934
|
-
else
|
935
|
-
next address
|
936
|
-
end
|
891
|
+
result = result.gsub(EmailInlineRx) {
|
892
|
+
address, tip = $&, $1
|
893
|
+
if tip
|
894
|
+
next (tip == RS ? address[1..-1] : address)
|
937
895
|
end
|
938
896
|
|
939
897
|
target = %(mailto:#{address})
|
@@ -944,12 +902,12 @@ module Substitutors
|
|
944
902
|
}
|
945
903
|
end
|
946
904
|
|
947
|
-
if
|
948
|
-
result = result.gsub(
|
905
|
+
if found_macroish_short && (result.include? 'footnote')
|
906
|
+
result = result.gsub(InlineFootnoteMacroRx) {
|
949
907
|
# alias match for Ruby 1.8.7 compat
|
950
908
|
m = $~
|
951
909
|
# honor the escape
|
952
|
-
if m[0].start_with?
|
910
|
+
if m[0].start_with? RS
|
953
911
|
next m[0][1..-1]
|
954
912
|
end
|
955
913
|
if m[1] == 'footnote'
|
@@ -963,9 +921,15 @@ module Substitutors
|
|
963
921
|
else
|
964
922
|
id, text = m[2].split(',', 2)
|
965
923
|
id = id.strip
|
966
|
-
|
967
|
-
|
968
|
-
|
924
|
+
if text
|
925
|
+
# REVIEW it's a dirty job, but somebody's gotta do it
|
926
|
+
text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
|
927
|
+
index = @document.counter('footnote-number')
|
928
|
+
@document.register(:footnotes, Document::Footnote.new(index, id, text))
|
929
|
+
type = :ref
|
930
|
+
target = nil
|
931
|
+
else
|
932
|
+
if (footnote = @document.footnotes.find {|fn| fn.id == id })
|
969
933
|
index = footnote.index
|
970
934
|
text = footnote.text
|
971
935
|
else
|
@@ -975,13 +939,6 @@ module Substitutors
|
|
975
939
|
target = id
|
976
940
|
id = nil
|
977
941
|
type = :xref
|
978
|
-
else
|
979
|
-
# REVIEW it's a dirty job, but somebody's gotta do it
|
980
|
-
text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
|
981
|
-
index = @document.counter('footnote-number')
|
982
|
-
@document.register(:footnotes, Document::Footnote.new(index, id, text))
|
983
|
-
type = :ref
|
984
|
-
target = nil
|
985
942
|
end
|
986
943
|
end
|
987
944
|
Inline.new(self, :footnote, text, :attributes => {'index' => index}, :id => id, :target => target, :type => type).convert
|
@@ -993,44 +950,29 @@ module Substitutors
|
|
993
950
|
|
994
951
|
# Internal: Substitute normal and bibliographic anchors
|
995
952
|
def sub_inline_anchors(text, found = nil)
|
996
|
-
if
|
997
|
-
text = text.
|
998
|
-
#
|
999
|
-
|
1000
|
-
# honor the escape
|
1001
|
-
if m[0].start_with? '\\'
|
1002
|
-
next m[0][1..-1]
|
1003
|
-
end
|
1004
|
-
id = reftext = m[1]
|
1005
|
-
Inline.new(self, :anchor, reftext, :type => :bibref, :target => id).convert
|
953
|
+
if @context == :list_item && @parent.style == 'bibliography'
|
954
|
+
text = text.sub(InlineBiblioAnchorRx) {
|
955
|
+
# NOTE target property on :bibref is deprecated
|
956
|
+
Inline.new(self, :anchor, %([#{$2 || $1}]), :type => :bibref, :id => $1, :target => $1).convert
|
1006
957
|
}
|
1007
958
|
end
|
1008
959
|
|
1009
960
|
if ((!found || found[:square_bracket]) && text.include?('[[')) ||
|
1010
|
-
((!found || found[:macroish]) && text.include?('
|
961
|
+
((!found || found[:macroish]) && text.include?('or:'))
|
1011
962
|
text = text.gsub(InlineAnchorRx) {
|
1012
|
-
# alias match for Ruby 1.8.7 compat
|
1013
|
-
m = $~
|
1014
963
|
# honor the escape
|
1015
|
-
if
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
964
|
+
next $&.slice 1, $&.length if $1
|
965
|
+
# NOTE reftext is only relevant for DocBook output; used as value of xreflabel attribute
|
966
|
+
if (id = $2)
|
967
|
+
reftext = $3
|
968
|
+
else
|
969
|
+
id = $4
|
970
|
+
if (reftext = $5) && (reftext.include? R_SB)
|
971
|
+
reftext = reftext.gsub ESC_R_SB, R_SB
|
972
|
+
end
|
1023
973
|
end
|
1024
|
-
|
1025
|
-
reftext
|
1026
|
-
# enable if we want to allow double quoted values
|
1027
|
-
#id = id.sub(DoubleQuotedRx, '\2')
|
1028
|
-
#if reftext
|
1029
|
-
# reftext = reftext.sub(DoubleQuotedMultiRx, '\2')
|
1030
|
-
#else
|
1031
|
-
# reftext = "[#{id}]"
|
1032
|
-
#end
|
1033
|
-
Inline.new(self, :anchor, reftext, :type => :ref, :target => id).convert
|
974
|
+
# NOTE target property on :ref is deprecated
|
975
|
+
Inline.new(self, :anchor, reftext, :type => :ref, :id => id, :target => id).convert
|
1034
976
|
}
|
1035
977
|
end
|
1036
978
|
|
@@ -1039,65 +981,67 @@ module Substitutors
|
|
1039
981
|
|
1040
982
|
# Internal: Substitute cross reference links
|
1041
983
|
def sub_inline_xrefs(text, found = nil)
|
1042
|
-
if (
|
1043
|
-
|
984
|
+
if ((found ? found[:macroish] : (text.include? '[')) && (text.include? 'xref:')) ||
|
985
|
+
((text.include? '&') && (text.include? '<<'))
|
986
|
+
text = text.gsub(InlineXrefMacroRx) {
|
1044
987
|
# alias match for Ruby 1.8.7 compat
|
1045
988
|
m = $~
|
1046
989
|
# honor the escape
|
1047
|
-
if m[0].start_with?
|
990
|
+
if m[0].start_with? RS
|
1048
991
|
next m[0][1..-1]
|
1049
992
|
end
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
end
|
1054
|
-
if m[1]
|
1055
|
-
id, reftext = m[1].split(',', 2).map {|it| it.strip }
|
1056
|
-
id = id.sub(DoubleQuotedRx, '\2')
|
1057
|
-
# NOTE In Opal, reftext is set to empty string if comma is missing
|
1058
|
-
reftext = if reftext.nil_or_empty?
|
1059
|
-
nil
|
1060
|
-
else
|
1061
|
-
reftext.sub(DoubleQuotedMultiRx, '\2')
|
1062
|
-
end
|
993
|
+
if (id = m[1])
|
994
|
+
id, reftext = id.split ',', 2
|
995
|
+
reftext = reftext.lstrip if reftext
|
1063
996
|
else
|
1064
997
|
id = m[2]
|
1065
|
-
reftext = m[3]
|
998
|
+
if (reftext = m[3]) && (reftext.include? R_SB)
|
999
|
+
reftext = reftext.gsub ESC_R_SB, R_SB
|
1000
|
+
end
|
1066
1001
|
end
|
1067
1002
|
|
1068
|
-
if id.
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1003
|
+
if (hash_idx = id.index '#')
|
1004
|
+
if hash_idx > 0
|
1005
|
+
if (fragment_len = id.length - hash_idx - 1) > 0
|
1006
|
+
path, fragment = (id.slice 0, hash_idx), (id.slice hash_idx + 1, fragment_len)
|
1007
|
+
else
|
1008
|
+
path, fragment = (id.slice 0, hash_idx), nil
|
1009
|
+
end
|
1010
|
+
else
|
1011
|
+
target, path, fragment = id, nil, (id.slice 1, id.length)
|
1012
|
+
end
|
1073
1013
|
else
|
1074
|
-
path = nil
|
1075
|
-
fragment = id
|
1014
|
+
path, fragment = nil, id
|
1076
1015
|
end
|
1077
1016
|
|
1078
|
-
# handles
|
1079
|
-
if
|
1080
|
-
|
1017
|
+
# handles: #id
|
1018
|
+
if target
|
1019
|
+
refid = fragment
|
1020
|
+
# handles: path#, path.adoc#, path#id, or path.adoc#id
|
1021
|
+
elsif path
|
1022
|
+
if (ext_idx = path.rindex '.') && ASCIIDOC_EXTENSIONS[path.slice ext_idx, path.length]
|
1023
|
+
path = path.slice 0, ext_idx
|
1024
|
+
end
|
1081
1025
|
# the referenced path is this document, or its contents has been included in this document
|
1082
|
-
if @document.attributes['docname'] == path || @document.
|
1083
|
-
refid = fragment
|
1084
|
-
path = nil
|
1085
|
-
target = %(##{fragment})
|
1026
|
+
if @document.attributes['docname'] == path || @document.catalog[:includes].include?(path)
|
1027
|
+
refid, path, target = fragment, nil, %(##{fragment})
|
1086
1028
|
else
|
1087
1029
|
refid = fragment ? %(#{path}##{fragment}) : path
|
1088
|
-
path =
|
1030
|
+
path = %(#{@document.attributes['relfileprefix']}#{path}#{@document.attributes.fetch 'outfilesuffix', '.html'})
|
1089
1031
|
target = fragment ? %(#{path}##{fragment}) : path
|
1090
1032
|
end
|
1091
|
-
# handles
|
1033
|
+
# handles: id or Section Title
|
1092
1034
|
else
|
1093
|
-
# resolve fragment as reftext if
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1035
|
+
# resolve fragment as reftext if it's not a known ID and resembles reftext (includes space or has uppercase char)
|
1036
|
+
unless @document.catalog[:ids].key? fragment
|
1037
|
+
if ((fragment.include? ' ') || fragment.downcase != fragment) &&
|
1038
|
+
(resolved_id = @document.catalog[:ids].key fragment)
|
1039
|
+
fragment = resolved_id
|
1040
|
+
elsif $VERBOSE
|
1041
|
+
warn %(asciidoctor: WARNING: invalid reference: #{fragment})
|
1042
|
+
end
|
1098
1043
|
end
|
1099
|
-
refid = fragment
|
1100
|
-
target = %(##{fragment})
|
1044
|
+
refid, target = fragment, %(##{fragment})
|
1101
1045
|
end
|
1102
1046
|
Inline.new(self, :anchor, reftext, :type => :xref, :target => target, :attributes => {'path' => path, 'fragment' => fragment, 'refid' => refid}).convert
|
1103
1047
|
}
|
@@ -1115,14 +1059,11 @@ module Substitutors
|
|
1115
1059
|
# FIXME cache this dynamic regex
|
1116
1060
|
callout_rx = (attr? 'line-comment') ? /(?:#{::Regexp.escape(attr 'line-comment')} )?#{CalloutSourceRxt}/ : CalloutSourceRx
|
1117
1061
|
text.gsub(callout_rx) {
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
if m[1] == '\\'
|
1122
|
-
# we have to do a sub since we aren't sure it's the first char
|
1123
|
-
next m[0].sub('\\', '')
|
1062
|
+
if $1
|
1063
|
+
# we have to use sub since we aren't sure it's the first char
|
1064
|
+
next $&.sub(RS, '')
|
1124
1065
|
end
|
1125
|
-
Inline.new(self, :callout,
|
1066
|
+
Inline.new(self, :callout, $3, :id => @document.callouts.read_next_id).convert
|
1126
1067
|
}
|
1127
1068
|
end
|
1128
1069
|
|
@@ -1132,13 +1073,15 @@ module Substitutors
|
|
1132
1073
|
#
|
1133
1074
|
# Returns the converted String text
|
1134
1075
|
def sub_post_replacements(text)
|
1135
|
-
if (@document.attributes.
|
1136
|
-
lines =
|
1137
|
-
return text if lines.size
|
1076
|
+
if (@document.attributes.key? 'hardbreaks') || (@attributes.key? 'hardbreaks-option')
|
1077
|
+
lines = text.split LF, -1
|
1078
|
+
return text if lines.size < 2
|
1138
1079
|
last = lines.pop
|
1139
|
-
lines.map {|line|
|
1140
|
-
|
1141
|
-
|
1080
|
+
(lines.map {|line|
|
1081
|
+
Inline.new(self, :break, (line.end_with? HARD_LINE_BREAK) ? (line.slice 0, line.length - 2) : line, :type => :line).convert
|
1082
|
+
} << last) * LF
|
1083
|
+
elsif (text.include? PLUS) && (text.include? HARD_LINE_BREAK)
|
1084
|
+
text.gsub(HardLineBreakRx) { Inline.new(self, :break, $1, :type => :line).convert }
|
1142
1085
|
else
|
1143
1086
|
text
|
1144
1087
|
end
|
@@ -1152,8 +1095,7 @@ module Substitutors
|
|
1152
1095
|
#
|
1153
1096
|
# Returns The converted String text for the quoted text region
|
1154
1097
|
def convert_quoted_text(match, type, scope)
|
1155
|
-
|
1156
|
-
if match[0].start_with? '\\'
|
1098
|
+
if match[0].start_with? RS
|
1157
1099
|
if scope == :constrained && !(attrs = match[2]).nil_or_empty?
|
1158
1100
|
unescaped_attrs = %([#{attrs}])
|
1159
1101
|
else
|
@@ -1165,44 +1107,39 @@ module Substitutors
|
|
1165
1107
|
if unescaped_attrs
|
1166
1108
|
%(#{unescaped_attrs}#{Inline.new(self, :quoted, match[3], :type => type).convert})
|
1167
1109
|
else
|
1168
|
-
if (
|
1169
|
-
id = attributes.delete 'id'
|
1110
|
+
if (attrlist = match[2])
|
1111
|
+
id = (attributes = parse_quoted_text_attributes attrlist).delete 'id'
|
1170
1112
|
type = :unquoted if type == :mark
|
1171
|
-
else
|
1172
|
-
id = nil
|
1173
1113
|
end
|
1174
1114
|
%(#{match[1]}#{Inline.new(self, :quoted, match[3], :type => type, :id => id, :attributes => attributes).convert})
|
1175
1115
|
end
|
1176
1116
|
else
|
1177
|
-
if (
|
1178
|
-
id = attributes.delete 'id'
|
1117
|
+
if (attrlist = match[1])
|
1118
|
+
id = (attributes = parse_quoted_text_attributes attrlist).delete 'id'
|
1179
1119
|
type = :unquoted if type == :mark
|
1180
|
-
else
|
1181
|
-
id = nil
|
1182
1120
|
end
|
1183
1121
|
Inline.new(self, :quoted, match[2], :type => type, :id => id, :attributes => attributes).convert
|
1184
1122
|
end
|
1185
1123
|
end
|
1186
1124
|
|
1187
|
-
# Internal: Parse the attributes that are defined on quoted text
|
1125
|
+
# Internal: Parse the attributes that are defined on quoted (aka formatted) text
|
1188
1126
|
#
|
1189
|
-
# str
|
1127
|
+
# str - A non-nil String of unprocessed attributes;
|
1128
|
+
# space-separated roles (e.g., role1 role2) or the id/role shorthand syntax (e.g., #idname.role)
|
1190
1129
|
#
|
1191
|
-
#
|
1192
|
-
def parse_quoted_text_attributes
|
1193
|
-
|
1194
|
-
|
1195
|
-
str = sub_attributes(str) if str.include?('{')
|
1196
|
-
str = str.strip
|
1130
|
+
# Returns a Hash of attributes (role and id only)
|
1131
|
+
def parse_quoted_text_attributes str
|
1132
|
+
# NOTE attributes are typically resolved after quoted text, so substitute eagerly
|
1133
|
+
str = sub_attributes str if str.include? '{'
|
1197
1134
|
# for compliance, only consider first positional attribute
|
1198
|
-
str
|
1135
|
+
str = str.slice 0, (str.index ',') if str.include? ','
|
1199
1136
|
|
1200
|
-
if str.empty?
|
1137
|
+
if (str = str.strip).empty?
|
1201
1138
|
{}
|
1202
|
-
elsif (str.start_with?
|
1139
|
+
elsif (str.start_with? '.', '#') && Compliance.shorthand_property_syntax
|
1203
1140
|
segments = str.split('#', 2)
|
1204
1141
|
|
1205
|
-
if segments.
|
1142
|
+
if segments.size > 1
|
1206
1143
|
id, *more_roles = segments[1].split('.')
|
1207
1144
|
else
|
1208
1145
|
id = nil
|
@@ -1210,11 +1147,11 @@ module Substitutors
|
|
1210
1147
|
end
|
1211
1148
|
|
1212
1149
|
roles = segments[0].empty? ? [] : segments[0].split('.')
|
1213
|
-
if roles.
|
1150
|
+
if roles.size > 1
|
1214
1151
|
roles.shift
|
1215
1152
|
end
|
1216
1153
|
|
1217
|
-
if more_roles.
|
1154
|
+
if more_roles.size > 0
|
1218
1155
|
roles.concat more_roles
|
1219
1156
|
end
|
1220
1157
|
|
@@ -1236,14 +1173,10 @@ module Substitutors
|
|
1236
1173
|
def parse_attributes(attrline, posattrs = ['role'], opts = {})
|
1237
1174
|
return unless attrline
|
1238
1175
|
return {} if attrline.empty?
|
1239
|
-
attrline = @document.sub_attributes(attrline) if opts[:sub_input]
|
1176
|
+
attrline = @document.sub_attributes(attrline) if opts[:sub_input] && (attrline.include? '{')
|
1240
1177
|
attrline = unescape_bracketed_text(attrline) if opts[:unescape_input]
|
1241
|
-
block
|
1242
|
-
|
1243
|
-
# substitutions are only performed on attribute values if block is not nil
|
1244
|
-
block = self
|
1245
|
-
end
|
1246
|
-
|
1178
|
+
# substitutions are only performed on attribute values if block is not nil
|
1179
|
+
block = opts.fetch(:sub_result, true) ? self : nil
|
1247
1180
|
if (into = opts[:into])
|
1248
1181
|
AttributeList.new(attrline, block).parse_into(into, posattrs)
|
1249
1182
|
else
|
@@ -1253,28 +1186,29 @@ module Substitutors
|
|
1253
1186
|
|
1254
1187
|
# Internal: Strip bounding whitespace, fold endlines and unescaped closing
|
1255
1188
|
# square brackets from text extracted from brackets
|
1256
|
-
def unescape_bracketed_text
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1189
|
+
def unescape_bracketed_text text
|
1190
|
+
if (text = text.strip.tr LF, ' ').include? R_SB
|
1191
|
+
text = text.gsub ESC_R_SB, R_SB
|
1192
|
+
end unless text.empty?
|
1193
|
+
text
|
1260
1194
|
end
|
1261
1195
|
|
1262
1196
|
# Internal: Strip bounding whitespace and fold endlines
|
1263
1197
|
def normalize_string str, unescape_brackets = false
|
1264
|
-
|
1265
|
-
''
|
1266
|
-
|
1267
|
-
unescape_brackets str.strip.tr(EOL, ' ')
|
1268
|
-
else
|
1269
|
-
str.strip.tr(EOL, ' ')
|
1198
|
+
unless str.empty?
|
1199
|
+
str = str.strip.tr LF, ' '
|
1200
|
+
str = str.gsub ESC_R_SB, R_SB if unescape_brackets && (str.include? R_SB)
|
1270
1201
|
end
|
1202
|
+
str
|
1271
1203
|
end
|
1272
1204
|
|
1273
1205
|
# Internal: Unescape closing square brackets.
|
1274
1206
|
# Intended for text extracted from square brackets.
|
1275
1207
|
def unescape_brackets str
|
1276
|
-
|
1277
|
-
|
1208
|
+
if str.include? RS
|
1209
|
+
str = str.gsub ESC_R_SB, R_SB
|
1210
|
+
end unless str.empty?
|
1211
|
+
str
|
1278
1212
|
end
|
1279
1213
|
|
1280
1214
|
# Internal: Split text formatted as CSV with support
|
@@ -1290,7 +1224,7 @@ module Substitutors
|
|
1290
1224
|
case c
|
1291
1225
|
when ','
|
1292
1226
|
if quote_open
|
1293
|
-
current
|
1227
|
+
current << c
|
1294
1228
|
else
|
1295
1229
|
values << current.join.strip
|
1296
1230
|
current = []
|
@@ -1298,7 +1232,7 @@ module Substitutors
|
|
1298
1232
|
when '"'
|
1299
1233
|
quote_open = !quote_open
|
1300
1234
|
else
|
1301
|
-
current
|
1235
|
+
current << c
|
1302
1236
|
end
|
1303
1237
|
end
|
1304
1238
|
|
@@ -1317,9 +1251,11 @@ module Substitutors
|
|
1317
1251
|
# returns An Array of Symbols representing the substitution operation
|
1318
1252
|
def resolve_subs subs, type = :block, defaults = nil, subject = nil
|
1319
1253
|
return [] if subs.nil_or_empty?
|
1254
|
+
# QUESTION should we store candidates as a Set instead of an Array?
|
1320
1255
|
candidates = nil
|
1321
|
-
|
1322
|
-
|
1256
|
+
subs = subs.delete ' ' if subs.include? ' '
|
1257
|
+
modifiers_present = SubModifierSniffRx.match? subs
|
1258
|
+
subs.split(',').each do |key|
|
1323
1259
|
modifier_operation = nil
|
1324
1260
|
if modifiers_present
|
1325
1261
|
if (first = key.chr) == '+'
|
@@ -1336,12 +1272,12 @@ module Substitutors
|
|
1336
1272
|
key = key.to_sym
|
1337
1273
|
# special case to disable callouts for inline subs
|
1338
1274
|
if type == :inline && (key == :verbatim || key == :v)
|
1339
|
-
resolved_keys =
|
1340
|
-
elsif
|
1341
|
-
resolved_keys =
|
1342
|
-
elsif type == :inline && key.length == 1 && (
|
1343
|
-
resolved_key =
|
1344
|
-
if (candidate =
|
1275
|
+
resolved_keys = BASIC_SUBS
|
1276
|
+
elsif SUB_GROUPS.key? key
|
1277
|
+
resolved_keys = SUB_GROUPS[key]
|
1278
|
+
elsif type == :inline && key.length == 1 && (SUB_HINTS.key? key)
|
1279
|
+
resolved_key = SUB_HINTS[key]
|
1280
|
+
if (candidate = SUB_GROUPS[resolved_key])
|
1345
1281
|
resolved_keys = candidate
|
1346
1282
|
else
|
1347
1283
|
resolved_keys = [resolved_key]
|
@@ -1365,8 +1301,8 @@ module Substitutors
|
|
1365
1301
|
candidates += resolved_keys
|
1366
1302
|
end
|
1367
1303
|
end
|
1368
|
-
|
1369
|
-
#
|
1304
|
+
return [] unless candidates
|
1305
|
+
# weed out invalid options and remove duplicates (order is preserved; first occurence wins)
|
1370
1306
|
resolved = candidates & SUB_OPTIONS[type]
|
1371
1307
|
unless (candidates - resolved).empty?
|
1372
1308
|
invalid = candidates - resolved
|
@@ -1401,7 +1337,7 @@ module Substitutors
|
|
1401
1337
|
unless (highlighter_loaded = defined? ::CodeRay) || @document.attributes['coderay-unavailable']
|
1402
1338
|
if (Helpers.require_library 'coderay', true, :warn).nil?
|
1403
1339
|
# prevent further attempts to load CodeRay
|
1404
|
-
@document.set_attr 'coderay-unavailable'
|
1340
|
+
@document.set_attr 'coderay-unavailable'
|
1405
1341
|
else
|
1406
1342
|
highlighter_loaded = true
|
1407
1343
|
end
|
@@ -1410,7 +1346,7 @@ module Substitutors
|
|
1410
1346
|
unless (highlighter_loaded = defined? ::Pygments) || @document.attributes['pygments-unavailable']
|
1411
1347
|
if (Helpers.require_library 'pygments', 'pygments.rb', :warn).nil?
|
1412
1348
|
# prevent further attempts to load Pygments
|
1413
|
-
@document.set_attr 'pygments-unavailable'
|
1349
|
+
@document.set_attr 'pygments-unavailable'
|
1414
1350
|
else
|
1415
1351
|
highlighter_loaded = true
|
1416
1352
|
end
|
@@ -1430,21 +1366,22 @@ module Substitutors
|
|
1430
1366
|
# FIXME cache this dynamic regex
|
1431
1367
|
callout_rx = (attr? 'line-comment') ? /(?:#{::Regexp.escape(attr 'line-comment')} )?#{CalloutExtractRxt}/ : CalloutExtractRx
|
1432
1368
|
# extract callout marks, indexed by line number
|
1433
|
-
source = source.split(
|
1369
|
+
source = source.split(LF, -1).map {|line|
|
1434
1370
|
lineno = lineno + 1
|
1435
1371
|
line.gsub(callout_rx) {
|
1436
1372
|
# alias match for Ruby 1.8.7 compat
|
1437
1373
|
m = $~
|
1438
1374
|
# honor the escape
|
1439
|
-
if m[1] ==
|
1440
|
-
|
1375
|
+
if m[1] == RS
|
1376
|
+
# we have to use sub since we aren't sure it's the first char
|
1377
|
+
m[0].sub RS, ''
|
1441
1378
|
else
|
1442
1379
|
(callout_marks[lineno] ||= []) << m[3]
|
1443
1380
|
last = lineno
|
1444
1381
|
nil
|
1445
1382
|
end
|
1446
1383
|
}
|
1447
|
-
} *
|
1384
|
+
} * LF
|
1448
1385
|
callout_on_last = (last == lineno)
|
1449
1386
|
callout_marks = nil if callout_marks.empty?
|
1450
1387
|
else
|
@@ -1456,7 +1393,7 @@ module Substitutors
|
|
1456
1393
|
|
1457
1394
|
case highlighter
|
1458
1395
|
when 'coderay'
|
1459
|
-
if (linenums_mode = (attr? 'linenums') ? (@document.attributes['coderay-linenums-mode'] || :table).to_sym : nil)
|
1396
|
+
if (linenums_mode = (attr? 'linenums', nil, false) ? (@document.attributes['coderay-linenums-mode'] || :table).to_sym : nil)
|
1460
1397
|
if attr? 'highlight', nil, false
|
1461
1398
|
highlight_lines = resolve_highlight_lines(attr 'highlight', nil, false)
|
1462
1399
|
end
|
@@ -1468,8 +1405,9 @@ module Substitutors
|
|
1468
1405
|
:highlight_lines => highlight_lines,
|
1469
1406
|
:bold_every => false}].highlight source
|
1470
1407
|
when 'pygments'
|
1471
|
-
lexer = ::Pygments::Lexer
|
1472
|
-
opts = { :cssclass => 'pyhl', :classprefix => 'tok-', :nobackground => true }
|
1408
|
+
lexer = ::Pygments::Lexer.find_by_alias(attr 'language', 'text', false) || ::Pygments::Lexer.find_by_mimetype('text/plain')
|
1409
|
+
opts = { :cssclass => 'pyhl', :classprefix => 'tok-', :nobackground => true, :stripnl => false }
|
1410
|
+
opts[:startinline] = !(option? 'mixed') if lexer.name == 'PHP'
|
1473
1411
|
unless (@document.attributes['pygments-css'] || 'class') == 'class'
|
1474
1412
|
opts[:noclasses] = true
|
1475
1413
|
opts[:style] = (@document.attributes['pygments-style'] || Stylesheets::DEFAULT_PYGMENTS_STYLE)
|
@@ -1479,56 +1417,42 @@ module Substitutors
|
|
1479
1417
|
opts[:hl_lines] = highlight_lines * ' '
|
1480
1418
|
end
|
1481
1419
|
end
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
linenums_mode = :table
|
1487
|
-
# NOTE these subs clean out HTML that messes up our styles
|
1488
|
-
result = lexer.highlight(source, :options => opts).
|
1489
|
-
sub(/<div class="pyhl">(.*)<\/div>/m, '\1').
|
1490
|
-
gsub(/<pre[^>]*>(.*?)<\/pre>\s*/m, '\1')
|
1491
|
-
else
|
1492
|
-
result = lexer.highlight(source, :options => opts).
|
1493
|
-
sub(/<div class="pyhl"><pre[^>]*>(.*?)<\/pre><\/div>/m, '\1')
|
1494
|
-
end
|
1420
|
+
# TODO we could add the line numbers in ourselves instead of having to strip out the junk
|
1421
|
+
if (attr? 'linenums', nil, false) && (opts[:linenos] = @document.attributes['pygments-linenums-mode'] || 'table') == 'table'
|
1422
|
+
linenums_mode = :table
|
1423
|
+
result = lexer.highlight(source, :options => opts).sub(PygmentsWrapperDivRx, '\1').gsub(PygmentsWrapperPreRx, '\1')
|
1495
1424
|
else
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1425
|
+
if PygmentsWrapperPreRx =~ (result = lexer.highlight(source, :options => opts))
|
1426
|
+
result = $1
|
1427
|
+
end
|
1499
1428
|
end
|
1500
1429
|
end
|
1501
1430
|
|
1502
1431
|
# fix passthrough placeholders that got caught up in syntax highlighting
|
1503
1432
|
unless @passthroughs.empty?
|
1504
|
-
result = result.gsub
|
1433
|
+
result = result.gsub HighlightedPassSlotRx, %(#{PASS_START}\\1#{PASS_END})
|
1505
1434
|
end
|
1506
1435
|
|
1507
1436
|
if process_callouts && callout_marks
|
1508
1437
|
lineno = 0
|
1509
1438
|
reached_code = linenums_mode != :table
|
1510
|
-
result.split(
|
1439
|
+
result.split(LF, -1).map {|line|
|
1511
1440
|
unless reached_code
|
1512
|
-
unless line.include?('<td class="code">')
|
1513
|
-
next line
|
1514
|
-
end
|
1441
|
+
next line unless line.include?('<td class="code">')
|
1515
1442
|
reached_code = true
|
1516
1443
|
end
|
1517
|
-
lineno
|
1444
|
+
lineno += 1
|
1518
1445
|
if (conums = callout_marks.delete(lineno))
|
1519
1446
|
tail = nil
|
1520
|
-
if callout_on_last && callout_marks.empty?
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
line =
|
1525
|
-
else
|
1526
|
-
# Give conum on final line breathing room if trailing space in source is dropped
|
1527
|
-
line = %(#{line.chomp ' '} )
|
1447
|
+
if callout_on_last && callout_marks.empty? && linenums_mode == :table
|
1448
|
+
if highlighter == 'coderay' && (pos = line.index '</pre>')
|
1449
|
+
line, tail = (line.slice 0, pos), (line.slice pos, line.length)
|
1450
|
+
elsif highlighter == 'pygments' && (pos = line.start_with? '</td>')
|
1451
|
+
line, tail = '', line
|
1528
1452
|
end
|
1529
1453
|
end
|
1530
1454
|
if conums.size == 1
|
1531
|
-
%(#{line}#{Inline.new(self, :callout, conums[0], :id => @document.callouts.read_next_id).convert
|
1455
|
+
%(#{line}#{Inline.new(self, :callout, conums[0], :id => @document.callouts.read_next_id).convert}#{tail})
|
1532
1456
|
else
|
1533
1457
|
conums_markup = conums.map {|conum| Inline.new(self, :callout, conum, :id => @document.callouts.read_next_id).convert } * ' '
|
1534
1458
|
%(#{line}#{conums_markup}#{tail})
|
@@ -1536,7 +1460,7 @@ module Substitutors
|
|
1536
1460
|
else
|
1537
1461
|
line
|
1538
1462
|
end
|
1539
|
-
} *
|
1463
|
+
} * LF
|
1540
1464
|
else
|
1541
1465
|
result
|
1542
1466
|
end
|
@@ -1545,7 +1469,7 @@ module Substitutors
|
|
1545
1469
|
# e.g., highlight="1-5, !2, 10" or highlight=1-5;!2,10
|
1546
1470
|
def resolve_highlight_lines spec
|
1547
1471
|
lines = []
|
1548
|
-
spec.delete
|
1472
|
+
((spec.include? ' ') ? (spec.delete ' ') : spec).split(DataDelimiterRx).map do |entry|
|
1549
1473
|
negate = false
|
1550
1474
|
if entry.start_with? '!'
|
1551
1475
|
entry = entry[1..-1]
|
@@ -1577,54 +1501,48 @@ module Substitutors
|
|
1577
1501
|
#
|
1578
1502
|
# returns the substituted source
|
1579
1503
|
def sub_source source, process_callouts
|
1580
|
-
|
1504
|
+
process_callouts ? sub_callouts(sub_specialchars source) : (sub_specialchars source)
|
1581
1505
|
end
|
1582
1506
|
|
1583
1507
|
# Internal: Lock-in the substitutions for this block
|
1584
1508
|
#
|
1585
|
-
# Looks for an attribute named "subs". If present, resolves
|
1586
|
-
#
|
1587
|
-
# Otherwise,
|
1588
|
-
#
|
1509
|
+
# Looks for an attribute named "subs". If present, resolves substitutions
|
1510
|
+
# from the value of that attribute and assigns them to the subs property on
|
1511
|
+
# this block. Otherwise, uses the substitutions assigned to the default_subs
|
1512
|
+
# property, if specified, or selects a default set of substitutions based on
|
1513
|
+
# the content model of the block.
|
1589
1514
|
#
|
1590
|
-
# Returns
|
1515
|
+
# Returns The Array of resolved substitutions now assigned to this block
|
1591
1516
|
def lock_in_subs
|
1592
|
-
|
1593
|
-
default_subs = @default_subs
|
1594
|
-
else
|
1517
|
+
unless (default_subs = @default_subs)
|
1595
1518
|
case @content_model
|
1596
1519
|
when :simple
|
1597
|
-
default_subs =
|
1520
|
+
default_subs = NORMAL_SUBS
|
1598
1521
|
when :verbatim
|
1599
1522
|
if @context == :listing || (@context == :literal && !(option? 'listparagraph'))
|
1600
|
-
default_subs =
|
1523
|
+
default_subs = VERBATIM_SUBS
|
1601
1524
|
elsif @context == :verse
|
1602
|
-
default_subs =
|
1525
|
+
default_subs = NORMAL_SUBS
|
1603
1526
|
else
|
1604
|
-
default_subs =
|
1527
|
+
default_subs = BASIC_SUBS
|
1605
1528
|
end
|
1606
1529
|
when :raw
|
1607
|
-
|
1608
|
-
|
1609
|
-
else
|
1610
|
-
default_subs = SUBS[:pass]
|
1611
|
-
end
|
1530
|
+
# TODO make pass subs a compliance setting; AsciiDoc Python performs :attributes and :macros on a pass block
|
1531
|
+
default_subs = @context == :stem ? BASIC_SUBS : NONE_SUBS
|
1612
1532
|
else
|
1613
|
-
return
|
1533
|
+
return @subs
|
1614
1534
|
end
|
1615
1535
|
end
|
1616
1536
|
|
1617
|
-
|
1618
|
-
@subs = resolve_block_subs custom_subs, default_subs, @context
|
1619
|
-
else
|
1620
|
-
@subs = default_subs.dup
|
1621
|
-
end
|
1537
|
+
@subs = (custom_subs = @attributes['subs']) ? (resolve_block_subs custom_subs, default_subs, @context) : default_subs.dup
|
1622
1538
|
|
1623
1539
|
# QUESION delegate this logic to a method?
|
1624
|
-
if @context == :listing && @style == 'source' && @attributes
|
1625
|
-
|
1626
|
-
@subs =
|
1540
|
+
if @context == :listing && @style == 'source' && (@attributes.key? 'language') && (@document.basebackend? 'html') &&
|
1541
|
+
(SUB_HIGHLIGHT.include? @document.attributes['source-highlighter']) && (idx = @subs.index :specialcharacters)
|
1542
|
+
@subs[idx] = :highlight
|
1627
1543
|
end
|
1544
|
+
|
1545
|
+
@subs
|
1628
1546
|
end
|
1629
1547
|
end
|
1630
1548
|
end
|