Skip to content

Commit 7cb476b

Browse files
committed
Implement goto definition for send and public send
1 parent e0b26e1 commit 7cb476b

File tree

2 files changed

+126
-7
lines changed

2 files changed

+126
-7
lines changed

lib/ruby_lsp/listeners/definition.rb

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,20 +75,27 @@ def on_string_node_enter(node)
7575
return unless enclosing_call
7676

7777
name = enclosing_call.name
78-
return unless name == :require || name == :require_relative
79-
80-
handle_require_definition(node, name)
78+
case name
79+
when :require, :require_relative
80+
handle_require_definition(node, name)
81+
when :send, :public_send
82+
method_name = node.content
83+
handle_method_definition(method_name, nil)
84+
end
8185
end
8286

8387
#: (Prism::SymbolNode node) -> void
8488
def on_symbol_node_enter(node)
8589
enclosing_call = @node_context.call_node
8690
return unless enclosing_call
8791

88-
name = enclosing_call.name
89-
return unless name == :autoload
90-
91-
handle_autoload_definition(enclosing_call)
92+
case enclosing_call.name
93+
when :autoload
94+
handle_autoload_definition(enclosing_call)
95+
when :send, :public_send
96+
method_name = node.unescaped
97+
handle_method_definition(method_name, nil)
98+
end
9299
end
93100

94101
#: (Prism::BlockArgumentNode node) -> void

test/requests/definition_expectations_test.rb

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,118 @@ class Foo::Bar; end
408408
end
409409
end
410410

411+
def test_go_to_definition_for_send_symbol
412+
source = <<~RUBY
413+
class Foo
414+
def foo; end
415+
end
416+
417+
obj = Foo.new
418+
obj.send(:foo)
419+
RUBY
420+
421+
with_server(source) do |server, uri|
422+
server.process_message(
423+
id: 1,
424+
method: "textDocument/definition",
425+
params: { textDocument: { uri: uri }, position: { character: 11, line: 5 } }
426+
)
427+
response = server.pop_response.response.first
428+
assert_equal(1, response.target_range.start.line)
429+
assert_equal(1, response.target_range.end.line)
430+
end
431+
end
432+
433+
def test_go_to_definition_for_send_symbol_multiple_objects
434+
source = <<~RUBY
435+
class Foo
436+
def foo; end
437+
end
438+
439+
class Bar
440+
def foo; end
441+
end
442+
443+
f = Foo.new
444+
b = Bar.new
445+
446+
f.send(:foo)
447+
b.send(:foo)
448+
RUBY
449+
450+
with_server(source) do |server, uri|
451+
server.process_message(
452+
id: 1,
453+
method: "textDocument/definition",
454+
params: { textDocument: { uri: uri }, position: { character: 9, line: 11 } }
455+
)
456+
response = server.pop_response.response
457+
assert_equal(2, response.size)
458+
459+
assert_equal(1, response[0].target_range.start.line)
460+
assert_equal(1, response[0].target_range.end.line)
461+
462+
assert_equal(5, response[1].target_range.start.line)
463+
assert_equal(5, response[1].target_range.end.line)
464+
end
465+
end
466+
467+
def test_go_to_definition_for_send_string
468+
source = <<~RUBY
469+
class Foo
470+
def foo; end
471+
end
472+
473+
obj = Foo.new
474+
obj.send("foo")
475+
RUBY
476+
477+
with_server(source) do |server, uri|
478+
server.process_message(
479+
id: 2,
480+
method: "textDocument/definition",
481+
params: { textDocument: { uri: uri }, position: { character: 11, line: 5 } }
482+
)
483+
response = server.pop_response.response.first
484+
assert_equal(1, response.target_range.start.line)
485+
assert_equal(1, response.target_range.end.line)
486+
end
487+
end
488+
489+
def test_go_to_definition_for_send_string_multiple_objects
490+
source = <<~RUBY
491+
class Foo
492+
def foo; end
493+
end
494+
495+
class Bar
496+
def foo; end
497+
end
498+
499+
f = Foo.new
500+
b = Bar.new
501+
502+
f.send("foo")
503+
b.send("foo")
504+
RUBY
505+
506+
with_server(source) do |server, uri|
507+
server.process_message(
508+
id: 1,
509+
method: "textDocument/definition",
510+
params: { textDocument: { uri: uri }, position: { character: 9, line: 11 } }
511+
)
512+
response = server.pop_response.response
513+
assert_equal(2, response.size)
514+
515+
assert_equal(1, response[0].target_range.start.line)
516+
assert_equal(1, response[0].target_range.end.line)
517+
518+
assert_equal(5, response[1].target_range.start.line)
519+
assert_equal(5, response[1].target_range.end.line)
520+
end
521+
end
522+
411523
def test_does_nothing_when_autoload_declaration_does_not_exist
412524
source = <<~RUBY
413525
# typed: ignore

0 commit comments

Comments
 (0)