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.
- data/CHANGELOG.md +7 -0
- data/REFERENCE.md +11 -9
- data/lib/haml/compiler.rb +2 -2
- data/lib/haml/error.rb +40 -1
- data/lib/haml/exec.rb +15 -3
- data/lib/haml/filters.rb +20 -17
- data/lib/haml/helpers.rb +3 -3
- data/lib/haml/options.rb +28 -10
- data/lib/haml/parser.rb +66 -39
- data/lib/haml/template.rb +1 -2
- data/lib/haml/util.rb +3 -9
- data/lib/haml/version.rb +1 -1
- data/test/engine_test.rb +110 -106
- data/test/filters_test.rb +97 -5
- data/test/gemfiles/Gemfile.rails-3.0.x +0 -1
- data/test/gemfiles/Gemfile.rails-3.1.x +0 -1
- data/test/gemfiles/Gemfile.rails-3.2.x +0 -1
- data/test/haml-spec/tests.json +62 -14
- data/test/helper_test.rb +1 -1
- data/test/parser_test.rb +59 -0
- data/test/template_test.rb +3 -2
- data/test/test_helper.rb +7 -2
- metadata +93 -136
- data/test/gemfiles/Gemfile.rails-3.0.x.lock +0 -101
- data/test/gemfiles/Gemfile.rails-3.1.x.lock +0 -111
- data/test/gemfiles/Gemfile.rails-3.2.x.lock +0 -109
data/CHANGELOG.md
CHANGED
@@ -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
|
data/REFERENCE.md
CHANGED
@@ -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
|
4
|
-
|
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
|
655
|
-
|
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
|
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
|
1235
|
+
converting them to the HTML whitespace escape code, `
`.
|
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 `=`.
|
data/lib/haml/compiler.rb
CHANGED
@@ -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(
|
263
|
+
raise Error.new(Error.message(:install_haml_contrib, name), @node.line - 1)
|
264
264
|
else
|
265
|
-
raise Error.new(
|
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])
|
data/lib/haml/error.rb
CHANGED
@@ -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 <
|
60
|
+
class SyntaxError < Error; end
|
22
61
|
end
|
data/lib/haml/exec.rb
CHANGED
@@ -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
|
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
|
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
|
data/lib/haml/filters.rb
CHANGED
@@ -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
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
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
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
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
|
307
|
-
rescue LoadError
|
308
|
-
|
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
|
|
data/lib/haml/helpers.rb
CHANGED
@@ -485,8 +485,8 @@ MESSAGE
|
|
485
485
|
end
|
486
486
|
|
487
487
|
if flags.include?(:/)
|
488
|
-
raise Error.new(
|
489
|
-
raise Error.new(
|
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(
|
509
|
+
raise Error.new(Error.message(:illegal_nesting_line, name))
|
510
510
|
end
|
511
511
|
|
512
512
|
if flags.include?(:<)
|
data/lib/haml/options.rb
CHANGED
@@ -11,20 +11,21 @@ module Haml
|
|
11
11
|
:escape_attrs => true,
|
12
12
|
:escape_html => false,
|
13
13
|
:filename => '(haml)',
|
14
|
-
:format => :
|
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 = [:
|
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.
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
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" ?
|
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
|
data/lib/haml/parser.rb
CHANGED
@@ -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
|
81
|
-
@options
|
82
|
-
@flat
|
83
|
-
@index
|
84
|
-
|
85
|
-
|
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(
|
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(
|
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(
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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(
|
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(
|
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(
|
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(
|
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
|
-
|
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(
|
350
|
-
raise SyntaxError.new(
|
351
|
-
raise SyntaxError.new(
|
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(
|
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(
|
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(
|
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(
|
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
|
-
|
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
|
-
|
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 ==
|
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(
|
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(
|
727
|
+
raise SyntaxError.new(Error.message(:unbalanced_brackets))
|
701
728
|
end
|
702
729
|
|
703
730
|
def block_opened?
|