slim 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -0
- data/CHANGES +12 -0
- data/README.md +20 -4
- data/lib/slim/compiler.rb +117 -7
- data/lib/slim/embedded_engine.rb +1 -0
- data/lib/slim/engine.rb +7 -4
- data/lib/slim/filter.rb +11 -0
- data/lib/slim/grammar.rb +12 -7
- data/lib/slim/interpolation.rb +1 -1
- data/lib/slim/parser.rb +83 -72
- data/lib/slim/sections.rb +4 -0
- data/lib/slim/version.rb +1 -1
- data/slim.gemspec +3 -3
- data/test/slim/helper.rb +1 -1
- data/test/slim/test_code_evaluation.rb +8 -0
- data/test/slim/test_code_output.rb +2 -2
- data/test/slim/test_embedded_engines.rb +2 -4
- data/test/slim/test_html_structure.rb +102 -2
- data/test/slim/test_parser_errors.rb +1 -1
- data/test/slim/test_ruby_errors.rb +8 -1
- metadata +23 -23
- data/extra/slim-mode.el +0 -409
- data/extra/test.slim +0 -49
data/.travis.yml
CHANGED
data/CHANGES
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
1.2.0
|
2
|
+
|
3
|
+
* Add option :shortcut which configures attribute shortcuts
|
4
|
+
Default setting:
|
5
|
+
Slim::Parser.default_options[:shortcut] = {'#' => 'id', '.' => 'class', '*' => '*'}
|
6
|
+
Define custom shortcut attribute (e.g. a@ajax-link renders <a role="ajax-link"></a>)
|
7
|
+
Slim::Parser.default_options[:shortcut] = {'@' => 'role'}
|
8
|
+
Define custom shortcut attribute with tag (e.g. @ajax-link renders <a role="ajax-link"></a>)
|
9
|
+
Slim::Parser.default_options[:shortcut] = {'@' => 'a role'}
|
10
|
+
* Add syntax for splat attributes (#109)
|
11
|
+
* Support for dynamic tags, e.g. *{:tag => 'img', :src => 'image.jpg'}
|
12
|
+
|
1
13
|
1.1.1
|
2
14
|
|
3
15
|
* Evaluating a html attribute now happens only once (#219)
|
data/README.md
CHANGED
@@ -41,11 +41,16 @@ If you want to use the Slim template directly, you can use the Tilt interface:
|
|
41
41
|
|
42
42
|
## Syntax Highlighters
|
43
43
|
|
44
|
-
|
44
|
+
There are plugins for Vim, Emacs, Textmate and Espresso text editor:
|
45
|
+
|
46
|
+
* [Vim](https://github.com/bbommarito/vim-slim)
|
47
|
+
* [Textmate](https://github.com/fredwu/ruby-slim-tmbundle)
|
48
|
+
* [Emacs](https://github.com/minad/emacs-slim)
|
49
|
+
* [Espresso text editor](https://github.com/CiiDub/Slim-Sugar)
|
45
50
|
|
46
51
|
## Template Converters
|
47
52
|
|
48
|
-
For Haml, there is a [Haml2Slim converter](https://github.com/fredwu/haml2slim).
|
53
|
+
For Haml, there is a [Haml2Slim converter](https://github.com/fredwu/haml2slim). For HTML, there is a [HTML2Slim converter](https://github.com/joaomilho/html2slim).
|
49
54
|
|
50
55
|
## The syntax
|
51
56
|
|
@@ -124,7 +129,7 @@ Here's a quick example to demonstrate what a Slim template looks like:
|
|
124
129
|
|
125
130
|
#### `/!`
|
126
131
|
|
127
|
-
> Use the forward slash immediately followed by an exclamation mark for html comments (
|
132
|
+
> Use the forward slash immediately followed by an exclamation mark for html comments (` <!-- --> `).
|
128
133
|
|
129
134
|
|
130
135
|
### Things to know
|
@@ -388,8 +393,19 @@ This project is released under the MIT license.
|
|
388
393
|
|
389
394
|
## Slim related projects
|
390
395
|
|
391
|
-
* [
|
396
|
+
* [Temple](https://github.com/judofyr/slim)
|
397
|
+
|
398
|
+
* [Vim syntax highlighting](https://github.com/bbommarito/vim-slim)
|
399
|
+
* [Emacs syntax highlighting](https://github.com/minad/emacs-slim)
|
392
400
|
* [Textmate bundle](https://github.com/fredwu/ruby-slim-tmbundle)
|
401
|
+
* [Slim support for the Espresso text editor from MacRabbits](https://github.com/CiiDub/Slim-Sugar)
|
402
|
+
|
393
403
|
* [Haml2Slim converter](https://github.com/fredwu/haml2slim)
|
404
|
+
* [Html2Slim converter](https://github.com/joaomilho/html2slim)
|
405
|
+
|
394
406
|
* [Rails 3 Generators](https://github.com/leogalmeida/slim-rails)
|
407
|
+
|
408
|
+
* [Skim (Slim for Javascript)](https://github.com/jfirebaugh/skim)
|
395
409
|
* [Slim for Clojure](https://github.com/chaslemley/slim.clj)
|
410
|
+
* [Hamlet.rb (Similar template language)](https://github.com/gregwebs/hamlet.rb)
|
411
|
+
* [Coffee script plugin for Slim](https://github.com/yury/coffee-views)
|
data/lib/slim/compiler.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
module Slim
|
2
|
-
# Compiles Slim expressions into Temple::HTML expressions.
|
3
2
|
# @api private
|
4
3
|
class Compiler < Filter
|
4
|
+
def call(exp)
|
5
|
+
@attr_delimiter, @splat_used = unique_name, false
|
6
|
+
exp = compile(exp)
|
7
|
+
if @splat_used
|
8
|
+
[:multi, [:code, "#{@attr_delimiter} = #{@options[:attr_delimiter].inspect}"], exp]
|
9
|
+
else
|
10
|
+
exp
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
# Handle control expression `[:slim, :control, code, content]`
|
6
15
|
#
|
7
16
|
# @param [String] ruby code
|
@@ -19,7 +28,11 @@ module Slim
|
|
19
28
|
# @param [Array] content Temple expression
|
20
29
|
# @return [Array] Compiled temple expression
|
21
30
|
def on_slim_condcomment(condition, content)
|
22
|
-
[:html, :comment,
|
31
|
+
[:html, :comment,
|
32
|
+
[:multi,
|
33
|
+
[:static, "[#{condition}]>"],
|
34
|
+
compile(content),
|
35
|
+
[:static, '<![endif]']]]
|
23
36
|
end
|
24
37
|
|
25
38
|
# Handle output expression `[:slim, :output, escape, code, content]`
|
@@ -54,6 +67,53 @@ module Slim
|
|
54
67
|
end
|
55
68
|
end
|
56
69
|
|
70
|
+
# Handle tag expression `[:slim, :tag, name, attrs, content]`
|
71
|
+
#
|
72
|
+
# @param [String] name Tag name
|
73
|
+
# @param [Array] attrs Temple expression
|
74
|
+
# @param [Array] content Temple expression
|
75
|
+
# @return [Array] Compiled temple expression
|
76
|
+
def on_slim_tag(name, attrs, content = nil)
|
77
|
+
if name == '*'
|
78
|
+
hash, merger, formatter = splat_attributes(attrs[2..-1])
|
79
|
+
tmp = unique_name
|
80
|
+
tag = [:multi,
|
81
|
+
merger,
|
82
|
+
[:code, "#{tmp} = #{hash}.delete('tag').to_s"],
|
83
|
+
[:if, "#{tmp}.empty?",
|
84
|
+
[:code, "#{tmp} = #{@options[:default_tag].inspect}"]],
|
85
|
+
[:static, '<'],
|
86
|
+
[:dynamic, "#{tmp}"],
|
87
|
+
formatter]
|
88
|
+
tag << if content
|
89
|
+
[:multi,
|
90
|
+
[:static, '>'],
|
91
|
+
compile(content),
|
92
|
+
[:static, '</'],
|
93
|
+
[:dynamic, "#{tmp}"],
|
94
|
+
[:static, '>']]
|
95
|
+
else
|
96
|
+
[:static, '/>']
|
97
|
+
end
|
98
|
+
else
|
99
|
+
tag = [:html, :tag, name, compile(attrs)]
|
100
|
+
content ? (tag << compile(content)) : tag
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Handle attributes expression `[:slim, :attrs, *attrs]`
|
105
|
+
#
|
106
|
+
# @param [Array] *attrs Array of temple expressions
|
107
|
+
# @return [Array] Compiled temple expression
|
108
|
+
def on_slim_attrs(*attrs)
|
109
|
+
if attrs.any? {|attr| attr[1] == :splat}
|
110
|
+
hash, merger, formatter = splat_attributes(attrs)
|
111
|
+
[:multi, merger, formatter]
|
112
|
+
else
|
113
|
+
[:html, :attrs, *attrs.map {|a| compile(a) }]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
57
117
|
# Handle attribute expression `[:slim, :attr, escape, code]`
|
58
118
|
#
|
59
119
|
# @param [Boolean] escape Escape html
|
@@ -62,10 +122,8 @@ module Slim
|
|
62
122
|
def on_slim_attr(name, escape, code)
|
63
123
|
value = case code
|
64
124
|
when 'true'
|
65
|
-
escape = false
|
66
125
|
[:static, name]
|
67
126
|
when 'false', 'nil'
|
68
|
-
escape = false
|
69
127
|
[:multi]
|
70
128
|
else
|
71
129
|
tmp = unique_name
|
@@ -75,15 +133,67 @@ module Slim
|
|
75
133
|
['true', [:static, name]],
|
76
134
|
['false, nil', [:multi]],
|
77
135
|
[:else,
|
78
|
-
[:dynamic,
|
136
|
+
[:escape, escape, [:dynamic,
|
79
137
|
if delimiter = options[:attr_delimiter][name]
|
80
138
|
"#{tmp}.respond_to?(:join) ? #{tmp}.flatten.compact.join(#{delimiter.inspect}) : #{tmp}"
|
81
139
|
else
|
82
140
|
tmp
|
83
141
|
end
|
84
|
-
]]]]
|
142
|
+
]]]]]
|
85
143
|
end
|
86
|
-
[:html, :attr, name,
|
144
|
+
[:html, :attr, name, value]
|
145
|
+
end
|
146
|
+
|
147
|
+
protected
|
148
|
+
|
149
|
+
def splat_attributes(attrs)
|
150
|
+
@splat_used = true
|
151
|
+
|
152
|
+
hash, name, value, tmp = unique_name, unique_name, unique_name, unique_name
|
153
|
+
|
154
|
+
merger = [:multi, [:code, "#{hash} = {}"]]
|
155
|
+
attrs.each do |attr|
|
156
|
+
merger << if attr[0] == :html && attr[1] == :attr
|
157
|
+
[:multi,
|
158
|
+
[:capture, tmp, compile(attr[3])],
|
159
|
+
[:code, "(#{hash}[#{attr[2].inspect}] ||= []) << #{tmp}"]]
|
160
|
+
elsif attr[0] == :slim
|
161
|
+
if attr[1] == :attr
|
162
|
+
[:code, "(#{hash}[#{attr[2].inspect}] ||= []) << (#{attr[4]})"]
|
163
|
+
elsif attr[1] == :splat
|
164
|
+
[:code, "(#{attr[2]}).each {|#{name},#{value}| (#{hash}[#{name}.to_s] ||= []) << (#{value}) }"]
|
165
|
+
else
|
166
|
+
attr
|
167
|
+
end
|
168
|
+
else
|
169
|
+
attr
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
merger << [:block, "#{hash}.keys.each do |#{name}|",
|
174
|
+
[:multi,
|
175
|
+
[:code, "#{value} = #{hash}[#{name}]"],
|
176
|
+
[:code, "#{value}.flatten!"],
|
177
|
+
[:block, "#{value}.map! do |#{tmp}|",
|
178
|
+
[:case, tmp,
|
179
|
+
['true', [:code, name]],
|
180
|
+
['false, nil', [:multi]],
|
181
|
+
[:else, [:code, tmp]]]],
|
182
|
+
[:if, "#{value}.size > 1 && !#{@attr_delimiter}[#{name}]",
|
183
|
+
[:code, "raise(\"Multiple #\{#{name}\} attributes specified\")"]],
|
184
|
+
[:code, "#{hash}[#{name}] = #{value}.compact.join(#{@attr_delimiter}[#{name}].to_s)"]]]
|
185
|
+
|
186
|
+
attr = [:multi,
|
187
|
+
[:static, ' '],
|
188
|
+
[:dynamic, name],
|
189
|
+
[:static, "=#{options[:attr_wrapper]}"],
|
190
|
+
[:escape, true, [:dynamic, value]],
|
191
|
+
[:static, options[:attr_wrapper]]]
|
192
|
+
attr = [:if, "!#{value}.empty?", attr] if options[:remove_empty_attrs]
|
193
|
+
enumerator = options[:sort_attrs] ? "#{hash}.sort_by {|#{name},#{value}| #{name} }" : hash
|
194
|
+
formatter = [:block, "#{enumerator}.each do |#{name},#{value}|", attr]
|
195
|
+
|
196
|
+
return hash, merger, formatter
|
87
197
|
end
|
88
198
|
end
|
89
199
|
end
|
data/lib/slim/embedded_engine.rb
CHANGED
@@ -179,6 +179,7 @@ module Slim
|
|
179
179
|
register :rdoc, InterpolateTiltEngine
|
180
180
|
register :creole, InterpolateTiltEngine
|
181
181
|
register :wiki, InterpolateTiltEngine
|
182
|
+
register :mediawiki, InterpolateTiltEngine
|
182
183
|
|
183
184
|
# These engines are executed at compile time
|
184
185
|
register :coffee, TagEngine, :tag => :script, :attributes => { :type => 'text/javascript' }, :engine => StaticTiltEngine
|
data/lib/slim/engine.rb
CHANGED
@@ -9,10 +9,12 @@ module Slim
|
|
9
9
|
#
|
10
10
|
# This overwrites some temple default options.
|
11
11
|
set_default_options :pretty => false,
|
12
|
+
:sort_attrs => true,
|
12
13
|
:attr_wrapper => '"',
|
13
14
|
:attr_delimiter => {'class' => ' '},
|
14
|
-
:
|
15
|
-
|
15
|
+
:remove_empty_attrs => true,
|
16
|
+
:generator => Temple::Generators::ArrayBuffer,
|
17
|
+
:default_tag => 'div'
|
16
18
|
#
|
17
19
|
# Document all supported options with purpose, type etc.
|
18
20
|
#
|
@@ -22,6 +24,7 @@ module Slim
|
|
22
24
|
# Integer | :tabsize | 4 | Number of whitespaces per tab (used by the parser)
|
23
25
|
# String | :encoding | "utf-8" | Set encoding of template
|
24
26
|
# String | :default_tag | "div" | Default tag to be used if tag name is omitted
|
27
|
+
# Hash | :shortcut | {'.' => 'class', ...} | Attribute shortcuts
|
25
28
|
# String list | :enable_engines | All enabled | List of enabled embedded engines (whitelist)
|
26
29
|
# String list | :disable_engines | None disabled | List of disabled embedded engines (blacklist)
|
27
30
|
# Boolean | :sections | false | Enable sections mode (logic-less)
|
@@ -56,12 +59,12 @@ module Slim
|
|
56
59
|
# It is recommended to set the default settings only once in the code and avoid duplication. Only use
|
57
60
|
# `set_default_options` when you have to override some default settings.
|
58
61
|
#
|
59
|
-
use Slim::Parser, :file, :tabsize, :encoding, :default_tag
|
62
|
+
use Slim::Parser, :file, :tabsize, :encoding, :shortcut, :default_tag
|
60
63
|
use Slim::EmbeddedEngine, :enable_engines, :disable_engines, :pretty
|
61
64
|
use Slim::Interpolation
|
62
65
|
use Slim::Sections, :sections, :dictionary, :dictionary_access
|
63
66
|
use Slim::EndInserter
|
64
|
-
use Slim::Compiler, :disable_capture, :attr_delimiter
|
67
|
+
use Slim::Compiler, :disable_capture, :attr_delimiter, :attr_wrapper, :sort_attrs, :remove_empty_attrs, :default_tag
|
65
68
|
html :AttributeMerger, :attr_delimiter
|
66
69
|
html :AttributeSorter, :sort_attrs
|
67
70
|
html :AttributeRemover, :remove_empty_attrs
|
data/lib/slim/filter.rb
CHANGED
@@ -26,5 +26,16 @@ module Slim
|
|
26
26
|
def on_slim_output(code, escape, content)
|
27
27
|
[:slim, :output, code, escape, compile(content)]
|
28
28
|
end
|
29
|
+
|
30
|
+
# Pass-through handler
|
31
|
+
def on_slim_attrs(*attrs)
|
32
|
+
[:slim, :attrs, *attrs.map {|a| compile(a) }]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Pass-through handler
|
36
|
+
def on_slim_tag(name, attrs, content = nil)
|
37
|
+
tag = [:slim, :tag, name, compile(attrs)]
|
38
|
+
content ? (tag << compile(content)) : tag
|
39
|
+
end
|
29
40
|
end
|
30
41
|
end
|
data/lib/slim/grammar.rb
CHANGED
@@ -5,14 +5,19 @@ module Slim
|
|
5
5
|
extend Temple::Grammar
|
6
6
|
|
7
7
|
Expression <<
|
8
|
-
[:slim, :control, String, Expression]
|
9
|
-
[:slim, :condcomment, String, Expression]
|
10
|
-
[:slim, :output, Bool, String, Expression]
|
11
|
-
[:slim, :interpolate, String]
|
12
|
-
[:slim, :embedded, String, Expression]
|
8
|
+
[:slim, :control, String, Expression] |
|
9
|
+
[:slim, :condcomment, String, Expression] |
|
10
|
+
[:slim, :output, Bool, String, Expression] |
|
11
|
+
[:slim, :interpolate, String] |
|
12
|
+
[:slim, :embedded, String, Expression] |
|
13
|
+
[:slim, :tag, String, SlimAttrs, 'Expression?']
|
13
14
|
|
14
|
-
|
15
|
-
[:slim, :
|
15
|
+
SlimAttrs <<
|
16
|
+
[:slim, :attrs, 'SlimAttr*']
|
16
17
|
|
18
|
+
SlimAttr <<
|
19
|
+
HTMLAttr |
|
20
|
+
[:slim, :attr, String, Bool, String] |
|
21
|
+
[:slim, :splat, String]
|
17
22
|
end
|
18
23
|
end
|
data/lib/slim/interpolation.rb
CHANGED
@@ -16,7 +16,7 @@ module Slim
|
|
16
16
|
case string
|
17
17
|
when /\A\\#\{/
|
18
18
|
# Escaped interpolation
|
19
|
-
#
|
19
|
+
# Use [:slim, :output] because this is used by InterpolateTiltEngine
|
20
20
|
# to filter out protected strings (Issue #141).
|
21
21
|
block << [:slim, :output, false, '\'#{\'', [:multi]]
|
22
22
|
string = $'
|
data/lib/slim/parser.rb
CHANGED
@@ -4,9 +4,12 @@ module Slim
|
|
4
4
|
class Parser
|
5
5
|
include Temple::Mixins::Options
|
6
6
|
|
7
|
-
set_default_options :tabsize
|
7
|
+
set_default_options :tabsize => 4,
|
8
8
|
:encoding => 'utf-8',
|
9
|
-
:
|
9
|
+
:shortcut => {
|
10
|
+
'#' => 'id',
|
11
|
+
'.' => 'class'
|
12
|
+
}
|
10
13
|
|
11
14
|
class SyntaxError < StandardError
|
12
15
|
attr_reader :error, :file, :line, :lineno, :column
|
@@ -33,6 +36,17 @@ module Slim
|
|
33
36
|
def initialize(options = {})
|
34
37
|
super
|
35
38
|
@tab = ' ' * @options[:tabsize]
|
39
|
+
@shortcut = {}
|
40
|
+
@options[:shortcut].each do |k,v|
|
41
|
+
@shortcut[k] = if v =~ /\A([^\s]+)\s+([^\s]+)\Z/
|
42
|
+
[$1, $2]
|
43
|
+
else
|
44
|
+
[@options[:default_tag], v]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
shortcut = "[#{Regexp.escape @shortcut.keys.join}]"
|
48
|
+
@shortcut_regex = /\A(#{shortcut})(\w[\w-]*\w|\w+)/
|
49
|
+
@tag_regex = /\A(?:#{shortcut}|\*(?=[^\s]+)|(\w[\w:-]*\w|\w+))/
|
36
50
|
end
|
37
51
|
|
38
52
|
# Compile string to Temple expression
|
@@ -66,15 +80,10 @@ module Slim
|
|
66
80
|
'{' => '}',
|
67
81
|
}.freeze
|
68
82
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
}
|
73
|
-
|
74
|
-
DELIMITER_REGEX = /\A[\(\[\{]/
|
75
|
-
ATTR_NAME_REGEX = '\A\s*(\w[:\w-]*)'
|
76
|
-
CLASS_ID_REGEX = /\A(#|\.)(\w[\w-]*\w|\w+)/
|
77
|
-
TAG_REGEX = /\A([#\.]|\w[\w:-]*\w|\w+)/
|
83
|
+
DELIMITER_REGEX = /\A[#{Regexp.escape DELIMITERS.keys.join}]/
|
84
|
+
ATTR_NAME = '\A\s*(\w[:\w-]*)'
|
85
|
+
QUOTED_ATTR_REGEX = /#{ATTR_NAME}=("|')/
|
86
|
+
CODE_ATTR_REGEX = /#{ATTR_NAME}=/
|
78
87
|
|
79
88
|
def reset(lines = nil, stacks = nil)
|
80
89
|
# Since you can indent however you like in Slim, we need to keep a list
|
@@ -208,8 +217,9 @@ module Slim
|
|
208
217
|
when /\Adoctype\s+/i
|
209
218
|
# Found doctype declaration
|
210
219
|
@stacks.last << [:html, :doctype, $'.strip]
|
211
|
-
when
|
220
|
+
when @tag_regex
|
212
221
|
# Found a HTML tag.
|
222
|
+
@line = $' if $1
|
213
223
|
parse_tag($&)
|
214
224
|
else
|
215
225
|
syntax_error! 'Unknown line indicator'
|
@@ -279,25 +289,20 @@ module Slim
|
|
279
289
|
end
|
280
290
|
|
281
291
|
def parse_tag(tag)
|
282
|
-
|
283
|
-
tag = options[:default_tag]
|
284
|
-
else
|
285
|
-
@line.slice!(0, tag.size)
|
286
|
-
end
|
287
|
-
|
288
|
-
tag = [:html, :tag, tag, parse_attributes]
|
292
|
+
tag = [:slim, :tag, @shortcut[tag] ? @shortcut[tag][0] : tag, parse_attributes]
|
289
293
|
@stacks.last << tag
|
290
294
|
|
291
295
|
case @line
|
292
296
|
when /\A\s*:\s*/
|
293
297
|
# Block expansion
|
294
298
|
@line = $'
|
295
|
-
(@line =~
|
299
|
+
(@line =~ @tag_regex) || syntax_error!('Expected tag')
|
300
|
+
@line = $' if $1
|
296
301
|
content = [:multi]
|
297
302
|
tag << content
|
298
303
|
i = @stacks.size
|
299
304
|
@stacks << content
|
300
|
-
parse_tag(
|
305
|
+
parse_tag($&)
|
301
306
|
@stacks.delete_at(i)
|
302
307
|
when /\A\s*=(=?)('?)/
|
303
308
|
# Handle output code
|
@@ -321,13 +326,14 @@ module Slim
|
|
321
326
|
end
|
322
327
|
|
323
328
|
def parse_attributes
|
324
|
-
attributes = [:
|
329
|
+
attributes = [:slim, :attrs]
|
330
|
+
attribute = nil
|
325
331
|
|
326
|
-
# Find any
|
327
|
-
while @line =~
|
332
|
+
# Find any shortcut attributes
|
333
|
+
while @line =~ @shortcut_regex
|
328
334
|
# The class/id attribute is :static instead of :slim :text,
|
329
335
|
# because we don't want text interpolation in .class or #id shortcut
|
330
|
-
attributes << [:html, :attr,
|
336
|
+
attributes << [:html, :attr, @shortcut[$1][1], [:static, $2]]
|
331
337
|
@line = $'
|
332
338
|
end
|
333
339
|
|
@@ -338,54 +344,67 @@ module Slim
|
|
338
344
|
@line.slice!(0)
|
339
345
|
end
|
340
346
|
|
341
|
-
|
342
|
-
|
347
|
+
if delimiter
|
348
|
+
boolean_attr_regex = /#{ATTR_NAME}(?=(\s|#{Regexp.escape delimiter}))/
|
349
|
+
end_regex = /\A\s*#{Regexp.escape delimiter}/
|
350
|
+
end
|
351
|
+
|
343
352
|
while true
|
344
|
-
|
345
|
-
|
346
|
-
|
353
|
+
case @line
|
354
|
+
when /\A\s*\*(?=[^\s]+)/
|
355
|
+
# Splat attribute
|
356
|
+
@line = $'
|
357
|
+
attributes << [:slim, :splat, parse_ruby_code(delimiter)]
|
358
|
+
when QUOTED_ATTR_REGEX
|
359
|
+
# Value is quoted (static)
|
347
360
|
@line = $'
|
361
|
+
attributes << [:html, :attr, $1, [:slim, :interpolate, parse_quoted_attribute($2)]]
|
362
|
+
when CODE_ATTR_REGEX
|
363
|
+
# Value is ruby code
|
364
|
+
@line = $'
|
365
|
+
escape = @line[0] != ?=
|
366
|
+
@line.slice!(0) unless escape
|
348
367
|
name = $1
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
368
|
+
value = parse_ruby_code(delimiter)
|
369
|
+
# Remove attribute wrapper which doesn't belong to the ruby code
|
370
|
+
# e.g id=[hash[:a] + hash[:b]]
|
371
|
+
value = value[1..-2] if value =~ DELIMITER_REGEX &&
|
372
|
+
DELIMITERS[$&] == value[-1, 1]
|
373
|
+
syntax_error!('Invalid empty attribute') if value.empty?
|
374
|
+
attributes << [:slim, :attr, name, escape, value]
|
375
|
+
else
|
376
|
+
break unless delimiter
|
377
|
+
|
378
|
+
case @line
|
379
|
+
when boolean_attr_regex
|
380
|
+
# Boolean attribute
|
353
381
|
@line = $'
|
354
|
-
attributes << [:
|
382
|
+
attributes << [:slim, :attr, $1, false, 'true']
|
383
|
+
when end_regex
|
384
|
+
# Find ending delimiter
|
385
|
+
@line = $'
|
386
|
+
break
|
355
387
|
else
|
356
|
-
#
|
357
|
-
|
358
|
-
|
359
|
-
|
388
|
+
# Found something where an attribute should be
|
389
|
+
@line.lstrip!
|
390
|
+
syntax_error!('Expected attribute') unless @line.empty?
|
391
|
+
|
392
|
+
# Attributes span multiple lines
|
393
|
+
@stacks.last << [:newline]
|
394
|
+
orig_line, lineno = @orig_line, @lineno
|
395
|
+
next_line || syntax_error!("Expected closing delimiter #{delimiter}",
|
396
|
+
:orig_line => orig_line,
|
397
|
+
:lineno => lineno,
|
398
|
+
:column => orig_line.size)
|
360
399
|
end
|
361
400
|
end
|
362
|
-
|
363
|
-
# No ending delimiter, attribute end
|
364
|
-
break unless delimiter
|
365
|
-
|
366
|
-
# Find ending delimiter
|
367
|
-
if @line =~ /\A\s*#{Regexp.escape delimiter}/
|
368
|
-
@line = $'
|
369
|
-
break
|
370
|
-
end
|
371
|
-
|
372
|
-
# Found something where an attribute should be
|
373
|
-
@line.lstrip!
|
374
|
-
syntax_error!('Expected attribute') unless @line.empty?
|
375
|
-
|
376
|
-
# Attributes span multiple lines
|
377
|
-
@stacks.last << [:newline]
|
378
|
-
next_line || syntax_error!("Expected closing delimiter #{delimiter}",
|
379
|
-
:orig_line => orig_line,
|
380
|
-
:lineno => lineno,
|
381
|
-
:column => orig_line.size)
|
382
401
|
end
|
383
402
|
|
384
403
|
attributes
|
385
404
|
end
|
386
405
|
|
387
|
-
def
|
388
|
-
|
406
|
+
def parse_ruby_code(outer_delimiter)
|
407
|
+
code, count, delimiter, close_delimiter = '', 0, nil, nil
|
389
408
|
|
390
409
|
# Attribute ends with space or attribute delimiter
|
391
410
|
end_regex = /\A[\s#{Regexp.escape outer_delimiter.to_s}]/
|
@@ -401,18 +420,10 @@ module Slim
|
|
401
420
|
count = 1
|
402
421
|
delimiter, close_delimiter = $&, DELIMITERS[$&]
|
403
422
|
end
|
404
|
-
|
423
|
+
code << @line.slice!(0)
|
405
424
|
end
|
406
|
-
|
407
|
-
|
408
|
-
syntax_error!('Invalid empty attribute') if value.empty?
|
409
|
-
|
410
|
-
# Remove attribute wrapper which doesn't belong to the ruby code
|
411
|
-
# e.g id=[hash[:a] + hash[:b]]
|
412
|
-
value = value[1..-2] if value =~ DELIMITER_REGEX &&
|
413
|
-
DELIMITERS[$&] == value[-1, 1]
|
414
|
-
|
415
|
-
value
|
425
|
+
syntax_error!("Expected closing delimiter #{close_delimiter}") if count != 0
|
426
|
+
code
|
416
427
|
end
|
417
428
|
|
418
429
|
def parse_quoted_attribute(quote)
|