haml 3.2.0.alpha.14 → 3.2.0.beta.1

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.

@@ -2,6 +2,13 @@
2
2
 
3
3
  ## 3.2.0 (Unreleased)
4
4
 
5
+ * HTML5 is now the default output format rather than XHTML. This was already
6
+ the default on Rails 3+, so many users will notice no difference.
7
+
8
+ * The :javascript and :css filters no longer add CDATA tags when the format is
9
+ html4 or html5. This can be overridden by setting the `cdata` option to
10
+ `true`. CDATA tags are always added when the format is xhtml.
11
+
5
12
  * HTML2Haml has been extracted to a separate gem, creatively named "html2haml".
6
13
 
7
14
  * Haml's internals have been refactored to move the parser, compiler and options
@@ -1,7 +1,7 @@
1
1
  # Haml (XHTML Abstraction Markup Language)
2
2
 
3
- Haml is a markup language that's used to cleanly and simply describe the XHTML
4
- of any web document, without the use of inline code. Haml functions as a
3
+ Haml is a markup language that's used to cleanly and simply describe the HTML of
4
+ any web document, without the use of inline code. Haml functions as a
5
5
  replacement for inline page templating systems such as PHP, ERB, and ASP.
6
6
  However, Haml avoids the need for explicitly coding HTML into the template,
7
7
  because it is actually an abstract description of the HTML, with some code to
@@ -651,8 +651,8 @@ is compiled to:
651
651
  </html>
652
652
 
653
653
  You can also specify the specific doctype after the `!!!` When the
