mirror of
https://github.com/haml/haml.git
synced 2022-11-09 12:33:31 -05:00
No more line number mungeing - @precompiled line numbers now sync up with template line numbers.
git-svn-id: svn://hamptoncatlin.com/haml/trunk@680 7063305b-7217-0410-af8c-cdc13e5119b9
This commit is contained in:
parent
890521383d
commit
34619eb403
5 changed files with 51 additions and 151 deletions
|
@ -40,7 +40,8 @@ module Haml
|
|||
'preserve' => Haml::Filters::Preserve,
|
||||
'redcloth' => Haml::Filters::RedCloth,
|
||||
'textile' => Haml::Filters::Textile,
|
||||
'markdown' => Haml::Filters::Markdown }
|
||||
'markdown' => Haml::Filters::Markdown },
|
||||
:filename => '(haml)'
|
||||
}
|
||||
@options.rec_merge! options
|
||||
|
||||
|
@ -68,9 +69,9 @@ END
|
|||
@flat_spaces = -1
|
||||
|
||||
precompile
|
||||
rescue Haml::Error => e
|
||||
e.add_backtrace_entry(@index, @options[:filename])
|
||||
raise e
|
||||
rescue Haml::Error
|
||||
$!.backtrace.unshift "#{@options[:filename]}:#{@index}"
|
||||
raise
|
||||
end
|
||||
|
||||
# Processes the template and returns the result as a string.
|
||||
|
@ -129,11 +130,7 @@ END
|
|||
@haml_is_haml = true
|
||||
end
|
||||
|
||||
begin
|
||||
eval(@precompiled, scope, '(haml-eval)')
|
||||
rescue Exception => e
|
||||
raise Engine.add_exception_info(e, @precompiled, @options[:filename])
|
||||
end
|
||||
eval(@precompiled, scope, @options[:filename], 0)
|
||||
|
||||
# Get rid of the current buffer
|
||||
scope_object.instance_eval do
|
||||
|
@ -174,12 +171,8 @@ END
|
|||
scope = scope_object.instance_eval{binding}
|
||||
end
|
||||
|
||||
begin
|
||||
eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" +
|
||||
precompiled_with_ambles(local_names) + "}\n", scope, '(haml-eval)')
|
||||
rescue Exception => e
|
||||
raise Haml::Engine.add_exception_info(e, @precompiled, @options[:filename])
|
||||
end
|
||||
eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" +
|
||||
precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], 0)
|
||||
end
|
||||
|
||||
# Defines a method on +object+
|
||||
|
@ -220,11 +213,8 @@ END
|
|||
def def_method(object, name, *local_names)
|
||||
method = object.is_a?(Module) ? :module_eval : :instance_eval
|
||||
|
||||
begin
|
||||
object.send(method, "def #{name}(_haml_locals = {}); #{precompiled_with_ambles(local_names)}; end", '(haml-eval)')
|
||||
rescue Exception => e
|
||||
raise Haml::Engine.add_exception_info(e, @precompiled, @options[:filename])
|
||||
end
|
||||
object.send(method, "def #{name}(_haml_locals = {}); #{precompiled_with_ambles(local_names)}; end",
|
||||
@options[:filename], 0)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -235,21 +225,6 @@ END
|
|||
eval(set_locals, scope)
|
||||
end
|
||||
|
||||
def self.add_exception_info(e, precompiled, filename)
|
||||
metaclass = class << e; self; end
|
||||
metaclass.send(:include, Haml::Error)
|
||||
|
||||
eval_line = e.backtrace[0].scan(/:([0-9]*)/)[0][0].to_i
|
||||
line_marker = precompiled.split("\n")[0...eval_line].grep(/#haml_lineno: [0-9]+/)[-1]
|
||||
e.add_backtrace_entry(line_marker ? line_marker.scan(/[0-9]+/)[0].to_i : -1, filename)
|
||||
|
||||
# Format Ruby compiler errors nicely
|
||||
message = e.message.scan(/compile error\n\(haml-eval\):[0-9]*: (.*)/)[0]
|
||||
metaclass.send(:define_method, :message) { "compile error: #{message}" } if message
|
||||
|
||||
return e
|
||||
end
|
||||
|
||||
# Returns a hash of options that Haml::Buffer cares about.
|
||||
# This should remain loadable form #inspect.
|
||||
def options_for_buffer
|
||||
|
|
|
@ -1,43 +1,13 @@
|
|||
module Haml
|
||||
# The abstract type of exception raised by Haml code.
|
||||
# Haml::SyntaxError includes this module,
|
||||
# as do all exceptions raised by Ruby code within Haml.
|
||||
#
|
||||
# Haml::Error encapsulates information about the exception,
|
||||
# such as the line of the Haml template it was raised on
|
||||
# and the Haml file that was being parsed (if applicable).
|
||||
# It also provides a handy way to rescue only exceptions raised
|
||||
# because of a faulty template.
|
||||
module Error
|
||||
# The line of the Haml template on which the exception was thrown.
|
||||
attr_reader :haml_line
|
||||
|
||||
# The name of the file that was being parsed when the exception was raised.
|
||||
# This will be nil unless Haml is being used as an ActionView plugin.
|
||||
attr_reader :haml_filename
|
||||
|
||||
# Adds a properly formatted entry to the exception's backtrace.
|
||||
# +lineno+ should be the line on which the error occurred.
|
||||
# +filename+ should be the file in which the error occurred,
|
||||
# if applicable (defaults to "(haml)").
|
||||
def add_backtrace_entry(lineno, filename = nil) # :nodoc:
|
||||
@haml_line = lineno
|
||||
@haml_filename = filename
|
||||
self.backtrace ||= []
|
||||
self.backtrace.unshift "#{filename || '(haml)'}:#{lineno}"
|
||||
end
|
||||
end
|
||||
class Error < StandardError; end
|
||||
|
||||
# SyntaxError is the type of exception raised when Haml encounters an
|
||||
# ill-formatted document.
|
||||
# It's not particularly interesting, except in that it includes Haml::Error.
|
||||
class SyntaxError < StandardError
|
||||
include Haml::Error
|
||||
end
|
||||
class SyntaxError < Haml::Error; end
|
||||
|
||||
# HamlError is the type of exception raised when Haml encounters an error
|
||||
# not of a syntactical nature, such as an undefined Filter.
|
||||
class HamlError < StandardError
|
||||
include Haml::Error
|
||||
end
|
||||
class HamlError < Haml::Error; end
|
||||
end
|
||||
|
|
|
@ -98,12 +98,8 @@ extend Haml::Helpers
|
|||
@haml_is_haml = true
|
||||
_hamlout = @haml_stack[-1]
|
||||
_erbout = _hamlout.buffer
|
||||
begin
|
||||
END
|
||||
postamble = <<END.gsub("\n", ";")
|
||||
rescue Exception => e
|
||||
raise Haml::Engine.add_exception_info(e, #{@precompiled.inspect}, #{@options[:filename].inspect})
|
||||
end
|
||||
@haml_is_haml = false
|
||||
_hamlout.buffer
|
||||
END
|
||||
|
@ -132,10 +128,15 @@ END
|
|||
|
||||
if line.text.empty?
|
||||
process_indent(old_line) unless !flat? || old_line.text.empty?
|
||||
next unless flat?
|
||||
|
||||
unless flat?
|
||||
newline
|
||||
next
|
||||
end
|
||||
|
||||
push_flat(old_line)
|
||||
old_line.text, old_line.unstripped, old_line.spaces = '', '', 0
|
||||
newline
|
||||
next
|
||||
end
|
||||
|
||||
|
@ -143,6 +144,7 @@ END
|
|||
|
||||
if old_line.text.nil? || suppress_render
|
||||
old_line = line
|
||||
newline
|
||||
next
|
||||
end
|
||||
|
||||
|
@ -151,6 +153,7 @@ END
|
|||
if flat?
|
||||
push_flat(old_line)
|
||||
old_line = line
|
||||
newline
|
||||
next
|
||||
end
|
||||
|
||||
|
@ -166,6 +169,7 @@ END
|
|||
raise SyntaxError.new("Illegal Indentation: Indenting more than once per line is illegal.")
|
||||
end
|
||||
old_line = line
|
||||
newline
|
||||
end
|
||||
|
||||
# Close all the open tags
|
||||
|
@ -186,8 +190,8 @@ END
|
|||
# This method doesn't return anything; it simply processes the line and
|
||||
# adds the appropriate code to <tt>@precompiled</tt>.
|
||||
def process_line(text, index, block_opened)
|
||||
@index = index + 1
|
||||
@block_opened = block_opened
|
||||
@index = index + 1
|
||||
|
||||
case text[0]
|
||||
when DIV_CLASS, DIV_ID: render_div(text)
|
||||
|
@ -200,9 +204,9 @@ END
|
|||
when SILENT_SCRIPT
|
||||
return start_haml_comment if text[1] == SILENT_COMMENT
|
||||
|
||||
mbk = mid_block_keyword?(text)
|
||||
push_silent(text[1..-1], !mbk, true)
|
||||
if (@block_opened && !mbk) || text[1..-1].split(' ', 2)[0] == "case"
|
||||
push_silent(text[1..-1], true)
|
||||
newline true
|
||||
if (@block_opened && !mid_block_keyword?(text)) || text[1..-1].split(' ', 2)[0] == "case"
|
||||
push_and_tabulate([:script])
|
||||
end
|
||||
when FILTER: start_filtered(text[1..-1].downcase)
|
||||
|
@ -257,12 +261,10 @@ END
|
|||
|
||||
# Evaluates <tt>text</tt> in the context of the scope object, but
|
||||
# does not output the result.
|
||||
def push_silent(text, add_index = false, can_suppress = false)
|
||||
def push_silent(text, can_suppress = false)
|
||||
flush_merged_text
|
||||
return if can_suppress && options[:suppress_eval]
|
||||
|
||||
@precompiled << "#haml_lineno: #{@index}\n" if add_index
|
||||
@precompiled << "#{text}\n"
|
||||
@precompiled << "#{text};"
|
||||
end
|
||||
|
||||
# Adds <tt>text</tt> to <tt>@buffer</tt> with appropriate tabulation
|
||||
|
@ -282,7 +284,7 @@ END
|
|||
|
||||
@precompiled << "_hamlout.push_text(#{@merged_text.dump}"
|
||||
@precompiled << ", #{@tab_change}" if @tab_change != 0 || @try_one_liner
|
||||
@precompiled << ")\n"
|
||||
@precompiled << ");"
|
||||
@merged_text = ''
|
||||
@tab_change = 0
|
||||
@try_one_liner = false
|
||||
|
@ -311,8 +313,9 @@ END
|
|||
flush_merged_text
|
||||
return if options[:suppress_eval]
|
||||
|
||||
push_silent("haml_temp = #{text}", true)
|
||||
out = "haml_temp = _hamlout.push_script(haml_temp, #{flattened.inspect}, #{close_tag.inspect})\n"
|
||||
push_silent "haml_temp = #{text}"
|
||||
newline true
|
||||
out = "haml_temp = _hamlout.push_script(haml_temp, #{flattened.inspect}, #{close_tag.inspect});"
|
||||
if @block_opened
|
||||
push_and_tabulate([:loud, out])
|
||||
else
|
||||
|
@ -359,7 +362,7 @@ END
|
|||
|
||||
# Closes a Ruby block.
|
||||
def close_block
|
||||
push_silent "end", false, true
|
||||
push_silent "end", true
|
||||
@template_tabs -= 1
|
||||
end
|
||||
|
||||
|
@ -373,7 +376,7 @@ END
|
|||
|
||||
# Closes a loud Ruby block.
|
||||
def close_loud(command)
|
||||
push_silent 'end', false, true
|
||||
push_silent 'end', true
|
||||
@precompiled << command
|
||||
@template_tabs -= 1
|
||||
end
|
||||
|
@ -384,7 +387,7 @@ END
|
|||
filtered = filter.new(@filter_buffer).render
|
||||
|
||||
if filter == Haml::Filters::Preserve
|
||||
push_silent("_hamlout.buffer << #{filtered.dump} << \"\\n\"\n")
|
||||
push_silent("_hamlout.buffer << #{filtered.dump} << \"\\n\";")
|
||||
else
|
||||
push_text(filtered.rstrip.gsub("\n", "\n#{' ' * @output_tabs}"))
|
||||
end
|
||||
|
@ -520,7 +523,7 @@ END
|
|||
else
|
||||
flush_merged_text
|
||||
content = value.empty? || parse ? 'nil' : value.dump
|
||||
push_silent "_hamlout.open_tag(#{tag_name.inspect}, #{atomic.inspect}, #{(!value.empty?).inspect}, #{attributes.inspect}, #{object_ref}, #{content}, #{attributes_hash[1...-1]})", true
|
||||
push_silent "_hamlout.open_tag(#{tag_name.inspect}, #{atomic.inspect}, #{(!value.empty?).inspect}, #{attributes.inspect}, #{object_ref}, #{content}, #{attributes_hash[1...-1]})"
|
||||
end
|
||||
|
||||
return if atomic
|
||||
|
@ -641,7 +644,6 @@ END
|
|||
spaces = line.index(/[^ ]/)
|
||||
if line[spaces] == ?\t
|
||||
return nil if line.strip.empty?
|
||||
|
||||
raise SyntaxError.new("Illegal Indentation: Only two space characters are allowed as tabulation.")
|
||||
end
|
||||
[spaces, spaces/2]
|
||||
|
@ -657,5 +659,11 @@ END
|
|||
def flat?
|
||||
@flat_spaces != -1
|
||||
end
|
||||
|
||||
def newline(skip_next = false)
|
||||
return @skip_next_newline = false if @skip_next_newline
|
||||
@skip_next_newline = true if skip_next
|
||||
@precompiled << "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -187,82 +187,29 @@ class EngineTest < Test::Unit::TestCase
|
|||
render("a\nb\n!!!\n c\nd")
|
||||
rescue Haml::SyntaxError => e
|
||||
assert_equal(e.message, "Illegal Nesting: Nesting within a header command is illegal.")
|
||||
assert_equal(3, e.haml_line)
|
||||
assert_equal(nil, e.haml_filename)
|
||||
assert_equal('(haml):3', e.backtrace[0])
|
||||
assert_equal("(haml):3", e.backtrace[0])
|
||||
rescue Exception => e
|
||||
assert(false, '"a\nb\n!!!\n c\nd" doesn\'t produce a Haml::SyntaxError')
|
||||
else
|
||||
assert(false, '"a\nb\n!!!\n c\nd" doesn\'t produce an exception')
|
||||
end
|
||||
|
||||
def test_exception_type
|
||||
render("%p hi\n= undefined\n= 12")
|
||||
def test_exception
|
||||
render("%p\n hi\n %a= undefined\n= 12")
|
||||
rescue Exception => e
|
||||
assert(e.is_a?(Haml::Error))
|
||||
assert_equal(2, e.haml_line)
|
||||
assert_equal(nil, e.haml_filename)
|
||||
assert_equal('(haml):2', e.backtrace[0])
|
||||
else
|
||||
# Test failed... should have raised an exception
|
||||
assert(false)
|
||||
end
|
||||
|
||||
def test_def_method_exception_type
|
||||
o = Object.new
|
||||
Haml::Engine.new("%p hi\n= undefined\n= 12").def_method(o, :render)
|
||||
o.render
|
||||
rescue Exception => e
|
||||
assert(e.is_a?(Haml::Error))
|
||||
assert_equal(2, e.haml_line)
|
||||
assert_equal(nil, e.haml_filename)
|
||||
assert_equal('(haml):2', e.backtrace[0])
|
||||
else
|
||||
# Test failed... should have raised an exception
|
||||
assert(false)
|
||||
end
|
||||
|
||||
def test_render_proc_exception_type
|
||||
Haml::Engine.new("%p hi\n= undefined\n= 12").render_proc.call
|
||||
rescue Exception => e
|
||||
assert(e.is_a?(Haml::Error))
|
||||
assert_equal(2, e.haml_line)
|
||||
assert_equal(nil, e.haml_filename)
|
||||
assert_equal('(haml):2', e.backtrace[0])
|
||||
assert_match("(haml):3", e.backtrace[0])
|
||||
else
|
||||
# Test failed... should have raised an exception
|
||||
assert(false)
|
||||
end
|
||||
|
||||
def test_compile_error
|
||||
render("a\nb\n- fee do\nc")
|
||||
render("a\nb\n- fee)\nc")
|
||||
rescue Exception => e
|
||||
assert_match(/^compile error: syntax error/, e.message)
|
||||
assert_equal(3, e.haml_line)
|
||||
assert_match(/^compile error\n\(haml\):3: syntax error/i, e.message)
|
||||
else
|
||||
assert(false,
|
||||
'"a\nb\n- fee do\nc" doesn\'t produce an exception!')
|
||||
end
|
||||
|
||||
def test_def_method_compile_error
|
||||
o = Object.new
|
||||
Haml::Engine.new("a\nb\n- fee do\nc").def_method(o, :render)
|
||||
rescue Exception => e
|
||||
assert_match(/^compile error: syntax error/, e.message)
|
||||
assert_equal(3, e.haml_line)
|
||||
else
|
||||
assert(false,
|
||||
'"a\nb\n- fee do\nc" doesn\'t produce an exception!')
|
||||
end
|
||||
|
||||
def test_render_proc_compile_error
|
||||
Haml::Engine.new("a\nb\n- fee do\nc").render_proc
|
||||
rescue Exception => e
|
||||
assert_match(/^compile error: syntax error/, e.message)
|
||||
assert_equal(3, e.haml_line)
|
||||
else
|
||||
assert(false,
|
||||
'"a\nb\n- fee do\nc" doesn\'t produce an exception!')
|
||||
'"a\nb\n- fee)\nc" doesn\'t produce an exception!')
|
||||
end
|
||||
|
||||
def test_unbalanced_brackets
|
||||
|
|
|
@ -145,7 +145,7 @@ class TemplateTest < Test::Unit::TestCase
|
|||
render("- raise 'oops!'")
|
||||
rescue Exception => e
|
||||
assert_equal("oops!", e.message)
|
||||
assert_equal("(haml):1", e.backtrace[0])
|
||||
assert_match(/^\(haml\):1/, e.backtrace[0])
|
||||
else
|
||||
assert false
|
||||
end
|
||||
|
@ -165,7 +165,7 @@ END
|
|||
begin
|
||||
render(template.chomp)
|
||||
rescue Exception => e
|
||||
assert_equal("(haml):5", e.backtrace[0])
|
||||
assert_match(/^\(haml\):5/, e.backtrace[0])
|
||||
else
|
||||
assert false
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue