haml 4.0.6 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +19 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +72 -0
  5. data/.yardopts +2 -3
  6. data/CHANGELOG.md +138 -4
  7. data/FAQ.md +4 -14
  8. data/Gemfile +16 -0
  9. data/MIT-LICENSE +2 -2
  10. data/README.md +79 -42
  11. data/REFERENCE.md +142 -67
  12. data/Rakefile +44 -63
  13. data/TODO +24 -0
  14. data/benchmark.rb +70 -0
  15. data/haml.gemspec +45 -0
  16. data/lib/haml.rb +2 -0
  17. data/lib/haml/.gitattributes +1 -0
  18. data/lib/haml/attribute_builder.rb +164 -0
  19. data/lib/haml/attribute_compiler.rb +235 -0
  20. data/lib/haml/attribute_parser.rb +150 -0
  21. data/lib/haml/buffer.rb +29 -136
  22. data/lib/haml/compiler.rb +110 -320
  23. data/lib/haml/engine.rb +34 -41
  24. data/lib/haml/error.rb +28 -24
  25. data/lib/haml/escapable.rb +77 -0
  26. data/lib/haml/exec.rb +38 -20
  27. data/lib/haml/filters.rb +22 -27
  28. data/lib/haml/generator.rb +42 -0
  29. data/lib/haml/helpers.rb +134 -89
  30. data/lib/haml/helpers/action_view_extensions.rb +4 -2
  31. data/lib/haml/helpers/action_view_mods.rb +45 -60
  32. data/lib/haml/helpers/action_view_xss_mods.rb +2 -0
  33. data/lib/haml/helpers/safe_erubi_template.rb +20 -0
  34. data/lib/haml/helpers/safe_erubis_template.rb +5 -1
  35. data/lib/haml/helpers/xss_mods.rb +23 -13
  36. data/lib/haml/options.rb +63 -69
  37. data/lib/haml/parser.rb +292 -228
  38. data/lib/haml/plugin.rb +37 -0
  39. data/lib/haml/railtie.rb +38 -12
  40. data/lib/haml/sass_rails_filter.rb +18 -4
  41. data/lib/haml/template.rb +13 -6
  42. data/lib/haml/template/options.rb +13 -2
  43. data/lib/haml/temple_engine.rb +123 -0
  44. data/lib/haml/temple_line_counter.rb +30 -0
  45. data/lib/haml/util.rb +83 -202
  46. data/lib/haml/version.rb +3 -1
  47. data/yard/default/.gitignore +1 -0
  48. data/yard/default/fulldoc/html/css/common.sass +15 -0
  49. data/yard/default/layout/html/footer.erb +12 -0
  50. metadata +73 -108
  51. data/lib/haml/template/plugin.rb +0 -41
  52. data/test/engine_test.rb +0 -2013
  53. data/test/erb/_av_partial_1.erb +0 -12
  54. data/test/erb/_av_partial_2.erb +0 -8
  55. data/test/erb/action_view.erb +0 -62
  56. data/test/erb/standard.erb +0 -55
  57. data/test/filters_test.rb +0 -254
  58. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  59. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  60. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  61. data/test/gemfiles/Gemfile.rails-4.0.x +0 -5
  62. data/test/helper_test.rb +0 -583
  63. data/test/markaby/standard.mab +0 -52
  64. data/test/mocks/article.rb +0 -6
  65. data/test/parser_test.rb +0 -105
  66. data/test/results/content_for_layout.xhtml +0 -12
  67. data/test/results/eval_suppressed.xhtml +0 -9
  68. data/test/results/helpers.xhtml +0 -70
  69. data/test/results/helpful.xhtml +0 -10
  70. data/test/results/just_stuff.xhtml +0 -70
  71. data/test/results/list.xhtml +0 -12
  72. data/test/results/nuke_inner_whitespace.xhtml +0 -40
  73. data/test/results/nuke_outer_whitespace.xhtml +0 -148
  74. data/test/results/original_engine.xhtml +0 -20
  75. data/test/results/partial_layout.xhtml +0 -5
  76. data/test/results/partial_layout_erb.xhtml +0 -5
  77. data/test/results/partials.xhtml +0 -21
  78. data/test/results/render_layout.xhtml +0 -3
  79. data/test/results/silent_script.xhtml +0 -74
  80. data/test/results/standard.xhtml +0 -162
  81. data/test/results/tag_parsing.xhtml +0 -23
  82. data/test/results/very_basic.xhtml +0 -5
  83. data/test/results/whitespace_handling.xhtml +0 -90
  84. data/test/template_test.rb +0 -354
  85. data/test/templates/_av_partial_1.haml +0 -9
  86. data/test/templates/_av_partial_1_ugly.haml +0 -9
  87. data/test/templates/_av_partial_2.haml +0 -5
  88. data/test/templates/_av_partial_2_ugly.haml +0 -5
  89. data/test/templates/_layout.erb +0 -3
  90. data/test/templates/_layout_for_partial.haml +0 -3
  91. data/test/templates/_partial.haml +0 -8
  92. data/test/templates/_text_area.haml +0 -3
  93. data/test/templates/_text_area_helper.html.haml +0 -4
  94. data/test/templates/action_view.haml +0 -47
  95. data/test/templates/action_view_ugly.haml +0 -47
  96. data/test/templates/breakage.haml +0 -8
  97. data/test/templates/content_for_layout.haml +0 -8
  98. data/test/templates/eval_suppressed.haml +0 -11
  99. data/test/templates/helpers.haml +0 -55
  100. data/test/templates/helpful.haml +0 -11
  101. data/test/templates/just_stuff.haml +0 -85
  102. data/test/templates/list.haml +0 -12
  103. data/test/templates/nuke_inner_whitespace.haml +0 -32
  104. data/test/templates/nuke_outer_whitespace.haml +0 -144
  105. data/test/templates/original_engine.haml +0 -17
  106. data/test/templates/partial_layout.haml +0 -3
  107. data/test/templates/partial_layout_erb.erb +0 -4
  108. data/test/templates/partialize.haml +0 -1
  109. data/test/templates/partials.haml +0 -12
  110. data/test/templates/render_layout.haml +0 -2
  111. data/test/templates/silent_script.haml +0 -45
  112. data/test/templates/standard.haml +0 -43
  113. data/test/templates/standard_ugly.haml +0 -43
  114. data/test/templates/tag_parsing.haml +0 -21
  115. data/test/templates/very_basic.haml +0 -4
  116. data/test/templates/whitespace_handling.haml +0 -87
  117. data/test/test_helper.rb +0 -81
  118. data/test/util_test.rb +0 -63
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'ripper'
5
+ rescue LoadError
6
+ end
7
+ require 'temple/static_analyzer'
8
+
9
+ module Haml
10
+ # Haml::AttriubuteParser parses Hash literal to { String (key name) => String (value literal) }.
11
+ module AttributeParser
12
+ class UnexpectedTokenError < StandardError; end
13
+ class UnexpectedKeyError < StandardError; end
14
+
15
+ # Indices in Ripper tokens
16
+ TYPE = 1
17
+ TEXT = 2
18
+
19
+ IGNORED_TYPES = %i[on_sp on_ignored_nl].freeze
20
+
21
+ class << self
22
+ # @return [Boolean] - return true if AttributeParser.parse can be used.
23
+ def available?
24
+ defined?(Ripper) && Temple::StaticAnalyzer.available?
25
+ end
26
+
27
+ # @param [String] exp - Old attributes literal or Hash literal generated from new attributes.
28
+ # @return [Hash<String, String>,nil] - Return parsed attribute Hash whose values are Ruby literals, or return nil if argument is not a single Hash literal.
29
+ def parse(exp)
30
+ return nil unless hash_literal?(exp)
31
+
32
+ hash = {}
33
+ each_attribute(exp) do |key, value|
34
+ hash[key] = value
35
+ end
36
+ hash
37
+ rescue UnexpectedTokenError, UnexpectedKeyError
38
+ nil
39
+ end
40
+
41
+ private
42
+
43
+ # @param [String] exp - Ruby expression
44
+ # @return [Boolean] - Return true if exp is a single Hash literal
45
+ def hash_literal?(exp)
46
+ return false if Temple::StaticAnalyzer.syntax_error?(exp)
47
+ sym, body = Ripper.sexp(exp)
48
+ sym == :program && body.is_a?(Array) && body.size == 1 && body[0] && body[0][0] == :hash
49
+ end
50
+
51
+ # @param [Array] tokens - Ripper tokens. Scanned tokens will be destructively removed from this argument.
52
+ # @return [String] - attribute name in String
53
+ def shift_key!(tokens)
54
+ while !tokens.empty? && IGNORED_TYPES.include?(tokens.first[TYPE])
55
+ tokens.shift # ignore spaces
56
+ end
57
+
58
+ _, type, first_text = tokens.shift
59
+ case type
60
+ when :on_label # `key:`
61
+ first_text.tr(':', '')
62
+ when :on_symbeg # `:key =>`, `:'key' =>` or `:"key" =>`
63
+ key = tokens.shift[TEXT]
64
+ if first_text != ':' # `:'key'` or `:"key"`
65
+ expect_string_end!(tokens.shift)
66
+ end
67
+ shift_hash_rocket!(tokens)
68
+ key
69
+ when :on_tstring_beg # `"key":`, `'key':` or `"key" =>`
70
+ key = tokens.shift[TEXT]
71
+ next_token = tokens.shift
72
+ if next_token[TYPE] != :on_label_end # on_label_end is `":` or `':`, so `"key" =>`
73
+ expect_string_end!(next_token)
74
+ shift_hash_rocket!(tokens)
75
+ end
76
+ key
77
+ else
78
+ raise UnexpectedKeyError.new("unexpected token is given!: #{first_text} (#{type})")
79
+ end
80
+ end
81
+
82
+ # @param [Array] token - Ripper token
83
+ def expect_string_end!(token)
84
+ if token[TYPE] != :on_tstring_end
85
+ raise UnexpectedTokenError
86
+ end
87
+ end
88
+
89
+ # @param [Array] tokens - Ripper tokens
90
+ def shift_hash_rocket!(tokens)
91
+ until tokens.empty?
92
+ _, type, str = tokens.shift
93
+ break if type == :on_op && str == '=>'
94
+ end
95
+ end
96
+
97
+ # @param [String] hash_literal
98
+ # @param [Proc] block - that takes [String, String] as arguments
99
+ def each_attribute(hash_literal, &block)
100
+ all_tokens = Ripper.lex(hash_literal.strip)
101
+ all_tokens = all_tokens[1...-1] || [] # strip tokens for brackets
102
+
103
+ each_balanced_tokens(all_tokens) do |tokens|
104
+ key = shift_key!(tokens)
105
+ value = tokens.map {|t| t[2] }.join.strip
106
+ block.call(key, value)
107
+ end
108
+ end
109
+
110
+ # @param [Array] tokens - Ripper tokens
111
+ # @param [Proc] block - that takes balanced Ripper tokens as arguments
112
+ def each_balanced_tokens(tokens, &block)
113
+ attr_tokens = []
114
+ open_tokens = Hash.new { |h, k| h[k] = 0 }
115
+
116
+ tokens.each do |token|
117
+ case token[TYPE]
118
+ when :on_comma
119
+ if open_tokens.values.all?(&:zero?)
120
+ block.call(attr_tokens)
121
+ attr_tokens = []
122
+ next
123
+ end
124
+ when :on_lbracket
125
+ open_tokens[:array] += 1
126
+ when :on_rbracket
127
+ open_tokens[:array] -= 1
128
+ when :on_lbrace
129
+ open_tokens[:block] += 1
130
+ when :on_rbrace
131
+ open_tokens[:block] -= 1
132
+ when :on_lparen
133
+ open_tokens[:paren] += 1
134
+ when :on_rparen
135
+ open_tokens[:paren] -= 1
136
+ when :on_embexpr_beg
137
+ open_tokens[:embexpr] += 1
138
+ when :on_embexpr_end
139
+ open_tokens[:embexpr] -= 1
140
+ when *IGNORED_TYPES
141
+ next if attr_tokens.empty?
142
+ end
143
+
144
+ attr_tokens << token
145
+ end
146
+ block.call(attr_tokens) unless attr_tokens.empty?
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Haml
2
4
  # This class is used only internally. It holds the buffer of HTML that
