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.

@@ -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 + " " + controller.action_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
- str = capture_haml(*args, &block)
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)
@@ -6,9 +6,7 @@ module Haml
6
6
 
7
7
  @defaults = {
8
8
  :attr_wrapper => "'",
9
- :autoclose => %w(area base basefont br col command embed frame
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.reject {|k, v| !defaults.has_key?(k) || v.nil?}.each {|k, v| send("#{k}=", v)}
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
- if RUBY_VERSION < "1.9"
249
- attr_writer :encoding
250
- else
251
- def encoding=(value)
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
@@ -71,7 +71,7 @@ module Haml
71
71
  # foo.each do | bar |
72
72
  # = bar
73
73
  #
74
- BLOCK_WITH_SPACES = /do[\s]*\|[\s]*[^\|]*[\s]+\|\z/
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)?)?[\s]*([a-z]*)\s*([^ ]+)?/i
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*)|(["'])((?!\\|\#\{|\#@|\#\$|\2).|\\.)*\2/
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
- while next_line
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.text, @line.index) unless @line.text.empty? || @haml_comment
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[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}"
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(:text, :unstripped, :full, :index, :compiler, :eod)
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
- line = self
152
- @tabs ||= compiler.instance_eval do
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
- message = Error.message(:inconsistent_indentation,
171
- Haml::Util.human_indentation(whitespace),
172
- Haml::Util.human_indentation(@indentation)
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
- text = "(#{type} #{value.inspect}"
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(text, index)
206
- @index = index + 1
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(text) if text[1] == ?{
212
- push div(text)
213
- when ELEMENT; push tag(text)
214
- when COMMENT; push comment(text[1..-1].strip)
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(text[3..-1].strip, :escape_html) if text[1..2] == "=="
217
- return push script(text[2..-1].strip, :escape_html) if text[1] == SCRIPT
218
- return push flat_script(text[2..-1].strip, :escape_html) if text[1] == FLAT_SCRIPT
219
- return push plain(text[1..-1].strip, :escape_html) if text[1] == ?\s
220
- push plain(text)
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(text[2..-1].strip) if text[1] == SCRIPT
223
- push script(text[1..-1])
224
- when FLAT_SCRIPT; push flat_script(text[1..-1])
225
- when SILENT_SCRIPT; push silent_script(text)
226
- when FILTER; push filter(text[1..-1].downcase)
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...3] == '!!!'
229
- return push plain(text[3..-1].strip, false) if text[1..2] == "=="
230
- return push script(text[2..-1].strip, false) if text[1] == SCRIPT
231
- return push flat_script(text[2..-1].strip, false) if text[1] == FLAT_SCRIPT
232
- return push plain(text[1..-1].strip, false) if text[1] == ?\s
233
- push plain(text)
234
- when ESCAPE; push plain(text[1..-1])
235
- else; push plain(text)
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(text, escape_html = nil)
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, @index, :text => text)
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[:escape_html] if escape_html.nil?
263
- script(unescape_interpolation(text, escape_html), false)
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(text, escape_html = nil, preserve = false)
267
- raise SyntaxError.new(Error.message(:no_ruby_code, '=')) if text.empty?
268
- text = handle_ruby_multiline(text)
269
- escape_html = @options[:escape_html] if escape_html.nil?
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, @index, :text => text, :escape_html => escape_html,
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(text, escape_html = nil)
279
- raise SyntaxError.new(Error.message(:no_ruby_code, '~')) if text.empty?
280
- script(text, escape_html, :preserve)
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(text)
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), @index - 1) if text[1..-1].strip == "end"
297
+ raise SyntaxError.new(Error.message(:no_end), line.index) if line.text[1..-1].strip == 'end'
287
298
 
288
- text = handle_ruby_multiline(text)
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[:preserve].include?(tag_name)
345
+ preserve_tag = @options.preserve.include?(tag_name)
335
346
  nuke_inner_whitespace ||= preserve_tag
336
- preserve_tag = false if @options[:ugly]
337
- escape_html = (action == '&' || (action != '!' && @options[:escape_html]))
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[:suppress_eval]
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?(value)
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[:autoclose].any? {|t| t === tag_name})
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
- value = handle_ruby_multiline(value) if parse
409
+ line.text = value
410
+ line = handle_ruby_multiline(line) if parse
399
411
 
400
- ParseNode.new(:tag, @index, :name => tag_name, :attributes => attributes,
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 => 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
- tag('%div' + line)
423
+ line.text = "%div#{line.text}"
424
+ tag(line)
412
425
  end
413
426
 
414
427
  # Renders an XHTML comment.
415
- def comment(line)
416
- conditional, line = balance(line, ?[, ?]) if line[0] == ?[
417
- line.strip!
418
- conditional << ">" if conditional
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? && !line.empty?
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 => line)
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(line)
447
+ def doctype(text)
429
448
  raise SyntaxError.new(Error.message(:illegal_nesting_header), @next_line.index) if block_opened?
430
- version, type, encoding = line[3..-1].strip.downcase.scan(DOCTYPE_REGEX)[0]
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(line)
519
- match = line.scan(/%([-:\w]+)([-:\w\.\#]*)(.*)/)[0]
520
- raise SyntaxError.new(Error.message(:invalid_tag, line)) unless match
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 =~ /[\.#](\.|#|\z)/
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 = "nil"
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 == "nil"
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
- nuke_outer_whitespace = nuke_whitespace.include? '>'
552
- nuke_inner_whitespace = nuke_whitespace.include? '<'
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[:remove_whitespace]
579
+ if @options.remove_whitespace
556
580
  nuke_outer_whitespace = true
557
581
  nuke_inner_whitespace = true
558
582
  end
559
583
 
560
- value = value.to_s.strip
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(line)
566
- line = line.dup
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(line, ?{, ?})
598
+ attributes_hash, rest = balance(text, ?{, ?})
571
599
  rescue SyntaxError => e
572
- if line.strip[-1] == ?, && e.message == Error.message(:unbalanced_brackets)
573
- line << "\n" << @next_line.text
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(line)
587
- line = line.dup
588
- scanner = StringScanner.new(line)
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
- text = (Haml::Util.balance(line, ?(, ?)) || [line]).first
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
- line << " " << @next_line.text
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) << " => " << val << ","
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
- '"' + content.map {|(t, v)| t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}"}.join + '"']
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
- text, index = raw_next_line
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
- while new_line = raw_next_line.first
706
- break if new_line == :eod
707
- next if new_line.strip.empty?
708
- break unless is_multiline?(new_line.strip)
709
- line.text << new_line.strip[0...-1]
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(text)
720
- text = text.rstrip
721
- return text unless is_ruby_multiline?(text)
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
- new_line = raw_next_line.first
725
- break if new_line == :eod
726
- next if new_line.strip.empty?
727
- text << " " << new_line.strip
728
- end while is_ruby_multiline?(new_line.strip)
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
- text
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..-2] =~ /\W\?/) || text[-3..-2] == "?\\")
745
+ !((text[-3, 2] =~ /\W\?/) || text[-3, 2] == "?\\")
742
746
  end
743
747
 
744
748
  def balance(*args)
745
- res = Haml::Util.balance(*args)
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?