haml 2.0.10 → 2.2.0
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.
- data/.yardopts +5 -0
- data/MIT-LICENSE +1 -1
- data/README.md +347 -0
- data/Rakefile +124 -19
- data/VERSION +1 -1
- data/VERSION_NAME +1 -0
- data/extra/haml-mode.el +397 -78
- data/extra/sass-mode.el +148 -36
- data/extra/update_watch.rb +13 -0
- data/lib/haml.rb +15 -993
- data/lib/haml/buffer.rb +131 -84
- data/lib/haml/engine.rb +129 -97
- data/lib/haml/error.rb +7 -7
- data/lib/haml/exec.rb +127 -42
- data/lib/haml/filters.rb +107 -42
- data/lib/haml/helpers.rb +210 -156
- data/lib/haml/helpers/action_view_extensions.rb +34 -39
- data/lib/haml/helpers/action_view_mods.rb +132 -139
- data/lib/haml/html.rb +77 -65
- data/lib/haml/precompiler.rb +404 -213
- data/lib/haml/shared.rb +78 -0
- data/lib/haml/template.rb +14 -14
- data/lib/haml/template/patch.rb +2 -2
- data/lib/haml/template/plugin.rb +2 -3
- data/lib/haml/util.rb +211 -6
- data/lib/haml/version.rb +30 -13
- data/lib/sass.rb +7 -856
- data/lib/sass/css.rb +169 -161
- data/lib/sass/engine.rb +344 -328
- data/lib/sass/environment.rb +79 -0
- data/lib/sass/error.rb +33 -11
- data/lib/sass/files.rb +139 -0
- data/lib/sass/plugin.rb +160 -117
- data/lib/sass/plugin/merb.rb +7 -6
- data/lib/sass/plugin/rails.rb +5 -6
- data/lib/sass/repl.rb +58 -0
- data/lib/sass/script.rb +59 -0
- data/lib/sass/script/bool.rb +17 -0
- data/lib/sass/script/color.rb +183 -0
- data/lib/sass/script/funcall.rb +50 -0
- data/lib/sass/script/functions.rb +198 -0
- data/lib/sass/script/lexer.rb +178 -0
- data/lib/sass/script/literal.rb +177 -0
- data/lib/sass/script/node.rb +14 -0
- data/lib/sass/script/number.rb +381 -0
- data/lib/sass/script/operation.rb +45 -0
- data/lib/sass/script/parser.rb +172 -0
- data/lib/sass/script/string.rb +12 -0
- data/lib/sass/script/unary_operation.rb +34 -0
- data/lib/sass/script/variable.rb +31 -0
- data/lib/sass/tree/comment_node.rb +73 -10
- data/lib/sass/tree/debug_node.rb +30 -0
- data/lib/sass/tree/directive_node.rb +42 -17
- data/lib/sass/tree/file_node.rb +41 -0
- data/lib/sass/tree/for_node.rb +48 -0
- data/lib/sass/tree/if_node.rb +54 -0
- data/lib/sass/tree/mixin_def_node.rb +29 -0
- data/lib/sass/tree/mixin_node.rb +48 -0
- data/lib/sass/tree/node.rb +214 -11
- data/lib/sass/tree/prop_node.rb +109 -0
- data/lib/sass/tree/rule_node.rb +178 -51
- data/lib/sass/tree/variable_node.rb +34 -0
- data/lib/sass/tree/while_node.rb +31 -0
- data/test/haml/engine_test.rb +331 -36
- data/test/haml/helper_test.rb +12 -1
- data/test/haml/results/content_for_layout.xhtml +0 -3
- data/test/haml/results/filters.xhtml +2 -0
- data/test/haml/results/list.xhtml +1 -1
- data/test/haml/template_test.rb +7 -2
- data/test/haml/templates/content_for_layout.haml +0 -2
- data/test/haml/templates/list.haml +1 -1
- data/test/haml/util_test.rb +92 -0
- data/test/sass/css2sass_test.rb +69 -24
- data/test/sass/engine_test.rb +586 -64
- data/test/sass/functions_test.rb +125 -0
- data/test/sass/more_results/more1.css +9 -0
- data/test/sass/more_results/more1_with_line_comments.css +26 -0
- data/test/sass/more_results/more_import.css +29 -0
- data/test/sass/more_templates/_more_partial.sass +2 -0
- data/test/sass/more_templates/more1.sass +23 -0
- data/test/sass/more_templates/more_import.sass +11 -0
- data/test/sass/plugin_test.rb +81 -28
- data/test/sass/results/line_numbers.css +49 -0
- data/test/sass/results/{constants.css → script.css} +4 -4
- data/test/sass/results/subdir/subdir.css +2 -0
- data/test/sass/results/units.css +11 -0
- data/test/sass/script_test.rb +258 -0
- data/test/sass/templates/import.sass +1 -1
- data/test/sass/templates/importee.sass +7 -2
- data/test/sass/templates/line_numbers.sass +13 -0
- data/test/sass/templates/{constants.sass → script.sass} +11 -10
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
- data/test/sass/templates/subdir/subdir.sass +2 -2
- data/test/sass/templates/units.sass +11 -0
- data/test/test_helper.rb +14 -0
- metadata +77 -19
- data/FAQ +0 -138
- data/README.rdoc +0 -319
- data/lib/sass/constant.rb +0 -216
- data/lib/sass/constant/color.rb +0 -101
- data/lib/sass/constant/literal.rb +0 -54
- data/lib/sass/constant/nil.rb +0 -9
- data/lib/sass/constant/number.rb +0 -87
- data/lib/sass/constant/operation.rb +0 -30
- data/lib/sass/constant/string.rb +0 -22
- data/lib/sass/tree/attr_node.rb +0 -57
- data/lib/sass/tree/value_node.rb +0 -20
data/lib/haml/precompiler.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'strscan'
|
2
|
+
require 'haml/shared'
|
2
3
|
|
3
4
|
module Haml
|
5
|
+
# Handles the internal pre-compilation from Haml into Ruby code,
|
6
|
+
# which then runs the final creation of the HTML string.
|
4
7
|
module Precompiler
|
8
|
+
include Haml::Util
|
9
|
+
|
5
10
|
# Designates an XHTML/XML element.
|
6
11
|
ELEMENT = ?%
|
7
12
|
|
@@ -61,13 +66,10 @@ module Haml
|
|
61
66
|
# of a multiline string.
|
62
67
|
MULTILINE_CHAR_VALUE = ?|
|
63
68
|
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
# Keywords that appear in the middle of a Ruby block with lowered
|
69
|
-
# indentation. If a block has been started using indentation,
|
70
|
-
# lowering the indentation with one of these won't end the block.
|
69
|
+
# Regex to match keywords that appear in the middle of a Ruby block
|
70
|
+
# with lowered indentation.
|
71
|
+
# If a block has been started using indentation,
|
72
|
+
# lowering the indentation with one of these won't end the block.
|
71
73
|
# For example:
|
72
74
|
#
|
73
75
|
# - if foo
|
@@ -77,7 +79,7 @@ module Haml
|
|
77
79
|
#
|
78
80
|
# The block is ended after <tt>%p no!</tt>, because <tt>else</tt>
|
79
81
|
# is a member of this array.
|
80
|
-
|
82
|
+
MID_BLOCK_KEYWORD_REGEX = /-\s*(#{%w[else elsif rescue ensure when end].join('|')})\b/
|
81
83
|
|
82
84
|
# The Regex that matches a Doctype command.
|
83
85
|
DOCTYPE_REGEX = /(\d\.\d)?[\s]*([a-z]*)/i
|
@@ -99,7 +101,7 @@ END
|
|
99
101
|
@haml_buffer = @haml_buffer.upper
|
100
102
|
_erbout
|
101
103
|
END
|
102
|
-
preamble + locals_code(local_names) +
|
104
|
+
preamble + locals_code(local_names) + precompiled + postamble
|
103
105
|
end
|
104
106
|
|
105
107
|
def locals_code(names)
|
@@ -113,57 +115,65 @@ END
|
|
113
115
|
end.join(';') + ';'
|
114
116
|
end
|
115
117
|
|
116
|
-
Line
|
118
|
+
class Line < Struct.new(:text, :unstripped, :full, :index, :precompiler, :eod)
|
119
|
+
alias_method :eod?, :eod
|
117
120
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
@template.split(/\r\n|\r|\n/).each_with_index do |text, index|
|
123
|
-
@next_line = line = Line.new(text.strip, text.lstrip.chomp, index)
|
124
|
-
line.spaces, line.tabs = count_soft_tabs(text)
|
121
|
+
def tabs
|
122
|
+
line = self
|
123
|
+
@tabs ||= precompiler.instance_eval do
|
124
|
+
break 0 if line.text.empty? || !(whitespace = line.full[/^\s+/])
|
125
125
|
|
126
|
-
|
126
|
+
if @indentation.nil?
|
127
|
+
@indentation = whitespace
|
127
128
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
129
|
+
if @indentation.include?(?\s) && @indentation.include?(?\t)
|
130
|
+
raise SyntaxError.new("Indentation can't use both tabs and spaces.", line.index)
|
131
|
+
end
|
132
|
+
|
133
|
+
@flat_spaces = @indentation * @template_tabs if flat?
|
134
|
+
break 1
|
135
|
+
end
|
134
136
|
|
135
|
-
|
137
|
+
tabs = whitespace.length / @indentation.length
|
138
|
+
break tabs if whitespace == @indentation * tabs
|
139
|
+
break @template_tabs if flat? && whitespace =~ /^#{@indentation * @template_tabs}/
|
136
140
|
|
137
|
-
|
138
|
-
|
139
|
-
|
141
|
+
raise SyntaxError.new(<<END.strip.gsub("\n", ' '), line.index)
|
142
|
+
Inconsistent indentation: #{Haml::Shared.human_indentation whitespace, true} used for indentation,
|
143
|
+
but the rest of the document was indented using #{Haml::Shared.human_indentation @indentation}.
|
144
|
+
END
|
140
145
|
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def precompile
|
150
|
+
@haml_comment = @dont_indent_next_line = @dont_tab_up_next_text = false
|
151
|
+
@indentation = nil
|
152
|
+
@line = next_line
|
153
|
+
resolve_newlines
|
154
|
+
newline
|
155
|
+
|
156
|
+
raise SyntaxError.new("Indenting at the beginning of the document is illegal.", @line.index) if @line.tabs != 0
|
157
|
+
|
158
|
+
while next_line
|
159
|
+
process_indent(@line) unless @line.text.empty?
|
141
160
|
|
142
161
|
if flat?
|
143
|
-
push_flat(
|
144
|
-
|
162
|
+
push_flat(@line)
|
163
|
+
@line = @next_line
|
145
164
|
newline
|
146
165
|
next
|
147
166
|
end
|
148
167
|
|
149
|
-
|
150
|
-
raise SyntaxError.new(<<END.strip, old_line.index)
|
151
|
-
#{old_line.spaces} space#{old_line.spaces == 1 ? ' was' : 's were'} used for indentation. Haml must be indented using two spaces.
|
152
|
-
END
|
153
|
-
end
|
168
|
+
process_line(@line.text, @line.index) unless @line.text.empty? || @haml_comment
|
154
169
|
|
155
|
-
|
156
|
-
|
170
|
+
if !flat? && @next_line.tabs - @line.tabs > 1
|
171
|
+
raise SyntaxError.new("The line was indented #{@next_line.tabs - @line.tabs} levels deeper than the previous line.", @next_line.index)
|
157
172
|
end
|
158
|
-
resolve_newlines
|
159
173
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
END
|
164
|
-
end
|
165
|
-
old_line = line
|
166
|
-
newline
|
174
|
+
resolve_newlines unless @next_line.eod?
|
175
|
+
@line = @next_line
|
176
|
+
newline unless @next_line.eod?
|
167
177
|
end
|
168
178
|
|
169
179
|
# Close all the open tags
|
@@ -183,22 +193,25 @@ END
|
|
183
193
|
#
|
184
194
|
# This method doesn't return anything; it simply processes the line and
|
185
195
|
# adds the appropriate code to <tt>@precompiled</tt>.
|
186
|
-
def process_line(text, index
|
187
|
-
@block_opened = block_opened
|
196
|
+
def process_line(text, index)
|
188
197
|
@index = index + 1
|
189
198
|
|
190
199
|
case text[0]
|
191
|
-
when DIV_CLASS
|
200
|
+
when DIV_CLASS; render_div(text)
|
201
|
+
when DIV_ID
|
202
|
+
return push_plain(text) if text[1] == ?{
|
203
|
+
render_div(text)
|
192
204
|
when ELEMENT; render_tag(text)
|
193
205
|
when COMMENT; render_comment(text[1..-1].strip)
|
194
206
|
when SANITIZE
|
195
|
-
return push_script(unescape_interpolation(text[3..-1].strip),
|
196
|
-
return push_script(text[2..-1].strip,
|
207
|
+
return push_script(unescape_interpolation(text[3..-1].strip), :escape_html => true) if text[1..2] == "=="
|
208
|
+
return push_script(text[2..-1].strip, :escape_html => true) if text[1] == SCRIPT
|
209
|
+
return push_script(unescape_interpolation(text[1..-1].strip), :escape_html => true) if text[1] == ?\s
|
197
210
|
push_plain text
|
198
211
|
when SCRIPT
|
199
|
-
return push_script(unescape_interpolation(text[2..-1].strip)
|
200
|
-
return push_script(text[1..-1],
|
201
|
-
push_script(text[1..-1]
|
212
|
+
return push_script(unescape_interpolation(text[2..-1].strip)) if text[1] == SCRIPT
|
213
|
+
return push_script(text[1..-1], :escape_html => true) if options[:escape_html]
|
214
|
+
push_script(text[1..-1])
|
202
215
|
when FLAT_SCRIPT; push_flat_script(text[1..-1])
|
203
216
|
when SILENT_SCRIPT
|
204
217
|
return start_haml_comment if text[1] == SILENT_COMMENT
|
@@ -214,15 +227,19 @@ END
|
|
214
227
|
push_silent(text[1..-1], true)
|
215
228
|
newline_now
|
216
229
|
|
217
|
-
|
218
|
-
|
230
|
+
# Handle stuff like - end.join("|")
|
231
|
+
@to_close_stack.first << false if text =~ /-\s*end\b/ && !block_opened?
|
232
|
+
|
233
|
+
case_stmt = text =~ /-\s*case\b/
|
234
|
+
block = block_opened? && !mid_block_keyword?(text)
|
219
235
|
push_and_tabulate([:script]) if block || case_stmt
|
220
|
-
push_and_tabulate(nil)
|
236
|
+
push_and_tabulate(:nil) if block && case_stmt
|
221
237
|
when FILTER; start_filtered(text[1..-1].downcase)
|
222
238
|
when DOCTYPE
|
223
239
|
return render_doctype(text) if text[0...3] == '!!!'
|
224
|
-
return push_script(unescape_interpolation(text[3..-1].strip)
|
225
|
-
return push_script(text[2..-1].strip
|
240
|
+
return push_script(unescape_interpolation(text[3..-1].strip)) if text[1..2] == "=="
|
241
|
+
return push_script(text[2..-1].strip) if text[1] == SCRIPT
|
242
|
+
return push_script(unescape_interpolation(text[1..-1].strip)) if text[1] == ?\s
|
226
243
|
push_plain text
|
227
244
|
when ESCAPE; push_plain text[1..-1]
|
228
245
|
else push_plain text
|
@@ -232,42 +249,7 @@ END
|
|
232
249
|
# Returns whether or not the text is a silent script text with one
|
233
250
|
# of Ruby's mid-block keywords.
|
234
251
|
def mid_block_keyword?(text)
|
235
|
-
|
236
|
-
end
|
237
|
-
|
238
|
-
# Deals with all the logic of figuring out whether a given line is
|
239
|
-
# the beginning, continuation, or end of a multiline sequence.
|
240
|
-
#
|
241
|
-
# This returns whether or not the line should be
|
242
|
-
# rendered normally.
|
243
|
-
def handle_multiline(line)
|
244
|
-
text = line.text
|
245
|
-
|
246
|
-
# A multiline string is active, and is being continued
|
247
|
-
if is_multiline?(text) && @multiline
|
248
|
-
@multiline.text << text[0...-1]
|
249
|
-
return true
|
250
|
-
end
|
251
|
-
|
252
|
-
# A multiline string has just been activated, start adding the lines
|
253
|
-
if is_multiline?(text) && (MULTILINE_STARTERS.include? text[0])
|
254
|
-
@multiline = Line.new text[0...-1], nil, line.index, nil, line.tabs
|
255
|
-
process_indent(line)
|
256
|
-
return true
|
257
|
-
end
|
258
|
-
|
259
|
-
# A multiline string has just ended, make line into the result
|
260
|
-
if @multiline && !line.text.empty?
|
261
|
-
process_line(@multiline.text, @multiline.index, line.tabs > @multiline.tabs)
|
262
|
-
@multiline = nil
|
263
|
-
end
|
264
|
-
|
265
|
-
return false
|
266
|
-
end
|
267
|
-
|
268
|
-
# Checks whether or not +line+ is in a multiline sequence.
|
269
|
-
def is_multiline?(text)
|
270
|
-
text && text.length > 1 && text[-1] == MULTILINE_CHAR_VALUE && text[-2] == ?\s
|
252
|
+
MID_BLOCK_KEYWORD_REGEX =~ text
|
271
253
|
end
|
272
254
|
|
273
255
|
# Evaluates <tt>text</tt> in the context of the scope object, but
|
@@ -281,14 +263,14 @@ END
|
|
281
263
|
# Adds <tt>text</tt> to <tt>@buffer</tt> with appropriate tabulation
|
282
264
|
# without parsing it.
|
283
265
|
def push_merged_text(text, tab_change = 0, indent = true)
|
284
|
-
|
266
|
+
text = !indent || @dont_indent_next_line || @options[:ugly] ? text : "#{' ' * @output_tabs}#{text}"
|
267
|
+
@to_merge << [:text, text, tab_change]
|
285
268
|
@dont_indent_next_line = false
|
286
|
-
@tab_change += tab_change
|
287
269
|
end
|
288
270
|
|
289
271
|
# Concatenate <tt>text</tt> to <tt>@buffer</tt> without tabulation.
|
290
272
|
def concat_merged_text(text)
|
291
|
-
@
|
273
|
+
@to_merge << [:text, text, 0]
|
292
274
|
end
|
293
275
|
|
294
276
|
def push_text(text, tab_change = 0)
|
@@ -296,59 +278,87 @@ END
|
|
296
278
|
end
|
297
279
|
|
298
280
|
def flush_merged_text
|
299
|
-
return if @
|
281
|
+
return if @to_merge.empty?
|
300
282
|
|
301
|
-
@
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
283
|
+
text, tab_change = @to_merge.inject(["", 0]) do |(str, mtabs), (type, val, tabs)|
|
284
|
+
case type
|
285
|
+
when :text
|
286
|
+
[str << val.inspect[1...-1], mtabs + tabs]
|
287
|
+
when :script
|
288
|
+
if mtabs != 0 && !@options[:ugly]
|
289
|
+
val = "_hamlout.adjust_tabs(#{mtabs}); " + val
|
290
|
+
end
|
291
|
+
[str << "\#{#{val}}", 0]
|
292
|
+
else
|
293
|
+
raise SyntaxError.new("[HAML BUG] Undefined entry in Haml::Precompiler@to_merge.")
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
@precompiled <<
|
298
|
+
if @options[:ugly]
|
299
|
+
"_erbout << \"#{text}\";"
|
300
|
+
else
|
301
|
+
"_hamlout.push_text(\"#{text}\", #{tab_change}, #{@dont_tab_up_next_text.inspect});"
|
302
|
+
end
|
303
|
+
@to_merge = []
|
306
304
|
@dont_tab_up_next_text = false
|
307
|
-
@tab_change = 0
|
308
305
|
end
|
309
306
|
|
310
307
|
# Renders a block of text as plain text.
|
311
308
|
# Also checks for an illegally opened block.
|
312
309
|
def push_plain(text)
|
313
|
-
if
|
310
|
+
if block_opened?
|
314
311
|
raise SyntaxError.new("Illegal nesting: nesting within plain text is illegal.", @next_line.index)
|
315
312
|
end
|
316
313
|
|
317
|
-
|
314
|
+
if contains_interpolation?(text)
|
315
|
+
push_script unescape_interpolation(text)
|
316
|
+
else
|
317
|
+
push_text text
|
318
|
+
end
|
318
319
|
end
|
319
320
|
|
320
321
|
# Adds +text+ to <tt>@buffer</tt> while flattening text.
|
321
322
|
def push_flat(line)
|
322
|
-
|
323
|
-
|
324
|
-
@filter_buffer << "#{
|
323
|
+
text = line.full.dup
|
324
|
+
text = "" unless text.gsub!(/^#{@flat_spaces}/, '')
|
325
|
+
@filter_buffer << "#{text}\n"
|
325
326
|
end
|
326
327
|
|
327
328
|
# Causes <tt>text</tt> to be evaluated in the context of
|
328
329
|
# the scope object and the result to be added to <tt>@buffer</tt>.
|
329
330
|
#
|
330
|
-
# If <tt>preserve_script</tt> is true, Haml::Helpers#find_and_flatten is run on
|
331
|
+
# If <tt>opts[:preserve_script]</tt> is true, Haml::Helpers#find_and_flatten is run on
|
331
332
|
# the result before it is added to <tt>@buffer</tt>
|
332
|
-
def push_script(text,
|
333
|
-
|
333
|
+
def push_script(text, opts = {})
|
334
|
+
raise SyntaxError.new("There's no Ruby code for = to evaluate.") if text.empty?
|
335
|
+
return if options[:suppress_eval]
|
336
|
+
|
337
|
+
args = %w[preserve_script in_tag preserve_tag escape_html nuke_inner_whitespace]
|
338
|
+
args.map! {|name| opts[name.to_sym]}
|
339
|
+
args << !block_opened? << @options[:ugly]
|
340
|
+
|
341
|
+
no_format = @options[:ugly] &&
|
342
|
+
!(opts[:preserve_script] || opts[:preserve_tag] || opts[:escape_html])
|
343
|
+
output_temp = "(haml_very_temp = haml_temp; haml_temp = nil; haml_very_temp)"
|
344
|
+
out = "_hamlout.#{static_method_name(:format_script, *args)}(#{output_temp});"
|
345
|
+
|
334
346
|
# Prerender tabulation unless we're in a tag
|
335
|
-
push_merged_text '' unless in_tag
|
347
|
+
push_merged_text '' unless opts[:in_tag]
|
336
348
|
|
337
|
-
|
338
|
-
|
349
|
+
unless block_opened?
|
350
|
+
@to_merge << [:script, no_format ? "#{text}\n" : "haml_temp = #{text}\n#{out}"]
|
351
|
+
concat_merged_text("\n") unless opts[:in_tag] || opts[:nuke_inner_whitespace]
|
352
|
+
@newlines -= 1
|
353
|
+
return
|
354
|
+
end
|
339
355
|
|
340
|
-
|
356
|
+
flush_merged_text
|
341
357
|
|
342
358
|
push_silent "haml_temp = #{text}"
|
343
359
|
newline_now
|
344
|
-
|
345
|
-
|
346
|
-
out = "haml_temp = _hamlout.push_script(haml_temp, #{args});"
|
347
|
-
if @block_opened
|
348
|
-
push_and_tabulate([:loud, out])
|
349
|
-
else
|
350
|
-
@precompiled << out
|
351
|
-
end
|
360
|
+
push_and_tabulate([:loud, "_erbout << #{no_format ? "#{output_temp}.to_s;" : out}",
|
361
|
+
!(opts[:in_tag] || opts[:nuke_inner_whitespace] || @options[:ugly])])
|
352
362
|
end
|
353
363
|
|
354
364
|
# Causes <tt>text</tt> to be evaluated, and Haml::Helpers#find_and_flatten
|
@@ -357,11 +367,11 @@ END
|
|
357
367
|
flush_merged_text
|
358
368
|
|
359
369
|
raise SyntaxError.new("There's no Ruby code for ~ to evaluate.") if text.empty?
|
360
|
-
push_script(text, true)
|
370
|
+
push_script(text, :preserve_script => true)
|
361
371
|
end
|
362
372
|
|
363
373
|
def start_haml_comment
|
364
|
-
return unless
|
374
|
+
return unless block_opened?
|
365
375
|
|
366
376
|
@haml_comment = true
|
367
377
|
push_and_tabulate([:haml_comment])
|
@@ -369,21 +379,13 @@ END
|
|
369
379
|
|
370
380
|
# Closes the most recent item in <tt>@to_close_stack</tt>.
|
371
381
|
def close
|
372
|
-
tag,
|
373
|
-
|
374
|
-
when :script; close_block
|
375
|
-
when :comment; close_comment value
|
376
|
-
when :element; close_tag value
|
377
|
-
when :loud; close_loud value
|
378
|
-
when :filtered; close_filtered value
|
379
|
-
when :haml_comment; close_haml_comment
|
380
|
-
when nil; close_nil
|
381
|
-
end
|
382
|
+
tag, *rest = @to_close_stack.pop
|
383
|
+
send("close_#{tag}", *rest)
|
382
384
|
end
|
383
385
|
|
384
386
|
# Puts a line in <tt>@precompiled</tt> that will add the closing tag of
|
385
387
|
# the most recently opened tag.
|
386
|
-
def
|
388
|
+
def close_element(value)
|
387
389
|
tag, nuke_outer_whitespace, nuke_inner_whitespace = value
|
388
390
|
@output_tabs -= 1 unless nuke_inner_whitespace
|
389
391
|
@template_tabs -= 1
|
@@ -394,7 +396,7 @@ END
|
|
394
396
|
end
|
395
397
|
|
396
398
|
# Closes a Ruby block.
|
397
|
-
def
|
399
|
+
def close_script
|
398
400
|
push_silent "end", true
|
399
401
|
@template_tabs -= 1
|
400
402
|
end
|
@@ -408,16 +410,18 @@ END
|
|
408
410
|
end
|
409
411
|
|
410
412
|
# Closes a loud Ruby block.
|
411
|
-
def close_loud(command)
|
412
|
-
push_silent
|
413
|
+
def close_loud(command, add_newline, push_end = true)
|
414
|
+
push_silent('end', true) if push_end
|
413
415
|
@precompiled << command
|
414
416
|
@template_tabs -= 1
|
417
|
+
concat_merged_text("\n") if add_newline
|
415
418
|
end
|
416
419
|
|
417
420
|
# Closes a filtered block.
|
418
421
|
def close_filtered(filter)
|
419
|
-
@flat_spaces = -1
|
420
422
|
filter.internal_compile(self, @filter_buffer)
|
423
|
+
@flat = false
|
424
|
+
@flat_spaces = nil
|
421
425
|
@filter_buffer = nil
|
422
426
|
@template_tabs -= 1
|
423
427
|
end
|
@@ -452,8 +456,6 @@ END
|
|
452
456
|
end
|
453
457
|
|
454
458
|
def parse_static_hash(text)
|
455
|
-
return {} unless text
|
456
|
-
|
457
459
|
attributes = {}
|
458
460
|
scanner = StringScanner.new(text)
|
459
461
|
scanner.scan(/\s+/)
|
@@ -464,6 +466,7 @@ END
|
|
464
466
|
attributes[eval(key).to_s] = eval(value).to_s
|
465
467
|
scanner.scan(/[,\s]*/)
|
466
468
|
end
|
469
|
+
text.count("\n").times { newline }
|
467
470
|
attributes
|
468
471
|
end
|
469
472
|
|
@@ -507,32 +510,130 @@ END
|
|
507
510
|
def parse_tag(line)
|
508
511
|
raise SyntaxError.new("Invalid tag: \"#{line}\".") unless match = line.scan(/%([-:\w]+)([-\w\.\#]*)(.*)/)[0]
|
509
512
|
tag_name, attributes, rest = match
|
510
|
-
|
513
|
+
new_attributes_hash = old_attributes_hash = last_line = object_ref = nil
|
514
|
+
attributes_hashes = []
|
515
|
+
while rest
|
516
|
+
case rest[0]
|
517
|
+
when ?{
|
518
|
+
break if old_attributes_hash
|
519
|
+
old_attributes_hash, rest, last_line = parse_old_attributes(rest)
|
520
|
+
attributes_hashes << [:old, old_attributes_hash]
|
521
|
+
when ?(
|
522
|
+
break if new_attributes_hash
|
523
|
+
new_attributes_hash, rest, last_line = parse_new_attributes(rest)
|
524
|
+
attributes_hashes << [:new, new_attributes_hash]
|
525
|
+
when ?[
|
526
|
+
break if object_ref
|
527
|
+
object_ref, rest = balance(rest, ?[, ?])
|
528
|
+
else; break
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
511
532
|
if rest
|
512
|
-
object_ref, rest = balance(rest, ?[, ?]) if rest[0] == ?[
|
513
|
-
attributes_hash, rest = parse_attributes(rest) if rest[0] == ?{ && attributes_hash.nil?
|
514
533
|
nuke_whitespace, action, value = rest.scan(/(<>|><|[><])?([=\/\~&!])?(.*)?/)[0]
|
515
534
|
nuke_whitespace ||= ''
|
516
535
|
nuke_outer_whitespace = nuke_whitespace.include? '>'
|
517
536
|
nuke_inner_whitespace = nuke_whitespace.include? '<'
|
518
537
|
end
|
538
|
+
|
519
539
|
value = value.to_s.strip
|
520
|
-
[tag_name, attributes,
|
521
|
-
nuke_inner_whitespace, action, value]
|
540
|
+
[tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
|
541
|
+
nuke_inner_whitespace, action, value, last_line || @index]
|
542
|
+
end
|
543
|
+
|
544
|
+
def parse_old_attributes(line)
|
545
|
+
line = line.dup
|
546
|
+
last_line = @index
|
547
|
+
|
548
|
+
begin
|
549
|
+
attributes_hash, rest = balance(line, ?{, ?})
|
550
|
+
rescue SyntaxError => e
|
551
|
+
if line.strip[-1] == ?, && e.message == "Unbalanced brackets."
|
552
|
+
line << "\n" << @next_line.text
|
553
|
+
last_line += 1
|
554
|
+
next_line
|
555
|
+
retry
|
556
|
+
end
|
557
|
+
|
558
|
+
raise e
|
559
|
+
end
|
560
|
+
|
561
|
+
attributes_hash = attributes_hash[1...-1] if attributes_hash
|
562
|
+
return attributes_hash, rest, last_line
|
522
563
|
end
|
523
564
|
|
524
|
-
def
|
565
|
+
def parse_new_attributes(line)
|
566
|
+
line = line.dup
|
525
567
|
scanner = StringScanner.new(line)
|
526
|
-
|
527
|
-
|
528
|
-
|
568
|
+
last_line = @index
|
569
|
+
attributes = {}
|
570
|
+
|
571
|
+
scanner.scan(/\(\s*/)
|
572
|
+
until (name, value = parse_new_attribute(scanner)).first.nil?
|
573
|
+
if name == false
|
574
|
+
text = (Haml::Shared.balance(line, ?(, ?)) || [line]).first
|
575
|
+
raise Haml::SyntaxError.new("Invalid attribute list: #{text.inspect}.", last_line - 1)
|
576
|
+
end
|
577
|
+
attributes[name] = value
|
578
|
+
scanner.scan(/\s*/)
|
579
|
+
|
580
|
+
if scanner.eos?
|
581
|
+
line << " " << @next_line.text
|
582
|
+
last_line += 1
|
583
|
+
next_line
|
584
|
+
scanner.scan(/\s*/)
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
static_attributes = {}
|
589
|
+
dynamic_attributes = "{"
|
590
|
+
attributes.each do |name, (type, val)|
|
591
|
+
if type == :static
|
592
|
+
static_attributes[name] = val
|
593
|
+
else
|
594
|
+
dynamic_attributes << name.inspect << " => " << val << ","
|
595
|
+
end
|
596
|
+
end
|
597
|
+
dynamic_attributes << "}"
|
598
|
+
dynamic_attributes = nil if dynamic_attributes == "{}"
|
599
|
+
|
600
|
+
return [static_attributes, dynamic_attributes], scanner.rest, last_line
|
601
|
+
end
|
602
|
+
|
603
|
+
def parse_new_attribute(scanner)
|
604
|
+
unless name = scanner.scan(/[-:\w]+/)
|
605
|
+
return if scanner.scan(/\)/)
|
606
|
+
return false
|
607
|
+
end
|
608
|
+
|
609
|
+
scanner.scan(/\s*/)
|
610
|
+
return name, [:static, true] unless scanner.scan(/=/) #/end
|
611
|
+
|
612
|
+
scanner.scan(/\s*/)
|
613
|
+
unless quote = scanner.scan(/["']/)
|
614
|
+
return false unless var = scanner.scan(/(@@?|\$)?\w+/)
|
615
|
+
return name, [:dynamic, var]
|
616
|
+
end
|
617
|
+
|
618
|
+
re = /((?:\\.|\#[^{]|[^#{quote}\\#])*)(#{quote}|#\{)/
|
619
|
+
content = []
|
620
|
+
loop do
|
621
|
+
return false unless scanner.scan(re)
|
622
|
+
content << [:str, scanner[1].gsub(/\\(.)/, '\1')]
|
623
|
+
break if scanner[2] == quote
|
624
|
+
content << [:ruby, balance(scanner, ?{, ?}, 1).first[0...-1]]
|
625
|
+
end
|
626
|
+
|
627
|
+
return name, [:static, content.first[1]] if content.size == 1
|
628
|
+
return name, [:dynamic,
|
629
|
+
'"' + content.map {|(t, v)| t == :str ? v.inspect[1...-1] : "\#{#{v}}"}.join + '"']
|
529
630
|
end
|
530
631
|
|
531
632
|
# Parses a line that will render as an XHTML tag, and adds the code that will
|
532
633
|
# render that tag to <tt>@precompiled</tt>.
|
533
634
|
def render_tag(line)
|
534
|
-
tag_name, attributes,
|
535
|
-
nuke_inner_whitespace, action, value = parse_tag(line)
|
635
|
+
tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
|
636
|
+
nuke_inner_whitespace, action, value, last_line = parse_tag(line)
|
536
637
|
|
537
638
|
raise SyntaxError.new("Illegal element: classes and ids must have values.") if attributes =~ /[\.#](\.|#|\z)/
|
538
639
|
|
@@ -552,7 +653,20 @@ END
|
|
552
653
|
when '&', '!'
|
553
654
|
if value[0] == ?=
|
554
655
|
parse = true
|
555
|
-
value =
|
656
|
+
value =
|
657
|
+
if value[1] == ?=
|
658
|
+
unescape_interpolation(value[2..-1].strip)
|
659
|
+
else
|
660
|
+
value[1..-1].strip
|
661
|
+
end
|
662
|
+
elsif contains_interpolation?(value)
|
663
|
+
parse = true
|
664
|
+
value = unescape_interpolation(value)
|
665
|
+
end
|
666
|
+
else
|
667
|
+
if contains_interpolation?(value)
|
668
|
+
parse = true
|
669
|
+
value = unescape_interpolation(value)
|
556
670
|
end
|
557
671
|
end
|
558
672
|
|
@@ -565,25 +679,32 @@ END
|
|
565
679
|
|
566
680
|
object_ref = "nil" if object_ref.nil? || @options[:suppress_eval]
|
567
681
|
|
568
|
-
static_attributes = parse_static_hash(attributes_hash) # Try pre-compiling a static attributes hash
|
569
|
-
attributes_hash = nil if static_attributes || @options[:suppress_eval]
|
570
682
|
attributes = parse_class_and_id(attributes)
|
571
|
-
|
683
|
+
attributes_hashes.map! do |syntax, attributes_hash|
|
684
|
+
if syntax == :old
|
685
|
+
static_attributes = parse_static_hash(attributes_hash)
|
686
|
+
attributes_hash = nil if static_attributes || @options[:suppress_eval]
|
687
|
+
else
|
688
|
+
static_attributes, attributes_hash = attributes_hash
|
689
|
+
end
|
690
|
+
Buffer.merge_attrs(attributes, static_attributes) if static_attributes
|
691
|
+
attributes_hash
|
692
|
+
end.compact!
|
572
693
|
|
573
|
-
raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if
|
574
|
-
raise SyntaxError.new("Illegal nesting: content can't be both given on the same line as %#{tag_name} and nested within it.", @next_line.index) if
|
575
|
-
raise SyntaxError.new("There's no Ruby code for #{action} to evaluate.") if parse && value.empty?
|
576
|
-
raise SyntaxError.new("Self-closing tags can't have content.") if self_closing && !value.empty?
|
694
|
+
raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if block_opened? && self_closing
|
695
|
+
raise SyntaxError.new("Illegal nesting: content can't be both given on the same line as %#{tag_name} and nested within it.", @next_line.index) if block_opened? && !value.empty?
|
696
|
+
raise SyntaxError.new("There's no Ruby code for #{action} to evaluate.", last_line - 1) if parse && value.empty?
|
697
|
+
raise SyntaxError.new("Self-closing tags can't have content.", last_line - 1) if self_closing && !value.empty?
|
577
698
|
|
578
|
-
self_closing ||= !!(
|
699
|
+
self_closing ||= !!( !block_opened? && value.empty? && @options[:autoclose].include?(tag_name) )
|
579
700
|
|
580
701
|
dont_indent_next_line =
|
581
|
-
(nuke_outer_whitespace &&
|
582
|
-
(nuke_inner_whitespace &&
|
702
|
+
(nuke_outer_whitespace && !block_opened?) ||
|
703
|
+
(nuke_inner_whitespace && block_opened?)
|
583
704
|
|
584
705
|
# Check if we can render the tag directly to text and not process it in the buffer
|
585
|
-
if object_ref == "nil" &&
|
586
|
-
tag_closed =
|
706
|
+
if object_ref == "nil" && attributes_hashes.empty? && !preserve_script
|
707
|
+
tag_closed = !block_opened? && !self_closing && !parse
|
587
708
|
|
588
709
|
open_tag = prerender_tag(tag_name, self_closing, attributes)
|
589
710
|
if tag_closed
|
@@ -601,11 +722,18 @@ END
|
|
601
722
|
else
|
602
723
|
flush_merged_text
|
603
724
|
content = value.empty? || parse ? 'nil' : value.dump
|
604
|
-
|
605
|
-
|
725
|
+
if attributes_hashes.empty?
|
726
|
+
attributes_hashes = ''
|
727
|
+
elsif attributes_hashes.size == 1
|
728
|
+
attributes_hashes = ", #{attributes_hashes.first}"
|
729
|
+
else
|
730
|
+
attributes_hashes = ", (#{attributes_hashes.join(").merge(")})"
|
731
|
+
end
|
732
|
+
|
733
|
+
args = [tag_name, self_closing, !block_opened?, preserve_tag, escape_html,
|
606
734
|
attributes, nuke_outer_whitespace, nuke_inner_whitespace
|
607
735
|
].map { |v| v.inspect }.join(', ')
|
608
|
-
push_silent "_hamlout.open_tag(#{args}, #{object_ref}, #{content}#{
|
736
|
+
push_silent "_hamlout.open_tag(#{args}, #{object_ref}, #{content}#{attributes_hashes})"
|
609
737
|
@dont_tab_up_next_text = @dont_indent_next_line = dont_indent_next_line
|
610
738
|
end
|
611
739
|
|
@@ -618,9 +746,9 @@ END
|
|
618
746
|
end
|
619
747
|
|
620
748
|
if parse
|
621
|
-
|
622
|
-
|
623
|
-
|
749
|
+
push_script(value, :preserve_script => preserve_script, :in_tag => true,
|
750
|
+
:preserve_tag => preserve_tag, :escape_html => escape_html,
|
751
|
+
:nuke_inner_whitespace => nuke_inner_whitespace)
|
624
752
|
concat_merged_text("</#{tag_name}>" + (nuke_outer_whitespace ? "" : "\n"))
|
625
753
|
end
|
626
754
|
end
|
@@ -637,7 +765,7 @@ END
|
|
637
765
|
line.strip!
|
638
766
|
conditional << ">" if conditional
|
639
767
|
|
640
|
-
if
|
768
|
+
if block_opened? && !line.empty?
|
641
769
|
raise SyntaxError.new('Illegal nesting: nesting within a tag that already has content is illegal.', @next_line.index)
|
642
770
|
end
|
643
771
|
|
@@ -659,7 +787,7 @@ END
|
|
659
787
|
|
660
788
|
# Renders an XHTML doctype or XML shebang.
|
661
789
|
def render_doctype(line)
|
662
|
-
raise SyntaxError.new("Illegal nesting: nesting within a header command is illegal.", @next_line.index) if
|
790
|
+
raise SyntaxError.new("Illegal nesting: nesting within a header command is illegal.", @next_line.index) if block_opened?
|
663
791
|
doctype = text_for_doctype(line)
|
664
792
|
push_text doctype if doctype
|
665
793
|
end
|
@@ -706,9 +834,80 @@ END
|
|
706
834
|
raise Error.new("Filter \"#{name}\" is not defined.") unless filter = Filters.defined[name]
|
707
835
|
|
708
836
|
push_and_tabulate([:filtered, filter])
|
709
|
-
@
|
837
|
+
@flat = true
|
710
838
|
@filter_buffer = String.new
|
711
|
-
|
839
|
+
|
840
|
+
# If we don't know the indentation by now, it'll be set in Line#tabs
|
841
|
+
@flat_spaces = @indentation * @template_tabs if @indentation
|
842
|
+
end
|
843
|
+
|
844
|
+
def raw_next_line
|
845
|
+
text = @template.shift
|
846
|
+
return unless text
|
847
|
+
|
848
|
+
index = @template_index
|
849
|
+
@template_index += 1
|
850
|
+
|
851
|
+
return text, index
|
852
|
+
end
|
853
|
+
|
854
|
+
def next_line
|
855
|
+
text, index = raw_next_line
|
856
|
+
return unless text
|
857
|
+
|
858
|
+
# :eod is a special end-of-document marker
|
859
|
+
line =
|
860
|
+
if text == :eod
|
861
|
+
Line.new '-#', '-#', '-#', index, self, true
|
862
|
+
else
|
863
|
+
Line.new text.strip, text.lstrip.chomp, text, index, self, false
|
864
|
+
end
|
865
|
+
|
866
|
+
# `flat?' here is a little outdated,
|
867
|
+
# so we have to manually check if either the previous or current line
|
868
|
+
# closes the flat block,
|
869
|
+
# as well as whether a new block is opened
|
870
|
+
@line.tabs if @line
|
871
|
+
unless (flat? && !closes_flat?(line) && !closes_flat?(@line)) ||
|
872
|
+
(@line && @line.text[0] == ?: && line.full =~ %r[^#{@line.full[/^\s+/]}\s])
|
873
|
+
if line.text.empty?
|
874
|
+
newline
|
875
|
+
return next_line
|
876
|
+
end
|
877
|
+
|
878
|
+
handle_multiline(line)
|
879
|
+
end
|
880
|
+
|
881
|
+
@next_line = line
|
882
|
+
end
|
883
|
+
|
884
|
+
def closes_flat?(line)
|
885
|
+
line && !line.text.empty? && line.full !~ /^#{@flat_spaces}/
|
886
|
+
end
|
887
|
+
|
888
|
+
def un_next_line(line)
|
889
|
+
@template.unshift line
|
890
|
+
@template_index -= 1
|
891
|
+
end
|
892
|
+
|
893
|
+
def handle_multiline(line)
|
894
|
+
if is_multiline?(line.text)
|
895
|
+
line.text.slice!(-1)
|
896
|
+
while new_line = raw_next_line.first
|
897
|
+
break if new_line == :eod
|
898
|
+
newline and next if new_line.strip.empty?
|
899
|
+
break unless is_multiline?(new_line.strip)
|
900
|
+
line.text << new_line.strip[0...-1]
|
901
|
+
newline
|
902
|
+
end
|
903
|
+
un_next_line new_line
|
904
|
+
resolve_newlines
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
# Checks whether or not +line+ is in a multiline sequence.
|
909
|
+
def is_multiline?(text)
|
910
|
+
text && text.length > 1 && text[-1] == MULTILINE_CHAR_VALUE && text[-2] == ?\s
|
712
911
|
end
|
713
912
|
|
714
913
|
def contains_interpolation?(str)
|
@@ -716,48 +915,27 @@ END
|
|
716
915
|
end
|
717
916
|
|
718
917
|
def unescape_interpolation(str)
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
while scan.scan(/(.*?)(\\+)\#\{/)
|
918
|
+
res = ''
|
919
|
+
rest = Haml::Shared.handle_interpolation str.dump do |scan|
|
723
920
|
escapes = (scan[2].size - 1) / 2
|
724
|
-
|
921
|
+
res << scan.matched[0...-3 - escapes]
|
725
922
|
if escapes % 2 == 1
|
726
|
-
|
923
|
+
res << '#{'
|
727
924
|
else
|
728
|
-
# Use eval to get rid of string escapes
|
729
|
-
str << '#{' + eval('"' + balance(scan, ?{, ?}, 1)[0][0...-1] + '"') + "}"
|
925
|
+
res << '#{' + eval('"' + balance(scan, ?{, ?}, 1)[0][0...-1] + '"') + "}"# Use eval to get rid of string escapes
|
730
926
|
end
|
731
927
|
end
|
732
|
-
|
733
|
-
str + scan.rest
|
928
|
+
res + rest
|
734
929
|
end
|
735
930
|
|
736
|
-
def balance(
|
737
|
-
|
738
|
-
|
739
|
-
regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]")
|
740
|
-
while scanner.scan(regexp)
|
741
|
-
str << scanner.matched
|
742
|
-
count += 1 if scanner.matched[-1] == start
|
743
|
-
count -= 1 if scanner.matched[-1] == finish
|
744
|
-
return [str.strip, scanner.rest] if count == 0
|
745
|
-
end
|
746
|
-
|
931
|
+
def balance(*args)
|
932
|
+
res = Haml::Shared.balance(*args)
|
933
|
+
return res if res
|
747
934
|
raise SyntaxError.new("Unbalanced brackets.")
|
748
935
|
end
|
749
936
|
|
750
|
-
|
751
|
-
|
752
|
-
spaces = line.index(/([^ ]|$)/)
|
753
|
-
if line[spaces] == ?\t
|
754
|
-
return 0, 0 if line.strip.empty?
|
755
|
-
raise SyntaxError.new(<<END.strip, @next_line.index)
|
756
|
-
A tab character was used for indentation. Haml must be indented using two spaces.
|
757
|
-
Are you sure you have soft tabs enabled in your editor?
|
758
|
-
END
|
759
|
-
end
|
760
|
-
[spaces, spaces/2]
|
937
|
+
def block_opened?
|
938
|
+
!flat? && @next_line.tabs > @line.tabs
|
761
939
|
end
|
762
940
|
|
763
941
|
# Pushes value onto <tt>@to_close_stack</tt> and increases
|
@@ -768,7 +946,7 @@ END
|
|
768
946
|
end
|
769
947
|
|
770
948
|
def flat?
|
771
|
-
@
|
949
|
+
@flat
|
772
950
|
end
|
773
951
|
|
774
952
|
def newline
|
@@ -789,11 +967,24 @@ END
|
|
789
967
|
# Get rid of and whitespace at the end of the buffer
|
790
968
|
# or the merged text
|
791
969
|
def rstrip_buffer!
|
792
|
-
|
793
|
-
@merged_text.rstrip!
|
794
|
-
else
|
970
|
+
if @to_merge.empty?
|
795
971
|
push_silent("_hamlout.rstrip!", false)
|
796
972
|
@dont_tab_up_next_text = true
|
973
|
+
return
|
974
|
+
end
|
975
|
+
|
976
|
+
last = @to_merge.last
|
977
|
+
case last.first
|
978
|
+
when :text
|
979
|
+
last[1].rstrip!
|
980
|
+
if last[1].empty?
|
981
|
+
@to_merge.pop
|
982
|
+
rstrip_buffer!
|
983
|
+
end
|
984
|
+
when :script
|
985
|
+
last[1].gsub!(/\(haml_temp, (.*?)\);$/, '(haml_temp.rstrip, \1);')
|
986
|
+
else
|
987
|
+
raise SyntaxError.new("[HAML BUG] Undefined entry in Haml::Precompiler@to_merge.")
|
797
988
|
end
|
798
989
|
end
|
799
990
|
end
|