ruby-lsp 0.17.9 → 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 +25 -24
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +70 -5
- data/lib/ruby_indexer/test/constant_test.rb +17 -17
- data/lib/ruby_indexer/test/index_test.rb +67 -10
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +38 -0
- data/lib/ruby_lsp/listeners/document_highlight.rb +5 -1
- data/lib/ruby_lsp/node_context.rb +2 -2
- data/lib/ruby_lsp/requests/on_type_formatting.rb +3 -2
- data/lib/ruby_lsp/server.rb +0 -2
- metadata +2 -2
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
|
@@ -91,8 +91,8 @@ module RubyIndexer
|
|
91
91
|
name: String,
|
92
92
|
).returns(T.nilable(T::Array[T.any(
|
93
93
|
Entry::Namespace,
|
94
|
-
Entry::
|
95
|
-
Entry::
|
94
|
+
Entry::ConstantAlias,
|
95
|
+
Entry::UnresolvedConstantAlias,
|
96
96
|
Entry::Constant,
|
97
97
|
)]))
|
98
98
|
end
|
@@ -105,8 +105,8 @@ module RubyIndexer
|
|
105
105
|
entries,
|
106
106
|
T.nilable(T::Array[T.any(
|
107
107
|
Entry::Namespace,
|
108
|
-
Entry::
|
109
|
-
Entry::
|
108
|
+
Entry::ConstantAlias,
|
109
|
+
Entry::UnresolvedConstantAlias,
|
110
110
|
Entry::Constant,
|
111
111
|
)]),
|
112
112
|
)
|
@@ -230,8 +230,8 @@ module RubyIndexer
|
|
230
230
|
seen_names: T::Array[String],
|
231
231
|
).returns(T.nilable(T::Array[T.any(
|
232
232
|
Entry::Namespace,
|
233
|
-
Entry::
|
234
|
-
Entry::
|
233
|
+
Entry::ConstantAlias,
|
234
|
+
Entry::UnresolvedConstantAlias,
|
235
235
|
)]))
|
236
236
|
end
|
237
237
|
def resolve(name, nesting, seen_names = [])
|
@@ -276,6 +276,7 @@ module RubyIndexer
|
|
276
276
|
).void
|
277
277
|
end
|
278
278
|
def index_all(indexable_paths: RubyIndexer.configuration.indexables, &block)
|
279
|
+
RBSIndexer.new(self).index_ruby_core
|
279
280
|
# Calculate how many paths are worth 1% of progress
|
280
281
|
progress_step = (indexable_paths.length / 100.0).ceil
|
281
282
|
|
@@ -333,13 +334,13 @@ module RubyIndexer
|
|
333
334
|
entry = @entries[current_name]&.first
|
334
335
|
|
335
336
|
case entry
|
336
|
-
when Entry::
|
337
|
+
when Entry::ConstantAlias
|
337
338
|
target = entry.target
|
338
339
|
return follow_aliased_namespace("#{target}::#{real_parts.join("::")}", seen_names)
|
339
|
-
when Entry::
|
340
|
+
when Entry::UnresolvedConstantAlias
|
340
341
|
resolved = resolve_alias(entry, seen_names)
|
341
342
|
|
342
|
-
if resolved.is_a?(Entry::
|
343
|
+
if resolved.is_a?(Entry::UnresolvedConstantAlias)
|
343
344
|
raise UnresolvableAliasError, "The constant #{resolved.name} is an alias to a non existing constant"
|
344
345
|
end
|
345
346
|
|
@@ -438,7 +439,7 @@ module RubyIndexer
|
|
438
439
|
case entry
|
439
440
|
when Entry::Namespace
|
440
441
|
entry
|
441
|
-
when Entry::
|
442
|
+
when Entry::ConstantAlias
|
442
443
|
self[entry.target]&.grep(Entry::Namespace)
|
443
444
|
end
|
444
445
|
end.flatten
|
@@ -448,7 +449,7 @@ module RubyIndexer
|
|
448
449
|
|
449
450
|
# The original nesting where we discovered this namespace, so that we resolve the correct names of the
|
450
451
|
# included/prepended/extended modules and parent classes
|
451
|
-
nesting = T.must(namespaces.first).nesting
|
452
|
+
nesting = T.must(namespaces.first).nesting.flat_map { |n| n.split("::") }
|
452
453
|
|
453
454
|
if nesting.any?
|
454
455
|
singleton_levels.times do
|
@@ -657,7 +658,7 @@ module RubyIndexer
|
|
657
658
|
|
658
659
|
if parent_class_name && fully_qualified_name != parent_class_name
|
659
660
|
|
660
|
-
parent_name_parts =
|
661
|
+
parent_name_parts = parent_class_name.split("::")
|
661
662
|
singleton_levels.times do
|
662
663
|
parent_name_parts << "<Class:#{parent_name_parts.last}>"
|
663
664
|
end
|
@@ -697,9 +698,9 @@ module RubyIndexer
|
|
697
698
|
# that doesn't exist, then we return the same UnresolvedAlias
|
698
699
|
sig do
|
699
700
|
params(
|
700
|
-
entry: Entry::
|
701
|
+
entry: Entry::UnresolvedConstantAlias,
|
701
702
|
seen_names: T::Array[String],
|
702
|
-
).returns(T.any(Entry::
|
703
|
+
).returns(T.any(Entry::ConstantAlias, Entry::UnresolvedConstantAlias))
|
703
704
|
end
|
704
705
|
def resolve_alias(entry, seen_names)
|
705
706
|
alias_name = entry.name
|
@@ -711,7 +712,7 @@ module RubyIndexer
|
|
711
712
|
return entry unless target
|
712
713
|
|
713
714
|
target_name = T.must(target.first).name
|
714
|
-
resolved_alias = Entry::
|
715
|
+
resolved_alias = Entry::ConstantAlias.new(target_name, entry)
|
715
716
|
|
716
717
|
# Replace the UnresolvedAlias by a resolved one so that we don't have to do this again later
|
717
718
|
original_entries = T.must(@entries[alias_name])
|
@@ -730,8 +731,8 @@ module RubyIndexer
|
|
730
731
|
seen_names: T::Array[String],
|
731
732
|
).returns(T.nilable(T::Array[T.any(
|
732
733
|
Entry::Namespace,
|
733
|
-
Entry::
|
734
|
-
Entry::
|
734
|
+
Entry::ConstantAlias,
|
735
|
+
Entry::UnresolvedConstantAlias,
|
735
736
|
)]))
|
736
737
|
end
|
737
738
|
def lookup_enclosing_scopes(name, nesting, seen_names)
|
@@ -759,8 +760,8 @@ module RubyIndexer
|
|
759
760
|
seen_names: T::Array[String],
|
760
761
|
).returns(T.nilable(T::Array[T.any(
|
761
762
|
Entry::Namespace,
|
762
|
-
Entry::
|
763
|
-
Entry::
|
763
|
+
Entry::ConstantAlias,
|
764
|
+
Entry::UnresolvedConstantAlias,
|
764
765
|
)]))
|
765
766
|
end
|
766
767
|
def lookup_ancestor_chain(name, nesting, seen_names)
|
@@ -820,8 +821,8 @@ module RubyIndexer
|
|
820
821
|
).returns(
|
821
822
|
T.nilable(T::Array[T.any(
|
822
823
|
Entry::Namespace,
|
823
|
-
Entry::
|
824
|
-
Entry::
|
824
|
+
Entry::ConstantAlias,
|
825
|
+
Entry::UnresolvedConstantAlias,
|
825
826
|
)]),
|
826
827
|
)
|
827
828
|
end
|
@@ -829,11 +830,11 @@ module RubyIndexer
|
|
829
830
|
entries = @entries[full_name] || @entries[follow_aliased_namespace(full_name)]
|
830
831
|
|
831
832
|
T.cast(
|
832
|
-
entries&.map { |e| e.is_a?(Entry::
|
833
|
+
entries&.map { |e| e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e },
|
833
834
|
T.nilable(T::Array[T.any(
|
834
835
|
Entry::Namespace,
|
835
|
-
Entry::
|
836
|
-
Entry::
|
836
|
+
Entry::ConstantAlias,
|
837
|
+
Entry::UnresolvedConstantAlias,
|
837
838
|
)]),
|
838
839
|
)
|
839
840
|
end
|
@@ -39,6 +39,9 @@ module RubyIndexer
|
|
39
39
|
case declaration
|
40
40
|
when RBS::AST::Declarations::Class, RBS::AST::Declarations::Module
|
41
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)
|
42
45
|
else # rubocop:disable Style/EmptyElse
|
43
46
|
# Other kinds not yet handled
|
44
47
|
end
|
@@ -51,7 +54,7 @@ module RubyIndexer
|
|
51
54
|
nesting = [declaration.name.name.to_s]
|
52
55
|
file_path = pathname.to_s
|
53
56
|
location = to_ruby_indexer_location(declaration.location)
|
54
|
-
comments =
|
57
|
+
comments = comments_to_string(declaration)
|
55
58
|
entry = if declaration.is_a?(RBS::AST::Declarations::Class)
|
56
59
|
parent_class = declaration.super_class&.name&.name&.to_s
|
57
60
|
Entry::Class.new(nesting, file_path, location, location, comments, parent_class)
|
@@ -61,9 +64,16 @@ module RubyIndexer
|
|
61
64
|
add_declaration_mixins_to_entry(declaration, entry)
|
62
65
|
@index.add(entry)
|
63
66
|
declaration.members.each do |member|
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
67
77
|
end
|
68
78
|
end
|
69
79
|
|
@@ -103,7 +113,7 @@ module RubyIndexer
|
|
103
113
|
name = member.name.name
|
104
114
|
file_path = member.location.buffer.name
|
105
115
|
location = to_ruby_indexer_location(member.location)
|
106
|
-
comments =
|
116
|
+
comments = comments_to_string(member)
|
107
117
|
|
108
118
|
visibility = case member.visibility
|
109
119
|
when :private
|
@@ -212,5 +222,60 @@ module RubyIndexer
|
|
212
222
|
|
213
223
|
Entry::KeywordRestParameter.new(name: name)
|
214
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
|
215
280
|
end
|
216
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
|
@@ -1765,5 +1765,62 @@ module RubyIndexer
|
|
1765
1765
|
@index.linearized_ancestors_of("A::<Class:A>")
|
1766
1766
|
end
|
1767
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
|
1768
1825
|
end
|
1769
1826
|
end
|
@@ -40,6 +40,27 @@ module RubyIndexer
|
|
40
40
|
assert_operator(entry.location.end_column, :>, 0)
|
41
41
|
end
|
42
42
|
|
43
|
+
def test_index_core_constants
|
44
|
+
entries = @index["RUBY_VERSION"]
|
45
|
+
refute_nil(entries)
|
46
|
+
assert_equal(1, entries.length)
|
47
|
+
|
48
|
+
# Complex::I is defined as `Complex::I = ...`
|
49
|
+
entries = @index["Complex::I"]
|
50
|
+
refute_nil(entries)
|
51
|
+
assert_equal(1, entries.length)
|
52
|
+
|
53
|
+
# Encoding::US_ASCII is defined as
|
54
|
+
# ```
|
55
|
+
# module Encoding
|
56
|
+
# US_ASCII = ...
|
57
|
+
# ...
|
58
|
+
# ````
|
59
|
+
entries = @index["Encoding::US_ASCII"]
|
60
|
+
refute_nil(entries)
|
61
|
+
assert_equal(1, entries.length)
|
62
|
+
end
|
63
|
+
|
43
64
|
def test_index_methods
|
44
65
|
entries = @index["initialize"]
|
45
66
|
refute_nil(entries)
|
@@ -310,6 +331,23 @@ module RubyIndexer
|
|
310
331
|
assert_kind_of(Entry::BlockParameter, parameters[3])
|
311
332
|
end
|
312
333
|
|
334
|
+
def test_signature_alias
|
335
|
+
# In RBS, an alias means that two methods have the same signature.
|
336
|
+
# It does not mean the same thing as a Ruby alias.
|
337
|
+
any_entries = @index["any?"]
|
338
|
+
|
339
|
+
assert_equal(["Array", "Enumerable", "Hash"], any_entries.map { _1.owner.name })
|
340
|
+
|
341
|
+
entry = any_entries.find { |entry| entry.owner.name == "Array" }
|
342
|
+
|
343
|
+
assert_kind_of(RubyIndexer::Entry::UnresolvedMethodAlias, entry)
|
344
|
+
assert_equal("any?", entry.name)
|
345
|
+
assert_equal("all?", entry.old_name)
|
346
|
+
assert_equal("Array", entry.owner.name)
|
347
|
+
assert(entry.file_path.end_with?("core/array.rbs"))
|
348
|
+
assert_includes(entry.comments[0], "Returns `true` if any element of `self` meets a given criterion.")
|
349
|
+
end
|
350
|
+
|
313
351
|
private
|
314
352
|
|
315
353
|
def parse_rbs_methods(rbs, method_name)
|
@@ -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 }
|
@@ -89,12 +89,12 @@ module RubyLsp
|
|
89
89
|
when Prism::ClassNode, Prism::ModuleNode
|
90
90
|
nesting << node.constant_path.slice
|
91
91
|
when Prism::SingletonClassNode
|
92
|
-
nesting << "<Class:#{nesting.last}>"
|
92
|
+
nesting << "<Class:#{nesting.flat_map { |n| n.split("::") }.last}>"
|
93
93
|
when Prism::DefNode
|
94
94
|
surrounding_method = node.name.to_s
|
95
95
|
next unless node.receiver.is_a?(Prism::SelfNode)
|
96
96
|
|
97
|
-
nesting << "<Class:#{nesting.last}>"
|
97
|
+
nesting << "<Class:#{nesting.flat_map { |n| n.split("::") }.last}>"
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
@@ -32,8 +32,9 @@ module RubyLsp
|
|
32
32
|
|
33
33
|
END_REGEXES = T.let(
|
34
34
|
[
|
35
|
-
/\b(if|unless|for|while|
|
36
|
-
|
35
|
+
/\b(if|unless|for|while|until)\b($|\s|\()/,
|
36
|
+
/\b(class|module|def|case)\b($|\s)/,
|
37
|
+
/.*\s\bdo\b($|\s)/,
|
37
38
|
],
|
38
39
|
T::Array[Regexp],
|
39
40
|
)
|
data/lib/ruby_lsp/server.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.17.
|
4
|
+
version: 0.17.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|