-
Notifications
You must be signed in to change notification settings - Fork 127
/
definition.rb
94 lines (85 loc) · 3.02 KB
/
definition.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# typed: strict
# frozen_string_literal: true
require "ruby_lsp/listeners/definition"
module RubyLsp
module Requests
# ![Definition demo](../../definition.gif)
#
# The [definition
# request](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) jumps to the
# definition of the symbol under the cursor.
#
# Currently supported targets:
#
# - Classes
# - Modules
# - Constants
# - Require paths
# - Methods invoked on self only
#
# # Example
#
# ```ruby
# require "some_gem/file" # <- Request go to definition on this string will take you to the file
# Product.new # <- Request go to definition on this class name will take you to its declaration.
# ```
class Definition < Request
extend T::Sig
extend T::Generic
sig do
params(
document: Document,
global_state: GlobalState,
position: T::Hash[Symbol, T.untyped],
dispatcher: Prism::Dispatcher,
typechecker_enabled: T::Boolean,
).void
end
def initialize(document, global_state, position, dispatcher, typechecker_enabled)
super()
@response_builder = T.let(
ResponseBuilders::CollectionResponseBuilder[Interface::Location].new,
ResponseBuilders::CollectionResponseBuilder[Interface::Location],
)
@dispatcher = dispatcher
target, parent, nesting = document.locate_node(
position,
node_types: [Prism::CallNode, Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::BlockArgumentNode],
)
if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
# If the target is part of a constant path node, we need to find the exact portion of the constant that the
# user is requesting to go to definition for
target = determine_target(
target,
parent,
position,
)
elsif target.is_a?(Prism::CallNode) && target.name != :require && target.name != :require_relative &&
!covers_position?(target.message_loc, position)
# If the target is a method call, we need to ensure that the requested position is exactly on top of the
# method identifier. Otherwise, we risk showing definitions for unrelated things
target = nil
end
if target
Listeners::Definition.new(
@response_builder,
global_state,
document.uri,
nesting,
dispatcher,
typechecker_enabled,
)
Addon.addons.each do |addon|
addon.create_definition_listener(@response_builder, document.uri, nesting, dispatcher)
end
end
@target = T.let(target, T.nilable(Prism::Node))
end
sig { override.returns(T::Array[Interface::Location]) }
def perform
@dispatcher.dispatch_once(@target) if @target
@response_builder.response
end
end
end
end