ruby-lsp 0.17.8 → 0.17.10
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/exe/ruby-lsp +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +3 -3
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +4 -4
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +49 -20
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +83 -30
- data/lib/ruby_indexer/test/constant_test.rb +17 -17
- data/lib/ruby_indexer/test/index_test.rb +82 -10
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +38 -0
- data/lib/ruby_lsp/document.rb +27 -4
- data/lib/ruby_lsp/global_state.rb +2 -1
- data/lib/ruby_lsp/listeners/completion.rb +53 -33
- data/lib/ruby_lsp/listeners/definition.rb +23 -11
- data/lib/ruby_lsp/listeners/document_highlight.rb +5 -1
- data/lib/ruby_lsp/listeners/hover.rb +21 -9
- data/lib/ruby_lsp/listeners/signature_help.rb +13 -6
- data/lib/ruby_lsp/node_context.rb +2 -2
- data/lib/ruby_lsp/requests/completion.rb +3 -3
- data/lib/ruby_lsp/requests/completion_resolve.rb +10 -3
- data/lib/ruby_lsp/requests/definition.rb +3 -3
- data/lib/ruby_lsp/requests/hover.rb +3 -3
- data/lib/ruby_lsp/requests/on_type_formatting.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/server.rb +9 -9
- data/lib/ruby_lsp/store.rb +6 -1
- data/lib/ruby_lsp/test_helper.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +48 -10
- data/lib/ruby_lsp/utils.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fafb2e9f1ef8ae394523d09256bce9016307821ab32ecd17fe645dd3f9d88f1
|
4
|
+
data.tar.gz: 74396754fdbf6dffb91ff4d6d237f67c26b92fd7ec0f187fc3e40627dc214de1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8901ced915cabc4e749cc9451de7d7f9927c4025c6484002a682e65b1a20f7e52d38d3e5b52255b061998e8c114da8efeaaf605f0419fff8ed93c09cf00a2dae
|
7
|
+
data.tar.gz: 5679783fa6a828a7e6a3db9fb193ec9b4c1c094c6a67c187f7c2169f5f11fe53fe5024b0c7b0f08273eb46293639ff1d67f7787a851f4f1ac599be23a10ff7dd
|
data/README.md
CHANGED
@@ -33,7 +33,7 @@ The Ruby LSP features include
|
|
33
33
|
- Completion for classes, modules, constants and require paths
|
34
34
|
- Fuzzy search classes, modules and constants anywhere in the project and its dependencies (workspace symbol)
|
35
35
|
|
36
|
-
|
36
|
+
As of July 2024, Ruby LSP has received significant enhancements to its code navigation features. For an in-depth look at these improvements, including video demonstrations, check out this [article](https://railsatscale.com/2024-07-18-mastering-ruby-code-navigation-major-enhancements-in-ruby-lsp-2024/). Despite these advancements, we plan to continue enhancing its code navigation support even further. You can follow our progress on this [GitHub issue](https://github.com/Shopify/ruby-lsp/issues/899).
|
37
37
|
|
38
38
|
See complete information about features [here](https://shopify.github.io/ruby-lsp/RubyLsp/Requests.html).
|
39
39
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.17.
|
1
|
+
0.17.10
|
data/exe/ruby-lsp
CHANGED
@@ -106,7 +106,7 @@ if options[:time_index]
|
|
106
106
|
|
107
107
|
puts <<~MSG
|
108
108
|
Ruby LSP v#{RubyLsp::VERSION}: Indexing took #{result.round(5)} seconds and generated:
|
109
|
-
- #{entries_by_entry_type.map { |k, v| "#{k.name.split("::").last}: #{v.size}" }.join("\n- ")}
|
109
|
+
- #{entries_by_entry_type.sort_by { |k, _| k.to_s }.map { |k, v| "#{k.name.split("::").last}: #{v.size}" }.join("\n- ")}
|
110
110
|
MSG
|
111
111
|
return
|
112
112
|
end
|
@@ -504,17 +504,17 @@ module RubyIndexer
|
|
504
504
|
@index.add(
|
505
505
|
case value
|
506
506
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
507
|
-
Entry::
|
507
|
+
Entry::UnresolvedConstantAlias.new(value.slice, @stack.dup, name, @file_path, node.location, comments)
|
508
508
|
when Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode,
|
509
509
|
Prism::ConstantOperatorWriteNode
|
510
510
|
|
511
511
|
# If the right hand side is another constant assignment, we need to visit it because that constant has to be
|
512
512
|
# indexed too
|
513
|
-
Entry::
|
513
|
+
Entry::UnresolvedConstantAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
|
514
514
|
when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
|
515
515
|
Prism::ConstantPathAndWriteNode
|
516
516
|
|
517
|
-
Entry::
|
517
|
+
Entry::UnresolvedConstantAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
|
518
518
|
else
|
519
519
|
Entry::Constant.new(name, @file_path, node.location, comments)
|
520
520
|
end,
|
@@ -411,7 +411,7 @@ module RubyIndexer
|
|
411
411
|
# All aliases are inserted as UnresolvedAlias in the index first and then we lazily resolve them to the correct
|
412
412
|
# target in [rdoc-ref:Index#resolve]. If the right hand side contains a constant that doesn't exist, then it's not
|
413
413
|
# possible to resolve the alias and it will remain an UnresolvedAlias until the right hand side constant exists
|
414
|
-
class
|
414
|
+
class UnresolvedConstantAlias < Entry
|
415
415
|
extend T::Sig
|
416
416
|
|
417
417
|
sig { returns(String) }
|
@@ -439,13 +439,13 @@ module RubyIndexer
|
|
439
439
|
end
|
440
440
|
|
441
441
|
# Alias represents a resolved alias, which points to an existing constant target
|
442
|
-
class
|
442
|
+
class ConstantAlias < Entry
|
443
443
|
extend T::Sig
|
444
444
|
|
445
445
|
sig { returns(String) }
|
446
446
|
attr_reader :target
|
447
447
|
|
448
|
-
sig { params(target: String, unresolved_alias:
|
448
|
+
sig { params(target: String, unresolved_alias: UnresolvedConstantAlias).void }
|
449
449
|
def initialize(target, unresolved_alias)
|
450
450
|
super(unresolved_alias.name, unresolved_alias.file_path, unresolved_alias.location, unresolved_alias.comments)
|
451
451
|
|
@@ -492,7 +492,7 @@ module RubyIndexer
|
|
492
492
|
old_name: String,
|
493
493
|
owner: T.nilable(Entry::Namespace),
|
494
494
|
file_path: String,
|
495
|
-
location: Prism::Location,
|
495
|
+
location: T.any(Prism::Location, RubyIndexer::Location),
|
496
496
|
comments: T::Array[String],
|
497
497
|
).void
|
498
498
|
end
|
@@ -84,6 +84,34 @@ module RubyIndexer
|
|
84
84
|
@require_paths_tree.search(query)
|
85
85
|
end
|
86
86
|
|
87
|
+
# Searches for a constant based on an unqualified name and returns the first possible match regardless of whether
|
88
|
+
# there are more possible matching entries
|
89
|
+
sig do
|
90
|
+
params(
|
91
|
+
name: String,
|
92
|
+
).returns(T.nilable(T::Array[T.any(
|
93
|
+
Entry::Namespace,
|
94
|
+
Entry::ConstantAlias,
|
95
|
+
Entry::UnresolvedConstantAlias,
|
96
|
+
Entry::Constant,
|
97
|
+
)]))
|
98
|
+
end
|
99
|
+
def first_unqualified_const(name)
|
100
|
+
_name, entries = @entries.find do |const_name, _entries|
|
101
|
+
const_name.end_with?(name)
|
102
|
+
end
|
103
|
+
|
104
|
+
T.cast(
|
105
|
+
entries,
|
106
|
+
T.nilable(T::Array[T.any(
|
107
|
+
Entry::Namespace,
|
108
|
+
Entry::ConstantAlias,
|
109
|
+
Entry::UnresolvedConstantAlias,
|
110
|
+
Entry::Constant,
|
111
|
+
)]),
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
87
115
|
# Searches entries in the index based on an exact prefix, intended for providing autocomplete. All possible matches
|
88
116
|
# to the prefix are returned. The return is an array of arrays, where each entry is the array of entries for a given
|
89
117
|
# name match. For example:
|
@@ -202,8 +230,8 @@ module RubyIndexer
|
|
202
230
|
seen_names: T::Array[String],
|
203
231
|
).returns(T.nilable(T::Array[T.any(
|
204
232
|
Entry::Namespace,
|
205
|
-
Entry::
|
206
|
-
Entry::
|
233
|
+
Entry::ConstantAlias,
|
234
|
+
Entry::UnresolvedConstantAlias,
|
207
235
|
)]))
|
208
236
|
end
|
209
237
|
def resolve(name, nesting, seen_names = [])
|
@@ -248,6 +276,7 @@ module RubyIndexer
|
|
248
276
|
).void
|
249
277
|
end
|
250
278
|
def index_all(indexable_paths: RubyIndexer.configuration.indexables, &block)
|
279
|
+
RBSIndexer.new(self).index_ruby_core
|
251
280
|
# Calculate how many paths are worth 1% of progress
|
252
281
|
progress_step = (indexable_paths.length / 100.0).ceil
|
253
282
|
|
@@ -305,13 +334,13 @@ module RubyIndexer
|
|
305
334
|
entry = @entries[current_name]&.first
|
306
335
|
|
307
336
|
case entry
|
308
|
-
when Entry::
|
337
|
+
when Entry::ConstantAlias
|
309
338
|
target = entry.target
|
310
339
|
return follow_aliased_namespace("#{target}::#{real_parts.join("::")}", seen_names)
|
311
|
-
when Entry::
|
340
|
+
when Entry::UnresolvedConstantAlias
|
312
341
|
resolved = resolve_alias(entry, seen_names)
|
313
342
|
|
314
|
-
if resolved.is_a?(Entry::
|
343
|
+
if resolved.is_a?(Entry::UnresolvedConstantAlias)
|
315
344
|
raise UnresolvableAliasError, "The constant #{resolved.name} is an alias to a non existing constant"
|
316
345
|
end
|
317
346
|
|
@@ -410,7 +439,7 @@ module RubyIndexer
|
|
410
439
|
case entry
|
411
440
|
when Entry::Namespace
|
412
441
|
entry
|
413
|
-
when Entry::
|
442
|
+
when Entry::ConstantAlias
|
414
443
|
self[entry.target]&.grep(Entry::Namespace)
|
415
444
|
end
|
416
445
|
end.flatten
|
@@ -420,7 +449,7 @@ module RubyIndexer
|
|
420
449
|
|
421
450
|
# The original nesting where we discovered this namespace, so that we resolve the correct names of the
|
422
451
|
# included/prepended/extended modules and parent classes
|
423
|
-
nesting = T.must(namespaces.first).nesting
|
452
|
+
nesting = T.must(namespaces.first).nesting.flat_map { |n| n.split("::") }
|
424
453
|
|
425
454
|
if nesting.any?
|
426
455
|
singleton_levels.times do
|
@@ -629,7 +658,7 @@ module RubyIndexer
|
|
629
658
|
|
630
659
|
if parent_class_name && fully_qualified_name != parent_class_name
|
631
660
|
|
632
|
-
parent_name_parts =
|
661
|
+
parent_name_parts = parent_class_name.split("::")
|
633
662
|
singleton_levels.times do
|
634
663
|
parent_name_parts << "<Class:#{parent_name_parts.last}>"
|
635
664
|
end
|
@@ -669,9 +698,9 @@ module RubyIndexer
|
|
669
698
|
# that doesn't exist, then we return the same UnresolvedAlias
|
670
699
|
sig do
|
671
700
|
params(
|
672
|
-
entry: Entry::
|
701
|
+
entry: Entry::UnresolvedConstantAlias,
|
673
702
|
seen_names: T::Array[String],
|
674
|
-
).returns(T.any(Entry::
|
703
|
+
).returns(T.any(Entry::ConstantAlias, Entry::UnresolvedConstantAlias))
|
675
704
|
end
|
676
705
|
def resolve_alias(entry, seen_names)
|
677
706
|
alias_name = entry.name
|
@@ -683,7 +712,7 @@ module RubyIndexer
|
|
683
712
|
return entry unless target
|
684
713
|
|
685
714
|
target_name = T.must(target.first).name
|
686
|
-
resolved_alias = Entry::
|
715
|
+
resolved_alias = Entry::ConstantAlias.new(target_name, entry)
|
687
716
|
|
688
717
|
# Replace the UnresolvedAlias by a resolved one so that we don't have to do this again later
|
689
718
|
original_entries = T.must(@entries[alias_name])
|
@@ -702,8 +731,8 @@ module RubyIndexer
|
|
702
731
|
seen_names: T::Array[String],
|
703
732
|
).returns(T.nilable(T::Array[T.any(
|
704
733
|
Entry::Namespace,
|
705
|
-
Entry::
|
706
|
-
Entry::
|
734
|
+
Entry::ConstantAlias,
|
735
|
+
Entry::UnresolvedConstantAlias,
|
707
736
|
)]))
|
708
737
|
end
|
709
738
|
def lookup_enclosing_scopes(name, nesting, seen_names)
|
@@ -731,8 +760,8 @@ module RubyIndexer
|
|
731
760
|
seen_names: T::Array[String],
|
732
761
|
).returns(T.nilable(T::Array[T.any(
|
733
762
|
Entry::Namespace,
|
734
|
-
Entry::
|
735
|
-
Entry::
|
763
|
+
Entry::ConstantAlias,
|
764
|
+
Entry::UnresolvedConstantAlias,
|
736
765
|
)]))
|
737
766
|
end
|
738
767
|
def lookup_ancestor_chain(name, nesting, seen_names)
|
@@ -792,8 +821,8 @@ module RubyIndexer
|
|
792
821
|
).returns(
|
793
822
|
T.nilable(T::Array[T.any(
|
794
823
|
Entry::Namespace,
|
795
|
-
Entry::
|
796
|
-
Entry::
|
824
|
+
Entry::ConstantAlias,
|
825
|
+
Entry::UnresolvedConstantAlias,
|
797
826
|
)]),
|
798
827
|
)
|
799
828
|
end
|
@@ -801,11 +830,11 @@ module RubyIndexer
|
|
801
830
|
entries = @entries[full_name] || @entries[follow_aliased_namespace(full_name)]
|
802
831
|
|
803
832
|
T.cast(
|
804
|
-
entries&.map { |e| e.is_a?(Entry::
|
833
|
+
entries&.map { |e| e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e },
|
805
834
|
T.nilable(T::Array[T.any(
|
806
835
|
Entry::Namespace,
|
807
|
-
Entry::
|
808
|
-
Entry::
|
836
|
+
Entry::ConstantAlias,
|
837
|
+
Entry::UnresolvedConstantAlias,
|
809
838
|
)]),
|
810
839
|
)
|
811
840
|
end
|
@@ -37,45 +37,43 @@ module RubyIndexer
|
|
37
37
|
sig { params(declaration: RBS::AST::Declarations::Base, pathname: Pathname).void }
|
38
38
|
def process_declaration(declaration, pathname)
|
39
39
|
case declaration
|
40
|
-
when RBS::AST::Declarations::Class
|
41
|
-
|
42
|
-
when RBS::AST::Declarations::
|
43
|
-
|
40
|
+
when RBS::AST::Declarations::Class, RBS::AST::Declarations::Module
|
41
|
+
handle_class_or_module_declaration(declaration, pathname)
|
42
|
+
when RBS::AST::Declarations::Constant
|
43
|
+
namespace_nesting = declaration.name.namespace.path.map(&:to_s)
|
44
|
+
handle_constant(declaration, namespace_nesting, pathname.to_s)
|
44
45
|
else # rubocop:disable Style/EmptyElse
|
45
46
|
# Other kinds not yet handled
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
|
-
sig
|
50
|
-
|
51
|
-
nesting = [declaration.name.name.to_s]
|
52
|
-
file_path = pathname.to_s
|
53
|
-
location = to_ruby_indexer_location(declaration.location)
|
54
|
-
comments = Array(declaration.comment&.string)
|
55
|
-
parent_class = declaration.super_class&.name&.name&.to_s
|
56
|
-
class_entry = Entry::Class.new(nesting, file_path, location, location, comments, parent_class)
|
57
|
-
add_declaration_mixins_to_entry(declaration, class_entry)
|
58
|
-
@index.add(class_entry)
|
59
|
-
declaration.members.each do |member|
|
60
|
-
next unless member.is_a?(RBS::AST::Members::MethodDefinition)
|
61
|
-
|
62
|
-
handle_method(member, class_entry)
|
63
|
-
end
|
50
|
+
sig do
|
51
|
+
params(declaration: T.any(RBS::AST::Declarations::Class, RBS::AST::Declarations::Module), pathname: Pathname).void
|
64
52
|
end
|
65
|
-
|
66
|
-
sig { params(declaration: RBS::AST::Declarations::Module, pathname: Pathname).void }
|
67
|
-
def handle_module_declaration(declaration, pathname)
|
53
|
+
def handle_class_or_module_declaration(declaration, pathname)
|
68
54
|
nesting = [declaration.name.name.to_s]
|
69
55
|
file_path = pathname.to_s
|
70
56
|
location = to_ruby_indexer_location(declaration.location)
|
71
|
-
comments =
|
72
|
-
|
73
|
-
|
74
|
-
|
57
|
+
comments = comments_to_string(declaration)
|
58
|
+
entry = if declaration.is_a?(RBS::AST::Declarations::Class)
|
59
|
+
parent_class = declaration.super_class&.name&.name&.to_s
|
60
|
+
Entry::Class.new(nesting, file_path, location, location, comments, parent_class)
|
61
|
+
else
|
62
|
+
Entry::Module.new(nesting, file_path, location, location, comments)
|
63
|
+
end
|
64
|
+
add_declaration_mixins_to_entry(declaration, entry)
|
65
|
+
@index.add(entry)
|
75
66
|
declaration.members.each do |member|
|
76
|
-
|
77
|
-
|
78
|
-
|
67
|
+
case member
|
68
|
+
when RBS::AST::Members::MethodDefinition
|
69
|
+
handle_method(member, entry)
|
70
|
+
when RBS::AST::Declarations::Constant
|
71
|
+
handle_constant(member, nesting, file_path)
|
72
|
+
when RBS::AST::Members::Alias
|
73
|
+
# In RBS, an alias means that two methods have the same signature.
|
74
|
+
# It does not mean the same thing as a Ruby alias.
|
75
|
+
handle_signature_alias(member, entry)
|
76
|
+
end
|
79
77
|
end
|
80
78
|
end
|
81
79
|
|
@@ -115,7 +113,7 @@ module RubyIndexer
|
|
115
113
|
name = member.name.name
|
116
114
|
file_path = member.location.buffer.name
|
117
115
|
location = to_ruby_indexer_location(member.location)
|
118
|
-
comments =
|
116
|
+
comments = comments_to_string(member)
|
119
117
|
|
120
118
|
visibility = case member.visibility
|
121
119
|
when :private
|
@@ -224,5 +222,60 @@ module RubyIndexer
|
|
224
222
|
|
225
223
|
Entry::KeywordRestParameter.new(name: name)
|
226
224
|
end
|
225
|
+
|
226
|
+
# RBS treats constant definitions differently depend on where they are defined.
|
227
|
+
# When constants' rbs are defined inside a class/module block, they are treated as
|
228
|
+
# members of the class/module.
|
229
|
+
#
|
230
|
+
# module Encoding
|
231
|
+
# US_ASCII = ... # US_ASCII is a member of Encoding
|
232
|
+
# end
|
233
|
+
#
|
234
|
+
# When constants' rbs are defined outside a class/module block, they are treated as
|
235
|
+
# top-level constants.
|
236
|
+
#
|
237
|
+
# Complex::I = ... # Complex::I is a top-level constant
|
238
|
+
#
|
239
|
+
# And we need to handle their nesting differently.
|
240
|
+
sig { params(declaration: RBS::AST::Declarations::Constant, nesting: T::Array[String], file_path: String).void }
|
241
|
+
def handle_constant(declaration, nesting, file_path)
|
242
|
+
fully_qualified_name = [*nesting, declaration.name.name.to_s].join("::")
|
243
|
+
@index.add(Entry::Constant.new(
|
244
|
+
fully_qualified_name,
|
245
|
+
file_path,
|
246
|
+
to_ruby_indexer_location(declaration.location),
|
247
|
+
comments_to_string(declaration),
|
248
|
+
))
|
249
|
+
end
|
250
|
+
|
251
|
+
sig { params(member: RBS::AST::Members::Alias, owner_entry: Entry::Namespace).void }
|
252
|
+
def handle_signature_alias(member, owner_entry)
|
253
|
+
file_path = member.location.buffer.name
|
254
|
+
comments = comments_to_string(member)
|
255
|
+
|
256
|
+
entry = Entry::UnresolvedMethodAlias.new(
|
257
|
+
member.new_name.to_s,
|
258
|
+
member.old_name.to_s,
|
259
|
+
owner_entry,
|
260
|
+
file_path,
|
261
|
+
to_ruby_indexer_location(member.location),
|
262
|
+
comments,
|
263
|
+
)
|
264
|
+
|
265
|
+
@index.add(entry)
|
266
|
+
end
|
267
|
+
|
268
|
+
sig do
|
269
|
+
params(declaration: T.any(
|
270
|
+
RBS::AST::Declarations::Class,
|
271
|
+
RBS::AST::Declarations::Module,
|
272
|
+
RBS::AST::Declarations::Constant,
|
273
|
+
RBS::AST::Members::MethodDefinition,
|
274
|
+
RBS::AST::Members::Alias,
|
275
|
+
)).returns(T::Array[String])
|
276
|
+
end
|
277
|
+
def comments_to_string(declaration)
|
278
|
+
Array(declaration.comment&.string)
|
279
|
+
end
|
227
280
|
end
|
228
281
|
end
|
@@ -200,12 +200,12 @@ module RubyIndexer
|
|
200
200
|
RUBY
|
201
201
|
|
202
202
|
unresolve_entry = @index["A::FIRST"].first
|
203
|
-
assert_instance_of(Entry::
|
203
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
|
204
204
|
assert_equal(["A"], unresolve_entry.nesting)
|
205
205
|
assert_equal("B::C", unresolve_entry.target)
|
206
206
|
|
207
207
|
resolved_entry = @index.resolve("A::FIRST", []).first
|
208
|
-
assert_instance_of(Entry::
|
208
|
+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
|
209
209
|
assert_equal("A::B::C", resolved_entry.target)
|
210
210
|
end
|
211
211
|
|
@@ -226,12 +226,12 @@ module RubyIndexer
|
|
226
226
|
RUBY
|
227
227
|
|
228
228
|
unresolve_entry = @index["A::ALIAS"].first
|
229
|
-
assert_instance_of(Entry::
|
229
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
|
230
230
|
assert_equal(["A"], unresolve_entry.nesting)
|
231
231
|
assert_equal("B", unresolve_entry.target)
|
232
232
|
|
233
233
|
resolved_entry = @index.resolve("ALIAS", ["A"]).first
|
234
|
-
assert_instance_of(Entry::
|
234
|
+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
|
235
235
|
assert_equal("A::B", resolved_entry.target)
|
236
236
|
|
237
237
|
resolved_entry = @index.resolve("ALIAS::C", ["A"]).first
|
@@ -239,7 +239,7 @@ module RubyIndexer
|
|
239
239
|
assert_equal("A::B::C", resolved_entry.name)
|
240
240
|
|
241
241
|
unresolve_entry = @index["Other::ONE_MORE"].first
|
242
|
-
assert_instance_of(Entry::
|
242
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
|
243
243
|
assert_equal(["Other"], unresolve_entry.nesting)
|
244
244
|
assert_equal("A::ALIAS", unresolve_entry.target)
|
245
245
|
|
@@ -259,12 +259,12 @@ module RubyIndexer
|
|
259
259
|
|
260
260
|
# B and C
|
261
261
|
unresolve_entry = @index["A::B"].first
|
262
|
-
assert_instance_of(Entry::
|
262
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
|
263
263
|
assert_equal(["A"], unresolve_entry.nesting)
|
264
264
|
assert_equal("C", unresolve_entry.target)
|
265
265
|
|
266
266
|
resolved_entry = @index.resolve("A::B", []).first
|
267
|
-
assert_instance_of(Entry::
|
267
|
+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
|
268
268
|
assert_equal("A::C", resolved_entry.target)
|
269
269
|
|
270
270
|
constant = @index["A::C"].first
|
@@ -272,38 +272,38 @@ module RubyIndexer
|
|
272
272
|
|
273
273
|
# D and E
|
274
274
|
unresolve_entry = @index["A::D"].first
|
275
|
-
assert_instance_of(Entry::
|
275
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
|
276
276
|
assert_equal(["A"], unresolve_entry.nesting)
|
277
277
|
assert_equal("E", unresolve_entry.target)
|
278
278
|
|
279
279
|
resolved_entry = @index.resolve("A::D", []).first
|
280
|
-
assert_instance_of(Entry::
|
280
|
+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
|
281
281
|
assert_equal("A::E", resolved_entry.target)
|
282
282
|
|
283
283
|
# F and G::H
|
284
284
|
unresolve_entry = @index["A::F"].first
|
285
|
-
assert_instance_of(Entry::
|
285
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
|
286
286
|
assert_equal(["A"], unresolve_entry.nesting)
|
287
287
|
assert_equal("G::H", unresolve_entry.target)
|
288
288
|
|
289
289
|
resolved_entry = @index.resolve("A::F", []).first
|
290
|
-
assert_instance_of(Entry::
|
290
|
+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
|
291
291
|
assert_equal("A::G::H", resolved_entry.target)
|
292
292
|
|
293
293
|
# I::J, K::L and M
|
294
294
|
unresolve_entry = @index["A::I::J"].first
|
295
|
-
assert_instance_of(Entry::
|
295
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, unresolve_entry)
|
296
296
|
assert_equal(["A"], unresolve_entry.nesting)
|
297
297
|
assert_equal("K::L", unresolve_entry.target)
|
298
298
|
|
299
299
|
resolved_entry = @index.resolve("A::I::J", []).first
|
300
|
-
assert_instance_of(Entry::
|
300
|
+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
|
301
301
|
assert_equal("A::K::L", resolved_entry.target)
|
302
302
|
|
303
303
|
# When we are resolving A::I::J, we invoke `resolve("K::L", ["A"])`, which recursively resolves A::K::L too.
|
304
304
|
# Therefore, both A::I::J and A::K::L point to A::M by the end of the previous resolve invocation
|
305
305
|
resolved_entry = @index["A::K::L"].first
|
306
|
-
assert_instance_of(Entry::
|
306
|
+
assert_instance_of(Entry::ConstantAlias, resolved_entry)
|
307
307
|
assert_equal("A::M", resolved_entry.target)
|
308
308
|
|
309
309
|
constant = @index["A::M"].first
|
@@ -344,11 +344,11 @@ module RubyIndexer
|
|
344
344
|
RUBY
|
345
345
|
|
346
346
|
assert_entry("A::B", Entry::Constant, "/fake/path/foo.rb:1-2:1-3")
|
347
|
-
assert_entry("A::C", Entry::
|
348
|
-
assert_entry("A::D::E", Entry::
|
347
|
+
assert_entry("A::C", Entry::UnresolvedConstantAlias, "/fake/path/foo.rb:1-5:1-6")
|
348
|
+
assert_entry("A::D::E", Entry::UnresolvedConstantAlias, "/fake/path/foo.rb:2-2:2-6")
|
349
349
|
assert_entry("A::F::G", Entry::Constant, "/fake/path/foo.rb:2-8:2-12")
|
350
350
|
assert_entry("A::H", Entry::Constant, "/fake/path/foo.rb:3-2:3-3")
|
351
|
-
assert_entry("A::I::J", Entry::
|
351
|
+
assert_entry("A::I::J", Entry::UnresolvedConstantAlias, "/fake/path/foo.rb:3-5:3-9")
|
352
352
|
assert_entry("A::K", Entry::Constant, "/fake/path/foo.rb:4-2:4-3")
|
353
353
|
assert_entry("A::L", Entry::Constant, "/fake/path/foo.rb:4-5:4-6")
|
354
354
|
end
|
@@ -181,20 +181,20 @@ module RubyIndexer
|
|
181
181
|
|
182
182
|
def test_resolving_aliases_to_non_existing_constants_with_conflicting_names
|
183
183
|
@index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
|
184
|
-
class
|
184
|
+
class Bar
|
185
185
|
end
|
186
186
|
|
187
187
|
module Foo
|
188
|
-
class
|
189
|
-
|
188
|
+
class Bar < self
|
189
|
+
BAZ = ::Bar::BAZ
|
190
190
|
end
|
191
191
|
end
|
192
192
|
RUBY
|
193
193
|
|
194
|
-
entry = @index.resolve("
|
194
|
+
entry = @index.resolve("BAZ", ["Foo", "Bar"]).first
|
195
195
|
refute_nil(entry)
|
196
196
|
|
197
|
-
assert_instance_of(Entry::
|
197
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, entry)
|
198
198
|
end
|
199
199
|
|
200
200
|
def test_visitor_does_not_visit_unnecessary_nodes
|
@@ -1015,11 +1015,11 @@ module RubyIndexer
|
|
1015
1015
|
|
1016
1016
|
foo_entry = T.must(@index.resolve("FOO", ["Namespace"])&.first)
|
1017
1017
|
assert_equal(2, foo_entry.location.start_line)
|
1018
|
-
assert_instance_of(Entry::
|
1018
|
+
assert_instance_of(Entry::ConstantAlias, foo_entry)
|
1019
1019
|
|
1020
1020
|
bar_entry = T.must(@index.resolve("BAR", ["Namespace"])&.first)
|
1021
1021
|
assert_equal(3, bar_entry.location.start_line)
|
1022
|
-
assert_instance_of(Entry::
|
1022
|
+
assert_instance_of(Entry::ConstantAlias, bar_entry)
|
1023
1023
|
end
|
1024
1024
|
|
1025
1025
|
def test_resolving_circular_alias_three_levels
|
@@ -1033,15 +1033,15 @@ module RubyIndexer
|
|
1033
1033
|
|
1034
1034
|
foo_entry = T.must(@index.resolve("FOO", ["Namespace"])&.first)
|
1035
1035
|
assert_equal(2, foo_entry.location.start_line)
|
1036
|
-
assert_instance_of(Entry::
|
1036
|
+
assert_instance_of(Entry::ConstantAlias, foo_entry)
|
1037
1037
|
|
1038
1038
|
bar_entry = T.must(@index.resolve("BAR", ["Namespace"])&.first)
|
1039
1039
|
assert_equal(3, bar_entry.location.start_line)
|
1040
|
-
assert_instance_of(Entry::
|
1040
|
+
assert_instance_of(Entry::ConstantAlias, bar_entry)
|
1041
1041
|
|
1042
1042
|
baz_entry = T.must(@index.resolve("BAZ", ["Namespace"])&.first)
|
1043
1043
|
assert_equal(4, baz_entry.location.start_line)
|
1044
|
-
assert_instance_of(Entry::
|
1044
|
+
assert_instance_of(Entry::ConstantAlias, baz_entry)
|
1045
1045
|
end
|
1046
1046
|
|
1047
1047
|
def test_resolving_constants_in_aliased_namespace
|
@@ -1542,6 +1542,21 @@ module RubyIndexer
|
|
1542
1542
|
assert_empty(@index.method_completion_candidates("bar", "Foo"))
|
1543
1543
|
end
|
1544
1544
|
|
1545
|
+
def test_first_unqualified_const
|
1546
|
+
index(<<~RUBY)
|
1547
|
+
module Foo
|
1548
|
+
class Bar; end
|
1549
|
+
end
|
1550
|
+
|
1551
|
+
module Baz
|
1552
|
+
class Bar; end
|
1553
|
+
end
|
1554
|
+
RUBY
|
1555
|
+
|
1556
|
+
entry = T.must(@index.first_unqualified_const("Bar")&.first)
|
1557
|
+
assert_equal("Foo::Bar", entry.name)
|
1558
|
+
end
|
1559
|
+
|
1545
1560
|
def test_completion_does_not_duplicate_overridden_methods
|
1546
1561
|
index(<<~RUBY)
|
1547
1562
|
class Foo
|
@@ -1750,5 +1765,62 @@ module RubyIndexer
|
|
1750
1765
|
@index.linearized_ancestors_of("A::<Class:A>")
|
1751
1766
|
end
|
1752
1767
|
end
|
1768
|
+
|
1769
|
+
def test_linearizing_singleton_parent_class_with_namespace
|
1770
|
+
index(<<~RUBY)
|
1771
|
+
class ActiveRecord::Base; end
|
1772
|
+
|
1773
|
+
class User < ActiveRecord::Base
|
1774
|
+
end
|
1775
|
+
RUBY
|
1776
|
+
|
1777
|
+
assert_equal(
|
1778
|
+
[
|
1779
|
+
"User::<Class:User>",
|
1780
|
+
"ActiveRecord::Base::<Class:Base>",
|
1781
|
+
"Object::<Class:Object>",
|
1782
|
+
"BasicObject::<Class:BasicObject>",
|
1783
|
+
"Class",
|
1784
|
+
"Module",
|
1785
|
+
"Object",
|
1786
|
+
"Kernel",
|
1787
|
+
"BasicObject",
|
1788
|
+
],
|
1789
|
+
@index.linearized_ancestors_of("User::<Class:User>"),
|
1790
|
+
)
|
1791
|
+
end
|
1792
|
+
|
1793
|
+
def test_singleton_nesting_is_correctly_split_during_linearization
|
1794
|
+
index(<<~RUBY)
|
1795
|
+
module Bar; end
|
1796
|
+
|
1797
|
+
module Foo
|
1798
|
+
class Namespace::Parent
|
1799
|
+
extend Bar
|
1800
|
+
end
|
1801
|
+
end
|
1802
|
+
|
1803
|
+
module Foo
|
1804
|
+
class Child < Namespace::Parent
|
1805
|
+
end
|
1806
|
+
end
|
1807
|
+
RUBY
|
1808
|
+
|
1809
|
+
assert_equal(
|
1810
|
+
[
|
1811
|
+
"Foo::Child::<Class:Child>",
|
1812
|
+
"Foo::Namespace::Parent::<Class:Parent>",
|
1813
|
+
"Bar",
|
1814
|
+
"Object::<Class:Object>",
|
1815
|
+
"BasicObject::<Class:BasicObject>",
|
1816
|
+
"Class",
|
1817
|
+
"Module",
|
1818
|
+
"Object",
|
1819
|
+
"Kernel",
|
1820
|
+
"BasicObject",
|
1821
|
+
],
|
1822
|
+
@index.linearized_ancestors_of("Foo::Child::<Class:Child>"),
|
1823
|
+
)
|
1824
|
+
end
|
1753
1825
|
end
|
1754
1826
|
end
|