3
5
  # is eventually output as the resulting document.
@@ -90,7 +92,8 @@ module Haml
90
92
  def initialize(upper = nil, options = {})
91
93
  @active = true
92
94
  @upper = upper
93
- @options = options
95
+ @options = Options.buffer_defaults
96
+ @options = @options.merge(options) unless options.empty?
94
97
  @buffer = new_encoded_string
95
98
  @tabulation = 0
96
99
 
@@ -115,8 +118,8 @@ module Haml
115
118
  text.sub!(tabs, '') if dont_tab_up
116
119
  end
117
120
 
118
- @buffer << text
119
121
  @real_tabs += tab_change
122
+ @buffer << text
120
123
  end
121
124
 
122
125
  # Modifies the indentation of the document.
@@ -127,82 +130,15 @@ module Haml
127
130
  @real_tabs += tab_change
128
131
  end
129
132
 
130
- Haml::Util.def_static_method(self, :format_script, [:result],
131
- :preserve_script, :in_tag, :preserve_tag, :escape_html,
132
- :nuke_inner_whitespace, :interpolated, :ugly, <<RUBY)
133
- <% # Escape HTML here so that the safety of the string is preserved in Rails
134
- result_name = escape_html ? "html_escape(result.to_s)" : "result.to_s" %>
135
- <% unless ugly %>
136
- # If we're interpolated,
137
- # then the custom tabulation is handled in #push_text.
138
- # The easiest way to avoid it here is to reset @tabulation.
139
- <% if interpolated %>
140
- old_tabulation = @tabulation
141
- @tabulation = 0
142
- <% end %>
143
-
144
- <% if !(in_tag && preserve_tag && !nuke_inner_whitespace) %>
145
- tabulation = @real_tabs
146
- <% end %>
147
- result = <%= result_name %>.<% if nuke_inner_whitespace %>strip<% else %>rstrip<% end %>
148
- <% else %>
149
- result = <%= result_name %><% if nuke_inner_whitespace %>.strip<% end %>
150
- <% end %>
151
-
152
- <% if preserve_tag %>
153
- result = Haml::Helpers.preserve(result)
154
- <% elsif preserve_script %>
155
- result = Haml::Helpers.find_and_preserve(result, options[:preserve])
156
- <% end %>
157
-
158
- <% if ugly %>
159
- fix_textareas!(result) if toplevel? && result.include?('<textarea')
160
- return result
161
- <% else %>
162
- <% if !(in_tag && preserve_tag && !nuke_inner_whitespace) %>
163
- has_newline = result.include?("\\n")
164
- <% end %>
165
-
166
- <% if in_tag && !nuke_inner_whitespace %>
167
- <% unless preserve_tag %> if !has_newline <% end %>
168
- @real_tabs -= 1
169
- <% if interpolated %> @tabulation = old_tabulation <% end %>
170
- return result
171
- <% unless preserve_tag %> end <% end %>
172
- <% end %>
173
-
174
- <% if !(in_tag && preserve_tag && !nuke_inner_whitespace) %>
175
- # Precompiled tabulation may be wrong
176
- <% if !interpolated && !in_tag %>
177
- result = tabs + result if @tabulation > 0
178
- <% end %>
179
-
180
- if has_newline
181
- result = result.gsub "\\n", "\\n" + tabs(tabulation)
182
-
183
- # Add tabulation if it wasn't precompiled
184
- <% if in_tag && !nuke_inner_whitespace %> result = tabs(tabulation) + result <% end %>
185
- end
186
-
187
- fix_textareas!(result) if toplevel? && result.include?('<textarea')
188
-
189
- <% if in_tag && !nuke_inner_whitespace %>
190
- result = "\\n\#{result}\\n\#{tabs(tabulation-1)}"
191
- @real_tabs -= 1
192
- <% end %>
193
- <% if interpolated %> @tabulation = old_tabulation <% end %>
194
- result
195
- <% end %>
196
- <% end %>
197
- RUBY
198
-
199
133
  def attributes(class_id, obj_ref, *attributes_hashes)
