[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide features on singletons #2188

Merged
merged 6 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Provide singleton context go to definition
  • Loading branch information
vinistock committed Jun 18, 2024
commit 3eebb0b73ff541e4d7bab4be2a67c33e1f006db4
18 changes: 11 additions & 7 deletions lib/ruby_lsp/listeners/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def initialize(response_builder, global_state, uri, node_context, dispatcher, ty
@response_builder = response_builder
@global_state = global_state
@index = T.let(global_state.index, RubyIndexer::Index)
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
@uri = uri
@node_context = node_context
@typechecker_enabled = typechecker_enabled
Expand All @@ -48,7 +49,7 @@ def on_call_node_enter(node)
message = node.message
return unless message

handle_method_definition(message, self_receiver?(node))
handle_method_definition(message, @type_inferrer.infer_receiver_type(@node_context))
end

sig { params(node: Prism::StringNode).void }
Expand All @@ -70,7 +71,7 @@ def on_block_argument_node_enter(node)
value = expression.value
return unless value

handle_method_definition(value, false)
handle_method_definition(value, nil)
end

sig { params(node: Prism::ConstantPathNode).void }
Expand Down Expand Up @@ -123,7 +124,10 @@ def on_instance_variable_target_node_enter(node)

sig { params(name: String).void }
def handle_instance_variable_definition(name)
entries = @index.resolve_instance_variable(name, @node_context.fully_qualified_name)
type = @type_inferrer.infer_receiver_type(@node_context)
return unless type

entries = @index.resolve_instance_variable(name, type)
return unless entries

entries.each do |entry|
Expand All @@ -141,10 +145,10 @@ def handle_instance_variable_definition(name)
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
end

sig { params(message: String, self_receiver: T::Boolean).void }
def handle_method_definition(message, self_receiver)
methods = if self_receiver
@index.resolve_method(message, @node_context.fully_qualified_name)
sig { params(message: String, receiver_type: T.nilable(String)).void }
def handle_method_definition(message, receiver_type)
methods = if receiver_type
@index.resolve_method(message, receiver_type)
else
# If the method doesn't have a receiver, then we provide a few candidates to jump to
# But we don't want to provide too many candidates, as it can be overwhelming
Expand Down
74 changes: 74 additions & 0 deletions test/requests/definition_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,80 @@ def do_something
end
end

def test_definition_for_singleton_methods
source = <<~RUBY
class Foo
def self.bar
end

class << self
def baz; end
end
end

Foo.bar
Foo.baz
RUBY

with_server(source) do |server, uri|
server.process_message(
id: 1,
method: "textDocument/definition",
params: { textDocument: { uri: uri }, position: { character: 4, line: 9 } },
)

response = server.pop_response.response
assert_equal(1, response[0].range.start.line)

server.process_message(
id: 1,
method: "textDocument/definition",
params: { textDocument: { uri: uri }, position: { character: 4, line: 10 } },
)

response = server.pop_response.response
assert_equal(5, response[0].range.start.line)
end
end

def test_definition_for_class_instance_variables
source = <<~RUBY
class Foo
@a = 123

def self.bar
@a
end

class << self
def baz
@a
end
end
end
RUBY

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

response = server.pop_response.response
assert_equal(1, response[0].range.start.line)

server.process_message(
id: 1,
method: "textDocument/definition",
params: { textDocument: { uri: uri }, position: { character: 6, line: 9 } },
)

response = server.pop_response.response
assert_equal(1, response[0].range.start.line)
end
end

private

def create_definition_addon
Expand Down