slim 1.1.1 → 1.2.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.
- 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)
|