haml 4.1.0.beta.1 → 5.0.0.beta.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.

Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +36 -6
  4. data/FAQ.md +4 -14
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +81 -48
  7. data/REFERENCE.md +86 -50
  8. data/Rakefile +28 -41
  9. data/lib/haml/attribute_builder.rb +163 -0
  10. data/lib/haml/attribute_compiler.rb +214 -0
  11. data/lib/haml/attribute_parser.rb +112 -0
  12. data/lib/haml/buffer.rb +24 -126
  13. data/lib/haml/compiler.rb +62 -281
  14. data/lib/haml/engine.rb +16 -23
  15. data/lib/haml/error.rb +2 -0
  16. data/lib/haml/escapable.rb +48 -0
  17. data/lib/haml/exec.rb +23 -12
  18. data/lib/haml/filters.rb +3 -4
  19. data/lib/haml/generator.rb +36 -0
  20. data/lib/haml/helpers.rb +61 -48
  21. data/lib/haml/helpers/action_view_extensions.rb +1 -1
  22. data/lib/haml/helpers/action_view_mods.rb +32 -50
  23. data/lib/haml/helpers/safe_erubi_template.rb +26 -0
  24. data/lib/haml/helpers/safe_erubis_template.rb +2 -0
  25. data/lib/haml/helpers/xss_mods.rb +17 -12
  26. data/lib/haml/options.rb +32 -36
  27. data/lib/haml/parser.rb +61 -38
  28. data/lib/haml/{template/plugin.rb → plugin.rb} +5 -2
  29. data/lib/haml/railtie.rb +14 -6
  30. data/lib/haml/template.rb +11 -6
  31. data/lib/haml/temple_engine.rb +119 -0
  32. data/lib/haml/temple_line_counter.rb +28 -0
  33. data/lib/haml/util.rb +17 -112
  34. data/lib/haml/version.rb +1 -1
  35. data/test/attribute_parser_test.rb +105 -0
  36. data/test/engine_test.rb +202 -106
  37. data/test/filters_test.rb +32 -19
  38. data/test/gemfiles/Gemfile.rails-4.0.x +7 -1
  39. data/test/gemfiles/Gemfile.rails-4.0.x.lock +57 -71
  40. data/test/gemfiles/Gemfile.rails-4.1.x +5 -0
  41. data/test/gemfiles/Gemfile.rails-4.2.x +5 -0
  42. data/test/gemfiles/Gemfile.rails-5.0.x +4 -0
  43. data/test/helper_test.rb +156 -109
  44. data/test/options_test.rb +21 -0
  45. data/test/parser_test.rb +49 -4
  46. data/test/results/eval_suppressed.xhtml +4 -4
  47. data/test/results/helpers.xhtml +43 -41
  48. data/test/results/helpful.xhtml +6 -3
  49. data/test/results/just_stuff.xhtml +21 -20
  50. data/test/results/list.xhtml +9 -9
  51. data/test/results/nuke_inner_whitespace.xhtml +22 -22
  52. data/test/results/nuke_outer_whitespace.xhtml +84 -92
  53. data/test/results/original_engine.xhtml +17 -17
  54. data/test/results/partial_layout.xhtml +4 -3
  55. data/test/results/partial_layout_erb.xhtml +4 -3
  56. data/test/results/partials.xhtml +11 -10
  57. data/test/results/silent_script.xhtml +63 -63
  58. data/test/results/standard.xhtml +156 -159
  59. data/test/results/tag_parsing.xhtml +19 -19
  60. data/test/results/very_basic.xhtml +2 -2
  61. data/test/results/whitespace_handling.xhtml +77 -76
  62. data/test/template_test.rb +21 -48
  63. data/test/template_test_helper.rb +38 -0
  64. data/test/templates/just_stuff.haml +1 -0
  65. data/test/templates/standard_ugly.haml +1 -0
  66. data/test/temple_line_counter_test.rb +40 -0
  67. data/test/test_helper.rb +10 -10
  68. data/test/util_test.rb +1 -48
  69. metadata +49 -35
  70. data/lib/haml/temple.rb +0 -85
  71. data/test/gemfiles/Gemfile.rails-3.2.x +0 -4
  72. data/test/templates/_av_partial_1_ugly.haml +0 -9
  73. data/test/templates/_av_partial_2_ugly.haml +0 -5
  74. data/test/templates/action_view_ugly.haml +0 -47
  75. data/test/templates/standard_ugly.haml +0 -43
