Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions lib/error_highlight/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,20 @@ def spot
when :OP_CDECL
spot_op_cdecl

when :DEFN
raise NotImplementedError if @point_type != :name
spot_defn

when :DEFS
raise NotImplementedError if @point_type != :name
spot_defs

when :LAMBDA
spot_lambda

when :ITER
spot_iter

when :call_node
case @point_type
when :name
Expand Down Expand Up @@ -280,6 +294,30 @@ def spot
when :constant_path_operator_write_node
prism_spot_constant_path_operator_write

when :def_node
case @point_type
when :name
prism_spot_def_for_name
when :args
raise NotImplementedError
end

when :lambda_node
case @point_type
when :name
prism_spot_lambda_for_name
when :args
raise NotImplementedError
end

when :block_node
case @point_type
when :name
prism_spot_block_for_name
when :args
raise NotImplementedError
end

end

if @snippet && @beg_column && @end_column && @beg_column < @end_column
Expand Down Expand Up @@ -621,6 +659,55 @@ def spot_op_cdecl
end
end

# Example:
# def bar; end
# ^^^
def spot_defn
mid, = @node.children
fetch_line(@node.first_lineno)
if @snippet.match(/\Gdef\s+(#{ Regexp.quote(mid) }\b)/, @node.first_column)
@beg_column = $~.begin(1)
@end_column = $~.end(1)
end
end

# Example:
# def Foo.bar; end
# ^^^^
def spot_defs
nd_recv, mid, = @node.children
fetch_line(nd_recv.last_lineno)
if @snippet.match(/\G\s*(\.\s*#{ Regexp.quote(mid) }\b)/, nd_recv.last_column)
@beg_column = $~.begin(1)
@end_column = $~.end(1)
end
end

# Example:
# -> { ... }
# ^^
def spot_lambda
fetch_line(@node.first_lineno)
if @snippet.match(/\G->/, @node.first_column)
@beg_column = $~.begin(0)
@end_column = $~.end(0)
end
end

# Example:
# lambda { ... }
# ^
# define_method :foo do
# ^^
def spot_iter
_nd_fcall, nd_scope = @node.children
fetch_line(nd_scope.first_lineno)
if @snippet.match(/\G(?:do\b|\{)/, nd_scope.first_column)
@beg_column = $~.begin(0)
@end_column = $~.end(0)
end
end

def fetch_line(lineno)
@beg_lineno = @end_lineno = lineno
@snippet = @fetch[lineno]
Expand Down Expand Up @@ -826,6 +913,31 @@ def prism_spot_constant_path_operator_write
prism_location(@node.binary_operator_loc.chop)
end
end

# Example:
# def foo()
# ^^^
def prism_spot_def_for_name
location = @node.name_loc
location = location.join(@node.operator_loc) if @node.operator_loc
prism_location(location)
end

# Example:
# -> x, y { }
# ^^
def prism_spot_lambda_for_name
prism_location(@node.operator_loc)
end

# Example:
# lambda { }
# ^
# define_method :foo do |x, y|
# ^
def prism_spot_block_for_name
prism_location(@node.opening_loc)
end
end

private_constant :Spotter
Expand Down
35 changes: 32 additions & 3 deletions lib/error_highlight/core_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,38 @@
module ErrorHighlight
module CoreExt
private def generate_snippet
spot = ErrorHighlight.spot(self)
return "" unless spot
return ErrorHighlight.formatter.message_for(spot)
if ArgumentError === self && message =~ /\A(?:wrong number of arguments|missing keyword|unknown keyword|no keywords accepted)\b/
locs = self.backtrace_locations
return "" if locs.size < 2
callee_loc, caller_loc = locs
callee_spot = ErrorHighlight.spot(self, backtrace_location: callee_loc, point_type: :name)
caller_spot = ErrorHighlight.spot(self, backtrace_location: caller_loc, point_type: :name)
if caller_spot && callee_spot &&
caller_loc.path == callee_loc.path &&
caller_loc.lineno == callee_loc.lineno &&
caller_spot == callee_spot
callee_loc = callee_spot = nil
end
ret = +"\n"
[["caller", caller_loc, caller_spot], ["callee", callee_loc, callee_spot]].each do |header, loc, spot|
out = nil
if loc
out = " #{ header }: #{ loc.path }:#{ loc.lineno }"
if spot
_, _, snippet, highlight = ErrorHighlight.formatter.message_for(spot).lines
out += "\n | #{ snippet } #{ highlight }"
else
out += "\n (cannot create a snippet of the method definition; use Ruby 3.5 or later)"
end
end
ret << "\n" + out if out
end
ret
else
spot = ErrorHighlight.spot(self)
return "" unless spot
return ErrorHighlight.formatter.message_for(spot)
end
end

if Exception.method_defined?(:detailed_message)
Expand Down
Loading