ruby-lsp 0.17.4 → 0.17.13
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 +11 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +26 -1
- data/exe/ruby-lsp-check +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +74 -43
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +26 -0
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +147 -29
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +383 -79
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +195 -61
- data/lib/ruby_indexer/ruby_indexer.rb +1 -8
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +71 -3
- data/lib/ruby_indexer/test/configuration_test.rb +1 -1
- data/lib/ruby_indexer/test/constant_test.rb +17 -17
- data/lib/ruby_indexer/test/enhancements_test.rb +197 -0
- data/lib/ruby_indexer/test/index_test.rb +367 -17
- data/lib/ruby_indexer/test/method_test.rb +58 -25
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +297 -0
- data/lib/ruby_indexer/test/test_case.rb +1 -5
- data/lib/ruby_lsp/addon.rb +22 -5
- data/lib/ruby_lsp/base_server.rb +8 -3
- data/lib/ruby_lsp/document.rb +27 -46
- data/lib/ruby_lsp/erb_document.rb +125 -0
- data/lib/ruby_lsp/global_state.rb +47 -19
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/completion.rb +161 -57
- data/lib/ruby_lsp/listeners/definition.rb +91 -27
- data/lib/ruby_lsp/listeners/document_highlight.rb +5 -1
- data/lib/ruby_lsp/listeners/hover.rb +61 -19
- data/lib/ruby_lsp/listeners/signature_help.rb +13 -6
- data/lib/ruby_lsp/node_context.rb +65 -5
- data/lib/ruby_lsp/requests/code_action_resolve.rb +107 -9
- data/lib/ruby_lsp/requests/code_actions.rb +11 -2
- data/lib/ruby_lsp/requests/completion.rb +4 -4
- data/lib/ruby_lsp/requests/completion_resolve.rb +14 -9
- data/lib/ruby_lsp/requests/definition.rb +18 -8
- data/lib/ruby_lsp/requests/diagnostics.rb +6 -5
- data/lib/ruby_lsp/requests/document_symbol.rb +2 -7
- data/lib/ruby_lsp/requests/folding_ranges.rb +6 -2
- data/lib/ruby_lsp/requests/formatting.rb +15 -0
- data/lib/ruby_lsp/requests/hover.rb +5 -5
- data/lib/ruby_lsp/requests/on_type_formatting.rb +6 -4
- data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -2
- data/lib/ruby_lsp/requests/signature_help.rb +3 -3
- data/lib/ruby_lsp/requests/support/common.rb +11 -2
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +2 -6
- data/lib/ruby_lsp/ruby_document.rb +74 -0
- data/lib/ruby_lsp/server.rb +129 -54
- data/lib/ruby_lsp/store.rb +33 -9
- data/lib/ruby_lsp/test_helper.rb +3 -1
- data/lib/ruby_lsp/type_inferrer.rb +61 -25
- data/lib/ruby_lsp/utils.rb +13 -0
- metadata +9 -8
- data/exe/ruby-lsp-doctor +0 -23
@@ -7,12 +7,56 @@ module RubyLsp
|
|
7
7
|
extend T::Sig
|
8
8
|
include Requests::Support::Common
|
9
9
|
|
10
|
+
KEYWORDS = [
|
11
|
+
"alias",
|
12
|
+
"and",
|
13
|
+
"begin",
|
14
|
+
"BEGIN",
|
15
|
+
"break",
|
16
|
+
"case",
|
17
|
+
"class",
|
18
|
+
"def",
|
19
|
+
"defined?",
|
20
|
+
"do",
|
21
|
+
"else",
|
22
|
+
"elsif",
|
23
|
+
"end",
|
24
|
+
"END",
|
25
|
+
"ensure",
|
26
|
+
"false",
|
27
|
+
"for",
|
28
|
+
"if",
|
29
|
+
"in",
|
30
|
+
"module",
|
31
|
+
"next",
|
32
|
+
"nil",
|
33
|
+
"not",
|
34
|
+
"or",
|
35
|
+
"redo",
|
36
|
+
"rescue",
|
37
|
+
"retry",
|
38
|
+
"return",
|
39
|
+
"self",
|
40
|
+
"super",
|
41
|
+
"then",
|
42
|
+
"true",
|
43
|
+
"undef",
|
44
|
+
"unless",
|
45
|
+
"until",
|
46
|
+
"when",
|
47
|
+
"while",
|
48
|
+
"yield",
|
49
|
+
"__ENCODING__",
|
50
|
+
"__FILE__",
|
51
|
+
"__LINE__",
|
52
|
+
].freeze
|
53
|
+
|
10
54
|
sig do
|
11
55
|
params(
|
12
56
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
|
13
57
|
global_state: GlobalState,
|
14
58
|
node_context: NodeContext,
|
15
|
-
|
59
|
+
sorbet_level: RubyDocument::SorbetLevel,
|
16
60
|
dispatcher: Prism::Dispatcher,
|
17
61
|
uri: URI::Generic,
|
18
62
|
trigger_character: T.nilable(String),
|
@@ -22,7 +66,7 @@ module RubyLsp
|
|
22
66
|
response_builder,
|
23
67
|
global_state,
|
24
68
|
node_context,
|
25
|
-
|
69
|
+
sorbet_level,
|
26
70
|
dispatcher,
|
27
71
|
uri,
|
28
72
|
trigger_character
|
@@ -32,7 +76,7 @@ module RubyLsp
|
|
32
76
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
33
77
|
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
34
78
|
@node_context = node_context
|
35
|
-
@
|
79
|
+
@sorbet_level = sorbet_level
|
36
80
|
@uri = uri
|
37
81
|
@trigger_character = trigger_character
|
38
82
|
|
@@ -53,7 +97,9 @@ module RubyLsp
|
|
53
97
|
# Handle completion on regular constant references (e.g. `Bar`)
|
54
98
|
sig { params(node: Prism::ConstantReadNode).void }
|
55
99
|
def on_constant_read_node_enter(node)
|
56
|
-
|
100
|
+
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
101
|
+
# no sigil, Sorbet will still provide completion for constants
|
102
|
+
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
57
103
|
|
58
104
|
name = constant_name(node)
|
59
105
|
return if name.nil?
|
@@ -74,7 +120,9 @@ module RubyLsp
|
|
74
120
|
# Handle completion on namespaced constant references (e.g. `Foo::Bar`)
|
75
121
|
sig { params(node: Prism::ConstantPathNode).void }
|
76
122
|
def on_constant_path_node_enter(node)
|
77
|
-
|
123
|
+
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
124
|
+
# no sigil, Sorbet will still provide completion for constants
|
125
|
+
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
78
126
|
|
79
127
|
name = constant_name(node)
|
80
128
|
return if name.nil?
|
@@ -84,28 +132,32 @@ module RubyLsp
|
|
84
132
|
|
85
133
|
sig { params(node: Prism::CallNode).void }
|
86
134
|
def on_call_node_enter(node)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
135
|
+
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
136
|
+
# no sigil, Sorbet will still provide completion for constants
|
137
|
+
if @sorbet_level == RubyDocument::SorbetLevel::Ignore
|
138
|
+
receiver = node.receiver
|
139
|
+
|
140
|
+
# When writing `Foo::`, the AST assigns a method call node (because you can use that syntax to invoke
|
141
|
+
# singleton methods). However, in addition to providing method completion, we also need to show possible
|
142
|
+
# constant completions
|
143
|
+
if (receiver.is_a?(Prism::ConstantReadNode) || receiver.is_a?(Prism::ConstantPathNode)) &&
|
144
|
+
node.call_operator == "::"
|
145
|
+
|
146
|
+
name = constant_name(receiver)
|
147
|
+
|
148
|
+
if name
|
149
|
+
start_loc = node.location
|
150
|
+
end_loc = T.must(node.call_operator_loc)
|
151
|
+
|
152
|
+
constant_path_completion(
|
153
|
+
"#{name}::",
|
154
|
+
Interface::Range.new(
|
155
|
+
start: Interface::Position.new(line: start_loc.start_line - 1, character: start_loc.start_column),
|
156
|
+
end: Interface::Position.new(line: end_loc.end_line - 1, character: end_loc.end_column),
|
157
|
+
),
|
158
|
+
)
|
159
|
+
return
|
160
|
+
end
|
109
161
|
end
|
110
162
|
end
|
111
163
|
|
@@ -118,7 +170,7 @@ module RubyLsp
|
|
118
170
|
when "require_relative"
|
119
171
|
complete_require_relative(node)
|
120
172
|
else
|
121
|
-
complete_methods(node, name)
|
173
|
+
complete_methods(node, name)
|
122
174
|
end
|
123
175
|
end
|
124
176
|
|
@@ -203,14 +255,23 @@ module RubyLsp
|
|
203
255
|
|
204
256
|
sig { params(name: String, location: Prism::Location).void }
|
205
257
|
def handle_instance_variable_completion(name, location)
|
258
|
+
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
259
|
+
# to provide all features for them
|
260
|
+
return if @sorbet_level == RubyDocument::SorbetLevel::Strict
|
261
|
+
|
206
262
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
207
263
|
return unless type
|
208
264
|
|
209
|
-
@index.instance_variable_completion_candidates(name, type).each do |entry|
|
265
|
+
@index.instance_variable_completion_candidates(name, type.name).each do |entry|
|
210
266
|
variable_name = entry.name
|
211
267
|
|
268
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
269
|
+
description: entry.file_name,
|
270
|
+
)
|
271
|
+
|
212
272
|
@response_builder << Interface::CompletionItem.new(
|
213
273
|
label: variable_name,
|
274
|
+
label_details: label_details,
|
214
275
|
text_edit: Interface::TextEdit.new(
|
215
276
|
range: range_from_location(location),
|
216
277
|
new_text: variable_name,
|
@@ -272,6 +333,16 @@ module RubyLsp
|
|
272
333
|
|
273
334
|
sig { params(node: Prism::CallNode, name: String).void }
|
274
335
|
def complete_methods(node, name)
|
336
|
+
# If the node has a receiver, then we don't need to provide local nor keyword completions. Sorbet can provide
|
337
|
+
# local and keyword completion for any file with a Sorbet level of true or higher
|
338
|
+
if !sorbet_level_true_or_higher?(@sorbet_level) && !node.receiver
|
339
|
+
add_local_completions(node, name)
|
340
|
+
add_keyword_completions(node, name)
|
341
|
+
end
|
342
|
+
|
343
|
+
# Sorbet can provide completion for methods invoked on self on typed true or higher files
|
344
|
+
return if sorbet_level_true_or_higher?(@sorbet_level) && self_receiver?(node)
|
345
|
+
|
275
346
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
276
347
|
return unless type
|
277
348
|
|
@@ -283,23 +354,37 @@ module RubyLsp
|
|
283
354
|
range = if method_name
|
284
355
|
range_from_location(T.must(node.message_loc))
|
285
356
|
else
|
286
|
-
loc =
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
357
|
+
loc = node.call_operator_loc
|
358
|
+
|
359
|
+
if loc
|
360
|
+
Interface::Range.new(
|
361
|
+
start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column + 1),
|
362
|
+
end: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column + 1),
|
363
|
+
)
|
364
|
+
end
|
291
365
|
end
|
292
366
|
|
293
|
-
|
367
|
+
return unless range
|
368
|
+
|
369
|
+
guessed_type = type.name
|
370
|
+
|
371
|
+
@index.method_completion_candidates(method_name, type.name).each do |entry|
|
294
372
|
entry_name = entry.name
|
373
|
+
owner_name = entry.owner&.name
|
295
374
|
|
375
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
376
|
+
description: entry.file_name,
|
377
|
+
detail: entry.decorated_parameters,
|
378
|
+
)
|
296
379
|
@response_builder << Interface::CompletionItem.new(
|
297
380
|
label: entry_name,
|
298
381
|
filter_text: entry_name,
|
382
|
+
label_details: label_details,
|
299
383
|
text_edit: Interface::TextEdit.new(range: range, new_text: entry_name),
|
300
384
|
kind: Constant::CompletionItemKind::METHOD,
|
301
385
|
data: {
|
302
|
-
owner_name:
|
386
|
+
owner_name: owner_name,
|
387
|
+
guessed_type: guessed_type,
|
303
388
|
},
|
304
389
|
)
|
305
390
|
end
|
@@ -307,29 +392,42 @@ module RubyLsp
|
|
307
392
|
# We have not indexed this namespace, so we can't provide any completions
|
308
393
|
end
|
309
394
|
|
310
|
-
sig
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
395
|
+
sig { params(node: Prism::CallNode, name: String).void }
|
396
|
+
def add_local_completions(node, name)
|
397
|
+
range = range_from_location(T.must(node.message_loc))
|
398
|
+
|
399
|
+
@node_context.locals_for_scope.each do |local|
|
400
|
+
local_name = local.to_s
|
401
|
+
next unless local_name.start_with?(name)
|
402
|
+
|
403
|
+
@response_builder << Interface::CompletionItem.new(
|
404
|
+
label: local_name,
|
405
|
+
filter_text: local_name,
|
406
|
+
text_edit: Interface::TextEdit.new(range: range, new_text: local_name),
|
407
|
+
kind: Constant::CompletionItemKind::VARIABLE,
|
408
|
+
data: {
|
409
|
+
skip_resolve: true,
|
410
|
+
},
|
411
|
+
)
|
412
|
+
end
|
315
413
|
end
|
316
|
-
def build_method_completion(entry, node)
|
317
|
-
name = entry.name
|
318
414
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
kind:
|
330
|
-
|
331
|
-
|
332
|
-
|
415
|
+
sig { params(node: Prism::CallNode, name: String).void }
|
416
|
+
def add_keyword_completions(node, name)
|
417
|
+
range = range_from_location(T.must(node.message_loc))
|
418
|
+
|
419
|
+
KEYWORDS.each do |keyword|
|
420
|
+
next unless keyword.start_with?(name)
|
421
|
+
|
422
|
+
@response_builder << Interface::CompletionItem.new(
|
423
|
+
label: keyword,
|
424
|
+
text_edit: Interface::TextEdit.new(range: range, new_text: keyword),
|
425
|
+
kind: Constant::CompletionItemKind::KEYWORD,
|
426
|
+
data: {
|
427
|
+
skip_resolve: true,
|
428
|
+
},
|
429
|
+
)
|
430
|
+
end
|
333
431
|
end
|
334
432
|
|
335
433
|
sig { params(label: String, node: Prism::StringNode).returns(Interface::CompletionItem) }
|
@@ -413,8 +511,14 @@ module RubyLsp
|
|
413
511
|
# When using a top level constant reference (e.g.: `::Bar`), the editor includes the `::` as part of the filter.
|
414
512
|
# For these top level references, we need to include the `::` as part of the filter text or else it won't match
|
415
513
|
# the right entries in the index
|
514
|
+
|
515
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
516
|
+
description: entries.map(&:file_name).join(","),
|
517
|
+
)
|
518
|
+
|
416
519
|
Interface::CompletionItem.new(
|
417
520
|
label: real_name,
|
521
|
+
label_details: label_details,
|
418
522
|
filter_text: filter_text,
|
419
523
|
text_edit: Interface::TextEdit.new(
|
420
524
|
range: range,
|
@@ -11,22 +11,27 @@ module RubyLsp
|
|
11
11
|
|
12
12
|
sig do
|
13
13
|
params(
|
14
|
-
response_builder: ResponseBuilders::CollectionResponseBuilder[
|
14
|
+
response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
|
15
|
+
Interface::Location,
|
16
|
+
Interface::LocationLink,
|
17
|
+
)],
|
15
18
|
global_state: GlobalState,
|
19
|
+
language_id: Document::LanguageId,
|
16
20
|
uri: URI::Generic,
|
17
21
|
node_context: NodeContext,
|
18
22
|
dispatcher: Prism::Dispatcher,
|
19
|
-
|
23
|
+
sorbet_level: RubyDocument::SorbetLevel,
|
20
24
|
).void
|
21
25
|
end
|
22
|
-
def initialize(response_builder, global_state, uri, node_context, dispatcher,
|
26
|
+
def initialize(response_builder, global_state, language_id, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
23
27
|
@response_builder = response_builder
|
24
28
|
@global_state = global_state
|
25
29
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
26
30
|
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
31
|
+
@language_id = language_id
|
27
32
|
@uri = uri
|
28
33
|
@node_context = node_context
|
29
|
-
@
|
34
|
+
@sorbet_level = sorbet_level
|
30
35
|
|
31
36
|
dispatcher.register(
|
32
37
|
self,
|
@@ -41,15 +46,29 @@ module RubyLsp
|
|
41
46
|
:on_instance_variable_or_write_node_enter,
|
42
47
|
:on_instance_variable_target_node_enter,
|
43
48
|
:on_string_node_enter,
|
49
|
+
:on_symbol_node_enter,
|
50
|
+
:on_super_node_enter,
|
51
|
+
:on_forwarding_super_node_enter,
|
44
52
|
)
|
45
53
|
end
|
46
54
|
|
47
55
|
sig { params(node: Prism::CallNode).void }
|
48
56
|
def on_call_node_enter(node)
|
57
|
+
# Sorbet can handle go to definition for methods invoked on self on typed true or higher
|
58
|
+
return if sorbet_level_true_or_higher?(@sorbet_level) && self_receiver?(node)
|
59
|
+
|
49
60
|
message = node.message
|
50
61
|
return unless message
|
51
62
|
|
52
|
-
|
63
|
+
inferrer_receiver_type = @type_inferrer.infer_receiver_type(@node_context)
|
64
|
+
|
65
|
+
# Until we can properly infer the receiver type in erb files (maybe with ruby-lsp-rails),
|
66
|
+
# treating method calls' type as `nil` will allow users to get some completion support first
|
67
|
+
if @language_id == Document::LanguageId::ERB && inferrer_receiver_type&.name == "Object"
|
68
|
+
inferrer_receiver_type = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
handle_method_definition(message, inferrer_receiver_type)
|
53
72
|
end
|
54
73
|
|
55
74
|
sig { params(node: Prism::StringNode).void }
|
@@ -63,6 +82,17 @@ module RubyLsp
|
|
63
82
|
handle_require_definition(node, name)
|
64
83
|
end
|
65
84
|
|
85
|
+
sig { params(node: Prism::SymbolNode).void }
|
86
|
+
def on_symbol_node_enter(node)
|
87
|
+
enclosing_call = @node_context.call_node
|
88
|
+
return unless enclosing_call
|
89
|
+
|
90
|
+
name = enclosing_call.name
|
91
|
+
return unless name == :autoload
|
92
|
+
|
93
|
+
handle_autoload_definition(enclosing_call)
|
94
|
+
end
|
95
|
+
|
66
96
|
sig { params(node: Prism::BlockArgumentNode).void }
|
67
97
|
def on_block_argument_node_enter(node)
|
68
98
|
expression = node.expression
|
@@ -120,14 +150,43 @@ module RubyLsp
|
|
120
150
|
handle_instance_variable_definition(node.name.to_s)
|
121
151
|
end
|
122
152
|
|
153
|
+
sig { params(node: Prism::SuperNode).void }
|
154
|
+
def on_super_node_enter(node)
|
155
|
+
handle_super_node_definition
|
156
|
+
end
|
157
|
+
|
158
|
+
sig { params(node: Prism::ForwardingSuperNode).void }
|
159
|
+
def on_forwarding_super_node_enter(node)
|
160
|
+
handle_super_node_definition
|
161
|
+
end
|
162
|
+
|
123
163
|
private
|
124
164
|
|
165
|
+
sig { void }
|
166
|
+
def handle_super_node_definition
|
167
|
+
# Sorbet can handle super hover on typed true or higher
|
168
|
+
return if sorbet_level_true_or_higher?(@sorbet_level)
|
169
|
+
|
170
|
+
surrounding_method = @node_context.surrounding_method
|
171
|
+
return unless surrounding_method
|
172
|
+
|
173
|
+
handle_method_definition(
|
174
|
+
surrounding_method,
|
175
|
+
@type_inferrer.infer_receiver_type(@node_context),
|
176
|
+
inherited_only: true,
|
177
|
+
)
|
178
|
+
end
|
179
|
+
|
125
180
|
sig { params(name: String).void }
|
126
181
|
def handle_instance_variable_definition(name)
|
182
|
+
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
183
|
+
# to provide all features for them
|
184
|
+
return if @sorbet_level == RubyDocument::SorbetLevel::Strict
|
185
|
+
|
127
186
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
128
187
|
return unless type
|
129
188
|
|
130
|
-
entries = @index.resolve_instance_variable(name, type)
|
189
|
+
entries = @index.resolve_instance_variable(name, type.name)
|
131
190
|
return unless entries
|
132
191
|
|
133
192
|
entries.each do |entry|
|
@@ -145,10 +204,10 @@ module RubyLsp
|
|
145
204
|
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
146
205
|
end
|
147
206
|
|
148
|
-
sig { params(message: String, receiver_type: T.nilable(
|
149
|
-
def handle_method_definition(message, receiver_type)
|
207
|
+
sig { params(message: String, receiver_type: T.nilable(TypeInferrer::Type), inherited_only: T::Boolean).void }
|
208
|
+
def handle_method_definition(message, receiver_type, inherited_only: false)
|
150
209
|
methods = if receiver_type
|
151
|
-
@index.resolve_method(message, receiver_type)
|
210
|
+
@index.resolve_method(message, receiver_type.name, inherited_only: inherited_only)
|
152
211
|
else
|
153
212
|
# If the method doesn't have a receiver, then we provide a few candidates to jump to
|
154
213
|
# But we don't want to provide too many candidates, as it can be overwhelming
|
@@ -158,16 +217,13 @@ module RubyLsp
|
|
158
217
|
return unless methods
|
159
218
|
|
160
219
|
methods.each do |target_method|
|
161
|
-
location = target_method.location
|
162
220
|
file_path = target_method.file_path
|
163
|
-
next if @
|
221
|
+
next if sorbet_level_true_or_higher?(@sorbet_level) && not_in_dependencies?(file_path)
|
164
222
|
|
165
|
-
@response_builder << Interface::
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
170
|
-
),
|
223
|
+
@response_builder << Interface::LocationLink.new(
|
224
|
+
target_uri: URI::Generic.from_path(path: file_path).to_s,
|
225
|
+
target_range: range_from_location(target_method.location),
|
226
|
+
target_selection_range: range_from_location(target_method.name_location),
|
171
227
|
)
|
172
228
|
end
|
173
229
|
end
|
@@ -207,6 +263,17 @@ module RubyLsp
|
|
207
263
|
end
|
208
264
|
end
|
209
265
|
|
266
|
+
sig { params(node: Prism::CallNode).void }
|
267
|
+
def handle_autoload_definition(node)
|
268
|
+
argument = node.arguments&.arguments&.first
|
269
|
+
return unless argument.is_a?(Prism::SymbolNode)
|
270
|
+
|
271
|
+
constant_name = argument.value
|
272
|
+
return unless constant_name
|
273
|
+
|
274
|
+
find_in_index(constant_name)
|
275
|
+
end
|
276
|
+
|
210
277
|
sig { params(value: String).void }
|
211
278
|
def find_in_index(value)
|
212
279
|
entries = @index.resolve(value, @node_context.nesting)
|
@@ -218,19 +285,16 @@ module RubyLsp
|
|
218
285
|
return if first_entry.private? && first_entry.name != "#{@node_context.fully_qualified_name}::#{value}"
|
219
286
|
|
220
287
|
entries.each do |entry|
|
221
|
-
location = entry.location
|
222
288
|
# If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
|
223
|
-
# additional behavior on top of jumping to RBIs.
|
224
|
-
#
|
289
|
+
# additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
|
290
|
+
# ignore
|
225
291
|
file_path = entry.file_path
|
226
|
-
next if @
|
292
|
+
next if @sorbet_level != RubyDocument::SorbetLevel::Ignore && not_in_dependencies?(file_path)
|
227
293
|
|
228
|
-
@response_builder << Interface::
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
233
|
-
),
|
294
|
+
@response_builder << Interface::LocationLink.new(
|
295
|
+
target_uri: URI::Generic.from_path(path: file_path).to_s,
|
296
|
+
target_range: range_from_location(entry.location),
|
297
|
+
target_selection_range: range_from_location(entry.name_location),
|
234
298
|
)
|
235
299
|
end
|
236
300
|
end
|
@@ -180,7 +180,11 @@ module RubyLsp
|
|
180
180
|
def on_call_node_enter(node)
|
181
181
|
return unless matches?(node, [Prism::CallNode, Prism::DefNode])
|
182
182
|
|
183
|
-
|
183
|
+
loc = node.message_loc
|
184
|
+
# if we have `foo.` it's a call node but there is no message yet.
|
185
|
+
return unless loc
|
186
|
+
|
187
|
+
add_highlight(Constant::DocumentHighlightKind::READ, loc)
|
184
188
|
end
|
185
189
|
|
186
190
|
sig { params(node: Prism::DefNode).void }
|