ruby-lsp 0.17.3 → 0.17.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +241 -91
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +74 -102
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +81 -19
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +50 -2
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +46 -0
- data/lib/ruby_indexer/test/index_test.rb +326 -27
- data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
- data/lib/ruby_indexer/test/method_test.rb +54 -24
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +27 -2
- data/lib/ruby_lsp/document.rb +37 -8
- data/lib/ruby_lsp/global_state.rb +7 -3
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/listeners/completion.rb +53 -14
- data/lib/ruby_lsp/listeners/definition.rb +11 -7
- data/lib/ruby_lsp/listeners/hover.rb +14 -7
- data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
- data/lib/ruby_lsp/node_context.rb +6 -1
- data/lib/ruby_lsp/requests/completion.rb +5 -4
- data/lib/ruby_lsp/requests/completion_resolve.rb +8 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
- data/lib/ruby_lsp/requests/support/common.rb +19 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +91 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
- data/lib/ruby_lsp/requests.rb +2 -0
- data/lib/ruby_lsp/server.rb +54 -15
- data/lib/ruby_lsp/test_helper.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +86 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71204369ec5201376f1af11875a2e1bf16df56a31719ea78932a7f5f6e9c7e5c
|
4
|
+
data.tar.gz: d83e299ade275415fe4cac25abe78f7ee7ec622c47cc1699d42b3b772816fdba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3ac517cb1c711dc1a5ddf2c4027f705e393e30028b9ae7063b18a5f51362fd4d7277607d19d8ca7a9a3c9146960bd612a02ed4a8933c8223a42daf3dfe5e63b
|
7
|
+
data.tar.gz: 0c4408865d1894547aeedfc0441fa558a8281f54e7e26c38b619e67fd67bfc3bed49aa27dc06d8b89924c7ee8db4452b5cd2f810c0dfb01157eef127910b2182
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.17.
|
1
|
+
0.17.4
|
@@ -5,6 +5,9 @@ module RubyIndexer
|
|
5
5
|
class DeclarationListener
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
+
OBJECT_NESTING = T.let(["Object"].freeze, T::Array[String])
|
9
|
+
BASIC_OBJECT_NESTING = T.let(["BasicObject"].freeze, T::Array[String])
|
10
|
+
|
8
11
|
sig do
|
9
12
|
params(index: Index, dispatcher: Prism::Dispatcher, parse_result: Prism::ParseResult, file_path: String).void
|
10
13
|
end
|
@@ -33,6 +36,8 @@ module RubyIndexer
|
|
33
36
|
:on_class_node_leave,
|
34
37
|
:on_module_node_enter,
|
35
38
|
:on_module_node_leave,
|
39
|
+
:on_singleton_class_node_enter,
|
40
|
+
:on_singleton_class_node_leave,
|
36
41
|
:on_def_node_enter,
|
37
42
|
:on_def_node_leave,
|
38
43
|
:on_call_node_enter,
|
@@ -64,15 +69,26 @@ module RubyIndexer
|
|
64
69
|
comments = collect_comments(node)
|
65
70
|
|
66
71
|
superclass = node.superclass
|
72
|
+
|
73
|
+
nesting = name.start_with?("::") ? [name.delete_prefix("::")] : @stack + [name.delete_prefix("::")]
|
74
|
+
|
67
75
|
parent_class = case superclass
|
68
76
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
69
77
|
superclass.slice
|
70
78
|
else
|
71
|
-
|
79
|
+
case nesting
|
80
|
+
when OBJECT_NESTING
|
81
|
+
# When Object is reopened, its parent class should still be the top-level BasicObject
|
82
|
+
"::BasicObject"
|
83
|
+
when BASIC_OBJECT_NESTING
|
84
|
+
# When BasicObject is reopened, its parent class should still be nil
|
85
|
+
nil
|
86
|
+
else
|
87
|
+
# Otherwise, the parent class should be the top-level Object
|
88
|
+
"::Object"
|
89
|
+
end
|
72
90
|
end
|
73
91
|
|
74
|
-
nesting = name.start_with?("::") ? [name.delete_prefix("::")] : @stack + [name.delete_prefix("::")]
|
75
|
-
|
76
92
|
entry = Entry::Class.new(
|
77
93
|
nesting,
|
78
94
|
@file_path,
|
@@ -82,7 +98,7 @@ module RubyIndexer
|
|
82
98
|
)
|
83
99
|
|
84
100
|
@owner_stack << entry
|
85
|
-
@index
|
101
|
+
@index.add(entry)
|
86
102
|
@stack << name
|
87
103
|
end
|
88
104
|
|
@@ -104,7 +120,7 @@ module RubyIndexer
|
|
104
120
|
entry = Entry::Module.new(nesting, @file_path, node.location, comments)
|
105
121
|
|
106
122
|
@owner_stack << entry
|
107
|
-
@index
|
123
|
+
@index.add(entry)
|
108
124
|
@stack << name
|
109
125
|
end
|
110
126
|
|
@@ -115,6 +131,37 @@ module RubyIndexer
|
|
115
131
|
@visibility_stack.pop
|
116
132
|
end
|
117
133
|
|
134
|
+
sig { params(node: Prism::SingletonClassNode).void }
|
135
|
+
def on_singleton_class_node_enter(node)
|
136
|
+
@visibility_stack.push(Entry::Visibility::PUBLIC)
|
137
|
+
|
138
|
+
current_owner = @owner_stack.last
|
139
|
+
|
140
|
+
if current_owner
|
141
|
+
expression = node.expression
|
142
|
+
@stack << (expression.is_a?(Prism::SelfNode) ? "<Class:#{@stack.last}>" : "<Class:#{expression.slice}>")
|
143
|
+
|
144
|
+
existing_entries = T.cast(@index[@stack.join("::")], T.nilable(T::Array[Entry::SingletonClass]))
|
145
|
+
|
146
|
+
if existing_entries
|
147
|
+
entry = T.must(existing_entries.first)
|
148
|
+
entry.update_singleton_information(node.location, collect_comments(node))
|
149
|
+
else
|
150
|
+
entry = Entry::SingletonClass.new(@stack, @file_path, node.location, collect_comments(node), nil)
|
151
|
+
@index.add(entry, skip_prefix_tree: true)
|
152
|
+
end
|
153
|
+
|
154
|
+
@owner_stack << entry
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
sig { params(node: Prism::SingletonClassNode).void }
|
159
|
+
def on_singleton_class_node_leave(node)
|
160
|
+
@stack.pop
|
161
|
+
@owner_stack.pop
|
162
|
+
@visibility_stack.pop
|
163
|
+
end
|
164
|
+
|
118
165
|
sig { params(node: Prism::MultiWriteNode).void }
|
119
166
|
def on_multi_write_node_enter(node)
|
120
167
|
value = node.value
|
@@ -246,119 +293,110 @@ module RubyIndexer
|
|
246
293
|
|
247
294
|
case node.receiver
|
248
295
|
when nil
|
249
|
-
@index
|
296
|
+
@index.add(Entry::Method.new(
|
250
297
|
method_name,
|
251
298
|
@file_path,
|
252
299
|
node.location,
|
253
300
|
comments,
|
254
|
-
node.parameters,
|
301
|
+
list_params(node.parameters),
|
255
302
|
current_visibility,
|
256
303
|
@owner_stack.last,
|
257
|
-
)
|
304
|
+
))
|
258
305
|
when Prism::SelfNode
|
259
|
-
|
306
|
+
singleton = singleton_klass
|
307
|
+
|
308
|
+
@index.add(Entry::Method.new(
|
260
309
|
method_name,
|
261
310
|
@file_path,
|
262
311
|
node.location,
|
263
312
|
comments,
|
264
|
-
node.parameters,
|
313
|
+
list_params(node.parameters),
|
265
314
|
current_visibility,
|
266
|
-
|
267
|
-
)
|
315
|
+
singleton,
|
316
|
+
))
|
317
|
+
|
318
|
+
if singleton
|
319
|
+
@owner_stack << singleton
|
320
|
+
@stack << "<Class:#{@stack.last}>"
|
321
|
+
end
|
268
322
|
end
|
269
323
|
end
|
270
324
|
|
271
325
|
sig { params(node: Prism::DefNode).void }
|
272
326
|
def on_def_node_leave(node)
|
273
327
|
@inside_def = false
|
328
|
+
|
329
|
+
if node.receiver.is_a?(Prism::SelfNode)
|
330
|
+
@owner_stack.pop
|
331
|
+
@stack.pop
|
332
|
+
end
|
274
333
|
end
|
275
334
|
|
276
335
|
sig { params(node: Prism::InstanceVariableWriteNode).void }
|
277
336
|
def on_instance_variable_write_node_enter(node)
|
278
|
-
|
279
|
-
return if name == "@"
|
280
|
-
|
281
|
-
@index << Entry::InstanceVariable.new(
|
282
|
-
name,
|
283
|
-
@file_path,
|
284
|
-
node.name_loc,
|
285
|
-
collect_comments(node),
|
286
|
-
@owner_stack.last,
|
287
|
-
)
|
337
|
+
handle_instance_variable(node, node.name_loc)
|
288
338
|
end
|
289
339
|
|
290
340
|
sig { params(node: Prism::InstanceVariableAndWriteNode).void }
|
291
341
|
def on_instance_variable_and_write_node_enter(node)
|
292
|
-
|
293
|
-
return if name == "@"
|
294
|
-
|
295
|
-
@index << Entry::InstanceVariable.new(
|
296
|
-
name,
|
297
|
-
@file_path,
|
298
|
-
node.name_loc,
|
299
|
-
collect_comments(node),
|
300
|
-
@owner_stack.last,
|
301
|
-
)
|
342
|
+
handle_instance_variable(node, node.name_loc)
|
302
343
|
end
|
303
344
|
|
304
345
|
sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
|
305
346
|
def on_instance_variable_operator_write_node_enter(node)
|
306
|
-
|
307
|
-
return if name == "@"
|
308
|
-
|
309
|
-
@index << Entry::InstanceVariable.new(
|
310
|
-
name,
|
311
|
-
@file_path,
|
312
|
-
node.name_loc,
|
313
|
-
collect_comments(node),
|
314
|
-
@owner_stack.last,
|
315
|
-
)
|
347
|
+
handle_instance_variable(node, node.name_loc)
|
316
348
|
end
|
317
349
|
|
318
350
|
sig { params(node: Prism::InstanceVariableOrWriteNode).void }
|
319
351
|
def on_instance_variable_or_write_node_enter(node)
|
320
|
-
|
321
|
-
return if name == "@"
|
322
|
-
|
323
|
-
@index << Entry::InstanceVariable.new(
|
324
|
-
name,
|
325
|
-
@file_path,
|
326
|
-
node.name_loc,
|
327
|
-
collect_comments(node),
|
328
|
-
@owner_stack.last,
|
329
|
-
)
|
352
|
+
handle_instance_variable(node, node.name_loc)
|
330
353
|
end
|
331
354
|
|
332
355
|
sig { params(node: Prism::InstanceVariableTargetNode).void }
|
333
356
|
def on_instance_variable_target_node_enter(node)
|
334
|
-
|
335
|
-
return if name == "@"
|
336
|
-
|
337
|
-
@index << Entry::InstanceVariable.new(
|
338
|
-
name,
|
339
|
-
@file_path,
|
340
|
-
node.location,
|
341
|
-
collect_comments(node),
|
342
|
-
@owner_stack.last,
|
343
|
-
)
|
357
|
+
handle_instance_variable(node, node.location)
|
344
358
|
end
|
345
359
|
|
346
360
|
sig { params(node: Prism::AliasMethodNode).void }
|
347
361
|
def on_alias_method_node_enter(node)
|
348
362
|
method_name = node.new_name.slice
|
349
363
|
comments = collect_comments(node)
|
350
|
-
@index
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
364
|
+
@index.add(
|
365
|
+
Entry::UnresolvedMethodAlias.new(
|
366
|
+
method_name,
|
367
|
+
node.old_name.slice,
|
368
|
+
@owner_stack.last,
|
369
|
+
@file_path,
|
370
|
+
node.new_name.location,
|
371
|
+
comments,
|
372
|
+
),
|
357
373
|
)
|
358
374
|
end
|
359
375
|
|
360
376
|
private
|
361
377
|
|
378
|
+
sig do
|
379
|
+
params(
|
380
|
+
node: T.any(
|
381
|
+
Prism::InstanceVariableAndWriteNode,
|
382
|
+
Prism::InstanceVariableOperatorWriteNode,
|
383
|
+
Prism::InstanceVariableOrWriteNode,
|
384
|
+
Prism::InstanceVariableTargetNode,
|
385
|
+
Prism::InstanceVariableWriteNode,
|
386
|
+
),
|
387
|
+
loc: Prism::Location,
|
388
|
+
).void
|
389
|
+
end
|
390
|
+
def handle_instance_variable(node, loc)
|
391
|
+
name = node.name.to_s
|
392
|
+
return if name == "@"
|
393
|
+
|
394
|
+
# When instance variables are declared inside the class body, they turn into class instance variables rather than
|
395
|
+
# regular instance variables
|
396
|
+
owner = @inside_def ? @owner_stack.last : singleton_klass
|
397
|
+
@index.add(Entry::InstanceVariable.new(name, @file_path, loc, collect_comments(node), owner))
|
398
|
+
end
|
399
|
+
|
362
400
|
sig { params(node: Prism::CallNode).void }
|
363
401
|
def handle_private_constant(node)
|
364
402
|
arguments = node.arguments&.arguments
|
@@ -411,13 +449,15 @@ module RubyIndexer
|
|
411
449
|
return unless old_name_value
|
412
450
|
|
413
451
|
comments = collect_comments(node)
|
414
|
-
@index
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
452
|
+
@index.add(
|
453
|
+
Entry::UnresolvedMethodAlias.new(
|
454
|
+
new_name_value,
|
455
|
+
old_name_value,
|
456
|
+
@owner_stack.last,
|
457
|
+
@file_path,
|
458
|
+
new_name.location,
|
459
|
+
comments,
|
460
|
+
),
|
421
461
|
)
|
422
462
|
end
|
423
463
|
|
@@ -443,22 +483,24 @@ module RubyIndexer
|
|
443
483
|
value = node.value unless node.is_a?(Prism::ConstantTargetNode) || node.is_a?(Prism::ConstantPathTargetNode)
|
444
484
|
comments = collect_comments(node)
|
445
485
|
|
446
|
-
@index
|
447
|
-
|
448
|
-
|
449
|
-
|
486
|
+
@index.add(
|
487
|
+
case value
|
488
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
489
|
+
Entry::UnresolvedAlias.new(value.slice, @stack.dup, name, @file_path, node.location, comments)
|
490
|
+
when Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode,
|
450
491
|
Prism::ConstantOperatorWriteNode
|
451
492
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
493
|
+
# If the right hand side is another constant assignment, we need to visit it because that constant has to be
|
494
|
+
# indexed too
|
495
|
+
Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
|
496
|
+
when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
|
456
497
|
Prism::ConstantPathAndWriteNode
|
457
498
|
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
499
|
+
Entry::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
|
500
|
+
else
|
501
|
+
Entry::Constant.new(name, @file_path, node.location, comments)
|
502
|
+
end,
|
503
|
+
)
|
462
504
|
end
|
463
505
|
|
464
506
|
sig { params(node: Prism::Node).returns(T::Array[String]) }
|
@@ -515,15 +557,20 @@ module RubyIndexer
|
|
515
557
|
|
516
558
|
next unless name && loc
|
517
559
|
|
518
|
-
|
519
|
-
|
560
|
+
if reader
|
561
|
+
@index.add(Entry::Accessor.new(name, @file_path, loc, comments, current_visibility, @owner_stack.last))
|
562
|
+
end
|
563
|
+
|
564
|
+
next unless writer
|
565
|
+
|
566
|
+
@index.add(Entry::Accessor.new(
|
520
567
|
"#{name}=",
|
521
568
|
@file_path,
|
522
569
|
loc,
|
523
570
|
comments,
|
524
571
|
current_visibility,
|
525
572
|
@owner_stack.last,
|
526
|
-
)
|
573
|
+
))
|
527
574
|
end
|
528
575
|
end
|
529
576
|
|
@@ -558,5 +605,108 @@ module RubyIndexer
|
|
558
605
|
def current_visibility
|
559
606
|
T.must(@visibility_stack.last)
|
560
607
|
end
|
608
|
+
|
609
|
+
sig { params(parameters_node: T.nilable(Prism::ParametersNode)).returns(T::Array[Entry::Parameter]) }
|
610
|
+
def list_params(parameters_node)
|
611
|
+
return [] unless parameters_node
|
612
|
+
|
613
|
+
parameters = []
|
614
|
+
|
615
|
+
parameters_node.requireds.each do |required|
|
616
|
+
name = parameter_name(required)
|
617
|
+
next unless name
|
618
|
+
|
619
|
+
parameters << Entry::RequiredParameter.new(name: name)
|
620
|
+
end
|
621
|
+
|
622
|
+
parameters_node.optionals.each do |optional|
|
623
|
+
name = parameter_name(optional)
|
624
|
+
next unless name
|
625
|
+
|
626
|
+
parameters << Entry::OptionalParameter.new(name: name)
|
627
|
+
end
|
628
|
+
|
629
|
+
rest = parameters_node.rest
|
630
|
+
|
631
|
+
if rest.is_a?(Prism::RestParameterNode)
|
632
|
+
rest_name = rest.name || Entry::RestParameter::DEFAULT_NAME
|
633
|
+
parameters << Entry::RestParameter.new(name: rest_name)
|
634
|
+
end
|
635
|
+
|
636
|
+
parameters_node.keywords.each do |keyword|
|
637
|
+
name = parameter_name(keyword)
|
638
|
+
next unless name
|
639
|
+
|
640
|
+
case keyword
|
641
|
+
when Prism::RequiredKeywordParameterNode
|
642
|
+
parameters << Entry::KeywordParameter.new(name: name)
|
643
|
+
when Prism::OptionalKeywordParameterNode
|
644
|
+
parameters << Entry::OptionalKeywordParameter.new(name: name)
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
keyword_rest = parameters_node.keyword_rest
|
649
|
+
|
650
|
+
if keyword_rest.is_a?(Prism::KeywordRestParameterNode)
|
651
|
+
keyword_rest_name = parameter_name(keyword_rest) || Entry::KeywordRestParameter::DEFAULT_NAME
|
652
|
+
parameters << Entry::KeywordRestParameter.new(name: keyword_rest_name)
|
653
|
+
end
|
654
|
+
|
655
|
+
parameters_node.posts.each do |post|
|
656
|
+
name = parameter_name(post)
|
657
|
+
next unless name
|
658
|
+
|
659
|
+
parameters << Entry::RequiredParameter.new(name: name)
|
660
|
+
end
|
661
|
+
|
662
|
+
block = parameters_node.block
|
663
|
+
parameters << Entry::BlockParameter.new(name: block.name || Entry::BlockParameter::DEFAULT_NAME) if block
|
664
|
+
|
665
|
+
parameters
|
666
|
+
end
|
667
|
+
|
668
|
+
sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(Symbol)) }
|
669
|
+
def parameter_name(node)
|
670
|
+
case node
|
671
|
+
when Prism::RequiredParameterNode, Prism::OptionalParameterNode,
|
672
|
+
Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode,
|
673
|
+
Prism::RestParameterNode, Prism::KeywordRestParameterNode
|
674
|
+
node.name
|
675
|
+
when Prism::MultiTargetNode
|
676
|
+
names = node.lefts.map { |parameter_node| parameter_name(parameter_node) }
|
677
|
+
|
678
|
+
rest = node.rest
|
679
|
+
if rest.is_a?(Prism::SplatNode)
|
680
|
+
name = rest.expression&.slice
|
681
|
+
names << (rest.operator == "*" ? "*#{name}".to_sym : name&.to_sym)
|
682
|
+
end
|
683
|
+
|
684
|
+
names << nil if rest.is_a?(Prism::ImplicitRestNode)
|
685
|
+
|
686
|
+
names.concat(node.rights.map { |parameter_node| parameter_name(parameter_node) })
|
687
|
+
|
688
|
+
names_with_commas = names.join(", ")
|
689
|
+
:"(#{names_with_commas})"
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
sig { returns(T.nilable(Entry::Class)) }
|
694
|
+
def singleton_klass
|
695
|
+
attached_class = @owner_stack.last
|
696
|
+
return unless attached_class
|
697
|
+
|
698
|
+
# Return the existing singleton class if available
|
699
|
+
owner = T.cast(
|
700
|
+
@index["#{attached_class.name}::<Class:#{attached_class.name}>"],
|
701
|
+
T.nilable(T::Array[Entry::SingletonClass]),
|
702
|
+
)
|
703
|
+
return owner.first if owner
|
704
|
+
|
705
|
+
# If not available, create the singleton class lazily
|
706
|
+
nesting = @stack + ["<Class:#{@stack.last}>"]
|
707
|
+
entry = Entry::SingletonClass.new(nesting, @file_path, attached_class.location, [], nil)
|
708
|
+
@index.add(entry, skip_prefix_tree: true)
|
709
|
+
entry
|
710
|
+
end
|
561
711
|
end
|
562
712
|
end
|