lib/sass/error.rb in haml-edge-2.3.27 vs lib/sass/error.rb in haml-edge-2.3.28

- old
+ new

@@ -2,53 +2,158 @@ # An exception class that keeps track of # the line of the Sass template it was raised on # and the Sass file that was being parsed (if applicable). # # All Sass errors are raised as {Sass::SyntaxError}s. + # + # When dealing with SyntaxErrors, + # it's important to provide filename and line number information. + # This will be used in various error reports to users, including backtraces; + # see \{#sass\_backtrace} for details. + # + # Some of this information is usually provided as part of the constructor. + # New backtrace entries can be added with \{#add\_backtrace}, + # which is called when an exception is raised between files (e.g. with `@import`). + # + # Often, a chunk of code will all have similar backtrace information - + # the same filename or even line. + # It may also be useful to have a default line number set. + # In those situations, the default values can be used + # by omitting the information on the original exception, + # and then calling \{#modify\_backtrace} in a wrapper `rescue`. + # When doing this, be sure that all exceptions ultimately end up + # with the information filled in. class SyntaxError < StandardError - # The line of the Sass template on which the error occurred. + # The backtrace of the error within Sass files. + # This is an array of hashes containing information for a single entry. + # The hashes have the following keys: # - # @return [Fixnum] - attr_accessor :sass_line + # `:filename` + # : The name of the file in which the exception was raised, + # or `nil` if no filename is available. + # + # `:line` + # : The line of the file on which the error occurred. Never nil. + # + # This information is also included in standard backtrace format + # in the output of \{#backtrace}. + # + # @return [Aray<Hash<Symbol, Object>>] + attr_accessor :sass_backtrace - # The name of the file that was being parsed when the exception was raised. - # This could be `nil` if no filename is available. + # The text of the template where this error was raised. # # @return [String] - attr_reader :sass_filename + attr_accessor :sass_template # @param msg [String] The error message - # @param lineno [Fixnum] See \{#sass\_line} - def initialize(msg, lineno = nil) + # @param attrs [Hash<Symbol, Object>] The information in the backtrace entry. + # See \{#sass\_backtrace} + def initialize(msg, attrs = {}) @message = msg - @sass_line = lineno + @sass_backtrace = [] + add_backtrace(attrs) end - # Add information about the filename and line on which the error was raised, - # and re-raises the exception. + # The name of the file in which the exception was raised. + # This could be `nil` if no filename is available. # - # @param filename [String] See \{#sass\_filename} - # @param line [Fixnum] See \{#sass\_line} - # @raise [Sass::SyntaxError] self - def add_metadata(filename, line) - self.sass_line ||= line - add_backtrace_entry(filename) unless sass_filename - raise self + # @return [String] + def sass_filename + sass_backtrace.first[:filename] end - # Adds a properly formatted entry to the exception's backtrace. + # The line of the Sass template on which the error occurred. # - # @param filename [String] The file in which the error occurred, - # if applicable (defaults to "(sass)") - def add_backtrace_entry(filename) # :nodoc: - @sass_filename ||= filename - self.backtrace ||= [] - self.backtrace.unshift "#{@sass_filename || '(sass)'}:#{@sass_line}" + # @return [Fixnum] + def sass_line + sass_backtrace.first[:line] end + # Adds an entry to the exception's Sass backtrace. + # + # @param attrs [Hash<Symbol, Object>] The information in the backtrace entry. + # See \{#sass\_backtrace} + def add_backtrace(attrs) + sass_backtrace << attrs.reject {|k, v| v.nil?} + end + + # Modify the top Sass backtrace entry (that is, the last one) + # to have the given attributes. + # If that entry already has one of the given attributes set, + # that takes precendence. + # + # @param attrs [Hash<Symbol, Object>] The information to add to the backtrace entry. + # See \{#sass\_backtrace} + def modify_backtrace(attrs) + sass_backtrace[-1] = attrs.reject {|k, v| v.nil?}.merge(sass_backtrace.last) + end + # @return [String] The error message def to_s @message + end + + # Returns the standard exception backtrace, + # including the Sass backtrace. + # + # @return [Array<String>] + def backtrace + return nil if super.nil? + sass_backtrace.map {|h| "#{h[:filename] || "(sass)"}:#{h[:line]}"} + super + end + + # Returns a string representation of the Sass backtrace. + # + # @param default_filename [String] The filename to use for unknown files + # @see #sass_backtrace + # @return [String] + def sass_backtrace_str(default_filename = "an unknown file") + "Syntax error: #{message}" + + Haml::Util.enum_with_index(sass_backtrace).map do |entry, i| + "\n #{i == 0 ? "on" : "from"} line #{entry[:line]}" + + " of #{entry[:filename] || default_filename}" + end.join + end + + class << self + # Returns an error report for an exception in CSS format. + # + # @param e [Exception] + # @param full_exception [Boolean] The value of + # \{file:SASS\_REFERENCE.md#full_exception-option `options[:full_exception]`} + def exception_to_css(e, options) + return "/* Internal stylesheet error */" unless options[:full_exception] + + header = header_string(e, options) + + <<END +/* +#{header} + +Backtrace:\n#{e.backtrace.join("\n")} +*/ +body:before { + white-space: pre; + font-family: monospace; + content: "#{header.gsub('"', '\"').gsub("\n", '\\A ')}"; } +END + end + + private + + def header_string(e, options) + return "#{e.class}: #{e.message}" unless e.is_a? Sass::SyntaxError + + line_offset = options[:line] || 1 + line_num = e.sass_line + 1 - line_offset + min = [line_num - 6, 0].max + section = e.sass_template.rstrip.split("\n")[min ... line_num + 5] + return e.sass_backtrace_str if section.nil? || section.empty? + + e.sass_backtrace_str + "\n\n" + Haml::Util.enum_with_index(section). + map {|line, i| "#{line_offset + min + i}: #{line}"}.join("\n") + end end end # The class for Sass errors that are raised due to invalid unit conversions # in SassScript.