@@ -45,7 +45,7 @@ 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
48
+ old = instance_variable_defined?(:@_haml_concat_raw) ? @_haml_concat_raw : false
49
49
  @_haml_concat_raw = true
50
50
  yield
51
51
  ensure
@@ -1,40 +1,39 @@
1
- module ActionView
2
- class Base
3
- def render_with_haml(*args, &block)
4
- options = args.first
5
-
6
- # If render :layout is used with a block, it concats rather than returning
7
- # a string so we need it to keep thinking it's Haml until it hits the
8
- # sub-render.
9
- if is_haml? && !(options.is_a?(Hash) && options[:layout] && block_given?)
10
- return non_haml { render_without_haml(*args, &block) }
1
+ module Haml
2
+ module Helpers
3
+ module ActionViewMods
4
+ def render(*args, &block)
5
+ options = args.first
6
+
7
+ # If render :layout is used with a block, it concats rather than returning
8
+ # a string so we need it to keep thinking it's Haml until it hits the
9
+ # sub-render.
10
+ if is_haml? && !(options.is_a?(Hash) && options[:layout] && block_given?)
11
+ return non_haml { super }
12
+ end
13
+ super
11
14
  end
12
- render_without_haml(*args, &block)
13
- end
14
- alias_method :render_without_haml, :render
15
- alias_method :render, :render_with_haml
16
15
 
17
- def output_buffer_with_haml
18
- return haml_buffer.buffer if is_haml?
19
- output_buffer_without_haml
20
- end
21
- alias_method :output_buffer_without_haml, :output_buffer
22
- alias_method :output_buffer, :output_buffer_with_haml
16
+ def output_buffer
17
+ return haml_buffer.buffer if is_haml?
18
+ super
19
+ end
23
20
 
24
- def set_output_buffer_with_haml(new_buffer)
25
- if is_haml?
26
- if Haml::Util.rails_xss_safe? && new_buffer.is_a?(ActiveSupport::SafeBuffer)
27
- new_buffer = String.new(new_buffer)
21
+ def output_buffer=(new_buffer)
22
+ if is_haml?
23
+ if Haml::Util.rails_xss_safe? && new_buffer.is_a?(ActiveSupport::SafeBuffer)
24
+ new_buffer = String.new(new_buffer)
25
+ end
26
+ haml_buffer.buffer = new_buffer
27
+ else
28
+ super
28
29
  end
29
- haml_buffer.buffer = new_buffer
30
- else
31
- set_output_buffer_without_haml new_buffer
32
30
  end
33
31
  end
34
- alias_method :set_output_buffer_without_haml, :output_buffer=
35
- alias_method :output_buffer=, :set_output_buffer_with_haml
32
+ ActionView::Base.send(:prepend, ActionViewMods)
36
33
  end
34
+ end
37
35
 
36
+ module ActionView
38
37
  module Helpers
39
38
  module CaptureHelper
40
39
  def capture_with_haml(*args, &block)
@@ -82,11 +81,9 @@ module ActionView
82
81
  end
83
82
  end
84
83
 
85
- if ActionPack::VERSION::MAJOR == 4
86
- module Tags
87
- class TextArea
88
- include HamlSupport
89
- end
84
+ module Tags
85
+ class TextArea
86
+ include HamlSupport
90
87
  end
91
88
  end
92
89
 
@@ -113,7 +110,7 @@ module ActionView
113
110
  with_tabs(1) {oldproc.call(*args)}
114
111
  end
115
112
  end
116
- res = form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc) + "\n"
113
+ res = form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc) << "\n"
117
114
  res << "\n" if wrap_block
118
115
  res
119
116
  else
@@ -123,20 +120,5 @@ module ActionView
123
120
  alias_method :form_tag_without_haml, :form_tag
124
121
  alias_method :form_tag, :form_tag_with_haml
125
122
  end
126
-
127
- module FormHelper
128
- def form_for_with_haml(object_name, *args, &proc)
129
- wrap_block = block_given? && is_haml? && block_is_haml?(proc)
130
- if wrap_block
131
- oldproc = proc
132
- proc = proc {|*subargs| with_tabs(1) {oldproc.call(*subargs)}}
133
- end
134
- res = form_for_without_haml(object_name, *args, &proc)
135
- res << "\n" if wrap_block
136
- res
137
- end
138
- alias_method :form_for_without_haml, :form_for
139
- alias_method :form_for, :form_for_with_haml
140
- end
141
123
  end
142
124
  end
@@ -0,0 +1,26 @@
1
+ require 'action_view'
2
+
3
+ module Haml
4
+ class ErubiTemplateHandler < ActionView::Template::Handlers::ERB::Erubi
5
+
6
+ def initialize(*args, &blk)
7
+ @newline_pending = 0
8
+ super
9
+ end
10
+ end
11
+
12
+ class SafeErubiTemplate < Tilt::ErubiTemplate
13
+ def prepare
14
+ @options.merge! engine_class: Haml::ErubiTemplateHandler
15
+ super
16
+ end
17
+
18
+ def precompiled_preamble(locals)
19
+ [super, "@output_buffer = ActionView::OutputBuffer.new;"].join("\n")
20
+ end
21
+
22
+ def precompiled_postamble(locals)
23
+ [super, '@output_buffer.to_s'].join("\n")
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,5 @@
1
+ require 'action_view'
2
+
1
3
  module Haml
2
4
 
3
5
  class ErubisTemplateHandler < ActionView::Template::Handlers::Erubis
@@ -7,8 +7,8 @@ module Haml
7
7
  module XssMods
8
8
  def self.included(base)
9
9
  %w[html_escape find_and_preserve preserve list_of surround
10
- precede succeed capture_haml haml_concat haml_indent
11
- haml_tag escape_once].each do |name|
10
+ precede succeed capture_haml haml_concat haml_internal_concat haml_indent
11
+ escape_once].each do |name|
12
12
  base.send(:alias_method, "#{name}_without_haml_xss", name)
13
13
  base.send(:alias_method, name, "#{name}_with_haml_xss")
14
14
  end
@@ -61,24 +61,29 @@ module Haml
61
61
  Haml::Util.html_safe(capture_haml_without_haml_xss(*args, &block))
62
62
  end
63
63
 
64
- # Input is escaped
64
+ # Input will be escaped unless this is in a `with_raw_haml_concat`
65
+ # block. See #Haml::Helpers::ActionViewExtensions#with_raw_haml_concat.
65
66
  def haml_concat_with_haml_xss(text = "")
66
- raw = instance_variable_defined?('@_haml_concat_raw') ? @_haml_concat_raw : false
67
- haml_concat_without_haml_xss(raw ? text : haml_xss_html_escape(text))
67
+ raw = instance_variable_defined?(:@_haml_concat_raw) ? @_haml_concat_raw : false
68
+ if raw
69
+ haml_internal_concat_raw text
70
+ else
71
+ haml_internal_concat text
72
+ end
73
+ ErrorReturn.new("haml_concat")
68
74
  end
69
75
 
