From ab1dffda9ec2dbc37b4929b72aef3eefd0feb470 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 13 Jul 2007 01:09:10 +0000 Subject: [PATCH] Merged text now possible. To make blocks of multiline text work, push_silent now takes a tag when it is being used as a one liner, allowing it to close itself so push_text can stop worrying about potential one liner pending situation. Same goes for opening non pre-rendered tags which can now close their own content after determining one liner status. Extra complexity there pays off in multi line plain text now being easy to dump. Sequential push_texts are now merged into single multi line calls, next step is merging pre-rendered open tags and close tags. git-svn-id: svn://hamptoncatlin.com/haml/trunk@553 7063305b-7217-0410-af8c-cdc13e5119b9 --- lib/haml/buffer.rb | 49 +++++++++++++++++++++++++++++------- lib/haml/engine.rb | 54 ++++++++++++++++++++++++++++++---------- test/haml/engine_test.rb | 2 +- 3 files changed, 82 insertions(+), 23 deletions(-) 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' }))