From b09e4589ad0b9f21779854cf0eb7ceb04e171964 Mon Sep 17 00:00:00 2001 From: Matt Wildig Date: Wed, 18 Mar 2015 18:22:28 +0000 Subject: [PATCH] Unify filter and haml_comment parsing * Fix bug in filter parsing A filter with no associated block wasn't being parsed properly, causing issues in any following code. * Dispatch haml_comment direct from process_line rather than silent_script. Syntactically it is a script with a comment, but the way it is handled is sufficiently different (especially with a block) to be dispatched as its own type. * Unify how haml_comment and filters are handled in the parser. Filters and comments with blocks are basically the same, the block isn't parsed (and so can have arbitrary indentation), so combine how they are handled in the parser. Remove special handling for haml_comment (e.g. @haml_comment variable), it now uses the same as filters (e.g. @flat). This means the text of the comment is now included in the parse tree (previously it was omitted). Fixes #840 and #838 (the latter more as a side effect). --- lib/haml/parser.rb | 43 +++++++++++++++++++++++++++---------------- test/parser_test.rb | 28 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/lib/haml/parser.rb b/lib/haml/parser.rb index 9d397e65..3ffb37ab 100644 --- a/lib/haml/parser.rb +++ b/lib/haml/parser.rb @@ -90,7 +90,6 @@ module Haml def initialize(template, options) @options = options - @flat = false # Record the indent levels of "if" statements to validate the subsequent # elsif and else statements are indented at the appropriate level. @script_level_stack = [] @@ -109,7 +108,8 @@ module Haml def parse @root = @parent = ParseNode.new(:root) - @haml_comment = false + @flat = false + @filter_buffer = nil @indentation = nil @line = next_line @@ -129,13 +129,13 @@ module Haml end @tab_up = nil - process_line(@line) unless @line.text.empty? || @haml_comment - if @parent.type != :haml_comment && (block_opened? || @tab_up) + process_line(@line) unless @line.text.empty? + if block_opened? || @tab_up @template_tabs += 1 @parent = @parent.children.last end - if !@haml_comment && !flat? && @next_line.tabs - @line.tabs > 1 + if !flat? && @next_line.tabs - @line.tabs > 1 raise SyntaxError.new(Error.message(:deeper_indenting, @next_line.tabs - @line.tabs), @next_line.index) end @@ -243,7 +243,9 @@ module Haml line.text = line.text[1..-1] push script(line) when FLAT_SCRIPT; push flat_script(line.strip!(1)) - when SILENT_SCRIPT; push silent_script(line) + when SILENT_SCRIPT + return push haml_comment(line.text[2..-1]) if line.text[1] == SILENT_COMMENT + push silent_script(line) when FILTER; push filter(line.text[1..-1].downcase) when DOCTYPE return push doctype(line.text) if line.text[0, 3] == '!!!' @@ -301,8 +303,6 @@ module Haml end def silent_script(line) - return haml_comment(line.text[2..-1]) if line.text[1] == SILENT_COMMENT - raise SyntaxError.new(Error.message(:no_end), line.index) if line.text[1..-1].strip == 'end' line = handle_ruby_multiline(line) @@ -343,7 +343,15 @@ module Haml end def haml_comment(text) - @haml_comment = block_opened? + if filter_opened? + @flat = true + @filter_buffer = String.new + @filter_buffer << "#{text}\n" unless text.empty? + text = @filter_buffer + # If we don't know the indentation by now, it'll be set in Line#tabs + @flat_spaces = @indentation * (@template_tabs+1) if @indentation + end + ParseNode.new(:haml_comment, @line.index + 1, :text => text) end @@ -469,10 +477,9 @@ module Haml def filter(name) raise Error.new(Error.message(:invalid_filter_name, name)) unless name =~ /^\w+$/ - @filter_buffer = String.new - if filter_opened? @flat = true + @filter_buffer = String.new # If we don't know the indentation by now, it'll be set in Line#tabs @flat_spaces = @indentation * (@template_tabs+1) if @indentation end @@ -487,13 +494,17 @@ module Haml end def close_filter(_) - @flat = false - @flat_spaces = nil - @filter_buffer = nil + close_flat_section end def close_haml_comment(_) - @haml_comment = false + close_flat_section + end + + def close_flat_section + @flat = false + @flat_spaces = nil + @filter_buffer = nil end def close_silent_script(node) @@ -772,7 +783,7 @@ module Haml # Same semantics as block_opened?, except that block_opened? uses Line#tabs, # which doesn't interact well with filter lines def filter_opened? - @next_line.full =~ (@indentation ? /^#{@indentation * @template_tabs}/ : /^\s/) + @next_line.full =~ (@indentation ? /^#{@indentation * (@template_tabs + 1)}/ : /^\s/) end def flat? diff --git a/test/parser_test.rb b/test/parser_test.rb index eb01b402..bf3c38d4 100644 --- a/test/parser_test.rb +++ b/test/parser_test.rb @@ -133,6 +133,34 @@ module Haml end end + test "empty filter doesn't hide following lines" do + root = parse "%p\n :plain\n %p\n" + p_element = root.children[0] + assert_equal 2, p_element.children.size + assert_equal :filter, p_element.children[0].type + assert_equal :tag, p_element.children[1].type + end + + # Previously blocks under a haml_comment would be rejected if any line was + # indented by a value that wasn't a multiple of the document indentation. + test "haml_comment accepts any indentation in content" do + begin + parse "-\#\n Indented two spaces\n Indented three spaces" + rescue Haml::SyntaxError + flunk "haml_comment should accept any combination of indentation" + end + end + + test "block haml_comment includes text" do + root = parse "-#\n Hello\n Hello\n" + assert_equal "Hello\n Hello\n", root.children[0].value[:text] + end + + test "block haml_comment includes first line if present" do + root = parse "-# First line\n Hello\n Hello\n" + assert_equal " First line\nHello\n Hello\n", root.children[0].value[:text] + end + private def parse(haml, options = nil)