76
+ # Input is escaped
77
+ def haml_internal_concat_with_haml_xss(text="", newline=true, indent=true)
78
+ haml_internal_concat_without_haml_xss(haml_xss_html_escape(text), newline, indent)
79
+ end
80
+ private :haml_internal_concat_with_haml_xss
81
+
70
82
  # Output is always HTML safe
71
83
  def haml_indent_with_haml_xss
72
84
  Haml::Util.html_safe(haml_indent_without_haml_xss)
73
85
  end
74
86
 
75
- # Input is escaped, haml_concat'ed output is always HTML safe
76
- def haml_tag_with_haml_xss(name, *rest, &block)
77
- name = haml_xss_html_escape(name.to_s)
78
- rest.unshift(haml_xss_html_escape(rest.shift.to_s)) unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
79
- with_raw_haml_concat {haml_tag_without_haml_xss(name, *rest, &block)}
80
- end
81
-
82
87
  # Output is always HTML safe
83
88
  def escape_once_with_haml_xss(*args)
84
89
  Haml::Util.html_safe(escape_once_without_haml_xss(*args))
@@ -4,37 +4,15 @@ module Haml
4
4
  # learn how to set the options.
5
5
  class Options
6
6
 
7
- @defaults = {
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),
12
- :encoding => "UTF-8",
13
- :escape_attrs => true,
14
- :escape_html => false,
15
- :filename => '(haml)',
16
- :format => :html5,
17
- :hyphenate_data_attrs => true,
18
- :line => 1,
19
- :mime_type => 'text/html',
20
- :preserve => %w(textarea pre code),
21
- :remove_whitespace => false,
22
- :suppress_eval => false,
23
- :ugly => false,
24
- :cdata => false,
25
- :parser_class => ::Haml::Parser,
26
- :compiler_class => ::Haml::Compiler
27
- }
28
-
29
7
  @valid_formats = [:html4, :html5, :xhtml]
30
8
 
