module Nugrant module Helper class Stack @@DEFAULT_MATCHER = /^(.+):([0-9]+)/ def self.fetch_error_region(stack, options = {}) entry = find_entry(stack, options) location = extract_error_location(entry, options) return (options[:unknown] || "Unknown") if not location[:file] and not location[:line] return location[:file] if not location[:line] fetch_error_region_from_location(location, options) end def self.fetch_error_region_from_location(location, options = {}) prefix = options[:prefix] || " " width = options[:width] || 4 file = File.new(location[:file], "r") line = location[:line] index = 0 lines = [] while (line_string = file.gets()) index += 1 next if (line - index).abs > width line_prefix = "#{prefix}#{index}:" line_prefix += (line == index ? ">> " : " ") lines << "#{line_prefix}#{line_string}" end lines.join().chomp() rescue return (options[:unknown] || "Unknown") if not location[:file] and not location[:line] return location[:file] if not location[:line] "#{location[:file]}:#{location[:line]}" ensure file.close() if file end ## # Search a stack list (as simple string array) for the first # entry that match the +:matcher+. # def self.find_entry(stack, options = {}) matcher = options[:matcher] || @@DEFAULT_MATCHER stack.find do |entry| entry =~ matcher end end ## # Extract error location information from a stack entry using the # matcher received in arguments. # # The usual stack entry format is: # > /home/users/joe/work/lib/ruby.rb:4:Error message # # This function will extract the file and line information from # the stack entry using the matcher. The matcher is expected to # have two groups, the first for the file and the second for # line. # # The results is returned in form of a hash with two keys, +:file+ # for the file information and +:line+ for the line information. # # If the matcher matched zero group, return +{:file => nil, :line => nil}+. # If the matcher matched one group, return +{:file => file, :line => nil}+. # If the matcher matched two groups, return +{:file => file, :line => line}+. # def self.extract_error_location(entry, options = {}) matcher = options[:matcher] || @@DEFAULT_MATCHER result = matcher.match(entry) captures = result ? result.captures : [] {:file => captures[0], :line => captures[1] ? captures[1].to_i() : nil} end end end end