asciidoctor 1.5.7.1 → 1.5.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +95 -5
- data/Gemfile +23 -13
- data/README-de.adoc +482 -0
- data/README-fr.adoc +128 -119
- data/README-jp.adoc +2 -3
- data/README-zh_CN.adoc +2 -3
- data/README.adoc +131 -106
- data/asciidoctor.gemspec +9 -7
- data/data/locale/attributes-ar.adoc +1 -1
- data/data/locale/attributes-bg.adoc +1 -1
- data/data/locale/attributes-ca.adoc +1 -1
- data/data/locale/attributes-cs.adoc +1 -1
- data/data/locale/attributes-da.adoc +1 -1
- data/data/locale/attributes-de.adoc +1 -1
- data/data/locale/attributes-en.adoc +1 -1
- data/data/locale/attributes-es.adoc +1 -1
- data/data/locale/attributes-fa.adoc +1 -1
- data/data/locale/attributes-fi.adoc +1 -1
- data/data/locale/attributes-fr.adoc +1 -1
- data/data/locale/attributes-hu.adoc +1 -1
- data/data/locale/attributes-id.adoc +1 -1
- data/data/locale/attributes-it.adoc +1 -1
- data/data/locale/attributes-ja.adoc +1 -1
- data/data/locale/attributes-kr.adoc +1 -1
- data/data/locale/attributes-nb.adoc +1 -1
- data/data/locale/attributes-nl.adoc +1 -1
- data/data/locale/attributes-nn.adoc +1 -1
- data/data/locale/attributes-pl.adoc +1 -1
- data/data/locale/attributes-pt.adoc +1 -1
- data/data/locale/attributes-pt_BR.adoc +1 -1
- data/data/locale/attributes-ro.adoc +1 -1
- data/data/locale/attributes-ru.adoc +1 -1
- data/data/locale/attributes-sr.adoc +5 -4
- data/data/locale/attributes-sr_Latn.adoc +5 -4
- data/data/locale/attributes-sv.adoc +23 -0
- data/data/locale/attributes-tr.adoc +1 -1
- data/data/locale/attributes-uk.adoc +1 -1
- data/data/locale/attributes-zh_CN.adoc +1 -1
- data/data/locale/attributes-zh_TW.adoc +1 -1
- data/data/stylesheets/asciidoctor-default.css +23 -23
- data/lib/asciidoctor.rb +110 -104
- data/lib/asciidoctor/abstract_block.rb +55 -32
- data/lib/asciidoctor/abstract_node.rb +32 -17
- data/lib/asciidoctor/attribute_list.rb +8 -7
- data/lib/asciidoctor/block.rb +5 -7
- data/lib/asciidoctor/cli/options.rb +5 -9
- data/lib/asciidoctor/converter.rb +2 -2
- data/lib/asciidoctor/converter/docbook45.rb +7 -20
- data/lib/asciidoctor/converter/docbook5.rb +36 -37
- data/lib/asciidoctor/converter/factory.rb +10 -8
- data/lib/asciidoctor/converter/html5.rb +90 -65
- data/lib/asciidoctor/converter/manpage.rb +72 -62
- data/lib/asciidoctor/converter/template.rb +8 -6
- data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +5 -0
- data/lib/asciidoctor/document.rb +62 -10
- data/lib/asciidoctor/extensions.rb +74 -16
- data/lib/asciidoctor/helpers.rb +11 -14
- data/lib/asciidoctor/list.rb +2 -2
- data/lib/asciidoctor/parser.rb +223 -195
- data/lib/asciidoctor/path_resolver.rb +15 -7
- data/lib/asciidoctor/reader.rb +65 -36
- data/lib/asciidoctor/section.rb +6 -4
- data/lib/asciidoctor/substitutors.rb +170 -149
- data/lib/asciidoctor/table.rb +16 -8
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +6 -5
- data/man/asciidoctor.adoc +3 -2
- data/test/api_test.rb +236 -0
- data/test/attribute_list_test.rb +242 -0
- data/test/attributes_test.rb +65 -52
- data/test/blocks_test.rb +408 -260
- data/test/converter_test.rb +7 -7
- data/test/document_test.rb +60 -54
- data/test/extensions_test.rb +218 -32
- data/test/fixtures/doctime-localtime.adoc +2 -0
- data/test/fixtures/section-a.adoc +4 -0
- data/test/fixtures/subs.adoc +0 -1
- data/test/invoker_test.rb +56 -18
- data/test/links_test.rb +105 -81
- data/test/lists_test.rb +636 -265
- data/test/logger_test.rb +1 -1
- data/test/manpage_test.rb +140 -3
- data/test/paragraphs_test.rb +42 -42
- data/test/parser_test.rb +63 -183
- data/test/paths_test.rb +21 -4
- data/test/preamble_test.rb +9 -9
- data/test/reader_test.rb +78 -28
- data/test/sections_test.rb +273 -151
- data/test/substitutions_test.rb +53 -19
- data/test/tables_test.rb +286 -163
- data/test/test_helper.rb +4 -3
- data/test/text_test.rb +65 -65
- metadata +16 -21
@@ -109,7 +109,7 @@ class PathResolver
|
|
109
109
|
SLASH = '/'
|
110
110
|
BACKSLASH = '\\'
|
111
111
|
DOUBLE_SLASH = '//'
|
112
|
-
WindowsRootRx = /^[a-zA-Z]:
|
112
|
+
WindowsRootRx = /^(?:[a-zA-Z]:)?[\\\/]/
|
113
113
|
|
114
114
|
attr_accessor :file_separator
|
115
115
|
attr_accessor :working_dir
|
@@ -132,11 +132,11 @@ class PathResolver
|
|
132
132
|
|
133
133
|
# Public: Check whether the specified path is an absolute path.
|
134
134
|
#
|
135
|
-
# This operation considers both posix paths and Windows paths.
|
136
|
-
#
|
135
|
+
# This operation considers both posix paths and Windows paths. The path does
|
136
|
+
# not have to be posixified beforehand. This operation does not handle URIs.
|
137
137
|
#
|
138
|
-
# Unix absolute paths
|
139
|
-
# start with a drive letter.
|
138
|
+
# Unix absolute paths start with a slash. UNC paths can start with a slash or
|
139
|
+
# backslash. Windows roots can start with a drive letter.
|
140
140
|
#
|
141
141
|
# path - the String path to check
|
142
142
|
#
|
@@ -214,7 +214,15 @@ class PathResolver
|
|
214
214
|
#
|
215
215
|
# Return the [String] relative path of the specified path calculated from the base directory.
|
216
216
|
def relative_path path, base
|
217
|
-
|
217
|
+
if root? path
|
218
|
+
if (offset = descends_from? path, base)
|
219
|
+
path.slice offset, path.length
|
220
|
+
else
|
221
|
+
(Pathname.new path).relative_path_from(Pathname.new base).to_s
|
222
|
+
end
|
223
|
+
else
|
224
|
+
path
|
225
|
+
end
|
218
226
|
end
|
219
227
|
|
220
228
|
# Public: Normalize path by converting any backslashes to forward slashes
|
@@ -313,7 +321,7 @@ class PathResolver
|
|
313
321
|
# returns a String path formed by joining the segments using the posix file
|
314
322
|
# separator and prepending the root, if specified
|
315
323
|
def join_path segments, root = nil
|
316
|
-
root ? %(#{root}#{segments
|
324
|
+
root ? %(#{root}#{segments.join SLASH}) : (segments.join SLASH)
|
317
325
|
end
|
318
326
|
|
319
327
|
# Public: Securely resolve a system path
|
data/lib/asciidoctor/reader.rb
CHANGED
@@ -60,12 +60,13 @@ class Reader
|
|
60
60
|
@lineno = cursor.lineno || 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
|
61
61
|
end
|
62
62
|
@lines = data ? (prepare_lines data, opts) : []
|
63
|
-
@source_lines = @lines.
|
63
|
+
@source_lines = @lines.drop 0
|
64
64
|
@mark = nil
|
65
65
|
@look_ahead = 0
|
66
66
|
@process_lines = true
|
67
67
|
@unescape_next_line = false
|
68
68
|
@unterminated = nil
|
69
|
+
@saved = nil
|
69
70
|
end
|
70
71
|
|
71
72
|
# Internal: Prepare the lines from the provided data
|
@@ -91,7 +92,7 @@ class Reader
|
|
91
92
|
elsif opts[:normalize]
|
92
93
|
Helpers.normalize_lines_array data
|
93
94
|
else
|
94
|
-
data.
|
95
|
+
data.drop 0
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
@@ -167,7 +168,7 @@ class Reader
|
|
167
168
|
# Returns nothing if there is no more data.
|
168
169
|
def peek_line direct = false
|
169
170
|
if direct || @look_ahead > 0
|
170
|
-
@unescape_next_line ? @lines[0]
|
171
|
+
@unescape_next_line ? ((line = @lines[0]).slice 1, line.length) : @lines[0]
|
171
172
|
elsif @lines.empty?
|
172
173
|
@look_ahead = 0
|
173
174
|
nil
|
@@ -245,7 +246,7 @@ class Reader
|
|
245
246
|
#
|
246
247
|
# Returns the lines read joined as a String
|
247
248
|
def read
|
248
|
-
read_lines
|
249
|
+
read_lines.join LF
|
249
250
|
end
|
250
251
|
|
251
252
|
# Public: Advance to the next line by discarding the line at the front of the stack
|
@@ -556,17 +557,39 @@ class Reader
|
|
556
557
|
#
|
557
558
|
# Returns A copy of the String Array of lines remaining in this Reader
|
558
559
|
def lines
|
559
|
-
@lines.
|
560
|
+
@lines.drop 0
|
560
561
|
end
|
561
562
|
|
562
563
|
# Public: Get a copy of the remaining lines managed by this Reader joined as a String
|
563
564
|
def string
|
564
|
-
@lines
|
565
|
+
@lines.join LF
|
565
566
|
end
|
566
567
|
|
567
568
|
# Public: Get the source lines for this Reader joined as a String
|
568
569
|
def source
|
569
|
-
@source_lines
|
570
|
+
@source_lines.join LF
|
571
|
+
end
|
572
|
+
|
573
|
+
def save
|
574
|
+
accum = {}
|
575
|
+
instance_variables.each do |name|
|
576
|
+
accum[name] = ::Array === (val = instance_variable_get name) ? val.dup : val unless name == :@saved || name == :@source_lines
|
577
|
+
end
|
578
|
+
@saved = accum
|
579
|
+
nil
|
580
|
+
end
|
581
|
+
|
582
|
+
def restore_save
|
583
|
+
if @saved
|
584
|
+
@saved.each do |name, val|
|
585
|
+
instance_variable_set name, val
|
586
|
+
end
|
587
|
+
@saved = nil
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
def discard_save
|
592
|
+
@saved = nil
|
570
593
|
end
|
571
594
|
|
572
595
|
# Public: Get a summary of this Reader.
|
@@ -602,7 +625,7 @@ class PreprocessorReader < Reader
|
|
602
625
|
# QUESTION should this work for AsciiDoc table cell content? Currently it does not.
|
603
626
|
if @document && @document.attributes['skip-front-matter']
|
604
627
|
if (front_matter = skip_front_matter! result)
|
605
|
-
@document.attributes['front-matter'] = front_matter
|
628
|
+
@document.attributes['front-matter'] = front_matter.join LF
|
606
629
|
end
|
607
630
|
end
|
608
631
|
|
@@ -633,7 +656,7 @@ class PreprocessorReader < Reader
|
|
633
656
|
if $1 == '\\'
|
634
657
|
@unescape_next_line = true
|
635
658
|
@look_ahead += 1
|
636
|
-
line
|
659
|
+
line.slice 1, line.length
|
637
660
|
elsif preprocess_conditional_directive $2, $3, $4, $5
|
638
661
|
# move the pointer past the conditional line
|
639
662
|
shift
|
@@ -653,7 +676,7 @@ class PreprocessorReader < Reader
|
|
653
676
|
if $1 == '\\'
|
654
677
|
@unescape_next_line = true
|
655
678
|
@look_ahead += 1
|
656
|
-
line
|
679
|
+
line.slice 1, line.length
|
657
680
|
# QUESTION should we strip whitespace from raw attributes in Substitutors#parse_attributes? (check perf)
|
658
681
|
elsif preprocess_include_directive $2, $3
|
659
682
|
# peek again since the content has changed
|
@@ -842,21 +865,22 @@ class PreprocessorReader < Reader
|
|
842
865
|
# Returns a [Boolean] indicating whether the line under the cursor was changed. To skip over the
|
843
866
|
# directive, call shift and return true.
|
844
867
|
def preprocess_include_directive target, attrlist
|
868
|
+
doc = @document
|
845
869
|
if ((expanded_target = target).include? ATTR_REF_HEAD) &&
|
846
|
-
(expanded_target =
|
870
|
+
(expanded_target = doc.sub_attributes target, :attribute_missing => 'drop-line').empty?
|
847
871
|
shift
|
848
|
-
if (
|
872
|
+
if (doc.attributes['attribute-missing'] || Compliance.attribute_missing) == 'skip'
|
849
873
|
unshift %(Unresolved directive in #{@path} - include::#{target}[#{attrlist}])
|
850
874
|
end
|
851
875
|
true
|
852
876
|
elsif include_processors? && (ext = @include_processor_extensions.find {|candidate| candidate.instance.handles? expanded_target })
|
853
877
|
shift
|
854
878
|
# FIXME parse attributes only if requested by extension
|
855
|
-
ext.process_method[
|
879
|
+
ext.process_method[doc, self, expanded_target, (doc.parse_attributes attrlist, [], :sub_input => true)]
|
856
880
|
true
|
857
881
|
# if running in SafeMode::SECURE or greater, don't process this directive
|
858
882
|
# however, be friendly and at least make it a link to the source document
|
859
|
-
elsif
|
883
|
+
elsif doc.safe >= SafeMode::SECURE
|
860
884
|
# FIXME we don't want to use a link macro if we are in a verbatim context
|
861
885
|
replace_next_line %(link:#{expanded_target}[])
|
862
886
|
elsif (abs_maxdepth = @maxdepth[:abs]) > 0
|
@@ -865,30 +889,30 @@ class PreprocessorReader < Reader
|
|
865
889
|
return
|
866
890
|
end
|
867
891
|
|
868
|
-
|
869
|
-
inc_path, target_type, relpath = resolve_include_path expanded_target, attrlist,
|
892
|
+
parsed_attrs = doc.parse_attributes attrlist, [], :sub_input => true
|
893
|
+
inc_path, target_type, relpath = resolve_include_path expanded_target, attrlist, parsed_attrs
|
870
894
|
return inc_path unless target_type
|
871
895
|
|
872
896
|
inc_linenos = inc_tags = nil
|
873
897
|
if attrlist
|
874
|
-
if
|
898
|
+
if parsed_attrs.key? 'lines'
|
875
899
|
inc_linenos = []
|
876
|
-
|
877
|
-
if linedef.include?
|
878
|
-
from, to = linedef.split
|
879
|
-
inc_linenos += to < 0 ? [from, 1.0/0.0] : ::Range.new(from, to).to_a
|
900
|
+
(split_delimited_value parsed_attrs['lines']).each do |linedef|
|
901
|
+
if linedef.include? '..'
|
902
|
+
from, to = linedef.split '..', 2
|
903
|
+
inc_linenos += (to.empty? || (to = to.to_i) < 0) ? [from.to_i, 1.0/0.0] : ::Range.new(from.to_i, to).to_a
|
880
904
|
else
|
881
905
|
inc_linenos << linedef.to_i
|
882
906
|
end
|
883
907
|
end
|
884
908
|
inc_linenos = inc_linenos.empty? ? nil : inc_linenos.sort.uniq
|
885
|
-
elsif
|
886
|
-
unless (tag =
|
909
|
+
elsif parsed_attrs.key? 'tag'
|
910
|
+
unless (tag = parsed_attrs['tag']).empty? || tag == '!'
|
887
911
|
inc_tags = (tag.start_with? '!') ? { (tag.slice 1, tag.length) => false } : { tag => true }
|
888
912
|
end
|
889
|
-
elsif
|
913
|
+
elsif parsed_attrs.key? 'tags'
|
890
914
|
inc_tags = {}
|
891
|
-
|
915
|
+
(split_delimited_value parsed_attrs['tags']).each do |tagdef|
|
892
916
|
if tagdef.start_with? '!'
|
893
917
|
inc_tags[tagdef.slice 1, tagdef.length] = false
|
894
918
|
else
|
@@ -928,8 +952,8 @@ class PreprocessorReader < Reader
|
|
928
952
|
shift
|
929
953
|
# FIXME not accounting for skipped lines in reader line numbering
|
930
954
|
if inc_offset
|
931
|
-
|
932
|
-
push_include inc_lines, inc_path, relpath, inc_offset,
|
955
|
+
parsed_attrs['partial-option'] = true
|
956
|
+
push_include inc_lines, inc_path, relpath, inc_offset, parsed_attrs
|
933
957
|
end
|
934
958
|
elsif inc_tags
|
935
959
|
inc_lines, inc_offset, inc_lineno, tag_stack, tags_used, active_tag = [], nil, 0, [], ::Set.new, nil
|
@@ -995,16 +1019,16 @@ class PreprocessorReader < Reader
|
|
995
1019
|
end
|
996
1020
|
shift
|
997
1021
|
if inc_offset
|
998
|
-
|
1022
|
+
parsed_attrs['partial-option'] = true unless base_select && wildcard && inc_tags.empty?
|
999
1023
|
# FIXME not accounting for skipped lines in reader line numbering
|
1000
|
-
push_include inc_lines, inc_path, relpath, inc_offset,
|
1024
|
+
push_include inc_lines, inc_path, relpath, inc_offset, parsed_attrs
|
1001
1025
|
end
|
1002
1026
|
else
|
1003
1027
|
begin
|
1004
1028
|
# NOTE read content first so that we only advance cursor if IO operation succeeds
|
1005
|
-
inc_content = target_type == :file ?
|
1029
|
+
inc_content = target_type == :file ? ::File.open(inc_path, 'rb') {|f| f.read } : open(inc_path, 'rb') {|f| f.read }
|
1006
1030
|
shift
|
1007
|
-
push_include inc_content, inc_path, relpath, 1,
|
1031
|
+
push_include inc_content, inc_path, relpath, 1, parsed_attrs
|
1008
1032
|
rescue
|
1009
1033
|
logger.error message_with_context %(include #{target_type} not readable: #{inc_path}), :source_location => cursor
|
1010
1034
|
return replace_next_line %(Unresolved directive in #{@path} - include::#{expanded_target}[#{attrlist}])
|
@@ -1182,19 +1206,24 @@ class PreprocessorReader < Reader
|
|
1182
1206
|
def shift
|
1183
1207
|
if @unescape_next_line
|
1184
1208
|
@unescape_next_line = false
|
1185
|
-
super
|
1209
|
+
(line = super).slice 1, line.length
|
1186
1210
|
else
|
1187
1211
|
super
|
1188
1212
|
end
|
1189
1213
|
end
|
1190
1214
|
|
1215
|
+
# Private: Split delimited value on comma (if found), otherwise semi-colon
|
1216
|
+
def split_delimited_value val
|
1217
|
+
(val.include? ',') ? (val.split ',') : (val.split ';')
|
1218
|
+
end
|
1219
|
+
|
1191
1220
|
# Private: Ignore front-matter, commonly used in static site generators
|
1192
1221
|
def skip_front_matter! data, increment_linenos = true
|
1193
1222
|
front_matter = nil
|
1194
1223
|
if data[0] == '---'
|
1195
|
-
original_data = data.
|
1196
|
-
front_matter = []
|
1224
|
+
original_data = data.drop 0
|
1197
1225
|
data.shift
|
1226
|
+
front_matter = []
|
1198
1227
|
@lineno += 1 if increment_linenos
|
1199
1228
|
while !data.empty? && data[0] != '---'
|
1200
1229
|
front_matter << data.shift
|
@@ -1248,7 +1277,7 @@ class PreprocessorReader < Reader
|
|
1248
1277
|
if ((val.start_with? '"') && (val.end_with? '"')) ||
|
1249
1278
|
((val.start_with? '\'') && (val.end_with? '\''))
|
1250
1279
|
quoted = true
|
1251
|
-
val = val
|
1280
|
+
val = val.slice 1, (val.length - 1)
|
1252
1281
|
else
|
1253
1282
|
quoted = false
|
1254
1283
|
end
|
@@ -1289,7 +1318,7 @@ class PreprocessorReader < Reader
|
|
1289
1318
|
end
|
1290
1319
|
|
1291
1320
|
def to_s
|
1292
|
-
%(#<#{self.class}@#{object_id} {path: #{@path.inspect}, line #: #{@lineno}, include depth: #{@include_stack.size}, include stack: [#{@include_stack.map {|inc| inc.to_s }
|
1321
|
+
%(#<#{self.class}@#{object_id} {path: #{@path.inspect}, line #: #{@lineno}, include depth: #{@include_stack.size}, include stack: [#{@include_stack.map {|inc| inc.to_s }.join ', '}]}>)
|
1293
1322
|
end
|
1294
1323
|
end
|
1295
1324
|
end
|
data/lib/asciidoctor/section.rb
CHANGED
@@ -30,7 +30,8 @@ class Section < AbstractBlock
|
|
30
30
|
# Public: Get/Set the flag to indicate whether this is a special section or a child of one
|
31
31
|
attr_accessor :special
|
32
32
|
|
33
|
-
# Public: Get the
|
33
|
+
# Public: Get/Set the flag to indicate whether this section should be numbered.
|
34
|
+
# The sectnum method should only be called if this flag is true.
|
34
35
|
attr_accessor :numbered
|
35
36
|
|
36
37
|
# Public: Get the caption for this section (only relevant for appendices)
|
@@ -111,11 +112,12 @@ class Section < AbstractBlock
|
|
111
112
|
def sectnum(delimiter = '.', append = nil)
|
112
113
|
append ||= (append == false ? '' : delimiter)
|
113
114
|
if @level == 1
|
114
|
-
%(#{@
|
115
|
+
%(#{@numeral}#{append})
|
115
116
|
elsif @level > 1
|
116
|
-
Section === @parent ? %(#{@parent.sectnum(delimiter)}#{@
|
117
|
+
Section === @parent ? %(#{@parent.sectnum(delimiter, delimiter)}#{@numeral}#{append}) : %(#{@numeral}#{append})
|
117
118
|
else # @level == 0
|
118
|
-
|
119
|
+
# NOTE coerce @numeral to int just in case not set; can happen if section nesting is out of sequence
|
120
|
+
%(#{Helpers.int_to_roman @numeral.to_i}#{append})
|
119
121
|
end
|
120
122
|
end
|
121
123
|
|
@@ -175,6 +175,7 @@ module Substitutors
|
|
175
175
|
# returns - The text with the passthrough region substituted with placeholders
|
176
176
|
def extract_passthroughs(text)
|
177
177
|
compat_mode = @document.compat_mode
|
178
|
+
passes = @passthroughs
|
178
179
|
text = text.gsub(InlinePassMacroRx) {
|
179
180
|
# alias match for Ruby 1.8.7 compat
|
180
181
|
m = $~
|
@@ -203,9 +204,9 @@ module Substitutors
|
|
203
204
|
else
|
204
205
|
if boundary == '++' && (attributes.end_with? 'x-')
|
205
206
|
old_behavior = true
|
206
|
-
attributes = attributes
|
207
|
+
attributes = attributes.slice 0, attributes.length - 2
|
207
208
|
end
|
208
|
-
attributes =
|
209
|
+
attributes = parse_quoted_text_attributes attributes
|
209
210
|
end
|
210
211
|
elsif escape_count > 0
|
211
212
|
# NOTE we don't look for nested unconstrained pass macros
|
@@ -213,23 +214,23 @@ module Substitutors
|
|
213
214
|
end
|
214
215
|
subs = (boundary == '+++' ? [] : BASIC_SUBS)
|
215
216
|
|
216
|
-
pass_key =
|
217
|
+
pass_key = passes.size
|
217
218
|
if attributes
|
218
219
|
if old_behavior
|
219
|
-
|
220
|
+
passes[pass_key] = {:text => content, :subs => NORMAL_SUBS, :type => :monospaced, :attributes => attributes}
|
220
221
|
else
|
221
|
-
|
222
|
+
passes[pass_key] = {:text => content, :subs => subs, :type => :unquoted, :attributes => attributes}
|
222
223
|
end
|
223
224
|
else
|
224
|
-
|
225
|
+
passes[pass_key] = {:text => content, :subs => subs}
|
225
226
|
end
|
226
227
|
else # pass:[]
|
227
228
|
if m[6] == RS
|
228
229
|
# NOTE we don't look for nested pass:[] macros
|
229
|
-
next m[0][
|
230
|
+
next m[0].slice 1, m[0].length
|
230
231
|
end
|
231
232
|
|
232
|
-
|
233
|
+
passes[pass_key = passes.size] = {:text => (unescape_brackets m[8]), :subs => (m[7] ? (resolve_pass_subs m[7]) : nil)}
|
233
234
|
end
|
234
235
|
|
235
236
|
%(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
|
@@ -241,7 +242,7 @@ module Substitutors
|
|
241
242
|
m = $~
|
242
243
|
preceding = m[1]
|
243
244
|
attributes = m[2]
|
244
|
-
escape_mark = RS if m[3].start_with? RS
|
245
|
+
escape_mark = RS if (quoted_text = m[3]).start_with? RS
|
245
246
|
format_mark = m[4]
|
246
247
|
content = m[5]
|
247
248
|
|
@@ -249,7 +250,7 @@ module Substitutors
|
|
249
250
|
old_behavior = true
|
250
251
|
else
|
251
252
|
if (old_behavior = (attributes && (attributes.end_with? 'x-')))
|
252
|
-
attributes = attributes
|
253
|
+
attributes = attributes.slice 0, attributes.length - 2
|
253
254
|
end
|
254
255
|
end
|
255
256
|
|
@@ -261,34 +262,34 @@ module Substitutors
|
|
261
262
|
|
262
263
|
if escape_mark
|
263
264
|
# honor the escape of the formatting mark
|
264
|
-
next %(#{preceding}[#{attributes}]#{
|
265
|
+
next %(#{preceding}[#{attributes}]#{quoted_text.slice 1, quoted_text.length})
|
265
266
|
elsif preceding == RS
|
266
267
|
# honor the escape of the attributes
|
267
268
|
preceding = %([#{attributes}])
|
268
269
|
attributes = nil
|
269
270
|
else
|
270
|
-
attributes =
|
271
|
+
attributes = parse_quoted_text_attributes attributes
|
271
272
|
end
|
272
273
|
elsif format_mark == '`' && !old_behavior
|
273
274
|
# extract nested single-plus passthrough; otherwise return unprocessed
|
274
275
|
next (extract_inner_passthrough content, %(#{preceding}#{escape_mark}))
|
275
276
|
elsif escape_mark
|
276
277
|
# honor the escape of the formatting mark
|
277
|
-
next %(#{preceding}#{
|
278
|
+
next %(#{preceding}#{quoted_text.slice 1, quoted_text.length})
|
278
279
|
end
|
279
280
|
|
280
|
-
pass_key =
|
281
|
+
pass_key = passes.size
|
281
282
|
if compat_mode
|
282
|
-
|
283
|
+
passes[pass_key] = {:text => content, :subs => BASIC_SUBS, :attributes => attributes, :type => :monospaced}
|
283
284
|
elsif attributes
|
284
285
|
if old_behavior
|
285
286
|
subs = (format_mark == '`' ? BASIC_SUBS : NORMAL_SUBS)
|
286
|
-
|
287
|
+
passes[pass_key] = {:text => content, :subs => subs, :attributes => attributes, :type => :monospaced}
|
287
288
|
else
|
288
|
-
|
289
|
+
passes[pass_key] = {:text => content, :subs => BASIC_SUBS, :attributes => attributes, :type => :unquoted}
|
289
290
|
end
|
290
291
|
else
|
291
|
-
|
292
|
+
passes[pass_key] = {:text => content, :subs => BASIC_SUBS}
|
292
293
|
end
|
293
294
|
|
294
295
|
%(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
|
@@ -299,8 +300,8 @@ module Substitutors
|
|
299
300
|
# alias match for Ruby 1.8.7 compat
|
300
301
|
m = $~
|
301
302
|
# honor the escape
|
302
|
-
if
|
303
|
-
next m[0][
|
303
|
+
if $&.start_with? RS
|
304
|
+
next m[0].slice 1, m[0].length
|
304
305
|
end
|
305
306
|
|
306
307
|
if (type = m[1].to_sym) == :stem
|
@@ -308,7 +309,7 @@ module Substitutors
|
|
308
309
|
end
|
309
310
|
content = unescape_brackets m[3]
|
310
311
|
subs = m[2] ? (resolve_pass_subs m[2]) : ((@document.basebackend? 'html') ? BASIC_SUBS : nil)
|
311
|
-
|
312
|
+
passes[pass_key = passes.size] = {:text => content, :subs => subs, :type => type}
|
312
313
|
%(#{PASS_START}#{pass_key}#{PASS_END})
|
313
314
|
} if (text.include? ':') && ((text.include? 'stem:') || (text.include? 'math:'))
|
314
315
|
|
@@ -337,13 +338,15 @@ module Substitutors
|
|
337
338
|
#
|
338
339
|
# returns The String text with the passthrough text restored
|
339
340
|
def restore_passthroughs text, outer = true
|
340
|
-
|
341
|
-
|
342
|
-
|
341
|
+
passes = @passthroughs
|
342
|
+
# passthroughs may have been eagerly restored (e.g., footnotes)
|
343
|
+
#if outer && (passes.empty? || !text.include?(PASS_START))
|
344
|
+
# return text
|
345
|
+
#end
|
343
346
|
|
344
347
|
text.gsub(PassSlotRx) {
|
345
348
|
# NOTE we can't remove entry from map because placeholder may have been duplicated by other substitutions
|
346
|
-
pass =
|
349
|
+
pass = passes[$1.to_i]
|
347
350
|
subbed_text = apply_subs(pass[:text], pass[:subs])
|
348
351
|
if (type = pass[:type])
|
349
352
|
subbed_text = Inline.new(self, :quoted, subbed_text, :type => type, :attributes => pass[:attributes]).convert
|
@@ -352,10 +355,9 @@ module Substitutors
|
|
352
355
|
}
|
353
356
|
ensure
|
354
357
|
# free memory if in outer call...we don't need these anymore
|
355
|
-
|
358
|
+
passes.clear if outer
|
356
359
|
end
|
357
360
|
|
358
|
-
|
359
361
|
if RUBY_ENGINE == 'opal'
|
360
362
|
def sub_quotes text
|
361
363
|
if QuotedTextSniffRx[compat = @document.compat_mode].match? text
|
@@ -463,7 +465,7 @@ module Substitutors
|
|
463
465
|
def sub_attributes text, opts = {}
|
464
466
|
doc_attrs = @document.attributes
|
465
467
|
drop = drop_line = drop_empty_line = attribute_undefined = attribute_missing = nil
|
466
|
-
|
468
|
+
text = text.gsub AttributeReferenceRx do
|
467
469
|
# escaped attribute, return unescaped
|
468
470
|
if $1 == RS || $4 == RS
|
469
471
|
%({#{$2}})
|
@@ -504,21 +506,21 @@ module Substitutors
|
|
504
506
|
end
|
505
507
|
|
506
508
|
if drop
|
507
|
-
# drop lines from
|
509
|
+
# drop lines from text
|
508
510
|
if drop_empty_line
|
509
|
-
lines = (
|
511
|
+
lines = (text.tr_s DEL, DEL).split LF, -1
|
510
512
|
if drop_line
|
511
513
|
(lines.reject {|line| line == DEL || line == CAN || (line.start_with? CAN) || (line.include? CAN) }.join LF).delete DEL
|
512
514
|
else
|
513
515
|
(lines.reject {|line| line == DEL }.join LF).delete DEL
|
514
516
|
end
|
515
|
-
elsif
|
516
|
-
(
|
517
|
+
elsif text.include? LF
|
518
|
+
(text.split LF, -1).reject {|line| line == CAN || (line.start_with? CAN) || (line.include? CAN) }.join LF
|
517
519
|
else
|
518
520
|
''
|
519
521
|
end
|
520
522
|
else
|
521
|
-
|
523
|
+
text
|
522
524
|
end
|
523
525
|
end
|
524
526
|
|
@@ -529,20 +531,19 @@ module Substitutors
|
|
529
531
|
# source - The String text to process
|
530
532
|
#
|
531
533
|
# returns The converted String text
|
532
|
-
def sub_macros(
|
533
|
-
#return
|
534
|
+
def sub_macros(text)
|
535
|
+
#return text if text.nil_or_empty?
|
534
536
|
# some look ahead assertions to cut unnecessary regex calls
|
535
537
|
found = {}
|
536
|
-
found_square_bracket = found[:square_bracket] = (
|
537
|
-
found_colon =
|
538
|
+
found_square_bracket = found[:square_bracket] = (text.include? '[')
|
539
|
+
found_colon = text.include? ':'
|
538
540
|
found_macroish = found[:macroish] = found_square_bracket && found_colon
|
539
|
-
found_macroish_short = found_macroish && (
|
541
|
+
found_macroish_short = found_macroish && (text.include? ':[')
|
540
542
|
doc_attrs = (doc = @document).attributes
|
541
|
-
result = source
|
542
543
|
|
543
544
|
if doc_attrs.key? 'experimental'
|
544
|
-
if found_macroish_short && ((
|
545
|
-
|
545
|
+
if found_macroish_short && ((text.include? 'kbd:') || (text.include? 'btn:'))
|
546
|
+
text = text.gsub(InlineKbdBtnMacroRx) {
|
546
547
|
# honor the escape
|
547
548
|
if $1
|
548
549
|
$&.slice 1, $&.length
|
@@ -570,13 +571,13 @@ module Substitutors
|
|
570
571
|
}
|
571
572
|
end
|
572
573
|
|
573
|
-
if found_macroish && (
|
574
|
-
|
574
|
+
if found_macroish && (text.include? 'menu:')
|
575
|
+
text = text.gsub(InlineMenuMacroRx) {
|
575
576
|
# alias match for Ruby 1.8.7 compat
|
576
577
|
m = $~
|
577
578
|
# honor the escape
|
578
|
-
if
|
579
|
-
next
|
579
|
+
if $&.start_with? RS
|
580
|
+
next m[0].slice 1, m[0].length
|
580
581
|
end
|
581
582
|
|
582
583
|
menu, items = m[1], m[2]
|
@@ -597,13 +598,13 @@ module Substitutors
|
|
597
598
|
}
|
598
599
|
end
|
599
600
|
|
600
|
-
if (
|
601
|
-
|
601
|
+
if (text.include? '"') && (text.include? '>')
|
602
|
+
text = text.gsub(InlineMenuRx) {
|
602
603
|
# alias match for Ruby 1.8.7 compat
|
603
604
|
m = $~
|
604
605
|
# honor the escape
|
605
|
-
if
|
606
|
-
next
|
606
|
+
if $&.start_with? RS
|
607
|
+
next m[0].slice 1, m[0].length
|
607
608
|
end
|
608
609
|
|
609
610
|
input = m[1]
|
@@ -619,12 +620,12 @@ module Substitutors
|
|
619
620
|
# TODO this handling needs some cleanup
|
620
621
|
if (extensions = doc.extensions) && extensions.inline_macros? # && found_macroish
|
621
622
|
extensions.inline_macros.each do |extension|
|
622
|
-
|
623
|
+
text = text.gsub(extension.instance.regexp) {
|
623
624
|
# alias match for Ruby 1.8.7 compat
|
624
625
|
m = $~
|
625
626
|
# honor the escape
|
626
|
-
if
|
627
|
-
next m[0][
|
627
|
+
if $&.start_with? RS
|
628
|
+
next m[0].slice 1, m[0].length
|
628
629
|
end
|
629
630
|
|
630
631
|
if (m.names rescue []).empty?
|
@@ -639,8 +640,8 @@ module Substitutors
|
|
639
640
|
content = unescape_bracketed_text content
|
640
641
|
if extconf[:content_model] == :attributes
|
641
642
|
# QUESTION should we store the text in the _text key?
|
642
|
-
#
|
643
|
-
parse_attributes content, extconf[:pos_attrs] || [], :
|
643
|
+
# NOTE bracked text has already been escaped
|
644
|
+
parse_attributes content, extconf[:pos_attrs] || [], :into => attributes
|
644
645
|
else
|
645
646
|
attributes['text'] = content
|
646
647
|
end
|
@@ -652,17 +653,15 @@ module Substitutors
|
|
652
653
|
end
|
653
654
|
end
|
654
655
|
|
655
|
-
if found_macroish && ((
|
656
|
+
if found_macroish && ((text.include? 'image:') || (text.include? 'icon:'))
|
656
657
|
# image:filename.png[Alt Text]
|
657
|
-
|
658
|
+
text = text.gsub(InlineImageMacroRx) {
|
658
659
|
# alias match for Ruby 1.8.7 compat
|
659
660
|
m = $~
|
660
661
|
# honor the escape
|
661
662
|
if (captured = $&).start_with? RS
|
662
|
-
next captured
|
663
|
-
|
664
|
-
|
665
|
-
if captured.start_with? 'icon:'
|
663
|
+
next captured.slice 1, captured.length
|
664
|
+
elsif captured.start_with? 'icon:'
|
666
665
|
type, posattrs = 'icon', ['size']
|
667
666
|
else
|
668
667
|
type, posattrs = 'image', ['alt', 'width', 'height']
|
@@ -671,25 +670,26 @@ module Substitutors
|
|
671
670
|
# TODO remove this special case once titles use normal substitution order
|
672
671
|
target = sub_attributes target
|
673
672
|
end
|
674
|
-
|
675
|
-
attrs =
|
673
|
+
attrs = parse_attributes m[2], posattrs, :unescape_input => true
|
674
|
+
doc.register :images, [target, (attrs['imagesdir'] = doc_attrs['imagesdir'])] unless type == 'icon'
|
676
675
|
attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
|
677
676
|
Inline.new(self, :image, nil, :type => type, :target => target, :attributes => attrs).convert
|
678
677
|
}
|
679
678
|
end
|
680
679
|
|
681
|
-
if ((
|
680
|
+
if ((text.include? '((') && (text.include? '))')) || (found_macroish_short && (text.include? 'dexterm'))
|
682
681
|
# (((Tigers,Big cats)))
|
683
682
|
# indexterm:[Tigers,Big cats]
|
684
683
|
# ((Tigers))
|
685
684
|
# indexterm2:[Tigers]
|
686
|
-
|
685
|
+
text = text.gsub(InlineIndextermMacroRx) {
|
686
|
+
captured = $&
|
687
687
|
case $1
|
688
688
|
when 'indexterm'
|
689
689
|
text = $2
|
690
690
|
# honor the escape
|
691
|
-
if
|
692
|
-
next
|
691
|
+
if captured.start_with? RS
|
692
|
+
next captured.slice 1, captured.length
|
693
693
|
end
|
694
694
|
# indexterm:[Tigers,Big cats]
|
695
695
|
terms = split_simple_csv normalize_string text, true
|
@@ -698,8 +698,8 @@ module Substitutors
|
|
698
698
|
when 'indexterm2'
|
699
699
|
text = $2
|
700
700
|
# honor the escape
|
701
|
-
if
|
702
|
-
next
|
701
|
+
if captured.start_with? RS
|
702
|
+
next captured.slice 1, captured.length
|
703
703
|
end
|
704
704
|
# indexterm2:[Tigers]
|
705
705
|
term = normalize_string text, true
|
@@ -708,13 +708,13 @@ module Substitutors
|
|
708
708
|
else
|
709
709
|
text = $3
|
710
710
|
# honor the escape
|
711
|
-
if
|
711
|
+
if captured.start_with? RS
|
712
712
|
# escape concealed index term, but process nested flow index term
|
713
713
|
if (text.start_with? '(') && (text.end_with? ')')
|
714
714
|
text = text.slice 1, text.length - 2
|
715
715
|
visible, before, after = true, '(', ')'
|
716
716
|
else
|
717
|
-
next
|
717
|
+
next captured.slice 1, captured.length
|
718
718
|
end
|
719
719
|
else
|
720
720
|
visible = true
|
@@ -732,29 +732,29 @@ module Substitutors
|
|
732
732
|
# ((Tigers))
|
733
733
|
term = normalize_string text
|
734
734
|
doc.register :indexterms, [term]
|
735
|
-
|
735
|
+
subbed_term = (Inline.new self, :indexterm, term, :type => :visible).convert
|
736
736
|
else
|
737
737
|
# (((Tigers,Big cats)))
|
738
738
|
terms = split_simple_csv(normalize_string text)
|
739
739
|
doc.register :indexterms, terms
|
740
|
-
|
740
|
+
subbed_term = (Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
|
741
741
|
end
|
742
|
-
before ? %(#{before}#{
|
742
|
+
before ? %(#{before}#{subbed_term}#{after}) : subbed_term
|
743
743
|
end
|
744
744
|
}
|
745
745
|
end
|
746
746
|
|
747
|
-
if found_colon && (
|
747
|
+
if found_colon && (text.include? '://')
|
748
748
|
# inline urls, target[text] (optionally prefixed with link: and optionally surrounded by <>)
|
749
|
-
|
749
|
+
text = text.gsub(InlineLinkRx) {
|
750
750
|
# alias match for Ruby 1.8.7 compat
|
751
751
|
m = $~
|
752
752
|
# honor the escape
|
753
|
-
if
|
754
|
-
next %(#{m[1]}#{
|
753
|
+
if (target = $2).start_with? RS
|
754
|
+
next %(#{m[1]}#{target.slice 1, target.length}#{m[3]})
|
755
755
|
end
|
756
756
|
# NOTE if text is non-nil, then we've matched a formal macro (i.e., trailing square brackets)
|
757
|
-
prefix,
|
757
|
+
prefix, text, suffix = m[1], (macro = m[3]) || '', ''
|
758
758
|
if prefix == 'link:'
|
759
759
|
if macro
|
760
760
|
prefix = ''
|
@@ -773,8 +773,8 @@ module Substitutors
|
|
773
773
|
when ';'
|
774
774
|
# strip <> around URI
|
775
775
|
if prefix.start_with?('<') && target.end_with?('>')
|
776
|
-
prefix = prefix
|
777
|
-
target = target
|
776
|
+
prefix = prefix.slice 4, prefix.length
|
777
|
+
target = target.slice 0, target.length - 4
|
778
778
|
# strip trailing ;
|
779
779
|
# check for trailing );
|
780
780
|
elsif (target = target.chop).end_with?(')')
|
@@ -825,6 +825,7 @@ module Substitutors
|
|
825
825
|
end
|
826
826
|
|
827
827
|
if text.empty?
|
828
|
+
# NOTE it's not possible for the URI scheme to be bare in this case
|
828
829
|
text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
|
829
830
|
if attrs
|
830
831
|
attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
|
@@ -839,14 +840,14 @@ module Substitutors
|
|
839
840
|
}
|
840
841
|
end
|
841
842
|
|
842
|
-
if found_macroish && ((
|
843
|
+
if found_macroish && ((text.include? 'link:') || (text.include? 'mailto:'))
|
843
844
|
# inline link macros, link:target[text]
|
844
|
-
|
845
|
+
text = text.gsub(InlineLinkMacroRx) {
|
845
846
|
# alias match for Ruby 1.8.7 compat
|
846
847
|
m = $~
|
847
848
|
# honor the escape
|
848
|
-
if
|
849
|
-
next m[0][
|
849
|
+
if $&.start_with? RS
|
850
|
+
next m[0].slice 1, m[0].length
|
850
851
|
end
|
851
852
|
target = (mailto = m[1]) ? %(mailto:#{m[2]}) : m[2]
|
852
853
|
attrs, link_opts = nil, { :type => :link }
|
@@ -893,7 +894,13 @@ module Substitutors
|
|
893
894
|
if mailto
|
894
895
|
text = m[2]
|
895
896
|
else
|
896
|
-
|
897
|
+
if doc_attrs.key? 'hide-uri-scheme'
|
898
|
+
if (text = target.sub UriSniffRx, '').empty?
|
899
|
+
text = target
|
900
|
+
end
|
901
|
+
else
|
902
|
+
text = target
|
903
|
+
end
|
897
904
|
if attrs
|
898
905
|
attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
|
899
906
|
else
|
@@ -909,11 +916,11 @@ module Substitutors
|
|
909
916
|
}
|
910
917
|
end
|
911
918
|
|
912
|
-
if
|
913
|
-
|
919
|
+
if text.include? '@'
|
920
|
+
text = text.gsub(InlineEmailRx) {
|
914
921
|
address, tip = $&, $1
|
915
922
|
if tip
|
916
|
-
next (tip == RS ? address
|
923
|
+
next (tip == RS ? (address.slice 1, address.length) : address)
|
917
924
|
end
|
918
925
|
|
919
926
|
target = %(mailto:#{address})
|
@@ -924,13 +931,13 @@ module Substitutors
|
|
924
931
|
}
|
925
932
|
end
|
926
933
|
|
927
|
-
if found_macroish && (
|
928
|
-
|
934
|
+
if found_macroish && (text.include? 'tnote')
|
935
|
+
text = text.gsub(InlineFootnoteMacroRx) {
|
929
936
|
# alias match for Ruby 1.8.7 compat
|
930
937
|
m = $~
|
931
938
|
# honor the escape
|
932
|
-
if
|
933
|
-
next m[0][
|
939
|
+
if $&.start_with? RS
|
940
|
+
next m[0].slice 1, m[0].length
|
934
941
|
end
|
935
942
|
if m[1] # footnoteref (legacy)
|
936
943
|
id, text = (m[3] || '').split(',', 2)
|
@@ -966,7 +973,7 @@ module Substitutors
|
|
966
973
|
}
|
967
974
|
end
|
968
975
|
|
969
|
-
sub_inline_xrefs(sub_inline_anchors(
|
976
|
+
sub_inline_xrefs(sub_inline_anchors(text, found), found)
|
970
977
|
end
|
971
978
|
|
972
979
|
# Internal: Substitute normal and bibliographic anchors
|
@@ -1007,8 +1014,8 @@ module Substitutors
|
|
1007
1014
|
# alias match for Ruby 1.8.7 compat
|
1008
1015
|
m = $~
|
1009
1016
|
# honor the escape
|
1010
|
-
if
|
1011
|
-
next m[0][
|
1017
|
+
if $&.start_with? RS
|
1018
|
+
next m[0].slice 1, m[0].length
|
1012
1019
|
end
|
1013
1020
|
attrs, doc = {}, @document
|
1014
1021
|
if (refid = m[1])
|
@@ -1098,14 +1105,16 @@ module Substitutors
|
|
1098
1105
|
#
|
1099
1106
|
# Returns the converted String text
|
1100
1107
|
def sub_callouts(text)
|
1101
|
-
|
1102
|
-
|
1108
|
+
callout_rx = (attr? 'line-comment') ? CalloutSourceRxMap[attr 'line-comment'] : CalloutSourceRx
|
1109
|
+
autonum = 0
|
1103
1110
|
text.gsub(callout_rx) {
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1111
|
+
# honor the escape
|
1112
|
+
if $2
|
1113
|
+
# use sub since it might be behind a line comment
|
1114
|
+
$&.sub(RS, '')
|
1115
|
+
else
|
1116
|
+
Inline.new(self, :callout, $4 == '.' ? (autonum += 1).to_s : $4, :id => @document.callouts.read_next_id, :attributes => { 'guard' => $1 }).convert
|
1107
1117
|
end
|
1108
|
-
Inline.new(self, :callout, $3, :id => @document.callouts.read_next_id).convert
|
1109
1118
|
}
|
1110
1119
|
end
|
1111
1120
|
|
@@ -1121,7 +1130,7 @@ module Substitutors
|
|
1121
1130
|
last = lines.pop
|
1122
1131
|
(lines.map {|line|
|
1123
1132
|
Inline.new(self, :break, (line.end_with? HARD_LINE_BREAK) ? (line.slice 0, line.length - 2) : line, :type => :line).convert
|
1124
|
-
} << last)
|
1133
|
+
} << last).join LF
|
1125
1134
|
elsif (text.include? PLUS) && (text.include? HARD_LINE_BREAK)
|
1126
1135
|
text.gsub(HardLineBreakRx) { Inline.new(self, :break, $1, :type => :line).convert }
|
1127
1136
|
else
|
@@ -1141,7 +1150,7 @@ module Substitutors
|
|
1141
1150
|
if scope == :constrained && (attrs = match[2])
|
1142
1151
|
unescaped_attrs = %([#{attrs}])
|
1143
1152
|
else
|
1144
|
-
return match[0][
|
1153
|
+
return match[0].slice 1, match[0].length
|
1145
1154
|
end
|
1146
1155
|
end
|
1147
1156
|
|
@@ -1172,7 +1181,7 @@ module Substitutors
|
|
1172
1181
|
# Returns a Hash of attributes (role and id only)
|
1173
1182
|
def parse_quoted_text_attributes str
|
1174
1183
|
# NOTE attributes are typically resolved after quoted text, so substitute eagerly
|
1175
|
-
str = sub_attributes str
|
1184
|
+
str = sub_attributes str if str.include? ATTR_REF_HEAD
|
1176
1185
|
# for compliance, only consider first positional attribute
|
1177
1186
|
str = str.slice 0, (str.index ',') if str.include? ','
|
1178
1187
|
|
@@ -1199,30 +1208,37 @@ module Substitutors
|
|
1199
1208
|
|
1200
1209
|
attrs = {}
|
1201
1210
|
attrs['id'] = id if id
|
1202
|
-
attrs['role'] = roles
|
1211
|
+
attrs['role'] = roles.join ' ' unless roles.empty?
|
1203
1212
|
attrs
|
1204
1213
|
else
|
1205
1214
|
{'role' => str}
|
1206
1215
|
end
|
1207
1216
|
end
|
1208
1217
|
|
1209
|
-
# Internal: Parse
|
1218
|
+
# Internal: Parse attributes in name or name=value format from a comma-separated String
|
1210
1219
|
#
|
1211
|
-
#
|
1212
|
-
# posattrs
|
1220
|
+
# attrlist - A comma-separated String list of attributes in name or name=value format.
|
1221
|
+
# posattrs - An Array of positional attribute names (default: []).
|
1222
|
+
# opts - A Hash of options to control how the string is parsed (default: {}):
|
1223
|
+
# :into - The Hash to parse the attributes into (optional, default: false).
|
1224
|
+
# :sub_input - A Boolean that indicates whether to substitute attributes prior to
|
1225
|
+
# parsing (optional, default: false).
|
1226
|
+
# :sub_result - A Boolean that indicates whether to apply substitutions
|
1227
|
+
# single-quoted attribute values (optional, default: true).
|
1228
|
+
# :unescape_input - A Boolean that indicates whether to unescape square brackets prior
|
1229
|
+
# to parsing (optional, default: false).
|
1213
1230
|
#
|
1214
|
-
#
|
1215
|
-
def parse_attributes
|
1216
|
-
return unless
|
1217
|
-
|
1218
|
-
|
1219
|
-
attrline = unescape_bracketed_text attrline if opts[:unescape_input]
|
1231
|
+
# Returns an empty Hash if attrlist is nil or empty, otherwise a Hash of parsed attributes.
|
1232
|
+
def parse_attributes attrlist, posattrs = [], opts = {}
|
1233
|
+
return {} unless attrlist && !attrlist.empty?
|
1234
|
+
attrlist = @document.sub_attributes attrlist if opts[:sub_input] && (attrlist.include? ATTR_REF_HEAD)
|
1235
|
+
attrlist = unescape_bracketed_text attrlist if opts[:unescape_input]
|
1220
1236
|
# substitutions are only performed on attribute values if block is not nil
|
1221
|
-
block = opts
|
1237
|
+
block = self if opts[:sub_result]
|
1222
1238
|
if (into = opts[:into])
|
1223
|
-
AttributeList.new(
|
1239
|
+
AttributeList.new(attrlist, block).parse_into(into, posattrs)
|
1224
1240
|
else
|
1225
|
-
AttributeList.new(
|
1241
|
+
AttributeList.new(attrlist, block).parse(posattrs)
|
1226
1242
|
end
|
1227
1243
|
end
|
1228
1244
|
|
@@ -1328,10 +1344,10 @@ module Substitutors
|
|
1328
1344
|
if modifiers_present
|
1329
1345
|
if (first = key.chr) == '+'
|
1330
1346
|
modifier_operation = :append
|
1331
|
-
key = key
|
1347
|
+
key = key.slice 1, key.length
|
1332
1348
|
elsif first == '-'
|
1333
1349
|
modifier_operation = :remove
|
1334
|
-
key = key
|
1350
|
+
key = key.slice 1, key.length
|
1335
1351
|
elsif key.end_with? '+'
|
1336
1352
|
modifier_operation = :prepend
|
1337
1353
|
key = key.chop
|
@@ -1355,7 +1371,7 @@ module Substitutors
|
|
1355
1371
|
end
|
1356
1372
|
|
1357
1373
|
if modifier_operation
|
1358
|
-
candidates ||= (defaults ? defaults.
|
1374
|
+
candidates ||= (defaults ? (defaults.drop 0) : [])
|
1359
1375
|
case modifier_operation
|
1360
1376
|
when :append
|
1361
1377
|
candidates += resolved_keys
|
@@ -1374,7 +1390,7 @@ module Substitutors
|
|
1374
1390
|
resolved = candidates & SUB_OPTIONS[type]
|
1375
1391
|
unless (candidates - resolved).empty?
|
1376
1392
|
invalid = candidates - resolved
|
1377
|
-
logger.warn %(invalid substitution type#{invalid.size > 1 ? 's' : ''}#{subject ? ' for ' : ''}#{subject}: #{invalid
|
1393
|
+
logger.warn %(invalid substitution type#{invalid.size > 1 ? 's' : ''}#{subject ? ' for ' : ''}#{subject}: #{invalid.join ', '})
|
1378
1394
|
end
|
1379
1395
|
resolved
|
1380
1396
|
end
|
@@ -1433,25 +1449,22 @@ module Substitutors
|
|
1433
1449
|
if process_callouts
|
1434
1450
|
callout_marks = {}
|
1435
1451
|
last = -1
|
1436
|
-
|
1437
|
-
callout_rx = (attr? 'line-comment') ? /(?:#{::Regexp.escape(attr 'line-comment')} )?#{CalloutExtractRxt}/ : CalloutExtractRx
|
1452
|
+
callout_rx = (attr? 'line-comment') ? CalloutExtractRxMap[attr 'line-comment'] : CalloutExtractRx
|
1438
1453
|
# extract callout marks, indexed by line number
|
1439
1454
|
source = source.split(LF, -1).map {|line|
|
1440
1455
|
lineno = lineno + 1
|
1441
1456
|
line.gsub(callout_rx) {
|
1442
|
-
# alias match for Ruby 1.8.7 compat
|
1443
|
-
m = $~
|
1444
1457
|
# honor the escape
|
1445
|
-
if
|
1446
|
-
#
|
1447
|
-
|
1458
|
+
if $2
|
1459
|
+
# use sub since it might be behind a line comment
|
1460
|
+
$&.sub(RS, '')
|
1448
1461
|
else
|
1449
|
-
(callout_marks[lineno] ||= []) <<
|
1462
|
+
(callout_marks[lineno] ||= []) << [$1, $4]
|
1450
1463
|
last = lineno
|
1451
1464
|
nil
|
1452
1465
|
end
|
1453
1466
|
}
|
1454
|
-
}
|
1467
|
+
}.join LF
|
1455
1468
|
callout_on_last = (last == lineno)
|
1456
1469
|
callout_marks = nil if callout_marks.empty?
|
1457
1470
|
else
|
@@ -1464,16 +1477,19 @@ module Substitutors
|
|
1464
1477
|
case highlighter
|
1465
1478
|
when 'coderay'
|
1466
1479
|
if (linenums_mode = (attr? 'linenums', nil, false) ? (@document.attributes['coderay-linenums-mode'] || :table).to_sym : nil)
|
1480
|
+
start = 1 if (start = (attr 'start', nil, 1).to_i) < 1
|
1467
1481
|
if attr? 'highlight', nil, false
|
1468
|
-
highlight_lines =
|
1482
|
+
highlight_lines = resolve_lines_to_highlight source, (attr 'highlight', nil, false)
|
1469
1483
|
end
|
1470
1484
|
end
|
1471
1485
|
result = ::CodeRay::Duo[attr('language', :text, false).to_sym, :html, {
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1486
|
+
:css => (@document.attributes['coderay-css'] || :class).to_sym,
|
1487
|
+
:line_numbers => linenums_mode,
|
1488
|
+
:line_number_start => start,
|
1489
|
+
:line_number_anchors => false,
|
1490
|
+
:highlight_lines => highlight_lines,
|
1491
|
+
:bold_every => false
|
1492
|
+
}].highlight source
|
1477
1493
|
when 'pygments'
|
1478
1494
|
lexer = ::Pygments::Lexer.find_by_alias(attr 'language', 'text', false) || ::Pygments::Lexer.find_by_mimetype('text/plain')
|
1479
1495
|
opts = { :cssclass => 'pyhl', :classprefix => 'tok-', :nobackground => true, :stripnl => false }
|
@@ -1483,13 +1499,14 @@ module Substitutors
|
|
1483
1499
|
opts[:style] = (@document.attributes['pygments-style'] || Stylesheets::DEFAULT_PYGMENTS_STYLE)
|
1484
1500
|
end
|
1485
1501
|
if attr? 'highlight', nil, false
|
1486
|
-
unless (highlight_lines =
|
1487
|
-
opts[:hl_lines] = highlight_lines
|
1502
|
+
unless (highlight_lines = resolve_lines_to_highlight source, (attr 'highlight', nil, false)).empty?
|
1503
|
+
opts[:hl_lines] = highlight_lines.join ' '
|
1488
1504
|
end
|
1489
1505
|
end
|
1490
1506
|
# NOTE highlight can return nil if something goes wrong; fallback to source if this happens
|
1491
1507
|
# TODO we could add the line numbers in ourselves instead of having to strip out the junk
|
1492
|
-
if (attr? 'linenums', nil, false) && (opts[:
|
1508
|
+
if (attr? 'linenums', nil, false) && (opts[:linenostart] = (start = attr 'start', 1, false).to_i < 1 ? 1 : start) &&
|
1509
|
+
(opts[:linenos] = @document.attributes['pygments-linenums-mode'] || 'table') == 'table'
|
1493
1510
|
linenums_mode = :table
|
1494
1511
|
if (result = lexer.highlight source, :options => opts)
|
1495
1512
|
result = (result.sub PygmentsWrapperDivRx, '\1').gsub PygmentsWrapperPreRx, '\1'
|
@@ -1510,6 +1527,7 @@ module Substitutors
|
|
1510
1527
|
|
1511
1528
|
if process_callouts && callout_marks
|
1512
1529
|
lineno = 0
|
1530
|
+
autonum = 0
|
1513
1531
|
reached_code = linenums_mode != :table
|
1514
1532
|
result.split(LF, -1).map {|line|
|
1515
1533
|
unless reached_code
|
@@ -1527,32 +1545,35 @@ module Substitutors
|
|
1527
1545
|
end
|
1528
1546
|
end
|
1529
1547
|
if conums.size == 1
|
1530
|
-
|
1548
|
+
guard, conum = conums[0]
|
1549
|
+
%(#{line}#{Inline.new(self, :callout, conum == '.' ? (autonum += 1).to_s : conum, :id => @document.callouts.read_next_id, :attributes => { 'guard' => guard }).convert}#{tail})
|
1531
1550
|
else
|
1532
|
-
conums_markup = conums.map {|
|
1551
|
+
conums_markup = conums.map {|guard_it, conum_it| Inline.new(self, :callout, conum_it == '.' ? (autonum += 1).to_s : conum_it, :id => @document.callouts.read_next_id, :attributes => { 'guard' => guard_it }).convert }.join ' '
|
1533
1552
|
%(#{line}#{conums_markup}#{tail})
|
1534
1553
|
end
|
1535
1554
|
else
|
1536
1555
|
line
|
1537
1556
|
end
|
1538
|
-
}
|
1557
|
+
}.join LF
|
1539
1558
|
else
|
1540
1559
|
result
|
1541
1560
|
end
|
1542
1561
|
end
|
1543
1562
|
|
1544
1563
|
# e.g., highlight="1-5, !2, 10" or highlight=1-5;!2,10
|
1545
|
-
def
|
1564
|
+
def resolve_lines_to_highlight source, spec
|
1546
1565
|
lines = []
|
1547
|
-
|
1566
|
+
spec = spec.delete ' ' if spec.include? ' '
|
1567
|
+
((spec.include? ',') ? (spec.split ',') : (spec.split ';')).map do |entry|
|
1548
1568
|
negate = false
|
1549
1569
|
if entry.start_with? '!'
|
1550
|
-
entry = entry
|
1570
|
+
entry = entry.slice 1, entry.length
|
1551
1571
|
negate = true
|
1552
1572
|
end
|
1553
|
-
if entry.include? '-'
|
1554
|
-
|
1555
|
-
|
1573
|
+
if (delim = (entry.include? '..') ? '..' : ((entry.include? '-') ? '-' : nil))
|
1574
|
+
from, to = entry.split delim, 2
|
1575
|
+
to = (source.count LF) + 1 if to.empty? || (to = to.to_i) < 0
|
1576
|
+
line_nums = (::Range.new from.to_i, to).to_a
|
1556
1577
|
if negate
|
1557
1578
|
lines -= line_nums
|
1558
1579
|
else
|
@@ -1612,7 +1633,7 @@ module Substitutors
|
|
1612
1633
|
if (custom_subs = @attributes['subs'])
|
1613
1634
|
@subs = (resolve_block_subs custom_subs, default_subs, @context) || []
|
1614
1635
|
else
|
1615
|
-
@subs = default_subs.
|
1636
|
+
@subs = default_subs.drop 0
|
1616
1637
|
end
|
1617
1638
|
|
1618
1639
|
# QUESION delegate this logic to a method?
|