ruby-lsp 0.17.9 → 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 +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
|