654
- [`:format`](#format-option) is set to `:xhtml` (the default except in Rails 3),
655
- the following doctypes are supported:
654
+ [`:format`](#format-option) is set to `:xhtml`. The following doctypes are
655
+ supported:
656
656
 
657
657
  `!!!`
658
658
  : XHTML 1.0 Transitional<br/>
@@ -1092,8 +1092,9 @@ Tilt.
1092
1092
 
1093
1093
  {#css-filter}
1094
1094
  ### `:css`
1095
- Surrounds the filtered text with `<style>` and CDATA tags. Useful for including
1096
- inline CSS.
1095
+ Surrounds the filtered text with `<style>` and (optionally) CDATA tags. Useful
1096
+ for including inline CSS. Use the {Haml::Options#cdata `:cdata` option} to
1097
+ control when CDATA tags are added.
1097
1098
 
1098
1099
  {#erb-filter}
1099
1100
  ### `:erb`
@@ -1109,8 +1110,9 @@ before placing it in the document.
1109
1110
 
1110
1111
  {#javascript-filter}
1111
1112
  ### `:javascript`
1112
- Surrounds the filtered text with `<script>` and CDATA tags.
1113
- Useful for including inline Javascript.
1113
+ Surrounds the filtered text with `<script>` and (optionally) CDATA tags.
1114
+ Useful for including inline Javascript. Use the {Haml::Options#cdata `:cdata`
1115
+ option} to control when CDATA tags are added.
1114
1116
 
1115
1117
  {#less-filter}
1116
1118
  ### `:less`
@@ -1230,7 +1232,7 @@ For example, tags like `pre` and `textarea` are whitespace-sensitive;
1230
1232
  indenting the text makes them render wrong.
1231
1233
 
1232
1234
  Haml deals with this by "preserving" newlines before they're put into the document --
1233
- converting them to the XHTML whitespace escape code, `&#x000A;`.
1235
+ converting them to the HTML whitespace escape code, `&#x000A;`.
1234
1236
  Then Haml won't try to re-format the indentation.
1235
1237
 
1236
1238
  Literal `textarea` and `pre` tags automatically preserve content given through `=`.
@@ -260,9 +260,9 @@ END
260
260
  unless filter = Filters.defined[@node.value[:name]]
261
261
  name = @node.value[:name]
262
262
  if ["maruku", "textile"].include?(name)
263
- raise Error.new("To use the \"#{name}\" filter, please install the haml-contrib gem.", @node.line - 1)
263
+ raise Error.new(Error.message(:install_haml_contrib, name), @node.line - 1)
264
264
  else
265
- raise Error.new("Filter \"#{name}\" is not defined.", @node.line - 1)
265
+ raise Error.new(Error.message(:filter_not_defined, name), @node.line - 1)
266
266
  end
267
267
  end
268
268
  filter.internal_compile(self, @node.value[:text])
@@ -1,6 +1,45 @@
1
1
  module Haml
2
2
  # An exception raised by Haml code.
3
3
  class Error < StandardError
4
+
5
+ MESSAGES = {
6
+ :bad_script_indent => '"%s" is indented at wrong level: expected %d, but was at %d.',
7
+ :cant_run_filter => 'Can\'t run "%s" filter; you must require its dependencies first',
8
+ :cant_use_tabs_and_spaces => "Indentation can't use both tabs and spaces.",
9
+ :deeper_indenting => "The line was indented %d levels deeper than the previous line.",
10
+ :filter_not_defined => 'Filter "%s" is not defined.',
11
+ :gem_install_filter_deps => '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
12
+ :illegal_element => "Illegal element: classes and ids must have values.",
13
+ :illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
14
+ :illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
15
+ :illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
16
+ :illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
17
+ :illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
18
+ :inconsistent_indentation => "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
19
+ :indenting_at_start => "Indenting at the beginning of the document is illegal.",
20
+ :install_haml_contrib => 'To use the "%s" filter, please install the haml-contrib gem.',
21
+ :invalid_attribute_list => 'Invalid attribute list: %s.',
22
+ :invalid_filter_name => 'Invalid filter name ":%s".',
23
+ :invalid_tag => 'Invalid tag: "%s".',
24
+ :missing_if => 'Got "%s" with no preceding "if"',
25
+ :no_ruby_code => "There's no Ruby code for %s to evaluate.",
26
+ :self_closing_content => "Self-closing tags can't have content.",
27
+ :unbalanced_brackets => 'Unbalanced brackets.',
28
+ :no_end => <<-END
29
+ You don't need to use "- end" in Haml. Un-indent to close a block:
30
+ - if foo?
31
+ %strong Foo!
32
+ - else
33
+ Not foo.
34
+ %p This line is un-indented, so it isn't part of the "if" block
35
+ END
36
+ }
37
+
38
+ def self.message(key, *args)
39
+ string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
40
+ (args.empty? ? string : string % args).rstrip
41
+ end
42
+
4
43
  # The line of the template on which the error occurred.
5
44
  #
6
45
  # @return [Fixnum]
@@ -18,5 +57,5 @@ module Haml
18
57
  # ill-formatted document.
19
58
  # It's not particularly interesting,
20
59
  # except in that it's a subclass of {Haml::Error}.
21
- class SyntaxError < Haml::Error; end
60
+ class SyntaxError < Error; end
22
61
  end
@@ -1,6 +1,7 @@
1
1
  require 'optparse'
2
2
  require 'fileutils'
3
3
  require 'rbconfig'
4
+ require 'pp'
4
5
 
5
6
  module Haml
6
7
  # This module handles the various Haml executables (`haml` and `haml-convert`).
@@ -94,7 +95,7 @@ module Haml
94
95
  end
95
96
 
96
97
  opts.on_tail("-v", "--version", "Print version") do
97
- puts("Haml #{::Haml.version[:string]}")
98
+ puts("Haml #{::Haml::VERSION}")
98
99
  exit
99
100
  end
100
101
  end
@@ -217,7 +218,7 @@ END
217
218
  end
218
219
 
219
220
  opts.on('-f', '--format NAME',
220
- 'Output format. Can be xhtml (default), html4, or html5.') do |name|
221
+ 'Output format. Can be html5 (default), xhtml, or html4.') do |name|
221
222
  @options[:for_engine][:format] = name.to_sym
222
223
  end
223
224
 
@@ -252,9 +253,14 @@ END
252
253
  end
253
254
  end
254
255
 
255
- opts.on('--debug', "Print out the precompiled Ruby source.") do
256
+ opts.on('-d', '--debug', "Print out the precompiled Ruby source.") do
256
257
  @options[:debug] = true
257
258
  end
259
+
260
+ opts.on('-p', '--parse', "Print out Haml parse tree.") do
261
+ @options[:parse] = true
262
+ end
263
+
258
264
  end
259
265
 
260
266
  # Processes the options set by the command-line arguments,
@@ -272,12 +278,18 @@ END
272
278
  @options[:requires].each {|f| require f}
273
279
 
274
280
  begin
281
+
275
282
  engine = ::Haml::Engine.new(template, @options[:for_engine])
276
283
  if @options[:check_syntax]
277
284
  puts "Syntax OK"
278
285
  return
279
286
  end
280
287
 
288
+ if @options[:parse]
289
+ pp engine.parser.root
290
+ return
291
+ end
292
+
281
293
  if @options[:debug]
282
294
  puts engine.precompiled
283
295
  puts '=' * 100
@@ -196,19 +196,20 @@ RUBY
196
196
 
197
197
  # @see Base#render_with_options
198
198
  def render_with_options(text, options)
199
+ indent = options[:cdata] ? ' ' : ' ' # 4 or 2 spaces
199
200
  if options[:format] == :html5
200
201
  type = ''
201
202
  else
202
203
  type = " type=#{options[:attr_wrapper]}text/javascript#{options[:attr_wrapper]}"
203
204
  end
204
205
 
205
- <<END
206
- <script#{type}>
207
- //<![CDATA[
208
- #{text.rstrip.gsub("\n", "\n ")}
209
- //]]>
210
- </script>
211
- END
206
+ str = "<script#{type}>\n"
207
+ str << " //<![CDATA[\n" if options[:cdata]
208
+ str << "#{indent}#{text.rstrip.gsub("\n", "\n#{indent}")}\n"
209
+ str << " //]]>\n" if options[:cdata]
210
+ str << "</script>"
211
+
212
+ str
212
213
  end
213
214
  end
214
215
 
@@ -219,19 +220,20 @@ END
219
220
 
220
221
  # @see Base#render_with_options
221
222
  def render_with_options(text, options)
223
+ indent = options[:cdata] ? ' ' : ' ' # 4 or 2 spaces
222
224
  if options[:format] == :html5
223
225
  type = ''
224
226
  else
225
227
  type = " type=#{options[:attr_wrapper]}text/css#{options[:attr_wrapper]}"
226
228
  end
227
229
 
228
- <<END
229
- <style#{type}>
230
- /*<![CDATA[*/
231
- #{text.rstrip.gsub("\n", "\n ")}
232
- /*]]>*/
233
- </style>
234
- END
230
+ str = "<style#{type}>\n"
231
+ str << " /*<![CDATA[*/\n" if options[:cdata]
232
+ str << "#{indent}#{text.rstrip.gsub("\n", "\n#{indent}")}\n"
233
+ str << " /*]]>*/\n" if options[:cdata]
234
+ str << "</style>"
235
+
236
+ str
235
237
  end
236
238
  end
237
239
 
@@ -303,9 +305,10 @@ END
303
305
  def template_class
304
306
  (@template_class if defined? @template_class) or begin
305
307
  @template_class = Tilt["t.#{tilt_extension}"] or
306
- raise "Can't run #{self} filter; you must require its dependencies first"
307
- rescue LoadError
308
- raise Error.new("Can't run #{self} filter; required dependencies not available")
308
+ raise Error.new(Error.message(:cant_run_filter, tilt_extension))
309
+ rescue LoadError => e
310
+ dep = e.message.split('--').last.strip
311
+ raise Error.new(Error.message(:gem_install_filter_deps, tilt_extension, dep))
309
312
  end
310
313
  end
311
314
 
@@ -485,8 +485,8 @@ MESSAGE
485
485
  end
486
486
 
487
487
  if flags.include?(:/)
488
- raise Error.new("Self-closing tags can't have content.") if text
489
- raise Error.new("Illegal nesting: nesting within a self-closing tag is illegal.") if block
488
+ raise Error.new(Error.message(:self_closing_content)) if text
489
+ raise Error.new(Error.message(:illegal_nesting_self_closing)) if block
490
490
  end
491
491
 
492
492
  tag = "<#{name}#{attributes}>"
@@ -506,7 +506,7 @@ MESSAGE
506
506
  end
507
507
 
508
508
  if text
509
- raise Error.new("Illegal nesting: content can't be both given to haml_tag :#{name} and nested within it.")
509
+ raise Error.new(Error.message(:illegal_nesting_line, name))
510
510
  end
511
511
 
512
512
  if flags.include?(:<)
@@ -11,20 +11,21 @@ module Haml
11
11
  :escape_attrs => true,
12
12
  :escape_html => false,
13
13
  :filename => '(haml)',
14
- :format => :xhtml,
14
+ :format => :html5,
15
15
  :hyphenate_data_attrs => true,
16
16
  :line => 1,
17
17
  :mime_type => 'text/html',
18
18
  :preserve => %w(textarea pre code),
19
19
  :remove_whitespace => false,
20
20
  :suppress_eval => false,
21
- :ugly => false
21
+ :ugly => false,
22
+ :cdata => false
22
23
  }
23
24
 
24
- @valid_formats = [:xhtml, :html4, :html5]
25
+ @valid_formats = [:html4, :html5, :xhtml]
25
26
 
26
27
  @buffer_option_keys = [:autoclose, :preserve, :attr_wrapper, :ugly, :format,
27
- :encoding, :escape_html, :escape_attrs, :hyphenate_data_attrs]
28
+ :encoding, :escape_html, :escape_attrs, :hyphenate_data_attrs, :cdata]
28
29
 
29
30
  # The default option values.
30
31
  # @return Hash
@@ -101,11 +102,13 @@ module Haml
101
102
  # inline templates, similar to the last argument to `Kernel#eval`.
102
103
  attr_accessor :line
103
104
 
104
- # Determines the output format. Normally the default is `:xhtml`, although
105
- # under Rails 3 it's `:html5`, since that's the Rails 3's default format.
106
- # Other options are `:html4` and `:html5`, which are identical to `:xhtml`
107
- # except there are no self-closing tags, the XML prolog is ignored and
108
- # correct DOCTYPEs are generated.
105
+ # Determines the output format. The default is `:html5`. The other options
106
+ # are `:html4` and `:xhtml`. If the output is set to XHTML, then Haml
107
+ # automatically generates self-closing tags and wraps the output of the
108
+ # Javascript and CSS-like filters inside CDATA. When the output is set to
109
+ # :html5 or :html4, XML prologs are ignored. In all cases, an appropriate
110
+ # doctype is generated from '!!!'.
111
+ #
109
112
  #
110
113
  # If the mime_type of the template being rendered is `text/xml` then a
111
114
  # format of `:xhtml` will be used even if the global output format is set to
@@ -147,6 +150,16 @@ module Haml
147
150
  # Defaults to `true` in Rails production mode, and `false` everywhere else.
148
151
  attr_accessor :ugly
149
152
 
153
+ # Whether to include CDATA sections around javascript and css blocks when
154
+ # using the `:javascript` or `:css` filters.
155
+ #
156
+ # This option also affects the `:sass`, `:scss`, `:less` and `:coffeescript`
157
+ # filters.
158
+ #
159
+ # Defaults to `false` for html, `true` for xhtml. Cannot be changed when using
160
+ # xhtml.
161
+ attr_accessor :cdata
162
+
150
163
  def initialize(values = {}, &block)
151
164
  defaults.each {|k, v| instance_variable_set :"@#{k}", v}
152
165
  values.reject {|k, v| !defaults.has_key?(k) || v.nil?}.each {|k, v| send("#{k}=", v)}
@@ -203,7 +216,7 @@ module Haml
203
216
  # macro in order to make it appear in Yard's list of instance attributes.
204
217
  undef :format
205
218
  def format
206
- mime_type == "text/xml" ? "xhtml" : @format
219
+ mime_type == "text/xml" ? :xhtml : @format
207
220
  end
208
221
 
209
222
  def format=(value)
@@ -213,6 +226,11 @@ module Haml
213
226
  @format = value
214
227
  end
215
228
 
229
+ undef :cdata
230
+ def cdata
231
+ xhtml? || @cdata
232
+ end
233
+
216
234
  def remove_whitespace=(value)
217
235
  @ugly = true if value
218
236
  @remove_whitespace = value
@@ -4,6 +4,8 @@ module Haml
4
4
  class Parser
5
5
  include Haml::Util
6
6
 
7
+ attr_reader :root
8
+
7
9
  # Designates an XHTML/XML element.
8
10
  ELEMENT = ?%
9
11
 
@@ -63,8 +65,16 @@ module Haml
63
65
  # of a multiline string.
64
66
  MULTILINE_CHAR_VALUE = ?|
65
67
 
68
+ # Regex to check for blocks with spaces around arguments. Not to be confused
69
+ # with multiline script.
70
+ # For example:
71
+ # foo.each do | bar |
72
+ # = bar
73
+ #
74
+ BLOCK_WITH_SPACES = /do[\s]*\|[\s]*[^\|]*[\s]+\|\z/
75
+
66
76
  MID_BLOCK_KEYWORDS = %w[else elsif rescue ensure end when]
67
- START_BLOCK_KEYWORDS = %w[if begin case]
77
+ START_BLOCK_KEYWORDS = %w[if begin case unless]
68
78
  # Try to parse assignments to block starters as best as possible
69
79
  START_BLOCK_KEYWORD_REGEX = /(?:\w+(?:,\s*\w+)*\s*=\s*)?(#{START_BLOCK_KEYWORDS.join('|')})/
70
80
  BLOCK_KEYWORD_REGEX = /^-\s*(?:(#{MID_BLOCK_KEYWORDS.join('|')})|#{START_BLOCK_KEYWORD_REGEX.source})\b/
@@ -75,14 +85,18 @@ module Haml
75
85
  # The Regex that matches a literal string or symbol value
76
86
  LITERAL_VALUE_REGEX = /:(\w*)|(["'])((?![\\#]|\2).|\\.)*\2/
77
87
 
88
+
78
89
  def initialize(template, options)
79
90
  # :eod is a special end-of-document marker
80
- @template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
81
- @options = options
82
- @flat = false
83
- @index = 0
84
- @template_index = 0
85
- @template_tabs = 0
91
+ @template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
92
+ @options = options
93
+ @flat = false
94
+ @index = 0
95
+ # Record the indent levels of "if" statements to validate the subsequent
96
+ # elsif and else statements are indented at the appropriate level.
97
+ @script_level_stack = []
98
+ @template_index = 0
99
+ @template_tabs = 0
86
100
  end
87
101
 
88
102
  def parse
@@ -91,7 +105,7 @@ module Haml
91
105
  @indentation = nil
92
106
  @line = next_line
93
107
 
94
- raise SyntaxError.new("Indenting at the beginning of the document is illegal.", @line.index) if @line.tabs != 0
108
+ raise SyntaxError.new(Error.message(:indenting_at_start), @line.index) if @line.tabs != 0
95
109
 
96
110
  while next_line
97
111
  process_indent(@line) unless @line.text.empty?
@@ -112,7 +126,7 @@ module Haml
112
126
  end
113
127
 
114
128
  if !@haml_comment && !flat? && @next_line.tabs - @line.tabs > 1
115
- raise SyntaxError.new("The line was indented #{@next_line.tabs - @line.tabs} levels deeper than the previous line.", @next_line.index)
129
+ raise SyntaxError.new(Error.message(:deeper_indenting, @next_line.tabs - @line.tabs), @next_line.index)
116
130
  end
117
131
 
118
132
  @line = @next_line
@@ -143,7 +157,7 @@ module Haml
143
157
  @indentation = whitespace
144
158
 
145
159
  if @indentation.include?(?\s) && @indentation.include?(?\t)
146
- raise SyntaxError.new("Indentation can't use both tabs and spaces.", line.index)
160
+ raise SyntaxError.new(Error.message(:cant_use_tabs_and_spaces), line.index)
147
161
  end
148
162
 
149
163
  @flat_spaces = @indentation * (@template_tabs+1) if flat?
@@ -154,10 +168,11 @@ module Haml
154
168
  break tabs if whitespace == @indentation * tabs
155
169
  break @template_tabs + 1 if flat? && whitespace =~ /^#{@flat_spaces}/
156
170
 
157
- raise SyntaxError.new(<<END.strip.gsub("\n", ' '), line.index)
158
- Inconsistent indentation: #{Haml::Util.human_indentation whitespace, true} used for indentation,
159
- but the rest of the document was indented using #{Haml::Util.human_indentation @indentation}.
160
- END
171
+ message = Error.message(:inconsistent_indentation,
172
+ Haml::Util.human_indentation(whitespace),
173
+ Haml::Util.human_indentation(@indentation)
174
+ )
175
+ raise SyntaxError.new(message, line.index)
161
176
  end
162
177
  end
163
178
  end
@@ -238,7 +253,7 @@ END
238
253
 
239
254
  def plain(text, escape_html = nil)
240
255
  if block_opened?
241
- raise SyntaxError.new("Illegal nesting: nesting within plain text is illegal.", @next_line.index)
256
+ raise SyntaxError.new(Error.message(:illegal_nesting_plain), @next_line.index)
242
257
  end
243
258
 
244
259
  unless contains_interpolation?(text)
@@ -250,7 +265,7 @@ END
250
265
  end
251
266
 
252
267
  def script(text, escape_html = nil, preserve = false)
253
- raise SyntaxError.new("There's no Ruby code for = to evaluate.") if text.empty?
268
+ raise SyntaxError.new(Error.message(:no_ruby_code, '=')) if text.empty?
254
269
  text = handle_ruby_multiline(text)
255
270
  escape_html = @options[:escape_html] if escape_html.nil?
256
271
 
@@ -259,26 +274,32 @@ END
259
274
  end
260
275
 
261
276
  def flat_script(text, escape_html = nil)
262
- raise SyntaxError.new("There's no Ruby code for ~ to evaluate.") if text.empty?
277
+ raise SyntaxError.new(Error.message(:no_ruby_code, '~')) if text.empty?
263
278
  script(text, escape_html, :preserve)
264
279
  end
265
280
 
266
281
  def silent_script(text)
267
282
  return haml_comment(text[2..-1]) if text[1] == SILENT_COMMENT
268
283
 
269
- raise SyntaxError.new(<<END.rstrip, @index - 1) if text[1..-1].strip == "end"
270
- You don't need to use "- end" in Haml. Un-indent to close a block:
271
- - if foo?
272
- %strong Foo!
273
- - else
274
- Not foo.
275
- %p This line is un-indented, so it isn't part of the "if" block
276
- END
284
+ raise SyntaxError.new(Error.message(:no_end), @index - 1) if text[1..-1].strip == "end"
277
285
 
278
286
  text = handle_ruby_multiline(text)
279
287
  keyword = block_keyword(text)
280
288
 
281
- @tab_up = ["if", "case"].include?(keyword)
289
+ if ["if", "case", "unless"].include?(keyword)
290
+ @script_level_stack.push(@line.tabs)
291
+ @tab_up = true
292
+ end
293
+
294
+ if ["else", "elsif"].include?(keyword)
295
+ if @script_level_stack.empty?
296
+ raise Haml::SyntaxError.new(Error.message(:missing_if, keyword), @line.index)
297
+ elsif @script_level_stack.last != @line.tabs
298
+ message = Error.message(:bad_script_indent, keyword, @script_level_stack.last, @line.tabs)
299
+ raise Haml::SyntaxError.new(message, @line.index)
300
+ end
301
+ end
302
+
282
303
  ParseNode.new(:silent_script, @index,
283
304
  :text => text[1..-1], :keyword => keyword)
284
305
  end
@@ -346,12 +367,12 @@ END
346
367
 
347
368
  attributes_list.compact!
348
369
 
349
- raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if block_opened? && self_closing
350
- raise SyntaxError.new("There's no Ruby code for #{action} to evaluate.", last_line - 1) if parse && value.empty?
351
- raise SyntaxError.new("Self-closing tags can't have content.", last_line - 1) if self_closing && !value.empty?
370
+ raise SyntaxError.new(Error.message(:illegal_nesting_self_closing), @next_line.index) if block_opened? && self_closing
371
+ raise SyntaxError.new(Error.message(:no_ruby_code, action), last_line - 1) if parse && value.empty?
372
+ raise SyntaxError.new(Error.message(:self_closing_content), last_line - 1) if self_closing && !value.empty?
352
373
 
353
374
  if block_opened? && !value.empty? && !is_ruby_multiline?(value)
354
- 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)
375
+ raise SyntaxError.new(Error.message(:illegal_nesting_line, tag_name), @next_line.index)
355
376
  end
356
377
 
357
378
  self_closing ||= !!(!block_opened? && value.empty? && @options[:autoclose].any? {|t| t === tag_name})
@@ -379,7 +400,7 @@ END
379
400
  conditional << ">" if conditional
380
401
 
381
402
  if block_opened? && !line.empty?
382
- raise SyntaxError.new('Illegal nesting: nesting within a tag that already has content is illegal.', @next_line.index)
403
+ raise SyntaxError.new(Haml::Error.message(:illegal_nesting_content), @next_line.index)
383
404
  end
384
405
 
385
406
  ParseNode.new(:comment, @index, :conditional => conditional, :text => line)
@@ -387,13 +408,13 @@ END
387
408
 
388
409
  # Renders an XHTML doctype or XML shebang.
389
410
  def doctype(line)
390
- raise SyntaxError.new("Illegal nesting: nesting within a header command is illegal.", @next_line.index) if block_opened?
411
+ raise SyntaxError.new(Error.message(:illegal_nesting_header), @next_line.index) if block_opened?
391
412
  version, type, encoding = line[3..-1].strip.downcase.scan(DOCTYPE_REGEX)[0]
392
413
  ParseNode.new(:doctype, @index, :version => version, :type => type, :encoding => encoding)
393
414
  end
394
415
 
395
416
  def filter(name)
396
- raise Error.new("Invalid filter name \":#{name}\".") unless name =~ /^\w+$/
417
+ raise Error.new(Error.message(:invalid_filter_name, name)) unless name =~ /^\w+$/
397
418
 
398
419
  @filter_buffer = String.new
399
420
 
@@ -423,6 +444,8 @@ END
423
444
  end
424
445
 
425
446
  def close_silent_script(node)
447
+ @script_level_stack.pop if node.value[:keyword] == "if"
448
+
426
449
  # Post-process case statements to normalize the nesting of "when" clauses
427
450
  return unless node.value[:keyword] == "case"
428
451
  return unless first = node.children.first
@@ -473,10 +496,14 @@ END
473
496
 
474
497
  # Parses a line into tag_name, attributes, attributes_hash, object_ref, action, value
475
498
  def parse_tag(line)
476
- raise SyntaxError.new("Invalid tag: \"#{line}\".") unless match = line.scan(/%([-:\w]+)([-:\w\.\#]*)(.*)/)[0]
499
+ match = line.scan(/%([-:\w]+)([-:\w\.\#]*)(.*)/)[0]
500
+ raise SyntaxError.new(Error.message(:invalid_tag, line)) unless match
477
501
 
478
502
  tag_name, attributes, rest = match
479
- raise SyntaxError.new("Illegal element: classes and ids must have values.") if attributes =~ /[\.#](\.|#|\z)/
503
+
504
+ if attributes =~ /[\.#](\.|#|\z)/
505
+ raise SyntaxError.new(Error.message(:illegal_element))
506
+ end
480
507
 
481
508
  new_attributes_hash = old_attributes_hash = last_line = nil
482
509
  object_ref = "nil"
@@ -522,7 +549,7 @@ END
522
549
  begin
523
550
  attributes_hash, rest = balance(line, ?{, ?})
524
551
  rescue SyntaxError => e
525
- if line.strip[-1] == ?, && e.message == "Unbalanced brackets."
552
+ if line.strip[-1] == ?, && e.message == Error.message(:unbalanced_brackets)
526
553
  line << "\n" << @next_line.text
527
554
  last_line += 1
528
555
  next_line
@@ -549,7 +576,7 @@ END
549
576
 
550
577
  if name == false
551
578
  text = (Haml::Util.balance(line, ?(, ?)) || [line]).first
552
- raise Haml::SyntaxError.new("Invalid attribute list: #{text.inspect}.", last_line - 1)
579
+ raise Haml::SyntaxError.new(Error.message(:invalid_attribute_list, text.inspect), last_line - 1)
553
580
  end
554
581
  attributes[name] = value
555
582
  scanner.scan(/\s*/)
@@ -666,7 +693,7 @@ END
666
693
 
667
694
  # Checks whether or not `line` is in a multiline sequence.
668
695
  def is_multiline?(text)
669
- text && text.length > 1 && text[-1] == MULTILINE_CHAR_VALUE && text[-2] == ?\s
696
+ text && text.length > 1 && text[-1] == MULTILINE_CHAR_VALUE && text[-2] == ?\s && text !~ BLOCK_WITH_SPACES
670
697
  end
671
698
 
672
699
  def handle_ruby_multiline(text)
@@ -697,7 +724,7 @@ END
697
724
  def balance(*args)
698
725
  res = Haml::Util.balance(*args)
699
726
  return res if res
700
- raise SyntaxError.new("Unbalanced brackets.")
727
+ raise SyntaxError.new(Error.message(:unbalanced_brackets))
701
728
  end
702
729
 
703
730
  def block_opened?