asciidoctor 0.1.2 → 0.1.3
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/Gemfile +10 -0
- data/Guardfile +18 -0
- data/LICENSE +1 -1
- data/README.adoc +65 -21
- data/Rakefile +10 -0
- data/asciidoctor.gemspec +17 -35
- data/compat/asciidoc.conf +130 -13
- data/lib/asciidoctor.rb +107 -87
- data/lib/asciidoctor/abstract_block.rb +6 -2
- data/lib/asciidoctor/abstract_node.rb +21 -13
- data/lib/asciidoctor/attribute_list.rb +2 -5
- data/{stylesheets/asciidoctor.css → lib/asciidoctor/backends/_stylesheets.rb} +96 -46
- data/lib/asciidoctor/backends/base_template.rb +9 -4
- data/lib/asciidoctor/backends/docbook45.rb +246 -138
- data/lib/asciidoctor/backends/html5.rb +580 -381
- data/lib/asciidoctor/block.rb +2 -50
- data/lib/asciidoctor/cli/options.rb +9 -8
- data/lib/asciidoctor/document.rb +35 -45
- data/lib/asciidoctor/helpers.rb +10 -0
- data/lib/asciidoctor/lexer.rb +456 -148
- data/lib/asciidoctor/list_item.rb +0 -21
- data/lib/asciidoctor/path_resolver.rb +18 -12
- data/lib/asciidoctor/reader.rb +71 -26
- data/lib/asciidoctor/renderer.rb +2 -19
- data/lib/asciidoctor/section.rb +0 -1
- data/lib/asciidoctor/substituters.rb +150 -36
- data/lib/asciidoctor/table.rb +30 -24
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +22 -16
- data/man/asciidoctor.ad +24 -16
- data/test/attributes_test.rb +50 -0
- data/test/blocks_test.rb +660 -9
- data/test/document_test.rb +191 -14
- data/test/fixtures/encoding.asciidoc +8 -0
- data/test/invoker_test.rb +47 -0
- data/test/lexer_test.rb +172 -0
- data/test/links_test.rb +28 -0
- data/test/lists_test.rb +172 -13
- data/test/options_test.rb +29 -2
- data/test/paragraphs_test.rb +105 -47
- data/test/paths_test.rb +3 -3
- data/test/reader_test.rb +46 -0
- data/test/sections_test.rb +365 -12
- data/test/substitutions_test.rb +127 -11
- data/test/tables_test.rb +81 -14
- data/test/test_helper.rb +18 -7
- data/test/text_test.rb +17 -5
- metadata +9 -36
@@ -55,27 +55,6 @@ class ListItem < AbstractBlock
|
|
55
55
|
nil
|
56
56
|
end
|
57
57
|
|
58
|
-
def splain(parent_level = 0)
|
59
|
-
parent_level += 1
|
60
|
-
Debug.puts_indented(parent_level, "List Item anchor: #{@anchor}") unless @anchor.nil?
|
61
|
-
Debug.puts_indented(parent_level, "Text: #{@text}") unless @text.nil?
|
62
|
-
|
63
|
-
Debug.puts_indented(parent_level, "Blocks: #{@blocks.count}")
|
64
|
-
|
65
|
-
if @blocks.any?
|
66
|
-
Debug.puts_indented(parent_level, "Blocks content (#{@blocks.count}):")
|
67
|
-
@blocks.each_with_index do |block, i|
|
68
|
-
Debug.puts_indented(parent_level, "v" * (60 - parent_level*2))
|
69
|
-
Debug.puts_indented(parent_level, "Block ##{i} is a #{block.class}")
|
70
|
-
Debug.puts_indented(parent_level, "Name is #{block.title rescue 'n/a'}")
|
71
|
-
Debug.puts_indented(parent_level, "=" * 40)
|
72
|
-
block.splain(parent_level) if block.respond_to? :splain
|
73
|
-
Debug.puts_indented(parent_level, "^" * (60 - parent_level*2))
|
74
|
-
end
|
75
|
-
end
|
76
|
-
nil
|
77
|
-
end
|
78
|
-
|
79
58
|
def to_s
|
80
59
|
"#{super.to_s} - #@context [text:#@text, blocks:#{(@blocks || []).size}]"
|
81
60
|
end
|
@@ -76,6 +76,12 @@ module Asciidoctor
|
|
76
76
|
# resolver.system_path('../../../css', '../../..', '/path/to/docs')
|
77
77
|
# => '/path/to/docs/css'
|
78
78
|
#
|
79
|
+
# resolver.system_path('..', 'C:\\data\\docs\\assets', 'C:\\data\\docs')
|
80
|
+
# => 'C:/data/docs'
|
81
|
+
#
|
82
|
+
# resolver.system_path('..\\..\\css', 'C:\\data\\docs\\assets', 'C:\\data\\docs')
|
83
|
+
# => 'C:/data/docs/css'
|
84
|
+
#
|
79
85
|
# begin
|
80
86
|
# resolver.system_path('../../../css', '../../..', '/path/to/docs', :recover => false)
|
81
87
|
# rescue SecurityError => e
|
@@ -98,14 +104,13 @@ class PathResolver
|
|
98
104
|
DOT_DOT = '..'
|
99
105
|
SLASH = '/'
|
100
106
|
BACKSLASH = '\\'
|
101
|
-
PARTITION_RE = /\/+/
|
102
107
|
WIN_ROOT_RE = /^[[:alpha:]]:(?:\\|\/)/
|
103
108
|
|
104
109
|
attr_accessor :file_separator
|
105
110
|
attr_accessor :working_dir
|
106
111
|
|
107
112
|
# Public: Construct a new instance of PathResolver, optionally specifying the
|
108
|
-
#
|
113
|
+
# file separator (to override the system default) and the working directory
|
109
114
|
# (to override the present working directory). The working directory will be
|
110
115
|
# expanded to an absolute path inside the constructor.
|
111
116
|
#
|
@@ -114,7 +119,7 @@ class PathResolver
|
|
114
119
|
# working_dir - the String working directory (optional, default: Dir.pwd)
|
115
120
|
#
|
116
121
|
def initialize(file_separator = nil, working_dir = nil)
|
117
|
-
@file_separator = file_separator.nil? ? File::SEPARATOR : file_separator
|
122
|
+
@file_separator = file_separator.nil? ? (File::ALT_SEPARATOR || File::SEPARATOR) : file_separator
|
118
123
|
if working_dir.nil?
|
119
124
|
@working_dir = File.expand_path(Dir.pwd)
|
120
125
|
else
|
@@ -170,7 +175,7 @@ class PathResolver
|
|
170
175
|
# returns a String path with any parent or self references resolved.
|
171
176
|
def expand_path(path)
|
172
177
|
path_segments, path_root, _ = partition_path(path)
|
173
|
-
join_path
|
178
|
+
join_path path_segments, path_root
|
174
179
|
end
|
175
180
|
|
176
181
|
# Public: Partition the path into path segments and remove any empty segments
|
@@ -186,7 +191,7 @@ class PathResolver
|
|
186
191
|
def partition_path(path, web_path = false)
|
187
192
|
posix_path = posixfy path
|
188
193
|
is_root = web_path ? is_web_root?(posix_path) : is_root?(posix_path)
|
189
|
-
path_segments = posix_path.split(
|
194
|
+
path_segments = posix_path.tr_s(SLASH, SLASH).split(SLASH)
|
190
195
|
# capture relative root
|
191
196
|
root = path_segments.first == DOT ? DOT : nil
|
192
197
|
path_segments.delete(DOT)
|
@@ -196,20 +201,21 @@ class PathResolver
|
|
196
201
|
[path_segments, root, posix_path]
|
197
202
|
end
|
198
203
|
|
199
|
-
# Public: Join the segments using the file separator
|
200
|
-
#
|
201
|
-
# Otherwise join the segments as
|
204
|
+
# Public: Join the segments using the posix file separator (since Ruby knows
|
205
|
+
# how to work with paths specified this way, regardless of OS). Use the root,
|
206
|
+
# if specified, to construct an absolute path. Otherwise join the segments as
|
207
|
+
# a relative path.
|
202
208
|
#
|
203
209
|
# segments - a String Array of path segments
|
204
210
|
# root - a String path root (optional, default: nil)
|
205
211
|
#
|
206
|
-
# returns a String path formed by joining the segments
|
207
|
-
# the root, if specified
|
212
|
+
# returns a String path formed by joining the segments using the posix file
|
213
|
+
# separator and prepending the root, if specified
|
208
214
|
def join_path(segments, root = nil)
|
209
215
|
if root
|
210
|
-
"#{root}#{
|
216
|
+
"#{root}#{SLASH}#{segments * SLASH}"
|
211
217
|
else
|
212
|
-
segments *
|
218
|
+
segments * SLASH
|
213
219
|
end
|
214
220
|
end
|
215
221
|
|
data/lib/asciidoctor/reader.rb
CHANGED
@@ -425,10 +425,15 @@ class Reader
|
|
425
425
|
#
|
426
426
|
# returns a Boolean indicating whether the line under the cursor has changed.
|
427
427
|
def preprocess_include(target, raw_attributes)
|
428
|
+
target = @document.sub_attributes target
|
429
|
+
if target.empty?
|
430
|
+
advance
|
431
|
+
@next_line_preprocessed = false
|
432
|
+
false
|
428
433
|
# if running in SafeMode::SECURE or greater, don't process this directive
|
429
434
|
# however, be friendly and at least make it a link to the source document
|
430
|
-
|
431
|
-
@lines[0] = "link:#{target}[#{target}]"
|
435
|
+
elsif @document.safe >= SafeMode::SECURE
|
436
|
+
@lines[0] = "link:#{target}[#{target}]\n"
|
432
437
|
@next_line_preprocessed = true
|
433
438
|
false
|
434
439
|
# assume that if a block is given, the developer wants
|
@@ -437,7 +442,7 @@ class Reader
|
|
437
442
|
elsif @include_block
|
438
443
|
advance
|
439
444
|
# FIXME this borks line numbers
|
440
|
-
@lines.unshift(
|
445
|
+
@lines.unshift(*normalize_include_data(@include_block.call(target)))
|
441
446
|
# FIXME currently we're not checking the upper bound of the include depth
|
442
447
|
elsif @document.attributes.fetch('include-depth', 0).to_i > 0
|
443
448
|
advance
|
@@ -448,48 +453,48 @@ class Reader
|
|
448
453
|
return true
|
449
454
|
end
|
450
455
|
|
451
|
-
|
456
|
+
inc_lines = nil
|
452
457
|
tags = nil
|
458
|
+
attributes = {}
|
453
459
|
if !raw_attributes.empty?
|
454
460
|
attributes = AttributeList.new(raw_attributes).parse
|
455
461
|
if attributes.has_key? 'lines'
|
456
|
-
|
457
|
-
attributes['lines'].split(REGEXP[:
|
462
|
+
inc_lines = []
|
463
|
+
attributes['lines'].split(REGEXP[:ssv_or_csv_delim]).each do |linedef|
|
458
464
|
if linedef.include?('..')
|
459
465
|
from, to = linedef.split('..').map(&:to_i)
|
460
466
|
if to == -1
|
461
|
-
|
462
|
-
|
467
|
+
inc_lines << from
|
468
|
+
inc_lines << 1.0/0.0
|
463
469
|
else
|
464
|
-
|
470
|
+
inc_lines.concat Range.new(from, to).to_a
|
465
471
|
end
|
466
472
|
else
|
467
|
-
|
473
|
+
inc_lines << linedef.to_i
|
468
474
|
end
|
469
475
|
end
|
470
|
-
|
471
|
-
#lines.push lines.shift if lines.first == -1
|
476
|
+
inc_lines = inc_lines.sort.uniq
|
472
477
|
elsif attributes.has_key? 'tags'
|
473
|
-
tags = attributes['tags'].split(REGEXP[:
|
478
|
+
tags = attributes['tags'].split(REGEXP[:ssv_or_csv_delim]).uniq
|
474
479
|
end
|
475
480
|
end
|
476
|
-
if !
|
477
|
-
if !
|
481
|
+
if !inc_lines.nil?
|
482
|
+
if !inc_lines.empty?
|
478
483
|
selected = []
|
479
484
|
f = File.new(include_file)
|
480
485
|
f.each_line do |l|
|
481
|
-
take =
|
486
|
+
take = inc_lines.first
|
482
487
|
if take.is_a?(Float) && take.infinite?
|
483
|
-
selected.push
|
488
|
+
selected.push l
|
484
489
|
else
|
485
490
|
if f.lineno == take
|
486
|
-
selected.push
|
487
|
-
|
491
|
+
selected.push l
|
492
|
+
inc_lines.shift
|
488
493
|
end
|
489
|
-
break if
|
494
|
+
break if inc_lines.empty?
|
490
495
|
end
|
491
496
|
end
|
492
|
-
@lines.unshift(*selected) unless selected.empty?
|
497
|
+
@lines.unshift(*normalize_include_data(selected, attributes['indent'])) unless selected.empty?
|
493
498
|
end
|
494
499
|
elsif !tags.nil?
|
495
500
|
if !tags.empty?
|
@@ -497,11 +502,12 @@ class Reader
|
|
497
502
|
active_tag = nil
|
498
503
|
f = File.new(include_file)
|
499
504
|
f.each_line do |l|
|
505
|
+
l.force_encoding(::Encoding::UTF_8) if ::Asciidoctor::FORCE_ENCODING
|
500
506
|
if !active_tag.nil?
|
501
507
|
if l.include?("end::#{active_tag}[]")
|
502
508
|
active_tag = nil
|
503
509
|
else
|
504
|
-
selected.push
|
510
|
+
selected.push "#{l.rstrip}\n"
|
505
511
|
end
|
506
512
|
else
|
507
513
|
tags.each do |tag|
|
@@ -512,10 +518,11 @@ class Reader
|
|
512
518
|
end
|
513
519
|
end
|
514
520
|
end
|
515
|
-
|
521
|
+
#@lines.unshift(*selected) unless selected.empty?
|
522
|
+
@lines.unshift(*normalize_include_data(selected, attributes['indent'])) unless selected.empty?
|
516
523
|
end
|
517
524
|
else
|
518
|
-
@lines.unshift(*File.readlines(include_file)
|
525
|
+
@lines.unshift(*normalize_include_data(File.readlines(include_file), attributes['indent']))
|
519
526
|
end
|
520
527
|
true
|
521
528
|
else
|
@@ -675,7 +682,7 @@ class Reader
|
|
675
682
|
end
|
676
683
|
|
677
684
|
if val.include? '{'
|
678
|
-
val = @document.sub_attributes
|
685
|
+
val = @document.sub_attributes val
|
679
686
|
end
|
680
687
|
|
681
688
|
if type != :s
|
@@ -695,6 +702,39 @@ class Reader
|
|
695
702
|
val
|
696
703
|
end
|
697
704
|
|
705
|
+
# Private: Normalize raw input read from an include directive
|
706
|
+
#
|
707
|
+
# This method strips whitespace from the end of every line of
|
708
|
+
# the source data and appends a LF (i.e., Unix endline). This
|
709
|
+
# whitespace substitution is very important to how Asciidoctor
|
710
|
+
# works.
|
711
|
+
#
|
712
|
+
# Any leading or trailing blank lines are also removed. (DISABLED)
|
713
|
+
#
|
714
|
+
# data - A String Array of input data to be normalized
|
715
|
+
#
|
716
|
+
# returns the processed lines
|
717
|
+
#-
|
718
|
+
# FIXME this shares too much in common w/ normalize_data; combine
|
719
|
+
# in a shared function
|
720
|
+
def normalize_include_data(data, indent = nil)
|
721
|
+
if ::Asciidoctor::FORCE_ENCODING
|
722
|
+
result = data.map {|line| "#{line.rstrip.force_encoding(::Encoding::UTF_8)}\n" }
|
723
|
+
else
|
724
|
+
result = data.map {|line| "#{line.rstrip}\n" }
|
725
|
+
end
|
726
|
+
|
727
|
+
unless indent.nil?
|
728
|
+
Lexer.reset_block_indent! result, indent.to_i
|
729
|
+
end
|
730
|
+
|
731
|
+
result
|
732
|
+
|
733
|
+
#data.shift while !data.first.nil? && data.first.chomp.empty?
|
734
|
+
#data.pop while !data.last.nil? && data.last.chomp.empty?
|
735
|
+
#data
|
736
|
+
end
|
737
|
+
|
698
738
|
# Private: Normalize raw input, used for the outermost Reader.
|
699
739
|
#
|
700
740
|
# This method strips whitespace from the end of every line of
|
@@ -712,7 +752,12 @@ class Reader
|
|
712
752
|
def normalize_data(data)
|
713
753
|
# normalize line ending to LF (purging occurrences of CRLF)
|
714
754
|
# this rstrip is *very* important to how Asciidoctor works
|
715
|
-
|
755
|
+
|
756
|
+
if ::Asciidoctor::FORCE_ENCODING
|
757
|
+
@lines = data.map {|line| "#{line.rstrip.force_encoding(::Encoding::UTF_8)}\n" }
|
758
|
+
else
|
759
|
+
@lines = data.map {|line| "#{line.rstrip}\n" }
|
760
|
+
end
|
716
761
|
|
717
762
|
@lines.shift && @lineno += 1 while !@lines.first.nil? && @lines.first.chomp.empty?
|
718
763
|
@lines.pop while !@lines.last.nil? && @lines.last.chomp.empty?
|
data/lib/asciidoctor/renderer.rb
CHANGED
@@ -55,18 +55,11 @@ class Renderer
|
|
55
55
|
:slim => { :disable_escape => true, :sort_attrs => false, :pretty => false }
|
56
56
|
}
|
57
57
|
|
58
|
-
|
58
|
+
# workaround until we have a proper way to configure
|
59
|
+
if {'html5' => true, 'dzslides' => true, 'deckjs' => true, 'revealjs' => true}.has_key? backend
|
59
60
|
view_opts[:haml][:format] = view_opts[:slim][:format] = :html5
|
60
61
|
end
|
61
62
|
|
62
|
-
Debug.debug {
|
63
|
-
msg = []
|
64
|
-
msg << "Views going in are like so:"
|
65
|
-
msg << @views.map {|k, v| "#{k}: #{v}"}
|
66
|
-
msg << '=' * 60
|
67
|
-
msg * "\n"
|
68
|
-
}
|
69
|
-
|
70
63
|
slim_loaded = false
|
71
64
|
helpers = nil
|
72
65
|
|
@@ -91,14 +84,6 @@ class Renderer
|
|
91
84
|
end
|
92
85
|
|
93
86
|
require helpers unless helpers.nil?
|
94
|
-
|
95
|
-
Debug.debug {
|
96
|
-
msg = []
|
97
|
-
msg << "Views going in are like so:"
|
98
|
-
msg << @views.map {|k, v| "#{k}: #{v}"}
|
99
|
-
msg << '=' * 60
|
100
|
-
msg * "\n"
|
101
|
-
}
|
102
87
|
end
|
103
88
|
end
|
104
89
|
|
@@ -110,8 +95,6 @@ class Renderer
|
|
110
95
|
def render(view, object, locals = {})
|
111
96
|
if !@views.has_key? view
|
112
97
|
raise "Couldn't find a view in @views for #{view}"
|
113
|
-
else
|
114
|
-
Debug.debug { "View for #{view} is #{@views[view]}, object is #{object}" }
|
115
98
|
end
|
116
99
|
|
117
100
|
@views[view].render(object, locals)
|
data/lib/asciidoctor/section.rb
CHANGED
@@ -90,7 +90,6 @@ class Section < AbstractBlock
|
|
90
90
|
# Public: Get the rendered String content for this Section and all its child
|
91
91
|
# Blocks.
|
92
92
|
def render
|
93
|
-
Debug.debug { "Now rendering section for #{self}" }
|
94
93
|
@document.playback_attributes @attributes
|
95
94
|
renderer.render('section', self)
|
96
95
|
end
|
@@ -40,8 +40,9 @@ module Substituters
|
|
40
40
|
multiline = lines.is_a?(Array)
|
41
41
|
text = multiline ? lines.join : lines
|
42
42
|
|
43
|
-
|
44
|
-
|
43
|
+
if (has_passthroughs = subs.include?(:macros))
|
44
|
+
text = extract_passthroughs(text)
|
45
|
+
end
|
45
46
|
|
46
47
|
subs.each {|type|
|
47
48
|
case type
|
@@ -63,7 +64,7 @@ module Substituters
|
|
63
64
|
puts "asciidoctor: WARNING: unknown substitution type #{type}"
|
64
65
|
end
|
65
66
|
}
|
66
|
-
text = restore_passthroughs(text) if
|
67
|
+
text = restore_passthroughs(text) if has_passthroughs
|
67
68
|
|
68
69
|
multiline ? text.lines.entries : text
|
69
70
|
end
|
@@ -165,7 +166,7 @@ module Substituters
|
|
165
166
|
# TODO move unescaping closing square bracket to an operation
|
166
167
|
@passthroughs << {:text => m[2] || m[4].gsub('\]', ']'), :subs => subs}
|
167
168
|
index = @passthroughs.size - 1
|
168
|
-
"\
|
169
|
+
"\e#{index}\e"
|
169
170
|
} unless !(result.include?('+++') || result.include?('$$') || result.include?('pass:'))
|
170
171
|
|
171
172
|
result.gsub!(REGEXP[:pass_lit]) {
|
@@ -179,7 +180,7 @@ module Substituters
|
|
179
180
|
|
180
181
|
@passthroughs << {:text => m[3], :subs => [:specialcharacters], :literal => true}
|
181
182
|
index = @passthroughs.size - 1
|
182
|
-
"#{m[1]}\
|
183
|
+
"#{m[1]}\e#{index}\e"
|
183
184
|
} unless !result.include?('`')
|
184
185
|
|
185
186
|
result
|
@@ -191,7 +192,7 @@ module Substituters
|
|
191
192
|
#
|
192
193
|
# returns The String text with the passthrough text restored
|
193
194
|
def restore_passthroughs(text)
|
194
|
-
return text if @passthroughs.nil? || @passthroughs.empty? || !text.include?("\
|
195
|
+
return text if @passthroughs.nil? || @passthroughs.empty? || !text.include?("\e")
|
195
196
|
|
196
197
|
text.gsub(REGEXP[:pass_placeholder]) {
|
197
198
|
pass = @passthroughs[$1.to_i];
|
@@ -275,41 +276,55 @@ module Substituters
|
|
275
276
|
def sub_attributes(data)
|
276
277
|
return data if data.nil? || data.empty?
|
277
278
|
|
279
|
+
string_data = data.is_a? String
|
278
280
|
# normalizes data type to an array (string becomes single-element array)
|
279
|
-
lines =
|
281
|
+
lines = string_data ? [data] : data
|
280
282
|
|
281
|
-
result =
|
283
|
+
result = []
|
284
|
+
lines.each {|line|
|
282
285
|
reject = false
|
283
|
-
|
284
|
-
subject.gsub!(REGEXP[:attr_ref]) {
|
286
|
+
line = line.gsub(REGEXP[:attr_ref]) {
|
285
287
|
# alias match for Ruby 1.8.7 compat
|
286
288
|
m = $~
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
289
|
+
# escaped attribute, return unescaped
|
290
|
+
if !m[1].nil? || !m[4].nil?
|
291
|
+
"{#{m[2]}}"
|
292
|
+
elsif (directive = m[3])
|
293
|
+
offset = directive.length + 1
|
294
|
+
expr = m[2][offset..-1]
|
295
|
+
case directive
|
296
|
+
when 'set'
|
297
|
+
args = expr.split(':')
|
298
|
+
_, value = Lexer::store_attribute(args[0], args[1] || '', @document)
|
299
|
+
if value.nil?
|
300
|
+
reject = true
|
301
|
+
break '{undefined}'
|
302
|
+
end
|
303
|
+
''
|
304
|
+
when 'counter', 'counter2'
|
305
|
+
args = expr.split(':')
|
306
|
+
val = @document.counter(args[0], args[1])
|
307
|
+
directive == 'counter2' ? '' : val
|
308
|
+
else
|
309
|
+
# if we get here, our attr_ref regex is too loose
|
310
|
+
puts "asciidoctor: WARNING: illegal attribute directive: #{m[2]}"
|
311
|
+
''
|
312
|
+
end
|
313
|
+
elsif (key = m[2].downcase) && @document.attributes.has_key?(key)
|
299
314
|
@document.attributes[key]
|
300
315
|
elsif INTRINSICS.has_key? key
|
301
316
|
INTRINSICS[key]
|
302
317
|
else
|
303
|
-
Debug.debug { "Missing attribute: #{
|
318
|
+
Debug.debug { "Missing attribute: #{key}, line marked for removal" }
|
304
319
|
reject = true
|
305
320
|
break '{undefined}'
|
306
321
|
end
|
307
|
-
} if
|
322
|
+
} if line.include? '{'
|
308
323
|
|
309
|
-
|
310
|
-
}
|
324
|
+
result << line unless reject
|
325
|
+
}
|
311
326
|
|
312
|
-
|
327
|
+
string_data ? result.join : result
|
313
328
|
end
|
314
329
|
|
315
330
|
# Public: Substitute inline macros (e.g., links, images, etc)
|
@@ -333,7 +348,90 @@ module Substituters
|
|
333
348
|
found[:macroish] = (found[:square_bracket] && found[:colon])
|
334
349
|
found[:macroish_short_form] = (found[:square_bracket] && found[:colon] && result.include?(':['))
|
335
350
|
found[:uri] = (found[:colon] && result.include?('://'))
|
336
|
-
|
351
|
+
use_link_attrs = @document.attributes.has_key?('linkattrs')
|
352
|
+
experimental = @document.attributes.has_key?('experimental')
|
353
|
+
|
354
|
+
if experimental
|
355
|
+
if found[:macroish_short_form] && (result.include?('kbd:') || result.include?('btn:'))
|
356
|
+
result.gsub!(REGEXP[:kbd_btn_macro]) {
|
357
|
+
# alias match for Ruby 1.8.7 compat
|
358
|
+
m = $~
|
359
|
+
# honor the escape
|
360
|
+
if (captured = m[0]).start_with? '\\'
|
361
|
+
next captured[1..-1]
|
362
|
+
end
|
363
|
+
|
364
|
+
if captured.start_with?('kbd')
|
365
|
+
keys = unescape_bracketed_text m[1]
|
366
|
+
|
367
|
+
if keys == '+'
|
368
|
+
keys = ['+']
|
369
|
+
else
|
370
|
+
# need to use closure to work around lack of negative lookbehind
|
371
|
+
keys = keys.split(REGEXP[:kbd_delim]).inject([]) {|c, key|
|
372
|
+
if key.end_with?('++')
|
373
|
+
c << key[0..-3].strip
|
374
|
+
c << '+'
|
375
|
+
else
|
376
|
+
c << key.strip
|
377
|
+
end
|
378
|
+
c
|
379
|
+
}
|
380
|
+
end
|
381
|
+
Inline.new(self, :kbd, nil, :attributes => {'keys' => keys}).render
|
382
|
+
elsif captured.start_with?('btn')
|
383
|
+
label = unescape_bracketed_text m[1]
|
384
|
+
Inline.new(self, :button, label).render
|
385
|
+
end
|
386
|
+
}
|
387
|
+
end
|
388
|
+
|
389
|
+
if found[:macroish] && result.include?('menu:')
|
390
|
+
result.gsub!(REGEXP[:menu_macro]) {
|
391
|
+
# alias match for Ruby 1.8.7 compat
|
392
|
+
m = $~
|
393
|
+
# honor the escape
|
394
|
+
if (captured = m[0]).start_with? '\\'
|
395
|
+
next captured[1..-1]
|
396
|
+
end
|
397
|
+
|
398
|
+
menu = m[1]
|
399
|
+
items = m[2]
|
400
|
+
|
401
|
+
if items.nil?
|
402
|
+
submenus = []
|
403
|
+
menuitem = nil
|
404
|
+
else
|
405
|
+
if (delim = items.include?('>') ? '>' : (items.include?(',') ? ',' : nil))
|
406
|
+
submenus = items.split(delim).map(&:strip)
|
407
|
+
menuitem = submenus.pop
|
408
|
+
else
|
409
|
+
submenus = []
|
410
|
+
menuitem = items.rstrip
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).render
|
415
|
+
}
|
416
|
+
end
|
417
|
+
|
418
|
+
if result.include?('"') && result.include?('>')
|
419
|
+
result.gsub!(REGEXP[:menu_inline_macro]) {
|
420
|
+
# alias match for Ruby 1.8.7 compat
|
421
|
+
m = $~
|
422
|
+
# honor the escape
|
423
|
+
if (captured = m[0]).start_with? '\\'
|
424
|
+
next captured[1..-1]
|
425
|
+
end
|
426
|
+
|
427
|
+
input = m[1]
|
428
|
+
|
429
|
+
menu, *submenus = input.split('>').map(&:strip)
|
430
|
+
menuitem = submenus.pop
|
431
|
+
Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).render
|
432
|
+
}
|
433
|
+
end
|
434
|
+
end
|
337
435
|
|
338
436
|
if found[:macroish] && result.include?('image:')
|
339
437
|
# image:filename.png[Alt Text]
|
@@ -365,7 +463,7 @@ module Substituters
|
|
365
463
|
next m[0][1..-1]
|
366
464
|
end
|
367
465
|
|
368
|
-
terms = unescape_bracketed_text(m[1] || m[2]).split(
|
466
|
+
terms = unescape_bracketed_text(m[1] || m[2]).split(',').map(&:strip)
|
369
467
|
document.register(:indexterms, [*terms])
|
370
468
|
Inline.new(self, :indexterm, text, :attributes => {'terms' => terms}).render
|
371
469
|
}
|
@@ -409,18 +507,27 @@ module Substituters
|
|
409
507
|
elsif prefix.start_with?('(') && target.end_with?(')')
|
410
508
|
target = target[0..-2]
|
411
509
|
suffix = ')'
|
510
|
+
elsif target.end_with?('):')
|
511
|
+
target = target[0..-3]
|
512
|
+
suffix = '):'
|
412
513
|
end
|
413
514
|
@document.register(:links, target)
|
414
515
|
|
415
516
|
attrs = nil
|
416
517
|
#text = !m[3].nil? ? sub_attributes(m[3].gsub('\]', ']')) : ''
|
417
518
|
if !m[3].to_s.empty?
|
418
|
-
if
|
419
|
-
attrs = parse_attributes(sub_attributes(m[3].gsub('\]', ']')))
|
519
|
+
if use_link_attrs && (m[3].start_with?('"') || m[3].include?(','))
|
520
|
+
attrs = parse_attributes(sub_attributes(m[3].gsub('\]', ']')), [])
|
420
521
|
text = attrs[1]
|
421
522
|
else
|
422
523
|
text = sub_attributes(m[3].gsub('\]', ']'))
|
423
524
|
end
|
525
|
+
|
526
|
+
if text.end_with? '^'
|
527
|
+
text = text.chop
|
528
|
+
attrs ||= {}
|
529
|
+
attrs['window'] = '_blank' unless attrs.has_key?('window')
|
530
|
+
end
|
424
531
|
else
|
425
532
|
text = ''
|
426
533
|
end
|
@@ -444,8 +551,8 @@ module Substituters
|
|
444
551
|
|
445
552
|
attrs = nil
|
446
553
|
#text = sub_attributes(m[2].gsub('\]', ']'))
|
447
|
-
if
|
448
|
-
attrs = parse_attributes(sub_attributes(m[2].gsub('\]', ']')))
|
554
|
+
if use_link_attrs && (m[2].start_with?('"') || m[2].include?(','))
|
555
|
+
attrs = parse_attributes(sub_attributes(m[2].gsub('\]', ']')), [])
|
449
556
|
text = attrs[1]
|
450
557
|
if mailto
|
451
558
|
if attrs.has_key? 2
|
@@ -459,6 +566,13 @@ module Substituters
|
|
459
566
|
else
|
460
567
|
text = sub_attributes(m[2].gsub('\]', ']'))
|
461
568
|
end
|
569
|
+
|
570
|
+
if text.end_with? '^'
|
571
|
+
text = text.chop
|
572
|
+
attrs ||= {}
|
573
|
+
attrs['window'] = '_blank' unless attrs.has_key?('window')
|
574
|
+
end
|
575
|
+
|
462
576
|
# QUESTION should a mailto be registered as an e-mail address?
|
463
577
|
@document.register(:links, target)
|
464
578
|
|
@@ -503,7 +617,7 @@ module Substituters
|
|
503
617
|
type = nil
|
504
618
|
target = nil
|
505
619
|
else
|
506
|
-
id, text = m[2].split(
|
620
|
+
id, text = m[2].split(',', 2).map(&:strip)
|
507
621
|
if !text.nil?
|
508
622
|
# hmmmm
|
509
623
|
text = restore_passthroughs(text)
|
@@ -533,7 +647,7 @@ module Substituters
|
|
533
647
|
next m[0][1..-1]
|
534
648
|
end
|
535
649
|
if !m[1].nil?
|
536
|
-
id, reftext = m[1].split(
|
650
|
+
id, reftext = m[1].split(',', 2).map(&:strip)
|
537
651
|
id.sub!(REGEXP[:dbl_quoted], '\2')
|
538
652
|
reftext.sub!(REGEXP[:m_dbl_quoted], '\2') unless reftext.nil?
|
539
653
|
else
|
@@ -565,7 +679,7 @@ module Substituters
|
|
565
679
|
if m[0].start_with? '\\'
|
566
680
|
next m[0][1..-1]
|
567
681
|
end
|
568
|
-
id, reftext = m[1].split(
|
682
|
+
id, reftext = m[1].split(',').map(&:strip)
|
569
683
|
id.sub!(REGEXP[:dbl_quoted], '\2')
|
570
684
|
if reftext.nil?
|
571
685
|
reftext = "[#{id}]"
|