ruby-lsp 0.16.7 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|