diff --git a/spec/backtracer/backtrace/frame/context_spec.cr b/spec/backtracer/backtrace/frame/context_spec.cr new file mode 100644 index 0000000..2ee0a82 --- /dev/null +++ b/spec/backtracer/backtrace/frame/context_spec.cr @@ -0,0 +1,33 @@ +require "../../../spec_helper" + +describe Backtracer::Backtrace::Frame::Context do + describe ".to_h" do + it "works with empty #pre and #post" do + context = Backtracer::Backtrace::Frame::Context.new( + lineno: 1, + pre: %w[], + line: "violent offender!", + post: %w[], + ) + context.to_h.should eq({1 => "violent offender!"}) + end + + it "returns hash with #pre, #line and #post strings" do + context = Backtracer::Backtrace::Frame::Context.new( + lineno: 10, + pre: %w[foo bar baz], + line: "violent offender!", + post: %w[boo far faz], + ) + context.to_h.should eq({ + 7 => "foo", + 8 => "bar", + 9 => "baz", + 10 => "violent offender!", + 11 => "boo", + 12 => "far", + 13 => "faz", + }) + end + end +end diff --git a/src/backtracer/backtrace/frame.cr b/src/backtracer/backtrace/frame.cr index e99aed2..cd44cfc 100644 --- a/src/backtracer/backtrace/frame.cr +++ b/src/backtracer/backtrace/frame.cr @@ -96,15 +96,18 @@ module Backtracer !!(relative_path.try(&.matches?(configuration.app_dirs_pattern))) end - # Returns a tuple consisting of 3 elements - an array of context lines + # Returns `Context` record consisting of 3 elements - an array of context lines # before the `lineno`, line at `lineno`, and an array of context lines # after the `lineno`. In case of failure it returns `nil`. # # Amount of returned context lines is taken from the *context_lines* # argument if given, or `configuration.context_lines` otherwise. # + # NOTE: amount of returned context lines might be lower than given + # in cases where `lineno` is near the start or the end of the file. + # # See `Configuration#context_lines` - def context(context_lines : Int32? = nil) : {Array(String), String, Array(String)}? + def context(context_lines : Int32? = nil) : Context? context_lines ||= configuration.context_lines return unless context_lines && (context_lines > 0) @@ -117,35 +120,13 @@ module Backtracer if context_line = lines[lineidx]? pre_context = lines[Math.max(0, lineidx - context_lines), context_lines] post_context = lines[Math.min(lines.size, lineidx + 1), context_lines] - {pre_context, context_line, post_context} - end - end - # Returns hash with context lines, where line numbers are keys and - # the lines itself are values. In case of failure it returns `nil`. - # - # Amount of returned context lines is taken from the *context_lines* - # argument if given, or `configuration.context_lines` otherwise. - # - # See `Configuration#context`, `Configuration#context_lines` - def context_hash(context_lines : Int32? = nil) : Hash(Int32, String)? - return unless context = self.context(context_lines) - return unless lineno = @lineno - - pre_context, context_line, post_context = context - - ({} of Int32 => String).tap do |hash| - pre_context.each_with_index do |code, index| - line = (lineno - pre_context.size) + index - hash[line] = code - end - - hash[lineno] = context_line - - post_context.each_with_index do |code, index| - line = lineno + (index + 1) - hash[line] = code - end + Context.new( + lineno: lineno, + pre: pre_context, + line: context_line, + post: post_context, + ) end end end diff --git a/src/backtracer/backtrace/frame/context.cr b/src/backtracer/backtrace/frame/context.cr new file mode 100644 index 0000000..12a9e1b --- /dev/null +++ b/src/backtracer/backtrace/frame/context.cr @@ -0,0 +1,36 @@ +module Backtracer + struct Backtrace::Frame::Context + # The line number this `Context` refers to. + getter lineno : Int32 + + # An array of lines before `lineno`. + getter pre : Array(String) + + # The line at `lineno`. + getter line : String + + # An array of lines after `lineno`. + getter post : Array(String) + + def initialize(@lineno, @pre, @line, @post) + end + + # Returns hash with context lines, where line numbers are + # the keys and the lines itself are the values. + def to_h : Hash(Int32, String) + ({} of Int32 => String).tap do |hash| + base_index = lineno - pre.size + pre.each_with_index do |code, index| + hash[base_index + index] = code + end + + hash[lineno] = line + + base_index = lineno + 1 + post.each_with_index do |code, index| + hash[base_index + index] = code + end + end + end + end +end