31
- @buffer_option_keys = [:autoclose, :preserve, :attr_wrapper, :ugly, :format,
9
+ @buffer_option_keys = [:autoclose, :preserve, :attr_wrapper, :format,
32
10
  :encoding, :escape_html, :escape_attrs, :hyphenate_data_attrs, :cdata]
33
11
 
34
12
  # The default option values.
35
13
  # @return Hash
36
14
  def self.defaults
37
- @defaults
15
+ @defaults ||= Haml::TempleEngine.options.to_hash.merge(encoding: 'UTF-8')
38
16
  end
39
17
 
40
18
  # An array of valid values for the `:format` option.
@@ -50,6 +28,22 @@ module Haml
50
28
  @buffer_option_keys
51
29
  end
52
30
 
31
+ # Returns a subset of defaults: those that {Haml::Buffer} cares about.
32
+ # @return [{Symbol => Object}] The options hash
33
+ def self.buffer_defaults
34
+ @buffer_defaults ||= buffer_option_keys.inject({}) do |hash, key|
35
+ hash.merge(key => defaults[key])
36
+ end
37
+ end
38
+
39
+ def self.wrap(options)
40
+ if options.is_a?(Options)
41
+ options
42
+ else
43
+ Options.new(options)
44
+ end
45
+ end
46
+
53
47
  # The character that should wrap element attributes. This defaults to `'`
54
48
  # (an apostrophe). Characters of this type within the attributes will be
55
49
  # escaped (e.g. by replacing them with `&apos;`) if the character is an
@@ -145,13 +139,6 @@ module Haml
145
139
  # Defaults to `false`.
146
140
  attr_accessor :suppress_eval
147
141
 
148
- # If set to `true`, Haml makes no attempt to properly indent or format the
149
- # HTML output. This significantly improves rendering performance but makes
150
- # viewing the source unpleasant.
151
- #
152
- # Defaults to `true` in Rails production mode, and `false` everywhere else.
153
- attr_accessor :ugly
154
-
155
142
  # Whether to include CDATA sections around javascript and css blocks when
156
143
  # using the `:javascript` or `:css` filters.
157
144
  #
@@ -168,6 +155,14 @@ module Haml
168
155
  # The compiler class to use. Defaults to Haml::Compiler.
169
156
  attr_accessor :compiler_class
170
157
 
158
+ # Enable template tracing. If true, it will add a 'data-trace' attribute to
159
+ # each tag generated by Haml. The value of the attribute will be the
160
+ # source template name and the line number from which the tag was generated,
161
+ # separated by a colon. On Rails applications, the path given will be a
162
+ # relative path as from the views directory. On non-Rails applications,
163
+ # the path will be the full path.
164
+ attr_accessor :trace
165
+
171
166
  def initialize(values = {}, &block)
172
167
  defaults.each {|k, v| instance_variable_set :"@#{k}", v}
173
168
  values.each {|k, v| send("#{k}=", v) if defaults.has_key?(k) && !v.nil?}
@@ -187,8 +182,7 @@ module Haml
187
182
  send "#{key}=", value
188
183
  end
189
184
 
190
- [:escape_attrs, :hyphenate_data_attrs, :remove_whitespace, :suppress_eval,
191
- :ugly].each do |method|
185
+ [:escape_attrs, :hyphenate_data_attrs, :remove_whitespace, :suppress_eval].each do |method|
192
186
  class_eval(<<-END)
193
187
  def #{method}?
194
188
  !! @#{method}
@@ -240,7 +234,6 @@ module Haml
240
234
  end
241
235
 
242
236
  def remove_whitespace=(value)
243
- @ugly = true if value
244
237
  @remove_whitespace = value
245
238
  end
246
239
 
@@ -250,7 +243,7 @@ module Haml
250
243
  @encoding = "UTF-8" if @encoding.upcase == "US-ASCII"
251
244
  end
252
245
 
253
- # Returns a subset of options: those that {Haml::Buffer} cares about.
246
+ # Returns a non-default subset of options: those that {Haml::Buffer} cares about.
254
247
  # All of the values here are such that when `#inspect` is called on the hash,
255
248
  # it can be `Kernel#eval`ed to get the same result back.
256
249
  #
@@ -259,7 +252,10 @@ module Haml
259
252
  # @return [{Symbol => Object}] The options hash
260
253
  def for_buffer
261
254
  self.class.buffer_option_keys.inject({}) do |hash, key|
262
- hash[key] = send(key)
255
+ value = public_send(key)
256
+ if self.class.buffer_defaults[key] != value
257
+ hash[key] = value
258
+ end
263
259
  hash
264
260
  end
265
261
  end
@@ -83,20 +83,21 @@ module Haml
83
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
  ID_KEY = 'id'.freeze
89
89
  CLASS_KEY = 'class'.freeze
90
90
 
91
- def initialize(template, options)
92
- @options = options
93
- @flat = false
91
+ def initialize(options)
92
+ @options = Options.wrap(options)
94
93
  # Record the indent levels of "if" statements to validate the subsequent
95
94
  # elsif and else statements are indented at the appropriate level.
96
95
  @script_level_stack = []
97
96
  @template_index = 0
98
97
  @template_tabs = 0
98
+ end
99
99
 
100
+ def call(template)
100
101
  match = template.rstrip.scan(/(([ \t]+)?(.*?))(?:\Z|\r\n|\r|\n)/m)
101
102
  # discard the last match which is always blank
102
103
  match.pop
@@ -105,11 +106,10 @@ module Haml
105
106
  end
106
107
  # Append special end-of-document marker
107
108
  @template << Line.new(nil, '-#', '-#', @template.size, self, true)
108
- end
109
109
 
110
- def parse
111
110
  @root = @parent = ParseNode.new(:root)
112
- @haml_comment = false
111
+ @flat = false
112
+ @filter_buffer = nil
113
113
  @indentation = nil
114
114
  @line = next_line
115
115
 
@@ -129,13 +129,13 @@ module Haml
129
129
  end
130
130
 
131
131
  @tab_up = nil
132
- process_line(@line) unless @line.text.empty? || @haml_comment
133
- if @parent.type != :haml_comment && (block_opened? || @tab_up)
132
+ process_line(@line) unless @line.text.empty?
133
+ if block_opened? || @tab_up
134
134
  @template_tabs += 1
135
135
  @parent = @parent.children.last
136
136
  end
137
137
 
138
- if !@haml_comment && !flat? && @next_line.tabs - @line.tabs > 1
138
+ if !flat? && @next_line.tabs - @line.tabs > 1
139
139
  raise SyntaxError.new(Error.message(:deeper_indenting, @next_line.tabs - @line.tabs), @next_line.index)
140
140
  end
141
141
 
@@ -200,7 +200,7 @@ module Haml
200
200
  end
201
201
 
202
202
  def inspect
203
- %Q[(#{type} #{value.inspect}#{children.map {|c| "\n#{c.inspect.gsub!(/^/, ' ')}"}.join})]
203
+ %Q[(#{type} #{value.inspect}#{children.each_with_object('') {|c, s| s << "\n#{c.inspect.gsub!(/^/, ' ')}"}})]
204
204
  end
205
205
  end
206
206
 
@@ -209,7 +209,15 @@ module Haml
209
209
  return unless line.tabs <= @template_tabs && @template_tabs > 0
210
210
 
211
211
  to_close = @template_tabs - line.tabs
212
- to_close.times {|i| close unless to_close - 1 - i == 0 && mid_block_keyword?(line.text)}
212
+ to_close.times {|i| close unless to_close - 1 - i == 0 && continuation_script?(line.text)}
213
+ end
214
+
215
+ def continuation_script?(text)
216
+ text[0] == SILENT_SCRIPT && mid_block_keyword?(text)
217
+ end
218
+
219
+ def mid_block_keyword?(text)
220
+ MID_BLOCK_KEYWORDS.include?(block_keyword(text))
213
221
  end
214
222
 
215
223
  # Processes a single line of Haml.
@@ -220,7 +228,7 @@ module Haml
220
228
  case line.text[0]
221
229
  when DIV_CLASS; push div(line)
222
230
  when DIV_ID
223
- return push plain(line) if line.text[1] == ?{
231
+ return push plain(line) if %w[{ @ $].include?(line.text[1])
224
232
  push div(line)
225
233
  when ELEMENT; push tag(line)
226
234
  when COMMENT; push comment(line.text[1..-1].lstrip)
@@ -228,23 +236,27 @@ module Haml
228
236
  return push plain(line.strip!(3), :escape_html) if line.text[1, 2] == '=='
229
237
  return push script(line.strip!(2), :escape_html) if line.text[1] == SCRIPT
230
238
  return push flat_script(line.strip!(2), :escape_html) if line.text[1] == FLAT_SCRIPT
231
- return push plain(line.strip!(1), :escape_html) if line.text[1] == ?\s
239
+ return push plain(line.strip!(1), :escape_html) if line.text[1] == ?\s || line.text[1..2] == '#{'
232
240
  push plain(line)
233
241
  when SCRIPT
234
242
  return push plain(line.strip!(2)) if line.text[1] == SCRIPT
235
243
  line.text = line.text[1..-1]
236
244
  push script(line)
237
245
  when FLAT_SCRIPT; push flat_script(line.strip!(1))
238
- when SILENT_SCRIPT; push silent_script(line)
246
+ when SILENT_SCRIPT
247
+ return push haml_comment(line.text[2..-1]) if line.text[1] == SILENT_COMMENT
248
+ push silent_script(line)
239
249
  when FILTER; push filter(line.text[1..-1].downcase)
240
250
  when DOCTYPE
241
251
  return push doctype(line.text) if line.text[0, 3] == '!!!'
242
252
  return push plain(line.strip!(3), false) if line.text[1, 2] == '=='
243
253
  return push script(line.strip!(2), false) if line.text[1] == SCRIPT
244
254
  return push flat_script(line.strip!(2), false) if line.text[1] == FLAT_SCRIPT
245
- return push plain(line.strip!(1), false) if line.text[1] == ?\s
255
+ return push plain(line.strip!(1), false) if line.text[1] == ?\s || line.text[1..2] == '#{'
256
+ push plain(line)
257
+ when ESCAPE
258
+ line.text = line.text[1..-1]
246
259
  push plain(line)
247
- when ESCAPE; push plain(line.strip!(1))
248
260
  else; push plain(line)
249
261
  end
250
262
  end
@@ -254,10 +266,6 @@ module Haml
254
266
  keyword[0] || keyword[1]
255
267
  end
256
268
 
257
- def mid_block_keyword?(text)
258
- MID_BLOCK_KEYWORDS.include?(block_keyword(text))
259
- end
260
-
261
269
  def push(node)
262
270
  @parent.children << node
263
271
  node.parent = @parent
@@ -295,8 +303,6 @@ module Haml
295
303
  end
296
304
 
297
305
  def silent_script(line)
298
- return haml_comment(line.text[2..-1]) if line.text[1] == SILENT_COMMENT
299
-
300
306
  raise SyntaxError.new(Error.message(:no_end), line.index) if line.text[1..-1].strip == 'end'
301
307
 
302
308
  line = handle_ruby_multiline(line)
@@ -337,7 +343,15 @@ module Haml
337
343
  end
338
344
 
339
345
  def haml_comment(text)
340
- @haml_comment = block_opened?
346
+ if filter_opened?
347
+ @flat = true
348
+ @filter_buffer = String.new
349
+ @filter_buffer << "#{text}\n" unless text.empty?
350
+ text = @filter_buffer
351
+ # If we don't know the indentation by now, it'll be set in Line#tabs
352
+ @flat_spaces = @indentation * (@template_tabs+1) if @indentation
353
+ end
354
+
341
355
  ParseNode.new(:haml_comment, @line.index + 1, :text => text)
342
356
  end
343
357
 
@@ -347,7 +361,6 @@ module Haml
347
361
 
348
362
  preserve_tag = @options.preserve.include?(tag_name)
349
363
  nuke_inner_whitespace ||= preserve_tag
350
- preserve_tag = false if @options.ugly
351
364
  escape_html = (action == '&' || (action != '!' && @options.escape_html))
352
365
 
353
366
  case action
@@ -387,13 +400,13 @@ module Haml
387
400
 
388
401
  if attributes_hashes[:new]
389
402
  static_attributes, attributes_hash = attributes_hashes[:new]
390
- Buffer.merge_attrs(attributes, static_attributes) if static_attributes
403
+ AttributeBuilder.merge_attributes!(attributes, static_attributes) if static_attributes
391
404
  attributes_list << attributes_hash
392
405
  end
393
406
 
394
407
  if attributes_hashes[:old]
395
408
  static_attributes = parse_static_hash(attributes_hashes[:old])
396
- Buffer.merge_attrs(attributes, static_attributes) if static_attributes
409
+ AttributeBuilder.merge_attributes!(attributes, static_attributes) if static_attributes
397
410
  attributes_list << attributes_hashes[:old] unless static_attributes || @options.suppress_eval
398
411
  end
399
412
 
@@ -403,7 +416,7 @@ module Haml
403
416
  raise SyntaxError.new(Error.message(:no_ruby_code, action), last_line - 1) if parse && value.empty?
404
417
  raise SyntaxError.new(Error.message(:self_closing_content), last_line - 1) if self_closing && !value.empty?
405
418
 
406
- if block_opened? && !value.empty? && !is_ruby_multiline?(line.text)
419
+ if block_opened? && !value.empty? && !is_ruby_multiline?(value)
407
420
  raise SyntaxError.new(Error.message(:illegal_nesting_line, tag_name), @next_line.index)
408
421
  end
409
422
 
@@ -439,11 +452,18 @@ module Haml
439
452
  conditional, text = balance(text, ?[, ?]) if text[0] == ?[
440
453
  text.strip!
441
454
 
455
+ if contains_interpolation?(text)
456
+ parse = true
457
+ text = unescape_interpolation(text)
458
+ else
459
+ parse = false
460
+ end
461
+
442
462
  if block_opened? && !text.empty?
443
463
  raise SyntaxError.new(Haml::Error.message(:illegal_nesting_content), @next_line.index)
444
464
  end
445
465
 
446
- ParseNode.new(:comment, @line.index + 1, :conditional => conditional, :text => text, :revealed => revealed)
466
+ ParseNode.new(:comment, @line.index + 1, :conditional => conditional, :text => text, :revealed => revealed, :parse => parse)
447
467
  end
448
468
 
449
469
  # Renders an XHTML doctype or XML shebang.
@@ -456,10 +476,9 @@ module Haml
456
476
  def filter(name)
457
477
  raise Error.new(Error.message(:invalid_filter_name, name)) unless name =~ /^\w+$/
458
478
 
459
- @filter_buffer = String.new
460
-
461
479
  if filter_opened?
462
480
  @flat = true
481
+ @filter_buffer = String.new
463
482
  # If we don't know the indentation by now, it'll be set in Line#tabs
464
483
  @flat_spaces = @indentation * (@template_tabs+1) if @indentation
465
484
  end
@@ -474,13 +493,17 @@ module Haml
474
493
  end
475
494
 
476
495
  def close_filter(_)
477
- @flat = false
478
- @flat_spaces = nil
479
- @filter_buffer = nil
496
+ close_flat_section
480
497
  end
481
498
 
482
499
  def close_haml_comment(_)
483
- @haml_comment = false
500
+ close_flat_section
501
+ end
502
+
503
+ def close_flat_section
504
+ @flat = false
505
+ @flat_spaces = nil
506
+ @filter_buffer = nil
484
507
  end
485
508
 
486
509
  def close_silent_script(node)
@@ -681,7 +704,7 @@ module Haml
681
704
 
682
705
  return name, [:static, content.first[1]] if content.size == 1
683
706
  return name, [:dynamic,
684
- %!"#{content.map {|(t, v)| t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}"}.join}"!]
707
+ %!"#{content.each_with_object('') {|(t, v), s| s << (t == :str ? inspect_obj(v)[1...-1] : "\#{#{v}}")}}"!]
685
708
  end
686
709
 
687
710
  def next_line
@@ -690,7 +713,7 @@ module Haml
690
713
  # `flat?' here is a little outdated,
691
714
  # so we have to manually check if either the previous or current line
692
715
  # closes the flat block, as well as whether a new block is opened.
693
- line_defined = instance_variable_defined?('@line')
716
+ line_defined = instance_variable_defined?(:@line)
694
717
  @line.tabs if line_defined
695
718
  unless (flat? && !closes_flat?(line) && !closes_flat?(@line)) ||
696
719
  (line_defined && @line.text[0] == ?: && line.full =~ %r[^#{@line.full[/^\s+/]}\s])
@@ -759,7 +782,7 @@ module Haml
759
782
  # Same semantics as block_opened?, except that block_opened? uses Line#tabs,
760
783
  # which doesn't interact well with filter lines
761
784
  def filter_opened?
762
- @next_line.full =~ (@indentation ? /^#{@indentation * @template_tabs}/ : /^\s/)
785
+ @next_line.full =~ (@indentation ? /^#{@indentation * (@template_tabs + 1)}/ : /^\s/)
763
786
  end
764
787
 
765
788
  def flat?