ruby-lsp 0.16.7 → 0.17.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/VERSION +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +139 -18
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +101 -12
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +183 -10
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +55 -9
- data/lib/ruby_indexer/test/configuration_test.rb +4 -5
- data/lib/ruby_indexer/test/constant_test.rb +8 -8
- data/lib/ruby_indexer/test/index_test.rb +528 -0
- data/lib/ruby_indexer/test/instance_variables_test.rb +131 -0
- data/lib/ruby_indexer/test/method_test.rb +37 -0
- data/lib/ruby_indexer/test/test_case.rb +3 -1
- data/lib/ruby_lsp/addon.rb +8 -8
- data/lib/ruby_lsp/document.rb +3 -3
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/listeners/completion.rb +74 -17
- data/lib/ruby_lsp/listeners/definition.rb +62 -6
- data/lib/ruby_lsp/listeners/hover.rb +60 -6
- data/lib/ruby_lsp/listeners/signature_help.rb +4 -4
- data/lib/ruby_lsp/node_context.rb +28 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +73 -2
- data/lib/ruby_lsp/requests/code_actions.rb +16 -15
- data/lib/ruby_lsp/requests/completion.rb +21 -13
- data/lib/ruby_lsp/requests/completion_resolve.rb +9 -0
- data/lib/ruby_lsp/requests/definition.rb +20 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
- data/lib/ruby_lsp/requests/hover.rb +5 -6
- data/lib/ruby_lsp/requests/on_type_formatting.rb +8 -4
- data/lib/ruby_lsp/requests/signature_help.rb +3 -3
- data/lib/ruby_lsp/requests/support/common.rb +4 -3
- data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -1
- data/lib/ruby_lsp/server.rb +10 -4
- data/lib/ruby_lsp/test_helper.rb +1 -0
- metadata +4 -2
@@ -15,7 +15,7 @@ module RubyIndexer
|
|
15
15
|
@index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), source)
|
16
16
|
end
|
17
17
|
|
18
|
-
def assert_entry(expected_name, type, expected_location)
|
18
|
+
def assert_entry(expected_name, type, expected_location, visibility: nil)
|
19
19
|
entries = @index[expected_name]
|
20
20
|
refute_empty(entries, "Expected #{expected_name} to be indexed")
|
21
21
|
|
@@ -28,6 +28,8 @@ module RubyIndexer
|
|
28
28
|
":#{location.end_line - 1}-#{location.end_column}"
|
29
29
|
|
30
30
|
assert_equal(expected_location, location_string)
|
31
|
+
|
32
|
+
assert_equal(visibility, entry.visibility) if visibility
|
31
33
|
end
|
32
34
|
|
33
35
|
def refute_entry(expected_name)
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -99,8 +99,8 @@ module RubyLsp
|
|
99
99
|
end
|
100
100
|
|
101
101
|
sig { returns(String) }
|
102
|
-
def
|
103
|
-
@errors.
|
102
|
+
def errors_details
|
103
|
+
@errors.map(&:full_message).join("\n\n")
|
104
104
|
end
|
105
105
|
|
106
106
|
# Each addon should implement `MyAddon#activate` and use to perform any sort of initialization, such as
|
@@ -131,11 +131,11 @@ module RubyLsp
|
|
131
131
|
sig do
|
132
132
|
overridable.params(
|
133
133
|
response_builder: ResponseBuilders::Hover,
|
134
|
-
|
134
|
+
node_context: NodeContext,
|
135
135
|
dispatcher: Prism::Dispatcher,
|
136
136
|
).void
|
137
137
|
end
|
138
|
-
def create_hover_listener(response_builder,
|
138
|
+
def create_hover_listener(response_builder, node_context, dispatcher); end
|
139
139
|
|
140
140
|
# Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
|
141
141
|
sig do
|
@@ -159,21 +159,21 @@ module RubyLsp
|
|
159
159
|
overridable.params(
|
160
160
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
|
161
161
|
uri: URI::Generic,
|
162
|
-
|
162
|
+
node_context: NodeContext,
|
163
163
|
dispatcher: Prism::Dispatcher,
|
164
164
|
).void
|
165
165
|
end
|
166
|
-
def create_definition_listener(response_builder, uri,
|
166
|
+
def create_definition_listener(response_builder, uri, node_context, dispatcher); end
|
167
167
|
|
168
168
|
# Creates a new Completion listener. This method is invoked on every Completion request
|
169
169
|
sig do
|
170
170
|
overridable.params(
|
171
171
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
|
172
|
-
|
172
|
+
node_context: NodeContext,
|
173
173
|
dispatcher: Prism::Dispatcher,
|
174
174
|
uri: URI::Generic,
|
175
175
|
).void
|
176
176
|
end
|
177
|
-
def create_completion_listener(response_builder,
|
177
|
+
def create_completion_listener(response_builder, node_context, dispatcher, uri); end
|
178
178
|
end
|
179
179
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -110,7 +110,7 @@ module RubyLsp
|
|
110
110
|
params(
|
111
111
|
position: T::Hash[Symbol, T.untyped],
|
112
112
|
node_types: T::Array[T.class_of(Prism::Node)],
|
113
|
-
).returns(
|
113
|
+
).returns(NodeContext)
|
114
114
|
end
|
115
115
|
def locate_node(position, node_types: [])
|
116
116
|
locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
|
@@ -121,7 +121,7 @@ module RubyLsp
|
|
121
121
|
node: Prism::Node,
|
122
122
|
char_position: Integer,
|
123
123
|
node_types: T::Array[T.class_of(Prism::Node)],
|
124
|
-
).returns(
|
124
|
+
).returns(NodeContext)
|
125
125
|
end
|
126
126
|
def locate(node, char_position, node_types: [])
|
127
127
|
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
|
@@ -170,7 +170,7 @@ module RubyLsp
|
|
170
170
|
end
|
171
171
|
end
|
172
172
|
|
173
|
-
|
173
|
+
NodeContext.new(closest, parent, nesting.map { |n| n.constant_path.location.slice })
|
174
174
|
end
|
175
175
|
|
176
176
|
sig { returns(T::Boolean) }
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -11,17 +11,17 @@ module RubyLsp
|
|
11
11
|
params(
|
12
12
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
|
13
13
|
global_state: GlobalState,
|
14
|
-
|
14
|
+
node_context: NodeContext,
|
15
15
|
typechecker_enabled: T::Boolean,
|
16
16
|
dispatcher: Prism::Dispatcher,
|
17
17
|
uri: URI::Generic,
|
18
18
|
).void
|
19
19
|
end
|
20
|
-
def initialize(response_builder, global_state,
|
20
|
+
def initialize(response_builder, global_state, node_context, typechecker_enabled, dispatcher, uri) # rubocop:disable Metrics/ParameterLists
|
21
21
|
@response_builder = response_builder
|
22
22
|
@global_state = global_state
|
23
23
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
24
|
-
@
|
24
|
+
@node_context = node_context
|
25
25
|
@typechecker_enabled = typechecker_enabled
|
26
26
|
@uri = uri
|
27
27
|
|
@@ -30,6 +30,12 @@ module RubyLsp
|
|
30
30
|
:on_constant_path_node_enter,
|
31
31
|
:on_constant_read_node_enter,
|
32
32
|
:on_call_node_enter,
|
33
|
+
:on_instance_variable_read_node_enter,
|
34
|
+
:on_instance_variable_write_node_enter,
|
35
|
+
:on_instance_variable_and_write_node_enter,
|
36
|
+
:on_instance_variable_operator_write_node_enter,
|
37
|
+
:on_instance_variable_or_write_node_enter,
|
38
|
+
:on_instance_variable_target_node_enter,
|
33
39
|
)
|
34
40
|
end
|
35
41
|
|
@@ -41,7 +47,7 @@ module RubyLsp
|
|
41
47
|
name = constant_name(node)
|
42
48
|
return if name.nil?
|
43
49
|
|
44
|
-
candidates = @index.prefix_search(name, @nesting)
|
50
|
+
candidates = @index.prefix_search(name, @node_context.nesting)
|
45
51
|
candidates.each do |entries|
|
46
52
|
complete_name = T.must(entries.first).name
|
47
53
|
@response_builder << build_entry_completion(
|
@@ -105,6 +111,36 @@ module RubyLsp
|
|
105
111
|
end
|
106
112
|
end
|
107
113
|
|
114
|
+
sig { params(node: Prism::InstanceVariableReadNode).void }
|
115
|
+
def on_instance_variable_read_node_enter(node)
|
116
|
+
handle_instance_variable_completion(node.name.to_s, node.location)
|
117
|
+
end
|
118
|
+
|
119
|
+
sig { params(node: Prism::InstanceVariableWriteNode).void }
|
120
|
+
def on_instance_variable_write_node_enter(node)
|
121
|
+
handle_instance_variable_completion(node.name.to_s, node.name_loc)
|
122
|
+
end
|
123
|
+
|
124
|
+
sig { params(node: Prism::InstanceVariableAndWriteNode).void }
|
125
|
+
def on_instance_variable_and_write_node_enter(node)
|
126
|
+
handle_instance_variable_completion(node.name.to_s, node.name_loc)
|
127
|
+
end
|
128
|
+
|
129
|
+
sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
|
130
|
+
def on_instance_variable_operator_write_node_enter(node)
|
131
|
+
handle_instance_variable_completion(node.name.to_s, node.name_loc)
|
132
|
+
end
|
133
|
+
|
134
|
+
sig { params(node: Prism::InstanceVariableOrWriteNode).void }
|
135
|
+
def on_instance_variable_or_write_node_enter(node)
|
136
|
+
handle_instance_variable_completion(node.name.to_s, node.name_loc)
|
137
|
+
end
|
138
|
+
|
139
|
+
sig { params(node: Prism::InstanceVariableTargetNode).void }
|
140
|
+
def on_instance_variable_target_node_enter(node)
|
141
|
+
handle_instance_variable_completion(node.name.to_s, node.location)
|
142
|
+
end
|
143
|
+
|
108
144
|
private
|
109
145
|
|
110
146
|
sig { params(name: String, range: Interface::Range).void }
|
@@ -125,20 +161,21 @@ module RubyLsp
|
|
125
161
|
T.must(namespace).join("::")
|
126
162
|
end
|
127
163
|
|
128
|
-
|
164
|
+
nesting = @node_context.nesting
|
165
|
+
namespace_entries = @index.resolve(aliased_namespace, nesting)
|
129
166
|
return unless namespace_entries
|
130
167
|
|
131
168
|
real_namespace = @index.follow_aliased_namespace(T.must(namespace_entries.first).name)
|
132
169
|
|
133
170
|
candidates = @index.prefix_search(
|
134
171
|
"#{real_namespace}::#{incomplete_name}",
|
135
|
-
top_level_reference ? [] :
|
172
|
+
top_level_reference ? [] : nesting,
|
136
173
|
)
|
137
174
|
candidates.each do |entries|
|
138
175
|
# The only time we may have a private constant reference from outside of the namespace is if we're dealing
|
139
176
|
# with ConstantPath and the entry name doesn't start with the current nesting
|
140
177
|
first_entry = T.must(entries.first)
|
141
|
-
next if first_entry.
|
178
|
+
next if first_entry.private? && !first_entry.name.start_with?("#{nesting}::")
|
142
179
|
|
143
180
|
constant_name = first_entry.name.delete_prefix("#{real_namespace}::")
|
144
181
|
full_name = aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}"
|
@@ -153,6 +190,27 @@ module RubyLsp
|
|
153
190
|
end
|
154
191
|
end
|
155
192
|
|
193
|
+
sig { params(name: String, location: Prism::Location).void }
|
194
|
+
def handle_instance_variable_completion(name, location)
|
195
|
+
@index.instance_variable_completion_candidates(name, @node_context.fully_qualified_name).each do |entry|
|
196
|
+
variable_name = entry.name
|
197
|
+
|
198
|
+
@response_builder << Interface::CompletionItem.new(
|
199
|
+
label: variable_name,
|
200
|
+
text_edit: Interface::TextEdit.new(
|
201
|
+
range: range_from_location(location),
|
202
|
+
new_text: variable_name,
|
203
|
+
),
|
204
|
+
kind: Constant::CompletionItemKind::FIELD,
|
205
|
+
data: {
|
206
|
+
owner_name: entry.owner&.name,
|
207
|
+
},
|
208
|
+
)
|
209
|
+
end
|
210
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
211
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
212
|
+
end
|
213
|
+
|
156
214
|
sig { params(node: Prism::CallNode).void }
|
157
215
|
def complete_require(node)
|
158
216
|
arguments_node = node.arguments
|
@@ -200,15 +258,12 @@ module RubyLsp
|
|
200
258
|
|
201
259
|
sig { params(node: Prism::CallNode, name: String).void }
|
202
260
|
def complete_self_receiver_method(node, name)
|
203
|
-
receiver_entries = @index[@
|
261
|
+
receiver_entries = @index[@node_context.fully_qualified_name]
|
204
262
|
return unless receiver_entries
|
205
263
|
|
206
264
|
receiver = T.must(receiver_entries.first)
|
207
265
|
|
208
|
-
@index.
|
209
|
-
entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name }
|
210
|
-
next unless entry
|
211
|
-
|
266
|
+
@index.method_completion_candidates(name, receiver.name).each do |entry|
|
212
267
|
@response_builder << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
|
213
268
|
end
|
214
269
|
end
|
@@ -297,13 +352,14 @@ module RubyLsp
|
|
297
352
|
#
|
298
353
|
# Foo::B # --> completion inserts `Bar` instead of `Foo::Bar`
|
299
354
|
# end
|
300
|
-
|
301
|
-
|
355
|
+
nesting = @node_context.nesting
|
356
|
+
unless @node_context.fully_qualified_name.start_with?(incomplete_name)
|
357
|
+
nesting.each do |namespace|
|
302
358
|
prefix = "#{namespace}::"
|
303
359
|
shortened_name = insertion_text.delete_prefix(prefix)
|
304
360
|
|
305
361
|
# If a different entry exists for the shortened name, then there's a conflict and we should not shorten it
|
306
|
-
conflict_name = "#{@
|
362
|
+
conflict_name = "#{@node_context.fully_qualified_name}::#{shortened_name}"
|
307
363
|
break if real_name != conflict_name && @index[conflict_name]
|
308
364
|
|
309
365
|
insertion_text = shortened_name
|
@@ -345,8 +401,9 @@ module RubyLsp
|
|
345
401
|
# ```
|
346
402
|
sig { params(entry_name: String).returns(T::Boolean) }
|
347
403
|
def top_level?(entry_name)
|
348
|
-
|
349
|
-
|
404
|
+
nesting = @node_context.nesting
|
405
|
+
nesting.length.downto(0).each do |i|
|
406
|
+
prefix = T.must(nesting[0...i]).join("::")
|
350
407
|
full_name = prefix.empty? ? entry_name : "#{prefix}::#{entry_name}"
|
351
408
|
next if full_name == entry_name
|
352
409
|
|
@@ -14,17 +14,17 @@ module RubyLsp
|
|
14
14
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
|
15
15
|
global_state: GlobalState,
|
16
16
|
uri: URI::Generic,
|
17
|
-
|
17
|
+
node_context: NodeContext,
|
18
18
|
dispatcher: Prism::Dispatcher,
|
19
19
|
typechecker_enabled: T::Boolean,
|
20
20
|
).void
|
21
21
|
end
|
22
|
-
def initialize(response_builder, global_state, uri,
|
22
|
+
def initialize(response_builder, global_state, uri, node_context, dispatcher, typechecker_enabled) # rubocop:disable Metrics/ParameterLists
|
23
23
|
@response_builder = response_builder
|
24
24
|
@global_state = global_state
|
25
25
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
26
26
|
@uri = uri
|
27
|
-
@
|
27
|
+
@node_context = node_context
|
28
28
|
@typechecker_enabled = typechecker_enabled
|
29
29
|
|
30
30
|
dispatcher.register(
|
@@ -33,6 +33,12 @@ module RubyLsp
|
|
33
33
|
:on_block_argument_node_enter,
|
34
34
|
:on_constant_read_node_enter,
|
35
35
|
:on_constant_path_node_enter,
|
36
|
+
:on_instance_variable_read_node_enter,
|
37
|
+
:on_instance_variable_write_node_enter,
|
38
|
+
:on_instance_variable_and_write_node_enter,
|
39
|
+
:on_instance_variable_operator_write_node_enter,
|
40
|
+
:on_instance_variable_or_write_node_enter,
|
41
|
+
:on_instance_variable_target_node_enter,
|
36
42
|
)
|
37
43
|
end
|
38
44
|
|
@@ -74,12 +80,62 @@ module RubyLsp
|
|
74
80
|
find_in_index(name)
|
75
81
|
end
|
76
82
|
|
83
|
+
sig { params(node: Prism::InstanceVariableReadNode).void }
|
84
|
+
def on_instance_variable_read_node_enter(node)
|
85
|
+
handle_instance_variable_definition(node.name.to_s)
|
86
|
+
end
|
87
|
+
|
88
|
+
sig { params(node: Prism::InstanceVariableWriteNode).void }
|
89
|
+
def on_instance_variable_write_node_enter(node)
|
90
|
+
handle_instance_variable_definition(node.name.to_s)
|
91
|
+
end
|
92
|
+
|
93
|
+
sig { params(node: Prism::InstanceVariableAndWriteNode).void }
|
94
|
+
def on_instance_variable_and_write_node_enter(node)
|
95
|
+
handle_instance_variable_definition(node.name.to_s)
|
96
|
+
end
|
97
|
+
|
98
|
+
sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
|
99
|
+
def on_instance_variable_operator_write_node_enter(node)
|
100
|
+
handle_instance_variable_definition(node.name.to_s)
|
101
|
+
end
|
102
|
+
|
103
|
+
sig { params(node: Prism::InstanceVariableOrWriteNode).void }
|
104
|
+
def on_instance_variable_or_write_node_enter(node)
|
105
|
+
handle_instance_variable_definition(node.name.to_s)
|
106
|
+
end
|
107
|
+
|
108
|
+
sig { params(node: Prism::InstanceVariableTargetNode).void }
|
109
|
+
def on_instance_variable_target_node_enter(node)
|
110
|
+
handle_instance_variable_definition(node.name.to_s)
|
111
|
+
end
|
112
|
+
|
77
113
|
private
|
78
114
|
|
115
|
+
sig { params(name: String).void }
|
116
|
+
def handle_instance_variable_definition(name)
|
117
|
+
entries = @index.resolve_instance_variable(name, @node_context.fully_qualified_name)
|
118
|
+
return unless entries
|
119
|
+
|
120
|
+
entries.each do |entry|
|
121
|
+
location = entry.location
|
122
|
+
|
123
|
+
@response_builder << Interface::Location.new(
|
124
|
+
uri: URI::Generic.from_path(path: entry.file_path).to_s,
|
125
|
+
range: Interface::Range.new(
|
126
|
+
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
127
|
+
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
128
|
+
),
|
129
|
+
)
|
130
|
+
end
|
131
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
132
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
133
|
+
end
|
134
|
+
|
79
135
|
sig { params(message: String, self_receiver: T::Boolean).void }
|
80
136
|
def handle_method_definition(message, self_receiver)
|
81
137
|
methods = if self_receiver
|
82
|
-
@index.resolve_method(message, @
|
138
|
+
@index.resolve_method(message, @node_context.fully_qualified_name)
|
83
139
|
else
|
84
140
|
# If the method doesn't have a receiver, then we provide a few candidates to jump to
|
85
141
|
# But we don't want to provide too many candidates, as it can be overwhelming
|
@@ -147,13 +203,13 @@ module RubyLsp
|
|
147
203
|
|
148
204
|
sig { params(value: String).void }
|
149
205
|
def find_in_index(value)
|
150
|
-
entries = @index.resolve(value, @nesting)
|
206
|
+
entries = @index.resolve(value, @node_context.nesting)
|
151
207
|
return unless entries
|
152
208
|
|
153
209
|
# We should only allow jumping to the definition of private constants if the constant is defined in the same
|
154
210
|
# namespace as the reference
|
155
211
|
first_entry = T.must(entries.first)
|
156
|
-
return if first_entry.
|
212
|
+
return if first_entry.private? && first_entry.name != "#{@node_context.fully_qualified_name}::#{value}"
|
157
213
|
|
158
214
|
entries.each do |entry|
|
159
215
|
location = entry.location
|
@@ -13,6 +13,12 @@ module RubyLsp
|
|
13
13
|
Prism::ConstantReadNode,
|
14
14
|
Prism::ConstantWriteNode,
|
15
15
|
Prism::ConstantPathNode,
|
16
|
+
Prism::InstanceVariableReadNode,
|
17
|
+
Prism::InstanceVariableAndWriteNode,
|
18
|
+
Prism::InstanceVariableOperatorWriteNode,
|
19
|
+
Prism::InstanceVariableOrWriteNode,
|
20
|
+
Prism::InstanceVariableTargetNode,
|
21
|
+
Prism::InstanceVariableWriteNode,
|
16
22
|
],
|
17
23
|
T::Array[T.class_of(Prism::Node)],
|
18
24
|
)
|
@@ -30,17 +36,17 @@ module RubyLsp
|
|
30
36
|
response_builder: ResponseBuilders::Hover,
|
31
37
|
global_state: GlobalState,
|
32
38
|
uri: URI::Generic,
|
33
|
-
|
39
|
+
node_context: NodeContext,
|
34
40
|
dispatcher: Prism::Dispatcher,
|
35
41
|
typechecker_enabled: T::Boolean,
|
36
42
|
).void
|
37
43
|
end
|
38
|
-
def initialize(response_builder, global_state, uri,
|
44
|
+
def initialize(response_builder, global_state, uri, node_context, dispatcher, typechecker_enabled) # rubocop:disable Metrics/ParameterLists
|
39
45
|
@response_builder = response_builder
|
40
46
|
@global_state = global_state
|
41
47
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
42
48
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
43
|
-
@
|
49
|
+
@node_context = node_context
|
44
50
|
@typechecker_enabled = typechecker_enabled
|
45
51
|
|
46
52
|
dispatcher.register(
|
@@ -49,6 +55,12 @@ module RubyLsp
|
|
49
55
|
:on_constant_write_node_enter,
|
50
56
|
:on_constant_path_node_enter,
|
51
57
|
:on_call_node_enter,
|
58
|
+
:on_instance_variable_read_node_enter,
|
59
|
+
:on_instance_variable_write_node_enter,
|
60
|
+
:on_instance_variable_and_write_node_enter,
|
61
|
+
:on_instance_variable_operator_write_node_enter,
|
62
|
+
:on_instance_variable_or_write_node_enter,
|
63
|
+
:on_instance_variable_target_node_enter,
|
52
64
|
)
|
53
65
|
end
|
54
66
|
|
@@ -93,7 +105,7 @@ module RubyLsp
|
|
93
105
|
message = node.message
|
94
106
|
return unless message
|
95
107
|
|
96
|
-
methods = @index.resolve_method(message, @
|
108
|
+
methods = @index.resolve_method(message, @node_context.fully_qualified_name)
|
97
109
|
return unless methods
|
98
110
|
|
99
111
|
categorized_markdown_from_index_entries(message, methods).each do |category, content|
|
@@ -101,17 +113,59 @@ module RubyLsp
|
|
101
113
|
end
|
102
114
|
end
|
103
115
|
|
116
|
+
sig { params(node: Prism::InstanceVariableReadNode).void }
|
117
|
+
def on_instance_variable_read_node_enter(node)
|
118
|
+
handle_instance_variable_hover(node.name.to_s)
|
119
|
+
end
|
120
|
+
|
121
|
+
sig { params(node: Prism::InstanceVariableWriteNode).void }
|
122
|
+
def on_instance_variable_write_node_enter(node)
|
123
|
+
handle_instance_variable_hover(node.name.to_s)
|
124
|
+
end
|
125
|
+
|
126
|
+
sig { params(node: Prism::InstanceVariableAndWriteNode).void }
|
127
|
+
def on_instance_variable_and_write_node_enter(node)
|
128
|
+
handle_instance_variable_hover(node.name.to_s)
|
129
|
+
end
|
130
|
+
|
131
|
+
sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
|
132
|
+
def on_instance_variable_operator_write_node_enter(node)
|
133
|
+
handle_instance_variable_hover(node.name.to_s)
|
134
|
+
end
|
135
|
+
|
136
|
+
sig { params(node: Prism::InstanceVariableOrWriteNode).void }
|
137
|
+
def on_instance_variable_or_write_node_enter(node)
|
138
|
+
handle_instance_variable_hover(node.name.to_s)
|
139
|
+
end
|
140
|
+
|
141
|
+
sig { params(node: Prism::InstanceVariableTargetNode).void }
|
142
|
+
def on_instance_variable_target_node_enter(node)
|
143
|
+
handle_instance_variable_hover(node.name.to_s)
|
144
|
+
end
|
145
|
+
|
104
146
|
private
|
105
147
|
|
148
|
+
sig { params(name: String).void }
|
149
|
+
def handle_instance_variable_hover(name)
|
150
|
+
entries = @index.resolve_instance_variable(name, @node_context.fully_qualified_name)
|
151
|
+
return unless entries
|
152
|
+
|
153
|
+
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
154
|
+
@response_builder.push(content, category: category)
|
155
|
+
end
|
156
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
157
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
158
|
+
end
|
159
|
+
|
106
160
|
sig { params(name: String, location: Prism::Location).void }
|
107
161
|
def generate_hover(name, location)
|
108
|
-
entries = @index.resolve(name, @nesting)
|
162
|
+
entries = @index.resolve(name, @node_context.nesting)
|
109
163
|
return unless entries
|
110
164
|
|
111
165
|
# We should only show hover for private constants if the constant is defined in the same namespace as the
|
112
166
|
# reference
|
113
167
|
first_entry = T.must(entries.first)
|
114
|
-
return if first_entry.
|
168
|
+
return if first_entry.private? && first_entry.name != "#{@node_context.fully_qualified_name}::#{name}"
|
115
169
|
|
116
170
|
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
117
171
|
@response_builder.push(content, category: category)
|
@@ -11,17 +11,17 @@ module RubyLsp
|
|
11
11
|
params(
|
12
12
|
response_builder: ResponseBuilders::SignatureHelp,
|
13
13
|
global_state: GlobalState,
|
14
|
-
|
14
|
+
node_context: NodeContext,
|
15
15
|
dispatcher: Prism::Dispatcher,
|
16
16
|
typechecker_enabled: T::Boolean,
|
17
17
|
).void
|
18
18
|
end
|
19
|
-
def initialize(response_builder, global_state,
|
19
|
+
def initialize(response_builder, global_state, node_context, dispatcher, typechecker_enabled)
|
20
20
|
@typechecker_enabled = typechecker_enabled
|
21
21
|
@response_builder = response_builder
|
22
22
|
@global_state = global_state
|
23
23
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
24
|
-
@
|
24
|
+
@node_context = node_context
|
25
25
|
dispatcher.register(self, :on_call_node_enter)
|
26
26
|
end
|
27
27
|
|
@@ -33,7 +33,7 @@ module RubyLsp
|
|
33
33
|
message = node.message
|
34
34
|
return unless message
|
35
35
|
|
36
|
-
methods = @index.resolve_method(message, @
|
36
|
+
methods = @index.resolve_method(message, @node_context.fully_qualified_name)
|
37
37
|
return unless methods
|
38
38
|
|
39
39
|
target_method = methods.first
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
# This class allows listeners to access contextual information about a node in the AST, such as its parent
|
6
|
+
# and its namespace nesting.
|
7
|
+
class NodeContext
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { returns(T.nilable(Prism::Node)) }
|
11
|
+
attr_reader :node, :parent
|
12
|
+
|
13
|
+
sig { returns(T::Array[String]) }
|
14
|
+
attr_reader :nesting
|
15
|
+
|
16
|
+
sig { params(node: T.nilable(Prism::Node), parent: T.nilable(Prism::Node), nesting: T::Array[String]).void }
|
17
|
+
def initialize(node, parent, nesting)
|
18
|
+
@node = node
|
19
|
+
@parent = parent
|
20
|
+
@nesting = nesting
|
21
|
+
end
|
22
|
+
|
23
|
+
sig { returns(String) }
|
24
|
+
def fully_qualified_name
|
25
|
+
@fully_qualified_name ||= T.let(@nesting.join("::"), T.nilable(String))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|