haml 4.0.7 → 4.1.0.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of haml might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -23
- data/README.md +3 -2
- data/REFERENCE.md +24 -9
- data/Rakefile +5 -4
- data/lib/haml/buffer.rb +27 -26
- data/lib/haml/compiler.rb +71 -70
- data/lib/haml/engine.rb +16 -26
- data/lib/haml/exec.rb +9 -7
- data/lib/haml/filters.rb +16 -22
- data/lib/haml/helpers.rb +97 -74
- data/lib/haml/helpers/action_view_extensions.rb +2 -2
- data/lib/haml/helpers/action_view_mods.rb +10 -3
- data/lib/haml/options.rb +6 -14
- data/lib/haml/parser.rb +184 -182
- data/lib/haml/util.rb +67 -78
- data/lib/haml/version.rb +1 -1
- data/test/engine_test.rb +80 -71
- data/test/gemfiles/Gemfile.rails-3.0.x +1 -1
- data/test/gemfiles/Gemfile.rails-3.1.x +1 -1
- data/test/gemfiles/Gemfile.rails-3.2.x +1 -1
- data/test/gemfiles/Gemfile.rails-4.0.x +1 -1
- data/test/gemfiles/Gemfile.rails-4.0.x.lock +101 -0
- data/test/helper_test.rb +85 -33
- data/test/parser_test.rb +22 -0
- data/test/template_test.rb +1 -29
- data/test/templates/with_bom.haml +1 -0
- data/test/test_helper.rb +4 -0
- data/test/util_test.rb +7 -1
- metadata +39 -44
- data/test/haml-spec/LICENSE +0 -14
- data/test/haml-spec/README.md +0 -106
- data/test/haml-spec/lua_haml_spec.lua +0 -38
- data/test/haml-spec/perl_haml_test.pl +0 -81
- data/test/haml-spec/ruby_haml_test.rb +0 -23
- data/test/haml-spec/tests.json +0 -660
@@ -32,7 +32,7 @@ module Haml
|
|
32
32
|
#
|
33
33
|
# @return [String] The class name for the current page
|
34
34
|
def page_class
|
35
|
-
controller.controller_name
|
35
|
+
"#{controller.controller_name} #{controller.action_name}"
|
36
36
|
end
|
37
37
|
alias_method :generate_content_class_names, :page_class
|
38
38
|
|
@@ -45,8 +45,8 @@ module Haml
|
|
45
45
|
# @yield A block in which all input to `#haml_concat` is treated as raw.
|
46
46
|
# @see Haml::Util#rails_xss_safe?
|
47
47
|
def with_raw_haml_concat
|
48
|
-
old = instance_variable_defined?('@_haml_concat_raw') ? @_haml_concat_raw : false
|
49
48
|
@_haml_concat_raw = true
|
49
|
+
old = @_haml_concat_raw
|
50
50
|
yield
|
51
51
|
ensure
|
52
52
|
@_haml_concat_raw = old
|
@@ -41,9 +41,16 @@ module ActionView
|
|
41
41
|
if Haml::Helpers.block_is_haml?(block)
|
42
42
|
#double assignment is to avoid warnings
|
43
43
|
_hamlout = _hamlout = eval('_hamlout', block.binding) # Necessary since capture_haml checks _hamlout
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
value = nil
|
45
|
+
buffer = capture_haml(*args) { value = yield(*args) }
|
46
|
+
str =
|
47
|
+
if !buffer.empty?
|
48
|
+
buffer
|
49
|
+
elsif value.is_a?(String)
|
50
|
+
value
|
51
|
+
else
|
52
|
+
nil
|
53
|
+
end
|
47
54
|
# NonCattingString is present in Rails less than 3.1.0. When support
|
48
55
|
# for 3.0 is dropped, this can be removed.
|
49
56
|
return ActionView::NonConcattingString.new(str) if str && defined?(ActionView::NonConcattingString)
|
data/lib/haml/options.rb
CHANGED
@@ -6,9 +6,7 @@ module Haml
|
|
6
6
|
|
7
7
|
@defaults = {
|
8
8
|
:attr_wrapper => "'",
|
9
|
-
:autoclose => %w(
|
10
|
-
hr img input isindex keygen link menuitem meta
|
11
|
-
param source track wbr),
|
9
|
+
:autoclose => %w(meta img link br hr input area param col base),
|
12
10
|
:encoding => "UTF-8",
|
13
11
|
:escape_attrs => true,
|
14
12
|
:escape_html => false,
|
@@ -63,7 +61,6 @@ module Haml
|
|
63
61
|
attr_accessor :autoclose
|
64
62
|
|
65
63
|
# The encoding to use for the HTML output.
|
66
|
-
# Only available on Ruby 1.9 or higher.
|
67
64
|
# This can be a string or an `Encoding` Object. Note that Haml **does not**
|
68
65
|
# automatically re-encode Ruby values; any strings coming from outside the
|
69
66
|
# application should be converted before being passed into the Haml
|
@@ -171,7 +168,7 @@ module Haml
|
|
171
168
|
|
172
169
|
def initialize(values = {}, &block)
|
173
170
|
defaults.each {|k, v| instance_variable_set :"@#{k}", v}
|
174
|
-
values.
|
171
|
+
values.each {|k, v| send("#{k}=", v) if defaults.has_key?(k) && !v.nil?}
|
175
172
|
yield if block_given?
|
176
173
|
end
|
177
174
|
|
@@ -245,14 +242,10 @@ module Haml
|
|
245
242
|
@remove_whitespace = value
|
246
243
|
end
|
247
244
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
return unless value
|
253
|
-
@encoding = value.is_a?(Encoding) ? value.name : value.to_s
|
254
|
-
@encoding = "UTF-8" if @encoding.upcase == "US-ASCII"
|
255
|
-
end
|
245
|
+
def encoding=(value)
|
246
|
+
return unless value
|
247
|
+
@encoding = value.is_a?(Encoding) ? value.name : value.to_s
|
248
|
+
@encoding = "UTF-8" if @encoding.upcase == "US-ASCII"
|
256
249
|
end
|
257
250
|
|
258
251
|
# Returns a subset of options: those that {Haml::Buffer} cares about.
|
@@ -274,6 +267,5 @@ module Haml
|
|
274
267
|
def defaults
|
275
268
|
self.class.defaults
|
276
269
|
end
|
277
|
-
|
278
270
|
end
|
279
271
|
end
|
data/lib/haml/parser.rb
CHANGED
@@ -71,7 +71,7 @@ module Haml
|
|
71
71
|
# foo.each do | bar |
|
72
72
|
# = bar
|
73
73
|
#
|
74
|
-
BLOCK_WITH_SPACES = /do
|
74
|
+
BLOCK_WITH_SPACES = /do\s*\|\s*[^\|]*\s+\|\z/
|
75
75
|
|
76
76
|
MID_BLOCK_KEYWORDS = %w[else elsif rescue ensure end when]
|
77
77
|
START_BLOCK_KEYWORDS = %w[if begin case unless]
|
@@ -80,22 +80,28 @@ module Haml
|
|
80
80
|
BLOCK_KEYWORD_REGEX = /^-?\s*(?:(#{MID_BLOCK_KEYWORDS.join('|')})|#{START_BLOCK_KEYWORD_REGEX.source})\b/
|
81
81
|
|
82
82
|
# The Regex that matches a Doctype command.
|
83
|
-
DOCTYPE_REGEX = /(\d(?:\.\d)?)
|
83
|
+
DOCTYPE_REGEX = /(\d(?:\.\d)?)?\s*([a-z]*)\s*([^ ]+)?/i
|
84
84
|
|
85
85
|
# The Regex that matches a literal string or symbol value
|
86
|
-
LITERAL_VALUE_REGEX = /:(\w*)|(["'])((
|
86
|
+
LITERAL_VALUE_REGEX = /:(\w*)|(["'])((?!\\|\#{|\#@|\#\$|\2).|\\.)*\2/
|
87
87
|
|
88
88
|
def initialize(template, options)
|
89
|
-
# :eod is a special end-of-document marker
|
90
|
-
@template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
|
91
89
|
@options = options
|
92
90
|
@flat = false
|
93
|
-
@index = 0
|
94
91
|
# Record the indent levels of "if" statements to validate the subsequent
|
95
92
|
# elsif and else statements are indented at the appropriate level.
|
96
93
|
@script_level_stack = []
|
97
94
|
@template_index = 0
|
98
95
|
@template_tabs = 0
|
96
|
+
|
97
|
+
match = template.rstrip.scan(/(([ \t]+)?(.*?))(?:\Z|\r\n|\r|\n)/m)
|
98
|
+
# discard the last match which is always blank
|
99
|
+
match.pop
|
100
|
+
@template = match.each_with_index.map do |(full, whitespace, text), index|
|
101
|
+
Line.new(whitespace, text.rstrip, full, index, self, false)
|
102
|
+
end
|
103
|
+
# Append special end-of-document marker
|
104
|
+
@template << Line.new(nil, '-#', '-#', @template.size, self, true)
|
99
105
|
end
|
100
106
|
|
101
107
|
def parse
|
@@ -106,7 +112,9 @@ module Haml
|
|
106
112
|
|
107
113
|
raise SyntaxError.new(Error.message(:indenting_at_start), @line.index) if @line.tabs != 0
|
108
114
|
|
109
|
-
|
115
|
+
loop do
|
116
|
+
next_line
|
117
|
+
|
110
118
|
process_indent(@line) unless @line.text.empty?
|
111
119
|
|
112
120
|
if flat?
|
@@ -118,7 +126,7 @@ module Haml
|
|
118
126
|
end
|
119
127
|
|
120
128
|
@tab_up = nil
|
121
|
-
process_line(@line
|
129
|
+
process_line(@line) unless @line.text.empty? || @haml_comment
|
122
130
|
if @parent.type != :haml_comment && (block_opened? || @tab_up)
|
123
131
|
@template_tabs += 1
|
124
132
|
@parent = @parent.children.last
|
@@ -130,49 +138,54 @@ module Haml
|
|
130
138
|
|
131
139
|
@line = @next_line
|
132
140
|
end
|
133
|
-
|
134
141
|
# Close all the open tags
|
135
142
|
close until @parent.type == :root
|
136
143
|
@root
|
137
144
|
rescue Haml::Error => e
|
138
|
-
e.backtrace.unshift "#{@options
|
145
|
+
e.backtrace.unshift "#{@options.filename}:#{(e.line ? e.line + 1 : @line.index + 1) + @options.line - 1}"
|
139
146
|
raise
|
140
147
|
end
|
141
148
|
|
149
|
+
def compute_tabs(line)
|
150
|
+
return 0 if line.text.empty? || !line.whitespace
|
151
|
+
|
152
|
+
if @indentation.nil?
|
153
|
+
@indentation = line.whitespace
|
154
|
+
|
155
|
+
if @indentation.include?(?\s) && @indentation.include?(?\t)
|
156
|
+
raise SyntaxError.new(Error.message(:cant_use_tabs_and_spaces), line.index)
|
157
|
+
end
|
158
|
+
|
159
|
+
@flat_spaces = @indentation * (@template_tabs+1) if flat?
|
160
|
+
return 1
|
161
|
+
end
|
162
|
+
|
163
|
+
tabs = line.whitespace.length / @indentation.length
|
164
|
+
return tabs if line.whitespace == @indentation * tabs
|
165
|
+
return @template_tabs + 1 if flat? && line.whitespace =~ /^#{@flat_spaces}/
|
166
|
+
|
167
|
+
message = Error.message(:inconsistent_indentation,
|
168
|
+
Haml::Util.human_indentation(line.whitespace),
|
169
|
+
Haml::Util.human_indentation(@indentation)
|
170
|
+
)
|
171
|
+
raise SyntaxError.new(message, line.index)
|
172
|
+
end
|
142
173
|
|
143
174
|
private
|
144
175
|
|
145
176
|
# @private
|
146
|
-
class Line < Struct.new(:
|
177
|
+
class Line < Struct.new(:whitespace, :text, :full, :index, :parser, :eod)
|
147
178
|
alias_method :eod?, :eod
|
148
179
|
|
149
180
|
# @private
|
150
181
|
def tabs
|
151
|
-
|
152
|
-
|
153
|
-
break 0 if line.text.empty? || !(whitespace = line.full[/^\s+/])
|
154
|
-
|
155
|
-
if @indentation.nil?
|
156
|
-
@indentation = whitespace
|
157
|
-
|
158
|
-
if @indentation.include?(?\s) && @indentation.include?(?\t)
|
159
|
-
raise SyntaxError.new(Error.message(:cant_use_tabs_and_spaces), line.index)
|
160
|
-
end
|
161
|
-
|
162
|
-
@flat_spaces = @indentation * (@template_tabs+1) if flat?
|
163
|
-
break 1
|
164
|
-
end
|
165
|
-
|
166
|
-
tabs = whitespace.length / @indentation.length
|
167
|
-
break tabs if whitespace == @indentation * tabs
|
168
|
-
break @template_tabs + 1 if flat? && whitespace =~ /^#{@flat_spaces}/
|
182
|
+
@tabs ||= parser.compute_tabs(self)
|
183
|
+
end
|
169
184
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
raise SyntaxError.new(message, line.index)
|
175
|
-
end
|
185
|
+
def strip!(from)
|
186
|
+
self.text = text[from..-1]
|
187
|
+
self.text.lstrip!
|
188
|
+
self
|
176
189
|
end
|
177
190
|
end
|
178
191
|
|
@@ -184,9 +197,7 @@ module Haml
|
|
184
197
|
end
|
185
198
|
|
186
199
|
def inspect
|
187
|
-
|
188
|
-
children.each {|c| text << "\n" << c.inspect.gsub(/^/, " ")}
|
189
|
-
text + ")"
|
200
|
+
%Q[(#{type} #{value.inspect}#{children.map {|c| "\n#{c.inspect.gsub!(/^/, ' ')}"}.join})]
|
190
201
|
end
|
191
202
|
end
|
192
203
|
|
@@ -202,37 +213,36 @@ module Haml
|
|
202
213
|
#
|
203
214
|
# This method doesn't return anything; it simply processes the line and
|
204
215
|
# adds the appropriate code to `@precompiled`.
|
205
|
-
def process_line(
|
206
|
-
|
207
|
-
|
208
|
-
case text[0]
|
209
|
-
when DIV_CLASS; push div(text)
|
216
|
+
def process_line(line)
|
217
|
+
case line.text[0]
|
218
|
+
when DIV_CLASS; push div(line)
|
210
219
|
when DIV_ID
|
211
|
-
return push plain(
|
212
|
-
push div(
|
213
|
-
when ELEMENT; push tag(
|
214
|
-
when COMMENT; push comment(text[1..-1].
|
220
|
+
return push plain(line) if line.text[1] == ?{
|
221
|
+
push div(line)
|
222
|
+
when ELEMENT; push tag(line)
|
223
|
+
when COMMENT; push comment(line.text[1..-1].lstrip)
|
215
224
|
when SANITIZE
|
216
|
-
return push plain(
|
217
|
-
return push script(
|
218
|
-
return push flat_script(
|
219
|
-
return push plain(
|
220
|
-
push plain(
|
225
|
+
return push plain(line.strip!(3), :escape_html) if line.text[1, 2] == '=='
|
226
|
+
return push script(line.strip!(2), :escape_html) if line.text[1] == SCRIPT
|
227
|
+
return push flat_script(line.strip!(2), :escape_html) if line.text[1] == FLAT_SCRIPT
|
228
|
+
return push plain(line.strip!(1), :escape_html) if line.text[1] == ?\s
|
229
|
+
push plain(line)
|
221
230
|
when SCRIPT
|
222
|
-
return push plain(
|
223
|
-
|
224
|
-
|
225
|
-
when
|
226
|
-
when
|
231
|
+
return push plain(line.strip!(2)) if line.text[1] == SCRIPT
|
232
|
+
line.text = line.text[1..-1]
|
233
|
+
push script(line)
|
234
|
+
when FLAT_SCRIPT; push flat_script(line.strip!(1))
|
235
|
+
when SILENT_SCRIPT; push silent_script(line)
|
236
|
+
when FILTER; push filter(line.text[1..-1].downcase)
|
227
237
|
when DOCTYPE
|
228
|
-
return push doctype(text) if text[0
|
229
|
-
return push plain(
|
230
|
-
return push script(
|
231
|
-
return push flat_script(
|
232
|
-
return push plain(
|
233
|
-
push plain(
|
234
|
-
when ESCAPE; push plain(
|
235
|
-
else; push plain(
|
238
|
+
return push doctype(line.text) if line.text[0, 3] == '!!!'
|
239
|
+
return push plain(line.strip!(3), false) if line.text[1, 2] == '=='
|
240
|
+
return push script(line.strip!(2), false) if line.text[1] == SCRIPT
|
241
|
+
return push flat_script(line.strip!(2), false) if line.text[1] == FLAT_SCRIPT
|
242
|
+
return push plain(line.strip!(1), false) if line.text[1] == ?\s
|
243
|
+
push plain(line)
|
244
|
+
when ESCAPE; push plain(line.strip!(1))
|
245
|
+
else; push plain(line)
|
236
246
|
end
|
237
247
|
end
|
238
248
|
|
@@ -250,43 +260,44 @@ module Haml
|
|
250
260
|
node.parent = @parent
|
251
261
|
end
|
252
262
|
|
253
|
-
def plain(
|
263
|
+
def plain(line, escape_html = nil)
|
254
264
|
if block_opened?
|
255
265
|
raise SyntaxError.new(Error.message(:illegal_nesting_plain), @next_line.index)
|
256
266
|
end
|
257
267
|
|
258
|
-
unless contains_interpolation?(text)
|
259
|
-
return ParseNode.new(:plain,
|
268
|
+
unless contains_interpolation?(line.text)
|
269
|
+
return ParseNode.new(:plain, line.index + 1, :text => line.text)
|
260
270
|
end
|
261
271
|
|
262
|
-
escape_html = @options
|
263
|
-
|
272
|
+
escape_html = @options.escape_html if escape_html.nil?
|
273
|
+
line.text = unescape_interpolation(line.text, escape_html)
|
274
|
+
script(line, false)
|
264
275
|
end
|
265
276
|
|
266
|
-
def script(
|
267
|
-
raise SyntaxError.new(Error.message(:no_ruby_code, '=')) if text.empty?
|
268
|
-
|
269
|
-
escape_html = @options
|
277
|
+
def script(line, escape_html = nil, preserve = false)
|
278
|
+
raise SyntaxError.new(Error.message(:no_ruby_code, '=')) if line.text.empty?
|
279
|
+
line = handle_ruby_multiline(line)
|
280
|
+
escape_html = @options.escape_html if escape_html.nil?
|
270
281
|
|
271
|
-
keyword = block_keyword(text)
|
282
|
+
keyword = block_keyword(line.text)
|
272
283
|
check_push_script_stack(keyword)
|
273
284
|
|
274
|
-
ParseNode.new(:script,
|
285
|
+
ParseNode.new(:script, line.index + 1, :text => line.text, :escape_html => escape_html,
|
275
286
|
:preserve => preserve, :keyword => keyword)
|
276
287
|
end
|
277
288
|
|
278
|
-
def flat_script(
|
279
|
-
raise SyntaxError.new(Error.message(:no_ruby_code, '~')) if text.empty?
|
280
|
-
script(
|
289
|
+
def flat_script(line, escape_html = nil)
|
290
|
+
raise SyntaxError.new(Error.message(:no_ruby_code, '~')) if line.text.empty?
|
291
|
+
script(line, escape_html, :preserve)
|
281
292
|
end
|
282
293
|
|
283
|
-
def silent_script(
|
284
|
-
return haml_comment(text[2..-1]) if text[1] == SILENT_COMMENT
|
294
|
+
def silent_script(line)
|
295
|
+
return haml_comment(line.text[2..-1]) if line.text[1] == SILENT_COMMENT
|
285
296
|
|
286
|
-
raise SyntaxError.new(Error.message(:no_end),
|
297
|
+
raise SyntaxError.new(Error.message(:no_end), line.index) if line.text[1..-1].strip == 'end'
|
287
298
|
|
288
|
-
|
289
|
-
keyword = block_keyword(text)
|
299
|
+
line = handle_ruby_multiline(line)
|
300
|
+
keyword = block_keyword(line.text)
|
290
301
|
|
291
302
|
check_push_script_stack(keyword)
|
292
303
|
|
@@ -308,8 +319,8 @@ module Haml
|
|
308
319
|
end
|
309
320
|
end
|
310
321
|
|
311
|
-
ParseNode.new(:silent_script, @index,
|
312
|
-
:text => text[1..-1], :keyword => keyword)
|
322
|
+
ParseNode.new(:silent_script, @line.index + 1,
|
323
|
+
:text => line.text[1..-1], :keyword => keyword)
|
313
324
|
end
|
314
325
|
|
315
326
|
def check_push_script_stack(keyword)
|
@@ -324,17 +335,17 @@ module Haml
|
|
324
335
|
|
325
336
|
def haml_comment(text)
|
326
337
|
@haml_comment = block_opened?
|
327
|
-
ParseNode.new(:haml_comment, @index, :text => text)
|
338
|
+
ParseNode.new(:haml_comment, @line.index + 1, :text => text)
|
328
339
|
end
|
329
340
|
|
330
341
|
def tag(line)
|
331
342
|
tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
|
332
|
-
nuke_inner_whitespace, action, value, last_line = parse_tag(line)
|
343
|
+
nuke_inner_whitespace, action, value, last_line = parse_tag(line.text)
|
333
344
|
|
334
|
-
preserve_tag = @options
|
345
|
+
preserve_tag = @options.preserve.include?(tag_name)
|
335
346
|
nuke_inner_whitespace ||= preserve_tag
|
336
|
-
preserve_tag = false if @options
|
337
|
-
escape_html = (action == '&' || (action != '!' && @options
|
347
|
+
preserve_tag = false if @options.ugly
|
348
|
+
escape_html = (action == '&' || (action != '!' && @options.escape_html))
|
338
349
|
|
339
350
|
case action
|
340
351
|
when '/'; self_closing = true
|
@@ -380,7 +391,7 @@ module Haml
|
|
380
391
|
if attributes_hashes[:old]
|
381
392
|
static_attributes = parse_static_hash(attributes_hashes[:old])
|
382
393
|
Buffer.merge_attrs(attributes, static_attributes) if static_attributes
|
383
|
-
attributes_list << attributes_hashes[:old] unless static_attributes || @options
|
394
|
+
attributes_list << attributes_hashes[:old] unless static_attributes || @options.suppress_eval
|
384
395
|
end
|
385
396
|
|
386
397
|
attributes_list.compact!
|
@@ -389,46 +400,54 @@ module Haml
|
|
389
400
|
raise SyntaxError.new(Error.message(:no_ruby_code, action), last_line - 1) if parse && value.empty?
|
390
401
|
raise SyntaxError.new(Error.message(:self_closing_content), last_line - 1) if self_closing && !value.empty?
|
391
402
|
|
392
|
-
if block_opened? && !value.empty? && !is_ruby_multiline?(
|
403
|
+
if block_opened? && !value.empty? && !is_ruby_multiline?(line.text)
|
393
404
|
raise SyntaxError.new(Error.message(:illegal_nesting_line, tag_name), @next_line.index)
|
394
405
|
end
|
395
406
|
|
396
|
-
self_closing ||= !!(!block_opened? && value.empty? && @options
|
407
|
+
self_closing ||= !!(!block_opened? && value.empty? && @options.autoclose.any? {|t| t === tag_name})
|
397
408
|
value = nil if value.empty? && (block_opened? || self_closing)
|
398
|
-
|
409
|
+
line.text = value
|
410
|
+
line = handle_ruby_multiline(line) if parse
|
399
411
|
|
400
|
-
ParseNode.new(:tag,
|
412
|
+
ParseNode.new(:tag, line.index + 1, :name => tag_name, :attributes => attributes,
|
401
413
|
:attributes_hashes => attributes_list, :self_closing => self_closing,
|
402
414
|
:nuke_inner_whitespace => nuke_inner_whitespace,
|
403
415
|
:nuke_outer_whitespace => nuke_outer_whitespace, :object_ref => object_ref,
|
404
416
|
:escape_html => escape_html, :preserve_tag => preserve_tag,
|
405
|
-
:preserve_script => preserve_script, :parse => parse, :value =>
|
417
|
+
:preserve_script => preserve_script, :parse => parse, :value => line.text)
|
406
418
|
end
|
407
419
|
|
408
420
|
# Renders a line that creates an XHTML tag and has an implicit div because of
|
409
421
|
# `.` or `#`.
|
410
422
|
def div(line)
|
411
|
-
|
423
|
+
line.text = "%div#{line.text}"
|
424
|
+
tag(line)
|
412
425
|
end
|
413
426
|
|
414
427
|
# Renders an XHTML comment.
|
415
|
-
def comment(
|
416
|
-
|
417
|
-
|
418
|
-
|
428
|
+
def comment(text)
|
429
|
+
if text[0..1] == '!['
|
430
|
+
revealed = true
|
431
|
+
text = text[1..-1]
|
432
|
+
else
|
433
|
+
revealed = false
|
434
|
+
end
|
435
|
+
|
436
|
+
conditional, text = balance(text, ?[, ?]) if text[0] == ?[
|
437
|
+
text.strip!
|
419
438
|
|
420
|
-
if block_opened? && !
|
439
|
+
if block_opened? && !text.empty?
|
421
440
|
raise SyntaxError.new(Haml::Error.message(:illegal_nesting_content), @next_line.index)
|
422
441
|
end
|
423
442
|
|
424
|
-
ParseNode.new(:comment, @index, :conditional => conditional, :text =>
|
443
|
+
ParseNode.new(:comment, @line.index + 1, :conditional => conditional, :text => text, :revealed => revealed)
|
425
444
|
end
|
426
445
|
|
427
446
|
# Renders an XHTML doctype or XML shebang.
|
428
|
-
def doctype(
|
447
|
+
def doctype(text)
|
429
448
|
raise SyntaxError.new(Error.message(:illegal_nesting_header), @next_line.index) if block_opened?
|
430
|
-
version, type, encoding =
|
431
|
-
ParseNode.new(:doctype, @index, :version => version, :type => type, :encoding => encoding)
|
449
|
+
version, type, encoding = text[3..-1].strip.downcase.scan(DOCTYPE_REGEX)[0]
|
450
|
+
ParseNode.new(:doctype, @line.index + 1, :version => version, :type => type, :encoding => encoding)
|
432
451
|
end
|
433
452
|
|
434
453
|
def filter(name)
|
@@ -442,7 +461,7 @@ module Haml
|
|
442
461
|
@flat_spaces = @indentation * (@template_tabs+1) if @indentation
|
443
462
|
end
|
444
463
|
|
445
|
-
ParseNode.new(:filter, @index, :name => name, :text => @filter_buffer)
|
464
|
+
ParseNode.new(:filter, @line.index + 1, :name => name, :text => @filter_buffer)
|
446
465
|
end
|
447
466
|
|
448
467
|
def close
|
@@ -485,6 +504,8 @@ module Haml
|
|
485
504
|
# that can then be merged with another attributes hash.
|
486
505
|
def self.parse_class_and_id(list)
|
487
506
|
attributes = {}
|
507
|
+
return attributes if list.empty?
|
508
|
+
|
488
509
|
list.scan(/([#.])([-:_a-zA-Z0-9]+)/) do |type, property|
|
489
510
|
case type
|
490
511
|
when '.'
|
@@ -502,6 +523,8 @@ module Haml
|
|
502
523
|
|
503
524
|
def parse_static_hash(text)
|
504
525
|
attributes = {}
|
526
|
+
return attributes if text.empty?
|
527
|
+
|
505
528
|
scanner = StringScanner.new(text)
|
506
529
|
scanner.scan(/\s+/)
|
507
530
|
until scanner.eos?
|
@@ -515,20 +538,20 @@ module Haml
|
|
515
538
|
end
|
516
539
|
|
517
540
|
# Parses a line into tag_name, attributes, attributes_hash, object_ref, action, value
|
518
|
-
def parse_tag(
|
519
|
-
match =
|
520
|
-
raise SyntaxError.new(Error.message(:invalid_tag,
|
541
|
+
def parse_tag(text)
|
542
|
+
match = text.scan(/%([-:\w]+)([-:\w.#]*)(.+)?/)[0]
|
543
|
+
raise SyntaxError.new(Error.message(:invalid_tag, text)) unless match
|
521
544
|
|
522
545
|
tag_name, attributes, rest = match
|
523
546
|
|
524
|
-
if attributes =~ /[
|
547
|
+
if !attributes.empty? && (attributes =~ /[.#](\.|#|\z)/)
|
525
548
|
raise SyntaxError.new(Error.message(:illegal_element))
|
526
549
|
end
|
527
550
|
|
528
551
|
new_attributes_hash = old_attributes_hash = last_line = nil
|
529
|
-
object_ref =
|
552
|
+
object_ref = :nil
|
530
553
|
attributes_hashes = {}
|
531
|
-
while rest
|
554
|
+
while rest && !rest.empty?
|
532
555
|
case rest[0]
|
533
556
|
when ?{
|
534
557
|
break if old_attributes_hash
|
@@ -539,38 +562,43 @@ module Haml
|
|
539
562
|
new_attributes_hash, rest, last_line = parse_new_attributes(rest)
|
540
563
|
attributes_hashes[:new] = new_attributes_hash
|
541
564
|
when ?[
|
542
|
-
break unless object_ref ==
|
565
|
+
break unless object_ref == :nil
|
543
566
|
object_ref, rest = balance(rest, ?[, ?])
|
544
567
|
else; break
|
545
568
|
end
|
546
569
|
end
|
547
570
|
|
548
|
-
if rest
|
571
|
+
if rest && !rest.empty?
|
549
572
|
nuke_whitespace, action, value = rest.scan(/(<>|><|[><])?([=\/\~&!])?(.*)?/)[0]
|
550
|
-
nuke_whitespace
|
551
|
-
|
552
|
-
|
573
|
+
if nuke_whitespace
|
574
|
+
nuke_outer_whitespace = nuke_whitespace.include? '>'
|
575
|
+
nuke_inner_whitespace = nuke_whitespace.include? '<'
|
576
|
+
end
|
553
577
|
end
|
554
578
|
|
555
|
-
if @options
|
579
|
+
if @options.remove_whitespace
|
556
580
|
nuke_outer_whitespace = true
|
557
581
|
nuke_inner_whitespace = true
|
558
582
|
end
|
559
583
|
|
560
|
-
|
584
|
+
if value.nil?
|
585
|
+
value = ''
|
586
|
+
else
|
587
|
+
value.strip!
|
588
|
+
end
|
561
589
|
[tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
|
562
|
-
nuke_inner_whitespace, action, value, last_line || @index]
|
590
|
+
nuke_inner_whitespace, action, value, last_line || @line.index + 1]
|
563
591
|
end
|
564
592
|
|
565
|
-
def parse_old_attributes(
|
566
|
-
|
567
|
-
last_line = @index
|
593
|
+
def parse_old_attributes(text)
|
594
|
+
text = text.dup
|
595
|
+
last_line = @line.index + 1
|
568
596
|
|
569
597
|
begin
|
570
|
-
attributes_hash, rest = balance(
|
598
|
+
attributes_hash, rest = balance(text, ?{, ?})
|
571
599
|
rescue SyntaxError => e
|
572
|
-
if
|
573
|
-
|
600
|
+
if text.strip[-1] == ?, && e.message == Error.message(:unbalanced_brackets)
|
601
|
+
text << "\n#{@next_line.text}"
|
574
602
|
last_line += 1
|
575
603
|
next_line
|
576
604
|
retry
|
@@ -583,10 +611,9 @@ module Haml
|
|
583
611
|
return attributes_hash, rest, last_line
|
584
612
|
end
|
585
613
|
|
586
|
-
def parse_new_attributes(
|
587
|
-
|
588
|
-
|
589
|
-
last_line = @index
|
614
|
+
def parse_new_attributes(text)
|
615
|
+
scanner = StringScanner.new(text)
|
616
|
+
last_line = @line.index + 1
|
590
617
|
attributes = {}
|
591
618
|
|
592
619
|
scanner.scan(/\(\s*/)
|
@@ -595,14 +622,15 @@ module Haml
|
|
595
622
|
break if name.nil?
|
596
623
|
|
597
624
|
if name == false
|
598
|
-
|
625
|
+
scanned = Haml::Util.balance(text, ?(, ?))
|
626
|
+
text = scanned ? scanned.first : text
|
599
627
|
raise Haml::SyntaxError.new(Error.message(:invalid_attribute_list, text.inspect), last_line - 1)
|
600
628
|
end
|
601
629
|
attributes[name] = value
|
602
630
|
scanner.scan(/\s*/)
|
603
631
|
|
604
632
|
if scanner.eos?
|
605
|
-
|
633
|
+
text << " #{@next_line.text}"
|
606
634
|
last_line += 1
|
607
635
|
next_line
|
608
636
|
scanner.scan(/\s*/)
|
@@ -615,7 +643,7 @@ module Haml
|
|
615
643
|
if type == :static
|
616
644
|
static_attributes[name] = val
|
617
645
|
else
|
618
|
-
dynamic_attributes << inspect_obj(name)
|
646
|
+
dynamic_attributes << "#{inspect_obj(name)} => #{val},"
|
619
647
|
end
|
620
648
|
end
|
621
649
|
dynamic_attributes << "}"
|
@@ -650,30 +678,11 @@ module Haml
|
|
650
678
|
|
651
679
|
return name, [:static, content.first[1]] if content.size == 1
|
652
680
|
return name, [:dynamic,
|
653
|
-
|
654
|
-
end
|
655
|
-
|
656
|
-
def raw_next_line
|
657
|
-
text = @template.shift
|
658
|
-
return unless text
|
659
|
-
|
660
|
-
index = @template_index
|
661
|
-
@template_index += 1
|
662
|
-
|
663
|
-
return text, index
|
681
|
+
%!"#{content.map {|(t, v)| t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}"}.join}"!]
|
664
682
|
end
|
665
683
|
|
666
684
|
def next_line
|
667
|
-
|
668
|
-
return unless text
|
669
|
-
|
670
|
-
# :eod is a special end-of-document marker
|
671
|
-
line =
|
672
|
-
if text == :eod
|
673
|
-
Line.new '-#', '-#', '-#', index, self, true
|
674
|
-
else
|
675
|
-
Line.new text.strip, text.lstrip.chomp, text, index, self, false
|
676
|
-
end
|
685
|
+
line = @template.shift || raise(StopIteration)
|
677
686
|
|
678
687
|
# `flat?' here is a little outdated,
|
679
688
|
# so we have to manually check if either the previous or current line
|
@@ -694,21 +703,17 @@ module Haml
|
|
694
703
|
line && !line.text.empty? && line.full !~ /^#{@flat_spaces}/
|
695
704
|
end
|
696
705
|
|
697
|
-
def un_next_line(line)
|
698
|
-
@template.unshift line
|
699
|
-
@template_index -= 1
|
700
|
-
end
|
701
|
-
|
702
706
|
def handle_multiline(line)
|
703
707
|
return unless is_multiline?(line.text)
|
704
708
|
line.text.slice!(-1)
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
709
|
+
loop do
|
710
|
+
new_line = @template.first
|
711
|
+
break if new_line.eod?
|
712
|
+
next @template.shift if new_line.text.strip.empty?
|
713
|
+
break unless is_multiline?(new_line.text.strip)
|
714
|
+
line.text << new_line.text.strip[0...-1]
|
715
|
+
@template.shift
|
710
716
|
end
|
711
|
-
un_next_line new_line
|
712
717
|
end
|
713
718
|
|
714
719
|
# Checks whether or not `line` is in a multiline sequence.
|
@@ -716,18 +721,18 @@ module Haml
|
|
716
721
|
text && text.length > 1 && text[-1] == MULTILINE_CHAR_VALUE && text[-2] == ?\s && text !~ BLOCK_WITH_SPACES
|
717
722
|
end
|
718
723
|
|
719
|
-
def handle_ruby_multiline(
|
720
|
-
text
|
721
|
-
return
|
722
|
-
un_next_line @next_line.full
|
724
|
+
def handle_ruby_multiline(line)
|
725
|
+
line.text.rstrip!
|
726
|
+
return line unless is_ruby_multiline?(line.text)
|
723
727
|
begin
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
728
|
+
# Use already fetched @next_line in the first loop. Otherwise, fetch next
|
729
|
+
new_line = new_line.nil? ? @next_line : @template.shift
|
730
|
+
break if new_line.eod?
|
731
|
+
next if new_line.text.empty?
|
732
|
+
line.text << " #{new_line.text.rstrip}"
|
733
|
+
end while is_ruby_multiline?(new_line.text)
|
729
734
|
next_line
|
730
|
-
|
735
|
+
line
|
731
736
|
end
|
732
737
|
|
733
738
|
# `text' is a Ruby multiline block if it:
|
@@ -735,16 +740,13 @@ module Haml
|
|
735
740
|
# - but not "?," which is a character literal
|
736
741
|
# (however, "x?," is a method call and not a literal)
|
737
742
|
# - and not "?\," which is a character literal
|
738
|
-
#
|
739
743
|
def is_ruby_multiline?(text)
|
740
744
|
text && text.length > 1 && text[-1] == ?, &&
|
741
|
-
!((text[-3
|
745
|
+
!((text[-3, 2] =~ /\W\?/) || text[-3, 2] == "?\\")
|
742
746
|
end
|
743
747
|
|
744
748
|
def balance(*args)
|
745
|
-
|
746
|
-
return res if res
|
747
|
-
raise SyntaxError.new(Error.message(:unbalanced_brackets))
|
749
|
+
Haml::Util.balance(*args) or raise(SyntaxError.new(Error.message(:unbalanced_brackets)))
|
748
750
|
end
|
749
751
|
|
750
752
|
def block_opened?
|