diff --git a/lib/haml/buffer.rb b/lib/haml/buffer.rb index 518cd85c..ea6723f5 100644 --- a/lib/haml/buffer.rb +++ b/lib/haml/buffer.rb @@ -42,7 +42,7 @@ module Haml # Renders +text+ with the proper tabulation. This also deals with # making a possible one-line tag one line or not. - def push_text(text, tabulation) + def push_text(text, tab_change = 0) if @one_liner_pending && Buffer.one_liner?(text) @buffer << text else @@ -50,13 +50,18 @@ module Haml @buffer << "\n" @one_liner_pending = false end - @buffer << "#{tabs(tabulation)}#{text}\n" + if(@tabulation > 0) + text.gsub!(/^/m, ' ') + end + + @buffer << "#{text}" end + @real_tabs += tab_change end # Properly formats the output of a script that was run in the # instance_eval. - def push_script(result, tabulation, flattened) + def push_script(result, tabulation, flattened, close_tag = nil) if flattened result = Haml::Helpers.find_and_preserve(result) end @@ -67,8 +72,25 @@ module Haml result = result[0...-1] end - result = result.gsub("\n", "\n#{tabs(tabulation)}") - push_text result, tabulation + if @one_liner_pending && Buffer.one_liner?(result) + @buffer << result + @buffer << "\n" + @one_liner_pending = false + @real_tabs -= 1 + else + if @one_liner_pending + @buffer << "\n" + tabulation += 1 + end + + result = result.gsub("\n", "\n#{tabs(tabulation)}") + @buffer << "#{tabs(tabulation)}#{result}\n" + + if @one_liner_pending + @one_liner_pending = false + @buffer << "#{tabs(tabulation-1)}\n" + end + end end nil end @@ -81,7 +103,7 @@ module Haml # Takes the various information about the opening tag for an # element, formats it, and adds it to the buffer. - def open_tag(name, tabulation, atomic, try_one_line, class_id, obj_ref, attributes_hash) + def open_tag(name, tabulation, atomic, try_one_line, class_id, obj_ref, content, attributes_hash) attributes = class_id if attributes_hash attributes_hash.keys.each { |key| attributes_hash[key.to_s] = attributes_hash.delete(key) } @@ -99,7 +121,16 @@ module Haml str = ">\n" end @buffer << "#{tabs(tabulation)}<#{name}#{build_attributes(attributes)}#{str}" - @real_tabs += 1 + if content + if Buffer.one_liner?(content) + @buffer << "#{content}\n" + else + @buffer << "\n#{tabs(@real_tabs+1)}#{content}\n#{tabs(@real_tabs)}\n" + end + @one_liner_pending = false + else + @real_tabs += 1 + end end def self.merge_attrs(to, from) @@ -117,11 +148,12 @@ module Haml # Creates a closing tag with the given name. def close_tag(name, tabulation) + @real_tabs = tabulation if @one_liner_pending @buffer << "\n" @one_liner_pending = false else - push_text("", tabulation) + push_text("#{' ' * tabulation}\n") end end @@ -159,7 +191,6 @@ module Haml @@tab_cache = {} # Gets count tabs. Mostly for internal use. def tabs(count) - @real_tabs = count tabs = count + @tabulation ' ' * tabs @@tab_cache[tabs] ||= ' ' * tabs diff --git a/lib/haml/engine.rb b/lib/haml/engine.rb index f2cf3a82..70867ae5 100644 --- a/lib/haml/engine.rb +++ b/lib/haml/engine.rb @@ -467,6 +467,8 @@ END # Evaluates text in the context of @scope_object, but # does not output the result. def push_silent(text, add_index = false, can_suppress = false) + flush_merged_text + unless (can_suppress && options[:suppress_eval]) if add_index @precompiled << "@haml_lineno = #{@index}\n#{text}\n" @@ -479,9 +481,22 @@ END # Adds text to @buffer with appropriate tabulation # without parsing it. - def push_text(text) - @precompiled << "_hamlout.push_text(#{text.dump}, #{@output_tabs})\n" + def push_text(text, tab_change = 0) + @merged_text ||= '' + @merged_text << "#{' ' * @output_tabs}#{text}\n" + @tab_change ||= 0 + @tab_change += tab_change end + + def flush_merged_text + if @merged_text && !@merged_text.empty? + args = @merged_text.dump + args += ", #{@tab_change}" if @tab_change != 0 + @precompiled << "_hamlout.push_text(#{args})\n" + @merged_text = nil + @tab_change = 0 + end + end # Renders a block of text as plain text. # Also checks for an illegally opened block. @@ -504,10 +519,12 @@ END # # If flattened is true, Haml::Helpers#find_and_flatten is run on # the result before it is added to @buffer - def push_script(text, flattened) + def push_script(text, flattened, close_tag = nil) + flush_merged_text + unless options[:suppress_eval] push_silent("haml_temp = #{text}", true) - out = "haml_temp = _hamlout.push_script(haml_temp, #{@output_tabs}, #{flattened})\n" + out = "haml_temp = _hamlout.push_script(haml_temp, #{@output_tabs}, #{flattened}, #{close_tag.inspect})\n" if @block_opened push_and_tabulate([:loud, out]) else @@ -519,6 +536,8 @@ END # Causes text to be evaluated, and Haml::Helpers#find_and_flatten # to be run on it afterwards. def push_flat_script(text) + flush_merged_text + if text.empty? raise SyntaxError.new("Tag has no content.") else @@ -555,6 +574,8 @@ END # Puts a line in @precompiled that will add the closing tag of # the most recently opened tag. def close_tag(tag) + flush_merged_text + @output_tabs -= 1 @template_tabs -= 1 @precompiled << "_hamlout.close_tag(#{tag.dump}, #{@output_tabs})\n" @@ -571,7 +592,7 @@ END @output_tabs -= 1 @template_tabs -= 1 close_tag = has_conditional ? "" : "-->" - push_text(close_tag) + push_text(close_tag, -1) end # Closes a loud Ruby block. @@ -699,6 +720,8 @@ END # Parses a line that will render as an XHTML tag, and adds the code that will # render that tag to @precompiled. def render_tag(line) + flush_merged_text + matched = false line.scan(TAG_REGEX) do |tag_name, attributes, attributes_hash, object_ref, action, value| matched = true @@ -718,6 +741,11 @@ END flattened = (action == '~') value_exists = !value.empty? + if value_exists && parse && @options[:suppress_eval] + parse = false + value = '' + end + literal_attributes = parse_literal_hash(attributes_hash) attributes_hash = "{nil}" if attributes_hash.nil? || literal_attributes || @options[:suppress_eval] object_ref = "nil" if object_ref.nil? || @options[:suppress_eval] @@ -761,20 +789,20 @@ END push_silent "_hamlout.open_prerendered_tag(#{open_tag.dump}, #{@output_tabs}, #{parse.inspect}, #{tag_closed.inspect})" return if tag_closed else - push_silent "_hamlout.open_tag(#{tag_name.inspect}, #{@output_tabs}, #{atomic.inspect}, #{value_exists.inspect}, #{attributes.inspect}, #{object_ref}, #{attributes_hash[1...-1]})", true + content = !value_exists || parse ? 'nil' : value.dump + push_silent "_hamlout.open_tag(#{tag_name.inspect}, #{@output_tabs}, #{atomic.inspect}, #{value_exists.inspect}, #{attributes.inspect}, #{object_ref}, #{content}, #{attributes_hash[1...-1]})", true end unless atomic - push_and_tabulate([:element, tag_name]) - @output_tabs += 1 + unless value_exists + push_and_tabulate([:element, tag_name]) + @output_tabs += 1 + end if value_exists if parse - push_script(value, flattened) - else - push_text(value) + push_script(value, flattened, tag_name) end - close elsif flattened raise SyntaxError.new("Tag has no content.") end @@ -807,7 +835,7 @@ END close_tag = conditional ? "" : "-->" push_text("#{text_out}#{content} #{close_tag}") else - push_text(text_out) + push_text(text_out, 1) @output_tabs += 1 push_and_tabulate([:comment, !conditional.nil?]) if !content.empty? diff --git a/test/haml/engine_test.rb b/test/haml/engine_test.rb index 03dcad01..b417c663 100644 --- a/test/haml/engine_test.rb +++ b/test/haml/engine_test.rb @@ -101,7 +101,7 @@ class EngineTest < Test::Unit::TestCase # Make sure the method called will return junk unless recompiled method_name = Haml::Engine.send(:class_variable_get, '@@method_names')[template] - Haml::Engine::CompiledTemplates.module_eval "def #{method_name}(stuff); @haml_stack[-1].push_text 'NOT RECOMPILED', 0; end" + Haml::Engine::CompiledTemplates.module_eval "def #{method_name}(stuff); @haml_stack[-1].push_text(\"NOT RECOMPILED\n\"); end" assert_equal("NOT RECOMPILED\n", render(template, :locals => { :text => "first time" })) assert_equal("

first time

\n", render(template, :locals => { :text => "first time", :foo => 'bar' }))