From 8d7b7d407cbca108f0e536793dfabf3b918485e7 Mon Sep 17 00:00:00 2001 From: hcatlin Date: Mon, 2 Oct 2006 23:49:53 +0000 Subject: [PATCH] Closing patch #34 and converting over to a buffered engine. git-svn-id: svn://hamptoncatlin.com/haml/trunk@58 7063305b-7217-0410-af8c-cdc13e5119b9 --- lib/haml/engine.rb | 143 +++++++++++++++++++++++------------- lib/haml/helpers.rb | 4 + test/results/helpful.xhtml | 12 ++- test/results/standard.xhtml | 8 +- test/rhtml/standard.rhtml | 2 +- 5 files changed, 113 insertions(+), 56 deletions(-) diff --git a/lib/haml/engine.rb b/lib/haml/engine.rb index 05975743..c7b7362f 100644 --- a/lib/haml/engine.rb +++ b/lib/haml/engine.rb @@ -17,128 +17,172 @@ module Haml #:nodoc: options.each { |k,v| eval("@#{k} = v") } @template = template #String - @result, @to_close_queue = String.new, [] + @result, @precompiled, @to_close_queue = String.new, String.new, [] + @scope_object = Object.new if @scope_object.nil? end def to_html # Process each line of the template @template.each_with_index do |line, index| count, line = count_soft_tabs(line) - surpress_render, line, count = handle_multiline(count, line) + suppress_render, line, count = handle_multiline(count, line) - if !surpress_render && count && line + if !suppress_render && count && line count, line = process_line(count, line) end end - + + # Make sure an ending multiline gets closed + handle_multiline(0, nil) + # Close all the open tags @to_close_queue.length.times { close_tag } - + + # Compile the @precompiled buffer + compile + # Return the result string @result end def process_line(count, line) if line.strip[0, 3] == '!!!' - @result << %|\n| + push_text %|| else if count <= @to_close_queue.size && @to_close_queue.size > 0 (@to_close_queue.size - count).times { close_tag } end - - case line[0..0] - when '.', '#' - render_div(line) - when '%' - render_tag(line) - when '/' - render_comment(line) - when '=' - add template_eval(line[1, line.length]).to_s - when '~' - add find_and_flatten(template_eval(line[1, line.length])).to_s - else - add line.strip + if line.length > 0 + case line[0].chr + when '.', '#' + render_div(line) + when '%' + render_tag(line) + when '/' + render_comment(line) + when '=' + push_script(line[1, line.length]) + when '~' + push_script(line[1, line.length], true) + else + push_text line.strip + end end end - return count, line end def handle_multiline(count, line) # Multilines are denoting by ending with a `|` (124) - if (line[-1] == MULTILINE_CHAR_VALUE) && @multiline_buffer + if line && (line[-1] == MULTILINE_CHAR_VALUE) && @multiline_buffer # A multiline string is active, and is being continued @multiline_buffer += line[0...-1] - supress_render = true - elsif (line[-1] == MULTILINE_CHAR_VALUE) && (MULTILINE_STARTERS.include? line[0]) + suppress_render = true + elsif line && (line[-1] == MULTILINE_CHAR_VALUE) && (MULTILINE_STARTERS.include? line[0]) # A multiline string has just been activated, start adding the lines @multiline_buffer = line[0...-1] @multiline_count = count - supress_render = true + suppress_render = true elsif @multiline_buffer # A multiline string has just ended, make line into the result process_line(@multiline_count, @multiline_buffer) @multiline_buffer = nil - supress_render = false + suppress_render = false end - - return supress_render, line, count + + return suppress_render, line, count end - def add(line) - return if line.nil? - line.to_s.each_line do |me| - @result << tabs(@to_close_queue.size) << me.chomp << "\n" + def compile + # Set the local variables pointing to the buffer + result = @result + @scope_object.instance_eval do + @haml_stack =|| Array.new + @haml_stack.push(result) + self.class.instance_eval { include Haml::Helpers } + end + + # Evaluate the buffer in the context of the scope object + # This automatically dumps the result into @result + @scope_object.instance_eval @precompiled + + # Get rid of the current buffer + @scope_object.instance_eval do + @haml_stack.pop + end + end + + def push_visible(text) + @precompiled << "@haml_stack[-1] << #{tabs(@to_close_queue.size).dump} << #{text}\n" + end + + def push_silent(text) + @precompiled << "#{text}\n" + end + + def push_text(text) + push_visible("#{text.dump} << \"\\n\"") + end + + def push_script(text, flattened = false) + unless @suppress_eval + push_silent("haml_temp = #{text}") + if flattened + push_silent("haml_temp = find_and_flatten(haml_temp)") + end + push_visible("#{wrap_script("haml_temp", @to_close_queue.size)} << \"\\n\"") end end def build_attributes(attributes = {}) - result = attributes.collect { |a,v| + result = attributes.collect do |a,v| unless v.nil? first_quote_type = v.to_s.scan(/['"]/).first quote_type = (first_quote_type == "'") ? '"' : "'" "#{a.to_s}=#{quote_type}#{v.to_s}#{quote_type}" end - } + end result = result.compact.join(' ') (attributes.empty? ? String.new : String.new(' ')) + result end def open_tag(name, attributes = {}) - add "<#{name.to_s}#{build_attributes(attributes)}>" + push_text "<#{name.to_s}#{build_attributes(attributes)}>" @to_close_queue.push name end def close_tag - add "" + push_text "" end def one_line_tag(name, value, attributes = {}) - add "<#{name.to_s}#{build_attributes(attributes)}>#{value}" + push_text "<#{name.to_s}#{build_attributes(attributes)}>#{value}" end def one_liner?(value) value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty? end - - def print_tag(name, value, attributes = {}) + + def push_tag(name, value, attributes = {}, parse = false, flattened = false) unless value.empty? - if one_liner? value + if !parse && one_liner?(value) one_line_tag(name, value, attributes) else open_tag(name, attributes) - add value + if parse + push_script(value, flattened) + else + push_text(value) + end close_tag end else open_tag(name, attributes) - add value end end # Creates single line tags, i.e. def atomic_tag(name, attributes = {}) - add "<#{name.to_s}#{build_attributes(attributes)} />" + push_text "<#{name.to_s}#{build_attributes(attributes)} />" end def parse_class_and_id(list) @@ -157,7 +201,7 @@ module Haml #:nodoc: def render_tag(line) line.scan(/[%]([-_a-z1-9]+)([-_a-z\.\#]*)(\{.*\})?(\[.*\])?([=\/\~]?)?(.*)?/).each do |tag_name, attributes, attributes_hash, object_ref, action, value| attributes = parse_class_and_id(attributes.to_s) - + #SimplyHelpful style logic with the [@model] helper if object_ref && (object_ref = template_eval(object_ref).first) class_name = object_ref.class.to_s.underscore @@ -174,11 +218,10 @@ module Haml #:nodoc: when '/' atomic_tag(tag_name, attributes) when '=', '~' - value = template_eval(value) - value = find_and_flatten(value) if action == '~' - print_tag(tag_name, value.to_s, attributes) + flattened = (action == '~') + push_tag(tag_name, value.to_s, attributes, true, flattened) if value else - print_tag(tag_name, value.to_s.strip, attributes) + push_tag(tag_name, value.to_s.strip, attributes) end end end @@ -188,9 +231,9 @@ module Haml #:nodoc: end def render_comment(line) - add "" + push_text "" end - + def template_eval(args) !@suppress_eval ? @scope_object.instance_eval(args) : "" end diff --git a/lib/haml/helpers.rb b/lib/haml/helpers.rb index f2d53ad3..60ee2943 100644 --- a/lib/haml/helpers.rb +++ b/lib/haml/helpers.rb @@ -20,6 +20,10 @@ module Haml def count_soft_tabs(line) line.index(/[^ ]/) ? [line.index(/[^ ]/)/2, line.strip] : [] end + + def wrap_script(script, count) + "(#{script}).to_s.chomp.gsub(\"\\n\", \"\\n#{tabs(count)}\")" + end # List_for is a really nifty little helper that helps # cleanup your code. Basically, give it an array of diff --git a/test/results/helpful.xhtml b/test/results/helpful.xhtml index 7be92c36..1870e903 100644 --- a/test/results/helpful.xhtml +++ b/test/results/helpful.xhtml @@ -1,5 +1,11 @@
-

Hello

-
World
+

+ Hello +

+
+ World +
-
boo
\ No newline at end of file +
+ boo +
\ No newline at end of file diff --git a/test/results/standard.xhtml b/test/results/standard.xhtml index be70cf25..07f871b4 100644 --- a/test/results/standard.xhtml +++ b/test/results/standard.xhtml @@ -12,14 +12,18 @@ The question is if this would translate! Ahah! 20 -
Quotes should be loved! Just like people!
+
+ Quotes should be loved! Just like people! +
Wow.|

Holy cow multiline tags! A pipe (|) even! PipesIgnored|PipesIgnored|PipesIgnored| 1|2|3

-
with this text
+
+ with this text +