ruby-lsp 0.17.8 → 0.17.10
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/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
|