200
134
  attributes = class_id
201
135
  attributes_hashes.each do |old|
202
- self.class.merge_attrs(attributes, Hash[old.map {|k, v| [k.to_s, v]}])
136
+ result = {}
137
+ old.each { |k, v| result[k.to_s] = v }
138
+ AttributeBuilder.merge_attributes!(attributes, result)
203
139
  end
204
- self.class.merge_attrs(attributes, parse_object_ref(obj_ref)) if obj_ref
205
- Compiler.build_attributes(
140
+ AttributeBuilder.merge_attributes!(attributes, parse_object_ref(obj_ref)) if obj_ref
141
+ AttributeBuilder.build_attributes(
206
142
  html?, @options[:attr_wrapper], @options[:escape_attrs], @options[:hyphenate_data_attrs], attributes)
207
143
  end
208
144
 
@@ -217,50 +153,6 @@ RUBY
217
153
  buffer << buffer.slice!(capture_position..-1).rstrip
218
154
  end
219
155
 
220
- # Merges two attribute hashes.
221
- # This is the same as `to.merge!(from)`,
222
- # except that it merges id, class, and data attributes.
223
- #
224
- # ids are concatenated with `"_"`,
225
- # and classes are concatenated with `" "`.
226
- # data hashes are simply merged.
227
- #
228
- # Destructively modifies both `to` and `from`.
229
- #
230
- # @param to [{String => String}] The attribute hash to merge into
231
- # @param from [{String => #to_s}] The attribute hash to merge from
232
- # @return [{String => String}] `to`, after being merged
233
- def self.merge_attrs(to, from)
234
- from['id'] = Compiler.filter_and_join(from['id'], '_') if from['id']
235
- if to['id'] && from['id']
236
- to['id'] << '_' << from.delete('id').to_s
237
- elsif to['id'] || from['id']
238
- from['id'] ||= to['id']
239
- end
240
-
241
- from['class'] = Compiler.filter_and_join(from['class'], ' ') if from['class']
242
- if to['class'] && from['class']
243
- # Make sure we don't duplicate class names
244
- from['class'] = (from['class'].to_s.split(' ') | to['class'].split(' ')).sort.join(' ')
245
- elsif to['class'] || from['class']
246
- from['class'] ||= to['class']
247
- end
248
-
249
- from_data = from.delete('data') || {}
250
- to_data = to.delete('data') || {}
251
-
252
- # forces to_data & from_data into a hash
253
- from_data = { nil => from_data } unless from_data.is_a?(Hash)
254
- to_data = { nil => to_data } unless to_data.is_a?(Hash)
255
-
256
- merged_data = to_data.merge(from_data)
257
-
258
- to['data'] = merged_data unless merged_data.empty?
259
- to.merge!(from)
260
- end
261
-
262
- private
263
-
264
156
  # Works like #{find_and_preserve}, but allows the first newline after a
265
157
  # preserved opening tag to remain unencoded, and then outdents the content.
266
158
  # This change was motivated primarily by the change in Rails 3.2.3 to emit
@@ -270,27 +162,26 @@ RUBY
270
162
  # @since Haml 4.0.1
271
163
  # @private
272
164
  def fix_textareas!(input)
273
- pattern = /([ ]*)<(textarea)([^>]*)>(\n|&#x000A;)(.*?)(<\/\2>)/im
165
+ return input unless input.include?('<textarea'.freeze)
166
+
167
+ pattern = /<(textarea)([^>]*)>(\n|&#x000A;)(.*?)<\/textarea>/im
274
168
  input.gsub!(pattern) do |s|
275
169
  match = pattern.match(s)
276
- content = match[5]
277
- if match[4] == '&#x000A;'
170
+ content = match[4]
171
+ if match[3] == '&#x000A;'
278
172
  content.sub!(/\A /, '&#x0020;')
279
173
  else
280
174
  content.sub!(/\A[ ]*/, '')
281
175
  end
282
- "#{match[1]}<#{match[2]}#{match[3]}>\n#{content}</#{match[2]}>"
176
+ "<#{match[1]}#{match[2]}>\n#{content}</#{match[1]}>"
283
177
  end
178
+ input
284
179
  end
285
180
 
286
- if RUBY_VERSION < "1.9"
287
- def new_encoded_string
288
- ""
289
- end
290
- else
291
- def new_encoded_string
292
- "".encode(Encoding.find(options[:encoding]))
293
- end
181
+ private
182
+
183
+ def new_encoded_string
184
+ "".encode(options[:encoding])
294
185
  end
295
186
 
296
187
  @@tab_cache = {}
@@ -328,18 +219,20 @@ RUBY
328
219
  id = "#{ prefix }_#{ id }"
329
220
  end
330
221
 
331
- {'id' => id, 'class' => class_name}
222
+ { 'id'.freeze => id, 'class'.freeze => class_name }
332
223
  end
333
224
 
334
225
  # Changes a word from camel case to underscores.
335
226
  # Based on the method of the same name in Rails' Inflector,
336
227
  # but copied here so it'll run properly without Rails.
337
228
  def underscore(camel_cased_word)
338
- camel_cased_word.to_s.gsub(/::/, '_').
339
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
340
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
341
- tr("-", "_").
342
- downcase
229
+ word = camel_cased_word.to_s.dup
230
+ word.gsub!(/::/, '_')
231
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
232
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
233
+ word.tr!('-', '_')
234
+ word.downcase!
235
+ word
343
236
  end
344
237
  end
345
238
  end
@@ -1,4 +1,8 @@
1
- require 'cgi'
1
+ # frozen_string_literal: true
2
+
3
+ require 'haml/attribute_builder'
4
+ require 'haml/attribute_compiler'
5
+ require 'haml/temple_line_counter'
2
6
 
3
7
  module Haml
4
8
  class Compiler
@@ -7,15 +11,21 @@ module Haml
7
11
  attr_accessor :options
8
12
 
9
13
  def initialize(options)
10
- @options = options
11
- @output_tabs = 0
14
+ @options = Options.wrap(options)
12
15
  @to_merge = []
13
- @precompiled = ''
16
+ @temple = [:multi]
17
+ @node = nil
18
+ @filters = Filters.defined.merge(options[:filters])
19
+ @attribute_compiler = AttributeCompiler.new(@options)
20
+ end
21
+
22
+ def call(node)
23
+ compile(node)
24
+ @temple
14
25
  end
15
26
 
16
27
  def compile(node)
17
- parent = instance_variable_defined?('@node') ? @node : nil
18
- @node = node
28
+ parent, @node = @node, node
19
29
  if node.children.empty?
20
30
  send(:"compile_#{node.type}")
21
31
  else
@@ -25,79 +35,16 @@ module Haml
25
35
  @node = parent
26
36
  end
27
37
 
28
- if RUBY_VERSION < "1.9"
29
- # The source code that is evaluated to produce the Haml document.
30
- #
31
- # In Ruby 1.9, this is automatically converted to the correct encoding
32
- # (see {file:REFERENCE.md#encodings the `:encoding` option}).
33
- #
34
- # @return [String]
35
- def precompiled
36
- @precompiled
37
- end
38
- else
39
- def precompiled
40
- encoding = Encoding.find(@options[:encoding])
41
- return @precompiled.force_encoding(encoding) if encoding == Encoding::BINARY
42
- return @precompiled.encode(encoding)
43
- end
44
- end
45
-
46
- def precompiled_with_return_value
47
- precompiled + ";" + precompiled_method_return_value
48
- end
49
-
50
- # Returns the precompiled string with the preamble and postamble.
51
- #
52
- # Initializes to ActionView::OutputBuffer when available; this is necessary
53
- # to avoid ordering issues with partial layouts in Rails. If not available,
54
- # initializes to nil.
55
- def precompiled_with_ambles(local_names)
56
- preamble = <<END.gsub("\n", ";")
57
- begin
58
- extend Haml::Helpers
59
- _hamlout = @haml_buffer = Haml::Buffer.new(haml_buffer, #{options.for_buffer.inspect})
60
- _erbout = _hamlout.buffer
61
- @output_buffer = output_buffer ||= ActionView::OutputBuffer.new rescue nil
62
- END
63
- postamble = <<END.gsub("\n", ";")
64
- #{precompiled_method_return_value}
65
- ensure
66
- @haml_buffer = @haml_buffer.upper if @haml_buffer
67
- end
68
- END
69
- preamble + locals_code(local_names) + precompiled + postamble
70
- end
71
-
72
38
  private
73
39
 
74
- # Returns the string used as the return value of the precompiled method.
75
- # This method exists so it can be monkeypatched to return modified values.
76
- def precompiled_method_return_value
77
- "_erbout"
78
- end
79
-
80
- def locals_code(names)
81
- names = names.keys if Hash == names
82
-
83
- names.map do |name|
84
- # Can't use || because someone might explicitly pass in false with a symbol
85
- sym_local = "_haml_locals[#{inspect_obj(name.to_sym)}]"
86
- str_local = "_haml_locals[#{inspect_obj(name.to_s)}]"
87
- "#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local}"
88
- end.join(';') + ';'
89
- end
90
-
91
40
  def compile_root
92
- @dont_indent_next_line = @dont_tab_up_next_text = false
93
41
  @output_line = 1
94
- @indentation = nil
95
- yield
42
+ yield if block_given?
96
43
  flush_merged_text
97
44
  end
98
45
 
99
46
  def compile_plain
100
- push_text @node.value[:text]
47
+ push_text("#{@node.value[:text]}\n")
101
48
  end
102
49
 
103
50
  def nuke_inner_whitespace?(node)
@@ -119,15 +66,11 @@ END
119
66
  end
120
67
 
121
68
  def compile_silent_script
122
- return if @options[:suppress_eval]
69
+ return if @options.suppress_eval
123
70
  push_silent(@node.value[:text])
124
71
  keyword = @node.value[:keyword]
125
72
 
126
73
  if block_given?
127
- # Store these values because for conditional statements,
128
- # we want to restore them for each branch
129
- @node.value[:dont_indent_next_line] = @dont_indent_next_line
130
- @node.value[:dont_tab_up_next_text] = @dont_tab_up_next_text
131
74
  yield
132
75
  push_silent("end", :can_suppress) unless @node.value[:dont_push_end]
133
76
  elsif keyword == "end"
@@ -137,10 +80,6 @@ END
137
80
  @node.parent.value[:dont_push_end] = true
138
81
  end
139
82
  # Don't restore dont_* for end because it isn't a conditional branch.
140
- elsif Parser::MID_BLOCK_KEYWORDS.include?(keyword)
141
- # Restore dont_* for this conditional branch
142
- @dont_indent_next_line = @node.parent.value[:dont_indent_next_line]
143
- @dont_tab_up_next_text = @node.parent.value[:dont_tab_up_next_text]
144
83
  end
145
84
  end
146
85
 
@@ -152,117 +91,84 @@ END
152
91
  # Get rid of whitespace outside of the tag if we need to
153
92
  rstrip_buffer! if t[:nuke_outer_whitespace]
154
93
 
155
- dont_indent_next_line =
156
- (t[:nuke_outer_whitespace] && !block_given?) ||
157
- (t[:nuke_inner_whitespace] && block_given?)
158
-
159
- if @options[:suppress_eval]
160
- object_ref = "nil"
94
+ if @options.suppress_eval
95
+ object_ref = :nil
161
96
  parse = false
162
97
  value = t[:parse] ? nil : t[:value]
163
- attributes_hashes = {}
164
- preserve_script = false
98
+ dynamic_attributes = Haml::Parser::DynamicAttributes.new
165
99
  else
166
100
  object_ref = t[:object_ref]
167
101
  parse = t[:parse]
168
102
  value = t[:value]
169
- attributes_hashes = t[:attributes_hashes]
170
- preserve_script = t[:preserve_script]
103
+ dynamic_attributes = t[:dynamic_attributes]
171
104
  end
172
105
 
173
- # Check if we can render the tag directly to text and not process it in the buffer
174
- if object_ref == "nil" && attributes_hashes.empty? && !preserve_script
175
- tag_closed = !block_given? && !t[:self_closing] && !parse
176
-
177
- open_tag = prerender_tag(t[:name], t[:self_closing], t[:attributes])
178
- if tag_closed
179
- open_tag << "#{value}</#{t[:name]}>"
180
- open_tag << "\n" unless t[:nuke_outer_whitespace]
181
- elsif !(parse || t[:nuke_inner_whitespace] ||
182
- (t[:self_closing] && t[:nuke_outer_whitespace]))
183
- open_tag << "\n"
184
- end
185
-
186
- push_merged_text(open_tag,
187
- tag_closed || t[:self_closing] || t[:nuke_inner_whitespace] ? 0 : 1,
188
- !t[:nuke_outer_whitespace])
106
+ if @options[:trace]
107
+ t[:attributes].merge!({"data-trace" => @options.filename.split('/views').last << ":" << @node.line.to_s})
108
+ end
189
109
 
190
- @dont_indent_next_line = dont_indent_next_line
191
- return if tag_closed
192
- else
193
- if attributes_hashes.empty?
194
- attributes_hashes = ''
195
- elsif attributes_hashes.size == 1
196
- attributes_hashes = ", #{attributes_hashes.first}"
110
+ push_text("<#{t[:name]}")
111
+ push_temple(@attribute_compiler.compile(t[:attributes], object_ref, dynamic_attributes))
112
+ push_text(
113
+ if t[:self_closing] && @options.xhtml?
114
+ " />#{"\n" unless t[:nuke_outer_whitespace]}"
197
115
  else
198
- attributes_hashes = ", (#{attributes_hashes.join(").merge(")})"
199
- end
200
-
201
- push_merged_text "<#{t[:name]}", 0, !t[:nuke_outer_whitespace]
202
- push_generated_script(
203
- "_hamlout.attributes(#{inspect_obj(t[:attributes])}, #{object_ref}#{attributes_hashes})")
204
- concat_merged_text(
205
- if t[:self_closing] && @options.xhtml?
206
- " />" + (t[:nuke_outer_whitespace] ? "" : "\n")
207
- else
208
- ">" + ((if t[:self_closing] && @options.html?
209
- t[:nuke_outer_whitespace]
210
- else
211
- !block_given? || t[:preserve_tag] || t[:nuke_inner_whitespace]
212
- end) ? "" : "\n")
213
- end)
214
-
215
- if value && !parse
216
- concat_merged_text("#{value}</#{t[:name]}>#{t[:nuke_outer_whitespace] ? "" : "\n"}")
217
- elsif !t[:nuke_inner_whitespace] && !t[:self_closing]
218
- @to_merge << [:text, '', 1]
116
+ ">#{"\n" unless (t[:self_closing] && @options.html?) ? t[:nuke_outer_whitespace] : (!block_given? || t[:preserve_tag] || t[:nuke_inner_whitespace])}"
219
117
  end
118
+ )
220
119
 
221
- @dont_indent_next_line = dont_indent_next_line
120
+ if value && !parse
121
+ push_text("#{value}</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
222
122
  end
223
123
 
224
124
  return if t[:self_closing]
225
125
 
226
126
  if value.nil?
227
- @output_tabs += 1 unless t[:nuke_inner_whitespace]
228
127
  yield if block_given?
229
- @output_tabs -= 1 unless t[:nuke_inner_whitespace]
230
128
  rstrip_buffer! if t[:nuke_inner_whitespace]
231
- push_merged_text("</#{t[:name]}>" + (t[:nuke_outer_whitespace] ? "" : "\n"),
232
- t[:nuke_inner_whitespace] ? 0 : -1, !t[:nuke_inner_whitespace])
233
- @dont_indent_next_line = t[:nuke_outer_whitespace]
129
+ push_text("</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
234
130
  return
235
131
  end
236
132
 
237
133
  if parse
238
134
  push_script(value, t.merge(:in_tag => true))
239
- concat_merged_text("</#{t[:name]}>" + (t[:nuke_outer_whitespace] ? "" : "\n"))
135
+ push_text("</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
240
136
  end
241
137
  end
242
138
 
243
139
  def compile_comment
244
- open = "<!--#{@node.value[:conditional]}"
140
+ condition = "#{@node.value[:conditional]}>" if @node.value[:conditional]
141
+ revealed = @node.value[:revealed]
142
+
143
+ open = "<!--#{condition}#{'<!-->' if revealed}"
144
+
145
+ close = "#{'<!--' if revealed}#{'<![endif]' if condition}-->"
245
146
 
246
- # Render it statically if possible
247
147
  unless block_given?
248
- push_text("#{open} #{@node.value[:text]} #{@node.value[:conditional] ? "<![endif]-->" : "-->"}")
148
+ push_text("#{open} ")
149
+
150
+ if @node.value[:parse]
151
+ push_script(@node.value[:text], :in_tag => true, :nuke_inner_whitespace => true)
152
+ else
153
+ push_text(@node.value[:text])
154
+ end
155
+
156
+ push_text(" #{close}\n")
249
157
  return
250
158
  end
251
159
 
252
- push_text(open, 1)
253
- @output_tabs += 1
160
+ push_text("#{open}\n")
254
161
  yield if block_given?
255
- @output_tabs -= 1
256
- push_text(@node.value[:conditional] ? "<![endif]-->" : "-->", -1)
162
+ push_text("#{close}\n")
257
163
  end
258
164
 
259
165
  def compile_doctype
260
166
  doctype = text_for_doctype
261
- push_text doctype if doctype
167
+ push_text("#{doctype}\n") if doctype
262
168
  end
263
169
 
264
170
  def compile_filter
265
- unless filter = Filters.defined[@node.value[:name]]
171
+ unless (filter = @filters[@node.value[:name]])
266
172
  name = @node.value[:name]
267
173
  if ["maruku", "textile"].include?(name)
268
174
  raise Error.new(Error.message(:install_haml_contrib, name), @node.line - 1)
@@ -276,36 +182,34 @@ END
276
182
  def text_for_doctype
277
183
  if @node.value[:type] == "xml"
278
184
  return nil if @options.html?
279
- wrapper = @options[:attr_wrapper]
185
+ wrapper = @options.attr_wrapper
280
186
  return "<?xml version=#{wrapper}1.0#{wrapper} encoding=#{wrapper}#{@node.value[:encoding] || "utf-8"}#{wrapper} ?>"
281
187
  end
282
188
 
283
189
  if @options.html5?
284
190
  '<!DOCTYPE html>'
285
- else
286
- if @options.xhtml?
287
- if @node.value[:version] == "1.1"
288
- '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
289
- elsif @node.value[:version] == "5"
290
- '<!DOCTYPE html>'
291
- else
292
- case @node.value[:type]
293
- when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
294
- when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
295
- when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
296
- when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
297
- when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
298
- else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
299
- end
300
- end
301
-
302
- elsif @options.html4?
191
+ elsif @options.xhtml?
192
+ if @node.value[:version] == "1.1"
193
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
194
+ elsif @node.value[:version] == "5"
195
+ '<!DOCTYPE html>'
196
+ else
303
197
  case @node.value[:type]
304
- when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
305
- when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
306
- else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
198
+ when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
199
+ when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
200
+ when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
201
+ when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
202
+ when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
203
+ else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
307
204
  end
308
205
  end
206
+
207
+ elsif @options.html4?
208
+ case @node.value[:type]
209
+ when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
210
+ when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
211
+ else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
212
+ end
309
213
  end
310
214
  end
311
215
 
@@ -315,83 +219,52 @@ END
315
219
  flush_merged_text
316
220
  return if can_suppress && @options.suppress_eval?
317
221
  newline = (text == "end") ? ";" : "\n"
318
- @precompiled << "#{resolve_newlines}#{text}#{newline}"
319
- @output_line += (text + newline).count("\n")
320
- end
321
-
322
- # Adds `text` to `@buffer` with appropriate tabulation
323
- # without parsing it.
324
- def push_merged_text(text, tab_change = 0, indent = true)
325
- text = !indent || @dont_indent_next_line || @options[:ugly] ? text : "#{' ' * @output_tabs}#{text}"
326
- @to_merge << [:text, text, tab_change]
327
- @dont_indent_next_line = false
222
+ @temple << [:code, "#{resolve_newlines}#{text}#{newline}"]
223
+ @output_line = @output_line + text.count("\n") + newline.count("\n")
328
224
  end
329
225
 
330
- # Concatenate `text` to `@buffer` without tabulation.
331
- def concat_merged_text(text)
332
- @to_merge << [:text, text, 0]
226
+ # Adds `text` to `@buffer`.
227
+ def push_text(text)
228
+ @to_merge << [:text, text]
333
229
  end
334
230
 
335
- def push_text(text, tab_change = 0)
336
- push_merged_text("#{text}\n", tab_change)
231
+ def push_temple(temple)
232
+ flush_merged_text
233
+ @temple.concat([[:newline]] * resolve_newlines.count("\n"))
234
+ @temple << temple
235
+ @output_line += TempleLineCounter.count_lines(temple)
337
236
  end
338
237
 
339
238
  def flush_merged_text
340
239
  return if @to_merge.empty?
341
240
 
342
- str = ""
343
- mtabs = 0
344
- @to_merge.each do |type, val, tabs|
241
+ @to_merge.each do |type, val|
345
242
  case type
346
243
  when :text
347
- str << inspect_obj(val)[1...-1]
348
- mtabs += tabs
244
+ @temple << [:static, val]
349
245
  when :script
350
- if mtabs != 0 && !@options[:ugly]
351
- val = "_hamlout.adjust_tabs(#{mtabs}); " + val
352
- end
353
- str << "\#{#{val}}"
354
- mtabs = 0
246
+ @temple << [:dynamic, val]
355
247
  else
356
248
  raise SyntaxError.new("[HAML BUG] Undefined entry in Haml::Compiler@to_merge.")
357
249
  end
358
250
  end
359
251
 
360
- unless str.empty?
361
- @precompiled <<
362
- if @options[:ugly]
363
- "_hamlout.buffer << \"#{str}\";"
364
- else
365
- "_hamlout.push_text(\"#{str}\", #{mtabs}, #{@dont_tab_up_next_text.inspect});"
366
- end
367
- end
368
252
  @to_merge = []
369
- @dont_tab_up_next_text = false
370
253
  end
371
254
 
372
255
  # Causes `text` to be evaluated in the context of
373
256
  # the scope object and the result to be added to `@buffer`.
374
257
  #
375
- # If `opts[:preserve_script]` is true, Haml::Helpers#find_and_flatten is run on
258
+ # If `opts[:preserve_script]` is true, Haml::Helpers#find_and_preserve is run on
376
259
  # the result before it is added to `@buffer`
377
260
  def push_script(text, opts = {})
378
261
  return if @options.suppress_eval?
379
262
 
380
- args = %w[preserve_script in_tag preserve_tag escape_html nuke_inner_whitespace]
381
- args.map! {|name| opts[name.to_sym]}
382
- args << !block_given? << @options[:ugly]
383
-
384
- no_format = @options[:ugly] &&
385
- !(opts[:preserve_script] || opts[:preserve_tag] || opts[:escape_html])
386
- output_expr = "(#{text}\n)"
387
- static_method = "_hamlout.#{static_method_name(:format_script, *args)}"
388
-
389
- # Prerender tabulation unless we're in a tag
390
- push_merged_text '' unless opts[:in_tag]
263
+ no_format = !(opts[:preserve_script] || opts[:preserve_tag] || opts[:escape_html])
391
264
 
392
265
  unless block_given?
393
- push_generated_script(no_format ? "#{text}\n" : "#{static_method}(#{output_expr});")
394
- concat_merged_text("\n") unless opts[:in_tag] || opts[:nuke_inner_whitespace]
266
+ push_generated_script(no_format ? "(#{text}\n).to_s" : build_script_formatter("(#{text}\n)", opts))
267
+ push_text("\n") unless opts[:in_tag] || opts[:nuke_inner_whitespace]
395
268
  return
396
269
  end
397
270
 
@@ -399,117 +272,35 @@ END
399
272
  push_silent "haml_temp = #{text}"
400
273
  yield
401
274
  push_silent('end', :can_suppress) unless @node.value[:dont_push_end]
402
- @precompiled << "_hamlout.buffer << #{no_format ? "haml_temp.to_s;" : "#{static_method}(haml_temp);"}"
403
- concat_merged_text("\n") unless opts[:in_tag] || opts[:nuke_inner_whitespace] || @options[:ugly]
275
+ @temple << [:dynamic, no_format ? 'haml_temp.to_s;' : build_script_formatter('haml_temp', opts)]
404
276
  end
405
277
 
406
- def push_generated_script(text)
407
- @to_merge << [:script, resolve_newlines + text]
408
- @output_line += text.count("\n")
409
- end
410
-
411
- # This is a class method so it can be accessed from Buffer.
412
- def self.build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {})
413
- # @TODO this is an absolutely ridiculous amount of arguments. At least
414
- # some of this needs to be moved into an instance method.
415
- quote_escape = attr_wrapper == '"' ? "&#x0022;" : "&#x0027;"
416
- other_quote_char = attr_wrapper == '"' ? "'" : '"'
417
- join_char = hyphenate_data_attrs ? '-' : '_'
418
-
419
- attributes.each do |key, value|
420
- if value.is_a?(Hash)
421
- data_attributes = attributes.delete(key)
422
- data_attributes = flatten_data_attributes(data_attributes, '', join_char)
423
- data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key)
424
- attributes = data_attributes.merge(attributes)
425
- end
278
+ def build_script_formatter(text, opts)
279
+ text = "(#{text}).to_s"
280
+ if opts[:escape_html]
281
+ text = "::Haml::Helpers.html_escape(#{text})"
426
282
  end
427
-
428
- result = attributes.collect do |attr, value|
429
- next if value.nil?
430
-
431
- value = filter_and_join(value, ' ') if attr == 'class'
432
- value = filter_and_join(value, '_') if attr == 'id'
433
-
434
- if value == true
435
- next " #{attr}" if is_html
436
- next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
437
- elsif value == false
438
- next
439
- end
440
-
441
- escaped =
442
- if escape_attrs == :once
443
- Haml::Helpers.escape_once(value.to_s)
444
- elsif escape_attrs
445
- Haml::Helpers.html_escape(value.to_s)
446
- else
447
- value.to_s
448
- end
449
- value = Haml::Helpers.preserve(escaped)
450
- if escape_attrs
451
- # We want to decide whether or not to escape quotes
452
- value.gsub!(/&quot;|&#x0022;/, '"')
453
- this_attr_wrapper = attr_wrapper
454
- if value.include? attr_wrapper
455
- if value.include? other_quote_char
456
- value.gsub!(attr_wrapper, quote_escape)
457
- else
458
- this_attr_wrapper = other_quote_char
459
- end
460
- end
461
- else
462
- this_attr_wrapper = attr_wrapper
463
- end
464
- " #{attr}=#{this_attr_wrapper}#{value}#{this_attr_wrapper}"
283
+ if opts[:nuke_inner_whitespace]
284
+ text = "(#{text}).strip"
465
285
  end
466
- result.compact.sort.join
467
- end
468
-
469
- def self.filter_and_join(value, separator)
470
- return "" if value == ""
471
- value = [value] unless value.is_a?(Array)
472
- value = value.flatten.collect {|item| item ? item.to_s : nil}.compact.join(separator)
473
- return !value.empty? && value
474
- end
475
-
476
- def self.build_data_keys(data_hash, hyphenate, attr_name="data")
477
- Hash[data_hash.map do |name, value|
478
- if name == nil
479
- [attr_name, value]
480
- elsif hyphenate
481
- ["#{attr_name}-#{name.to_s.gsub(/_/, '-')}", value]
482
- else
483
- ["#{attr_name}-#{name}", value]
484
- end
485
- end]
486
- end
487
-
488
- def self.flatten_data_attributes(data, key, join_char, seen = [])
489
- return {key => data} unless data.is_a?(Hash)
490
-
491
- return {key => nil} if seen.include? data.object_id
492
- seen << data.object_id
493
-
494
- data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, array|
495
- k, v = array
496
- joined = key == '' ? k : [key, k].join(join_char)
497
- hash.merge! flatten_data_attributes(v, joined, join_char, seen)
286
+ if opts[:preserve_tag]
287
+ text = "_hamlout.fix_textareas!(::Haml::Helpers.preserve(#{text}))"
288
+ elsif opts[:preserve_script]
289
+ text = "_hamlout.fix_textareas!(::Haml::Helpers.find_and_preserve(#{text}, _hamlout.options[:preserve]))"
498
290
  end
291
+ "#{text};"
499
292
  end
500
293
 
501
- def prerender_tag(name, self_close, attributes)
502
- # TODO: consider just passing in the damn options here
503
- attributes_string = Compiler.build_attributes(
504
- @options.html?, @options[:attr_wrapper], @options[:escape_attrs], @options[:hyphenate_data_attrs], attributes)
505
- "<#{name}#{attributes_string}#{self_close && @options.xhtml? ? ' /' : ''}>"
294
+ def push_generated_script(text)
295
+ @to_merge << [:script, resolve_newlines + text]
296
+ @output_line += text.count("\n")
506
297
  end
507
298
 
508
299
  def resolve_newlines
509
300
  diff = @node.line - @output_line
510
301
  return "" if diff <= 0
511
302
  @output_line = @node.line
512
- "\n" * [diff, 0].max
303
+ "\n" * diff
513
304
  end
514
305
 
515
306
  # Get rid of and whitespace at the end of the buffer
@@ -518,13 +309,12 @@ END
518
309
  last = @to_merge[index]
519
310
  if last.nil?
520
311
  push_silent("_hamlout.rstrip!", false)
521
- @dont_tab_up_next_text = true
522
312
  return
523
313
  end
524
314
 
525
315
  case last.first
526
316
  when :text
527
- last[1].rstrip!
317
+ last[1] = last[1].rstrip
528
318
  if last[1].empty?
529
319
  @to_merge.slice! index
530
320
  rstrip_buffer! index