Skip to content

Commit

Permalink
Support inherited methods in definition, hover and completion
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed May 9, 2024
1 parent 367a84d commit 3464078
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 8 deletions.
17 changes: 10 additions & 7 deletions lib/ruby_indexer/lib/ruby_indexer/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,16 +249,19 @@ def follow_aliased_namespace(name)
def resolve_method(method_name, receiver_name)
method_entries = self[method_name]
ancestors = linearized_ancestors_of(receiver_name.delete_prefix("::"))
return unless ancestors && method_entries
return unless method_entries && ancestors.any?

T.cast(
method_entries.select do |entry|
ancestors.each do |ancestor|
found = method_entries.select do |entry|
next unless entry.is_a?(Entry::Member)

ancestors.any?(entry.owner&.name)
end,
T::Array[Entry::Member],
)
entry.owner&.name == ancestor
end

return T.cast(found, T::Array[Entry::Member]) unless found.empty?
end

nil
end

# Linearizes the ancestors for a given name, returning the order of namespace in which Ruby will search for method
Expand Down
25 changes: 25 additions & 0 deletions lib/ruby_indexer/test/index_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -635,5 +635,30 @@ class Wow < Bar
assert_equal("qux", entry.name)
assert_equal("Bar", T.must(entry.owner).name)
end

def test_resolving_an_inherited_method_lands_on_first_match
index(<<~RUBY)
module Foo
def qux; end
end
class Bar
def qux; end
end
class Wow < Bar
prepend Foo
def qux; end
end
RUBY

entries = T.must(@index.resolve_method("qux", "Wow"))
assert_equal(1, entries.length)

entry = T.must(entries.first)
assert_equal("qux", entry.name)
assert_equal("Foo", T.must(entry.owner).name)
end
end
end
3 changes: 2 additions & 1 deletion lib/ruby_lsp/listeners/completion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,10 @@ def complete_self_receiver_method(node, name)
return unless receiver_entries

receiver = T.must(receiver_entries.first)
ancestors = @index.linearized_ancestors_of(receiver.name)

@index.prefix_search(name).each do |entries|
entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name }
entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && ancestors.any?(e.owner&.name) }
next unless entry

@response_builder << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
Expand Down
34 changes: 34 additions & 0 deletions test/requests/completion_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,40 @@ def you
end
end

def test_completion_for_inherited_methods
source = <<~RUBY
module Foo
module First
def method1; end
end
class Bar
def method2; end
end
class Baz < Bar
include First
def do_it
m
end
end
end
RUBY

with_server(source) do |server, uri|
with_file_structure(server) do
server.process_message(id: 1, method: "textDocument/completion", params: {
textDocument: { uri: uri },
position: { line: 13, character: 7 },
})

result = server.pop_response.response
assert_equal(["method1", "method2"], result.map(&:label))
end
end
end

def test_relative_completion_command
prefix = "support/"
source = <<~RUBY
Expand Down
41 changes: 41 additions & 0 deletions test/requests/definition_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,47 @@ def baz
end
end

def test_definition_for_inherited_methods
source = <<~RUBY
module Foo
module First
def method1; end
end
class Bar
def method2; end
end
class Baz < Bar
include First
def method3
method1
method2
end
end
end
RUBY

with_server(source) do |server, uri|
server.process_message(
id: 1,
method: "textDocument/definition",
params: { textDocument: { uri: uri }, position: { character: 6, line: 13 } },
)
response = server.pop_response.response.first
assert_equal(2, response.range.start.line)

server.process_message(
id: 1,
method: "textDocument/definition",
params: { textDocument: { uri: uri }, position: { character: 6, line: 14 } },
)
response = server.pop_response.response.first
assert_equal(6, response.range.start.line)
end
end

private

def create_definition_addon
Expand Down
42 changes: 42 additions & 0 deletions test/requests/hover_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,48 @@ def bar
end
end

def test_hovering_over_inherited_methods
source = <<~RUBY
module Foo
module First
# Method 1
def method1; end
end
class Bar
# Method 2
def method2; end
end
class Baz < Bar
include First
def method3
method1
method2
end
end
end
RUBY

# Going to definition on `argument` should not take you to the `foo` method definition
with_server(source) do |server, uri|
server.process_message(
id: 1,
method: "textDocument/hover",
params: { textDocument: { uri: uri }, position: { character: 6, line: 15 } },
)
assert_match("Method 1", server.pop_response.response.contents.value)

server.process_message(
id: 1,
method: "textDocument/hover",
params: { textDocument: { uri: uri }, position: { character: 6, line: 16 } },
)
assert_match("Method 2", server.pop_response.response.contents.value)
end
end

private

def create_hover_addon
Expand Down

0 comments on commit 3464078

Please sign in to comment.