slim 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +7 -8
- data/.yardopts +1 -1
- data/CHANGES +23 -0
- data/Gemfile +2 -1
- data/LICENSE +1 -1
- data/README.jp.md +497 -268
- data/README.md +505 -278
- data/Rakefile +14 -2
- data/benchmarks/view.haml +2 -2
- data/doc/include.md +20 -0
- data/doc/jp/include.md +20 -0
- data/doc/jp/logic_less.md +137 -0
- data/doc/jp/smart.md +102 -0
- data/doc/jp/translator.md +28 -0
- data/doc/smart.md +102 -0
- data/lib/slim/command.rb +8 -2
- data/lib/slim/controls.rb +4 -3
- data/lib/slim/do_inserter.rb +1 -1
- data/lib/slim/embedded.rb +26 -22
- data/lib/slim/end_inserter.rb +1 -1
- data/lib/slim/engine.rb +3 -2
- data/lib/slim/filter.rb +2 -2
- data/lib/slim/grammar.rb +3 -1
- data/lib/slim/include.rb +54 -0
- data/lib/slim/interpolation.rb +1 -1
- data/lib/slim/parser.rb +61 -46
- data/lib/slim/smart.rb +8 -0
- data/lib/slim/smart/escaper.rb +42 -0
- data/lib/slim/smart/filter.rb +96 -0
- data/lib/slim/smart/parser.rb +33 -0
- data/lib/slim/splat/builder.rb +3 -3
- data/lib/slim/splat/filter.rb +2 -2
- data/lib/slim/translator.rb +2 -2
- data/lib/slim/version.rb +1 -1
- data/slim.gemspec +1 -1
- data/test/core/helper.rb +5 -3
- data/test/core/test_code_blocks.rb +11 -0
- data/test/core/test_code_escaping.rb +14 -4
- data/test/core/test_embedded_engines.rb +26 -0
- data/test/core/test_html_structure.rb +39 -0
- data/test/core/test_parser_errors.rb +8 -25
- data/test/core/test_pretty.rb +15 -0
- data/test/core/test_ruby_errors.rb +0 -10
- data/test/include/files/recursive.slim +1 -0
- data/test/include/files/slimfile.slim +3 -0
- data/test/include/files/subdir/test.slim +1 -0
- data/test/include/files/textfile +1 -0
- data/test/include/test_include.rb +23 -0
- data/test/literate/TESTS.md +17 -2
- data/test/rails/test/test_slim.rb +1 -1
- data/test/smart/test_smart_text.rb +300 -0
- data/test/translator/test_translator.rb +7 -6
- metadata +22 -4
data/lib/slim/command.rb
CHANGED
@@ -39,16 +39,22 @@ module Slim
|
|
39
39
|
@options[:erb] = true
|
40
40
|
end
|
41
41
|
|
42
|
-
opts.on('
|
42
|
+
opts.on('--rails', 'Generate rails compatible code (Implies --compile)') do
|
43
43
|
Engine.set_default_options :disable_capture => true, :generator => Temple::Generators::RailsOutputBuffer
|
44
44
|
@options[:compile] = true
|
45
45
|
end
|
46
46
|
|
47
|
+
opts.on('-r library', "Load library or plugin with -r slim/plugin") do |lib|
|
48
|
+
require lib
|
49
|
+
end
|
50
|
+
|
47
51
|
opts.on('-t', '--translator', 'Enable translator plugin') do
|
52
|
+
puts "Deprecated option: Use -r slim/translator"
|
48
53
|
require 'slim/translator'
|
49
54
|
end
|
50
55
|
|
51
56
|
opts.on('-l', '--logic-less', 'Enable logic less plugin') do
|
57
|
+
puts "Deprecated option: Use -r slim/logic_less"
|
52
58
|
require 'slim/logic_less'
|
53
59
|
end
|
54
60
|
|
@@ -56,7 +62,7 @@ module Slim
|
|
56
62
|
Engine.set_default_options :pretty => true
|
57
63
|
end
|
58
64
|
|
59
|
-
opts.on('-o', '--option
|
65
|
+
opts.on('-o', '--option name=code', String, 'Set slim option') do |str|
|
60
66
|
parts = str.split('=', 2)
|
61
67
|
Engine.default_options[parts.first.gsub(/\A:/, '').to_sym] = eval(parts.last)
|
62
68
|
end
|
data/lib/slim/controls.rb
CHANGED
@@ -3,7 +3,7 @@ module Slim
|
|
3
3
|
class Controls < Filter
|
4
4
|
define_options :disable_capture
|
5
5
|
|
6
|
-
IF_RE = /\A(if|unless)\b
|
6
|
+
IF_RE = /\A(if|unless)\b|\bdo\s*(\|[^\|]*\|)?\s*$/
|
7
7
|
|
8
8
|
# Handle control expression `[:slim, :control, code, content]`
|
9
9
|
#
|
@@ -48,11 +48,12 @@ module Slim
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
# Handle text expression `[:slim, :text, content]`
|
51
|
+
# Handle text expression `[:slim, :text, type, content]`
|
52
52
|
#
|
53
|
+
# @param [Symbol] type Text type
|
53
54
|
# @param [Array] content Temple expression
|
54
55
|
# @return [Array] Compiled temple expression
|
55
|
-
def on_slim_text(content)
|
56
|
+
def on_slim_text(type, content)
|
56
57
|
compile(content)
|
57
58
|
end
|
58
59
|
end
|
data/lib/slim/do_inserter.rb
CHANGED
@@ -7,7 +7,7 @@ module Slim
|
|
7
7
|
#
|
8
8
|
# @api private
|
9
9
|
class DoInserter < Filter
|
10
|
-
BLOCK_REGEX = /(\A(if|unless|else|elsif|when|begin|rescue|ensure|case)\b)
|
10
|
+
BLOCK_REGEX = /(\A(if|unless|else|elsif|when|begin|rescue|ensure|case)\b)|\bdo\s*(\|[^\|]*\|\s*)?\Z/
|
11
11
|
|
12
12
|
# Handle control expression `[:slim, :control, code, content]`
|
13
13
|
#
|
data/lib/slim/embedded.rb
CHANGED
@@ -30,8 +30,7 @@ module Slim
|
|
30
30
|
# @api private
|
31
31
|
class OutputProtector < Filter
|
32
32
|
def call(exp)
|
33
|
-
@protect = []
|
34
|
-
@collected = ''
|
33
|
+
@protect, @collected, @tag = [], '', "%#{object_id.abs.to_s(36)}%"
|
35
34
|
super(exp)
|
36
35
|
@collected
|
37
36
|
end
|
@@ -42,16 +41,16 @@ module Slim
|
|
42
41
|
end
|
43
42
|
|
44
43
|
def on_slim_output(escape, text, content)
|
45
|
-
@collected <<
|
44
|
+
@collected << @tag
|
46
45
|
@protect << [:slim, :output, escape, text, content]
|
47
46
|
nil
|
48
47
|
end
|
49
48
|
|
50
49
|
def unprotect(text)
|
51
50
|
block = [:multi]
|
52
|
-
while text =~ /
|
51
|
+
while text =~ /#{@tag}/
|
53
52
|
block << [:static, $`]
|
54
|
-
block << @protect
|
53
|
+
block << @protect.shift
|
55
54
|
text = $'
|
56
55
|
end
|
57
56
|
block << [:static, text]
|
@@ -63,25 +62,29 @@ module Slim
|
|
63
62
|
class Embedded < Filter
|
64
63
|
@engines = {}
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
65
|
+
class << self
|
66
|
+
attr_reader :engines
|
67
|
+
|
68
|
+
# Register embedded engine
|
69
|
+
#
|
70
|
+
# @param [String] name Name of the engine
|
71
|
+
# @param [Class] klass Engine class
|
72
|
+
# @param option_filter List of options to pass to engine.
|
73
|
+
# Last argument can be default option hash.
|
74
|
+
def register(name, klass, *option_filter)
|
75
|
+
name = name.to_sym
|
76
|
+
local_options = option_filter.last.respond_to?(:to_hash) ? option_filter.pop.to_hash : {}
|
77
|
+
define_options(name, *option_filter)
|
78
|
+
klass.define_options(name)
|
79
|
+
engines[name.to_sym] = proc do |options|
|
80
|
+
klass.new({}.update(options).delete_if {|k,v| !option_filter.include?(k) && k != name }.update(local_options))
|
81
|
+
end
|
79
82
|
end
|
80
|
-
end
|
81
83
|
|
82
|
-
|
83
|
-
|
84
|
-
|
84
|
+
def create(name, options)
|
85
|
+
constructor = engines[name] || raise(Temple::FilterError, "Embedded engine #{name} not found")
|
86
|
+
constructor.call(options)
|
87
|
+
end
|
85
88
|
end
|
86
89
|
|
87
90
|
define_options :enable_engines, :disable_engines
|
@@ -252,6 +255,7 @@ module Slim
|
|
252
255
|
|
253
256
|
# These engines are executed at compile time
|
254
257
|
register :coffee, JavaScriptEngine, :engine => StaticTiltEngine
|
258
|
+
register :opal, JavaScriptEngine, :engine => StaticTiltEngine
|
255
259
|
register :less, TagEngine, :tag => :style, :attributes => { :type => 'text/css' }, :engine => StaticTiltEngine
|
256
260
|
register :styl, TagEngine, :tag => :style, :attributes => { :type => 'text/css' }, :engine => StaticTiltEngine
|
257
261
|
register :sass, TagEngine, :pretty, :tag => :style, :attributes => { :type => 'text/css' }, :engine => SassEngine
|
data/lib/slim/end_inserter.rb
CHANGED
@@ -10,7 +10,7 @@ module Slim
|
|
10
10
|
#
|
11
11
|
# @api private
|
12
12
|
class EndInserter < Filter
|
13
|
-
IF_RE = /\A(if|unless|else|elsif|when|rescue|ensure)\b
|
13
|
+
IF_RE = /\A(if|unless|else|elsif|when|rescue|ensure)\b|\bdo\s*(\|[^\|]*\|)?\s*$/
|
14
14
|
ELSE_RE = /\A(else|elsif|when|rescue|ensure)\b/
|
15
15
|
END_RE = /\Aend\b/
|
16
16
|
|
data/lib/slim/engine.rb
CHANGED
@@ -12,13 +12,14 @@ module Slim
|
|
12
12
|
:merge_attrs => {'class' => ' '},
|
13
13
|
:generator => Temple::Generators::ArrayBuffer,
|
14
14
|
:default_tag => 'div'
|
15
|
+
define_deprecated_options :attr_delims
|
15
16
|
|
16
17
|
filter :Encoding, :encoding
|
17
18
|
filter :RemoveBOM
|
18
|
-
use Slim::Parser, :file, :tabsize, :shortcut, :default_tag, :attr_delims
|
19
|
+
use Slim::Parser, :file, :tabsize, :shortcut, :default_tag, :attr_delims, :attr_list_delims, :code_attr_delims
|
19
20
|
use Slim::Embedded, :enable_engines, :disable_engines, :pretty
|
20
21
|
use Slim::Interpolation
|
21
|
-
use Slim::Splat::Filter, :merge_attrs, :attr_quote, :sort_attrs, :default_tag, :hyphen_attrs, :format
|
22
|
+
use Slim::Splat::Filter, :merge_attrs, :attr_quote, :sort_attrs, :default_tag, :hyphen_attrs, :format, :use_html_safe
|
22
23
|
use Slim::DoInserter
|
23
24
|
use Slim::EndInserter
|
24
25
|
use Slim::Controls, :disable_capture
|
data/lib/slim/filter.rb
CHANGED
@@ -8,8 +8,8 @@ module Slim
|
|
8
8
|
# @api private
|
9
9
|
class Filter < Temple::HTML::Filter
|
10
10
|
# Pass-through handler
|
11
|
-
def on_slim_text(content)
|
12
|
-
[:slim, :text, compile(content)]
|
11
|
+
def on_slim_text(type, content)
|
12
|
+
[:slim, :text, type, compile(content)]
|
13
13
|
end
|
14
14
|
|
15
15
|
# Pass-through handler
|
data/lib/slim/grammar.rb
CHANGED
@@ -4,12 +4,14 @@ module Slim
|
|
4
4
|
module Grammar
|
5
5
|
extend Temple::Grammar
|
6
6
|
|
7
|
+
TextTypes << :verbatim | :explicit | :implicit | :inline
|
8
|
+
|
7
9
|
Expression <<
|
8
10
|
[:slim, :control, String, Expression] |
|
9
11
|
[:slim, :output, Bool, String, Expression] |
|
10
12
|
[:slim, :interpolate, String] |
|
11
13
|
[:slim, :embedded, String, Expression] |
|
12
|
-
[:slim, :text, Expression]
|
14
|
+
[:slim, :text, TextTypes, Expression] |
|
13
15
|
[:slim, :attrvalue, Bool, String]
|
14
16
|
|
15
17
|
HTMLAttr <<
|
data/lib/slim/include.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'slim'
|
2
|
+
|
3
|
+
module Slim
|
4
|
+
# Handles inlined includes
|
5
|
+
#
|
6
|
+
# Slim files are compiled, non-Slim files are included as text with `#{interpolation}`
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class Include < Slim::Filter
|
10
|
+
define_options :file, :include_dirs => [Dir.pwd, '.']
|
11
|
+
|
12
|
+
def on_html_tag(tag, attributes, content)
|
13
|
+
return super if tag != 'include'
|
14
|
+
name = content.flatten.select {|s| String === s }.join
|
15
|
+
raise ArgumentError, 'Invalid include statement' unless attributes == [:html, :attrs] && !name.empty?
|
16
|
+
unless file = find_file(name)
|
17
|
+
name = "#{name}.slim" if name !~ /\.slim\Z/i
|
18
|
+
file = find_file(name)
|
19
|
+
end
|
20
|
+
raise Temple::FilterError, "'#{name}' not found in #{options[:include_dirs].join(':')}" unless file
|
21
|
+
content = File.read(file)
|
22
|
+
if file =~ /\.slim\Z/i
|
23
|
+
Thread.current[:slim_include_engine].call(content)
|
24
|
+
else
|
25
|
+
[:slim, :interpolate, content]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def find_file(name)
|
32
|
+
current_dir = File.dirname(File.expand_path(options[:file]))
|
33
|
+
options[:include_dirs].map {|dir| File.expand_path(File.join(dir, name), current_dir) }.find {|file| File.file?(file) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Engine
|
38
|
+
after Slim::Parser, Slim::Include, :file, :include_dirs
|
39
|
+
after Slim::Include, :stop do |exp|
|
40
|
+
throw :stop, exp if Thread.current[:slim_include_level] > 1
|
41
|
+
exp
|
42
|
+
end
|
43
|
+
|
44
|
+
alias call_without_include call
|
45
|
+
def call(input)
|
46
|
+
Thread.current[:slim_include_engine] = self
|
47
|
+
Thread.current[:slim_include_level] ||= 0
|
48
|
+
Thread.current[:slim_include_level] += 1
|
49
|
+
catch(:stop) { call_without_include(input) }
|
50
|
+
ensure
|
51
|
+
Thread.current[:slim_include_engine] = nil if (Thread.current[:slim_include_level] -= 1) == 0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/slim/interpolation.rb
CHANGED
@@ -23,7 +23,7 @@ module Slim
|
|
23
23
|
string, code = parse_expression($')
|
24
24
|
escape = code !~ /\A\{.*\}\Z/
|
25
25
|
block << [:slim, :output, escape, escape ? code : code[1..-2], [:multi]]
|
26
|
-
when /\A([#\\]
|
26
|
+
when /\A([#\\]?[^#\\]*([#\\][^\\#\{][^#\\]*)*)/
|
27
27
|
# Static text
|
28
28
|
block << [:static, $&]
|
29
29
|
string = $'
|
data/lib/slim/parser.rb
CHANGED
@@ -5,6 +5,8 @@ module Slim
|
|
5
5
|
class Parser < Temple::Parser
|
6
6
|
define_options :file,
|
7
7
|
:default_tag,
|
8
|
+
:code_attr_delims,
|
9
|
+
:attr_list_delims,
|
8
10
|
:tabsize => 4,
|
9
11
|
:shortcut => {
|
10
12
|
'#' => { :attr => 'id' },
|
@@ -14,7 +16,7 @@ module Slim
|
|
14
16
|
'(' => ')',
|
15
17
|
'[' => ']',
|
16
18
|
'{' => '}',
|
17
|
-
|
19
|
+
}
|
18
20
|
|
19
21
|
class SyntaxError < StandardError
|
20
22
|
attr_reader :error, :file, :line, :lineno, :column
|
@@ -40,6 +42,8 @@ module Slim
|
|
40
42
|
|
41
43
|
def initialize(opts = {})
|
42
44
|
super
|
45
|
+
@attr_list_delims = options[:attr_list_delims] || options[:attr_delims]
|
46
|
+
@code_attr_delims = options[:code_attr_delims] || options[:attr_delims]
|
43
47
|
tabsize = options[:tabsize]
|
44
48
|
if tabsize > 1
|
45
49
|
@tab_re = /\G((?: {#{tabsize}})*) {0,#{tabsize-1}}\t/
|
@@ -61,9 +65,12 @@ module Slim
|
|
61
65
|
@attr_shortcut_re = /\A(#{keys}+)((?:#{WORD_RE}|-)*)/
|
62
66
|
keys = Regexp.union @tag_shortcut.keys.sort_by {|k| -k.size }
|
63
67
|
@tag_re = /\A(?:#{keys}|\*(?=[^\s]+)|(#{WORD_RE}(?:#{WORD_RE}|:|-)*#{WORD_RE}|#{WORD_RE}+))/
|
64
|
-
keys = Regexp.escape
|
65
|
-
@
|
66
|
-
|
68
|
+
keys = Regexp.escape @code_attr_delims.keys.join
|
69
|
+
@code_attr_delims_re = /\A[#{keys}]/
|
70
|
+
keys = Regexp.escape @attr_list_delims.keys.join
|
71
|
+
@attr_list_delims_re = /\A\s*([#{keys}])/
|
72
|
+
# Access available engine keys to allow nicer one-line syntax
|
73
|
+
@embedded_re = /\A(#{Regexp.union(Embedded.engines.keys.map(&:to_s))}):(\s*)/
|
67
74
|
end
|
68
75
|
|
69
76
|
# Compile string to Temple expression
|
@@ -83,6 +90,7 @@ module Slim
|
|
83
90
|
protected
|
84
91
|
|
85
92
|
WORD_RE = ''.respond_to?(:encoding) ? '\p{Word}' : '\w'
|
93
|
+
LC_WORD_RE = '[_a-z0-9]'
|
86
94
|
ATTR_NAME = "\\A\\s*(#{WORD_RE}(?:#{WORD_RE}|:|-)*)"
|
87
95
|
QUOTED_ATTR_RE = /#{ATTR_NAME}\s*=(=?)\s*("|')/
|
88
96
|
CODE_ATTR_RE = /#{ATTR_NAME}\s*=(=?)\s*/
|
@@ -100,7 +108,7 @@ module Slim
|
|
100
108
|
#
|
101
109
|
# We uses this information to figure out how many steps we must "jump"
|
102
110
|
# out when we see an de-indented line.
|
103
|
-
@indents = [
|
111
|
+
@indents = []
|
104
112
|
|
105
113
|
# Whenever we want to output something, we'll *always* output it to the
|
106
114
|
# last stack in this array. So when there's a line that expects
|
@@ -138,6 +146,9 @@ module Slim
|
|
138
146
|
|
139
147
|
indent = get_indent(@line)
|
140
148
|
|
149
|
+
# Choose first indentation yourself
|
150
|
+
@indents << indent if @indents.empty?
|
151
|
+
|
141
152
|
# Remove the indentation
|
142
153
|
@line.lstrip!
|
143
154
|
|
@@ -159,7 +170,7 @@ module Slim
|
|
159
170
|
# This line was deindented.
|
160
171
|
# Now we're have to go through the all the indents and figure out
|
161
172
|
# how many levels we've deindented.
|
162
|
-
while indent < @indents.last
|
173
|
+
while indent < @indents.last && @indents.size > 1
|
163
174
|
@indents.pop
|
164
175
|
@stacks.pop
|
165
176
|
end
|
@@ -180,7 +191,7 @@ module Slim
|
|
180
191
|
case @line
|
181
192
|
when /\A\/!( ?)/
|
182
193
|
# HTML comment
|
183
|
-
@stacks.last << [:html, :comment, [:slim, :text, parse_text_block($', @indents.last + $1.size + 2)]]
|
194
|
+
@stacks.last << [:html, :comment, [:slim, :text, :verbatim, parse_text_block($', @indents.last + $1.size + 2)]]
|
184
195
|
when /\A\/\[\s*(.*?)\s*\]\s*\Z/
|
185
196
|
# HTML conditional comment
|
186
197
|
block = [:multi]
|
@@ -190,9 +201,9 @@ module Slim
|
|
190
201
|
# Slim comment
|
191
202
|
parse_comment_block
|
192
203
|
when /\A([\|'])( ?)/
|
193
|
-
# Found
|
204
|
+
# Found verbatim text block.
|
194
205
|
trailing_ws = $1 == "'"
|
195
|
-
@stacks.last << [:slim, :text, parse_text_block($', @indents.last + $2.size + 1)]
|
206
|
+
@stacks.last << [:slim, :text, :verbatim, parse_text_block($', @indents.last + $2.size + 1)]
|
196
207
|
@stacks.last << [:static, ' '] if trailing_ws
|
197
208
|
when /\A</
|
198
209
|
# Inline html
|
@@ -216,10 +227,10 @@ module Slim
|
|
216
227
|
@stacks.last << [:slim, :output, $1.empty?, parse_broken_line, block]
|
217
228
|
@stacks.last << [:static, ' '] if trailing_ws
|
218
229
|
@stacks << block
|
219
|
-
when
|
230
|
+
when @embedded_re
|
220
231
|
# Embedded template detected. It is treated as block.
|
221
|
-
@stacks.last << [:slim, :embedded, $1, parse_text_block]
|
222
|
-
when /\Adoctype\s+/
|
232
|
+
@stacks.last << [:slim, :embedded, $1, parse_text_block($', @orig_line.size - $'.size + $2.size)]
|
233
|
+
when /\Adoctype\s+/
|
223
234
|
# Found doctype declaration
|
224
235
|
@stacks.last << [:html, :doctype, $'.strip]
|
225
236
|
when @tag_re
|
@@ -227,11 +238,15 @@ module Slim
|
|
227
238
|
@line = $' if $1
|
228
239
|
parse_tag($&)
|
229
240
|
else
|
230
|
-
|
241
|
+
unknown_line_indicator
|
231
242
|
end
|
232
243
|
@stacks.last << [:newline]
|
233
244
|
end
|
234
245
|
|
246
|
+
def unknown_line_indicator
|
247
|
+
syntax_error! 'Unknown line indicator'
|
248
|
+
end
|
249
|
+
|
235
250
|
def parse_comment_block
|
236
251
|
while !@lines.empty? && (@lines.first =~ /\A\s*\Z/ || get_indent(@lines.first) > @indents.last)
|
237
252
|
next_line
|
@@ -239,7 +254,7 @@ module Slim
|
|
239
254
|
end
|
240
255
|
end
|
241
256
|
|
242
|
-
def parse_text_block(first_line = nil, text_indent = nil
|
257
|
+
def parse_text_block(first_line = nil, text_indent = nil)
|
243
258
|
result = [:multi]
|
244
259
|
if !first_line || first_line.empty?
|
245
260
|
text_indent = nil
|
@@ -269,11 +284,9 @@ module Slim
|
|
269
284
|
# as deep as the first line.
|
270
285
|
offset = text_indent ? indent - text_indent : 0
|
271
286
|
if offset < 0
|
272
|
-
|
273
|
-
|
274
|
-
(in_tag ? "\nAre you trying to nest a child tag in a tag containing text? Use | for the text block!" : ''))
|
287
|
+
text_indent += offset
|
288
|
+
offset = 0
|
275
289
|
end
|
276
|
-
|
277
290
|
result << [:newline] << [:slim, :interpolate, (text_indent ? "\n" : '') + (' ' * offset) + @line]
|
278
291
|
|
279
292
|
# The indentation of first line of the text block
|
@@ -326,19 +339,22 @@ module Slim
|
|
326
339
|
when /\A\s*:\s*/
|
327
340
|
# Block expansion
|
328
341
|
@line = $'
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
342
|
+
if @line =~ @embedded_re
|
343
|
+
tag << [:slim, :embedded, $1, parse_text_block($', @orig_line.size - @line.size + $2.size)]
|
344
|
+
else
|
345
|
+
(@line =~ @tag_re) || syntax_error!('Expected tag')
|
346
|
+
@line = $' if $1
|
347
|
+
content = [:multi]
|
348
|
+
tag << content
|
349
|
+
i = @stacks.size
|
350
|
+
@stacks << content
|
351
|
+
parse_tag($&)
|
352
|
+
@stacks.delete_at(i)
|
353
|
+
end
|
337
354
|
when /\A\s*=(=?)(['<>]*)/
|
338
355
|
# Handle output code
|
339
356
|
@line = $'
|
340
357
|
trailing_ws2 = $2.include?('\'') || $2.include?('>')
|
341
|
-
leading_ws2 = $2.include?('<')
|
342
358
|
block = [:multi]
|
343
359
|
@stacks.last.insert(-2, [:static, ' ']) if !leading_ws && $2.include?('<')
|
344
360
|
tag << [:slim, :output, $1 != '=', parse_broken_line, block]
|
@@ -353,17 +369,17 @@ module Slim
|
|
353
369
|
content = [:multi]
|
354
370
|
tag << content
|
355
371
|
@stacks << content
|
356
|
-
when /\A
|
372
|
+
when /\A ?/
|
357
373
|
# Text content
|
358
|
-
tag << [:slim, :text, parse_text_block($
|
374
|
+
tag << [:slim, :text, :inline, parse_text_block($', @orig_line.size - $'.size)]
|
359
375
|
end
|
360
376
|
end
|
361
377
|
|
362
378
|
def parse_attributes(attributes)
|
363
379
|
# Check to see if there is a delimiter right after the tag name
|
364
380
|
delimiter = nil
|
365
|
-
if @line =~ @
|
366
|
-
delimiter =
|
381
|
+
if @line =~ @attr_list_delims_re
|
382
|
+
delimiter = @attr_list_delims[$1]
|
367
383
|
@line = $'
|
368
384
|
end
|
369
385
|
|
@@ -434,9 +450,9 @@ module Slim
|
|
434
450
|
elsif @line[0] == close_delimiter[0]
|
435
451
|
count -= 1
|
436
452
|
end
|
437
|
-
elsif @line =~ @
|
453
|
+
elsif @line =~ @code_attr_delims_re
|
438
454
|
count = 1
|
439
|
-
delimiter, close_delimiter = $&,
|
455
|
+
delimiter, close_delimiter = $&, @code_attr_delims[$&]
|
440
456
|
end
|
441
457
|
code << @line.slice!(0)
|
442
458
|
end
|
@@ -448,27 +464,21 @@ module Slim
|
|
448
464
|
def parse_quoted_attribute(quote)
|
449
465
|
value, count = '', 0
|
450
466
|
|
451
|
-
until
|
452
|
-
if @line =~ /\A
|
453
|
-
value << ' '
|
467
|
+
until count == 0 && @line[0] == quote[0]
|
468
|
+
if @line =~ /\A(\\)?\Z/
|
469
|
+
value << ($1 ? ' ' : "\n")
|
454
470
|
expect_next_line
|
455
471
|
else
|
456
|
-
if
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
count -= 1
|
461
|
-
end
|
462
|
-
elsif @line =~ /\A#\{/
|
463
|
-
value << @line.slice!(0)
|
464
|
-
count = 1
|
472
|
+
if @line[0] == ?{
|
473
|
+
count += 1
|
474
|
+
elsif @line[0] == ?}
|
475
|
+
count -= 1
|
465
476
|
end
|
466
477
|
value << @line.slice!(0)
|
467
478
|
end
|
468
479
|
end
|
469
480
|
|
470
481
|
syntax_error!("Expected closing brace }") if count != 0
|
471
|
-
syntax_error!("Expected closing quote #{quote}") if @line[0] != quote[0]
|
472
482
|
@line.slice!(0)
|
473
483
|
|
474
484
|
value
|
@@ -478,6 +488,11 @@ module Slim
|
|
478
488
|
def syntax_error!(message)
|
479
489
|
raise SyntaxError.new(message, options[:file], @orig_line, @lineno,
|
480
490
|
@orig_line && @line ? @orig_line.size - @line.size : 0)
|
491
|
+
rescue SyntaxError => ex
|
492
|
+
# HACK: Manipulate stacktrace for Rails and other frameworks
|
493
|
+
# to find the right file.
|
494
|
+
ex.backtrace.unshift "#{options[:file]}:#{@lineno}"
|
495
|
+
raise
|
481
496
|
end
|
482
497
|
|
483
498
|
def expect_next_line
|