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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3deccbc9952a61d46392be2f050a4684b108e7cd3da0085a4e8997168dfc021
4
- data.tar.gz: d1b9a804d9e67aa2d23b39f12c79c6e1d5ba940ec360170623f7e0870e7d6c78
3
+ metadata.gz: 7fafb2e9f1ef8ae394523d09256bce9016307821ab32ecd17fe645dd3f9d88f1
4
+ data.tar.gz: 74396754fdbf6dffb91ff4d6d237f67c26b92fd7ec0f187fc3e40627dc214de1
5
5
  SHA512:
6
- metadata.gz: fe2e99145d0f268644a5d8feeff49146e635998ab8ff3eff18f47b1f555bb249b296966d0a431c930ab6ebca58032ed954a338f7c075300869545711bcfae50e
7
- data.tar.gz: 36f253a202755a49e33b8d6c4906aa0acfebe9fe52772519684d433649d7fbadfee186274eba94027017535e55438333759477e2dbb87a7da7e9ead0bc98aaa7
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
- Adding method support for definition, completion, hover and workspace symbol is partially supported, but not yet complete. Follow progress in https://github.com/Shopify/ruby-lsp/issues/899
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.9
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::UnresolvedAlias.new(value.slice, @stack.dup, name, @file_path, node.location, comments)
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::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
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::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
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 UnresolvedAlias < Entry
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 Alias < Entry
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: UnresolvedAlias).void }
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::Alias,
95
- Entry::UnresolvedAlias,
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::Alias,
109
- Entry::UnresolvedAlias,
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::Alias,
234
- Entry::UnresolvedAlias,
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::Alias
337
+ when Entry::ConstantAlias
337
338
  target = entry.target
338
339
  return follow_aliased_namespace("#{target}::#{real_parts.join("::")}", seen_names)
339
- when Entry::UnresolvedAlias
340
+ when Entry::UnresolvedConstantAlias
340
341
  resolved = resolve_alias(entry, seen_names)
341
342
 
342
- if resolved.is_a?(Entry::UnresolvedAlias)
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::Alias
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 = [parent_class_name]
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::UnresolvedAlias,
701
+ entry: Entry::UnresolvedConstantAlias,
701
702
  seen_names: T::Array[String],
702
- ).returns(T.any(Entry::Alias, Entry::UnresolvedAlias))
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::Alias.new(target_name, 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::Alias,
734
- Entry::UnresolvedAlias,
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::Alias,
763
- Entry::UnresolvedAlias,
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::Alias,
824
- Entry::UnresolvedAlias,
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::UnresolvedAlias) ? resolve_alias(e, seen_names) : e },
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::Alias,
836
- Entry::UnresolvedAlias,
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 = Array(declaration.comment&.string)
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
- next unless member.is_a?(RBS::AST::Members::MethodDefinition)
65
-
66
- handle_method(member, entry)
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 = Array(member.comment&.string)
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::UnresolvedAlias, unresolve_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::Alias, resolved_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::UnresolvedAlias, unresolve_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::Alias, resolved_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::UnresolvedAlias, unresolve_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::UnresolvedAlias, unresolve_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::Alias, resolved_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::UnresolvedAlias, unresolve_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::Alias, resolved_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::UnresolvedAlias, unresolve_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::Alias, resolved_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::UnresolvedAlias, unresolve_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::Alias, resolved_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::Alias, resolved_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::UnresolvedAlias, "/fake/path/foo.rb:1-5:1-6")
348
- assert_entry("A::D::E", Entry::UnresolvedAlias, "/fake/path/foo.rb:2-2:2-6")
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::UnresolvedAlias, "/fake/path/foo.rb:3-5:3-9")
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 Float
184
+ class Bar
185
185
  end
186
186
 
187
187
  module Foo
188
- class Float < self
189
- INFINITY = ::Float::INFINITY
188
+ class Bar < self
189
+ BAZ = ::Bar::BAZ
190
190
  end
191
191
  end
192
192
  RUBY
193
193
 
194
- entry = @index.resolve("INFINITY", ["Foo", "Float"]).first
194
+ entry = @index.resolve("BAZ", ["Foo", "Bar"]).first
195
195
  refute_nil(entry)
196
196
 
197
- assert_instance_of(Entry::UnresolvedAlias, 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::Alias, foo_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::Alias, bar_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::Alias, foo_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::Alias, bar_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::Alias, baz_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
- add_highlight(Constant::DocumentHighlightKind::READ, node.location)
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|class|module|until|def|case)\b.*/,
36
- /.*\s\bdo\b/,
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
  )
@@ -765,8 +765,6 @@ module RubyLsp
765
765
  # stuck indexing files
766
766
  Thread.new do
767
767
  begin
768
- RubyIndexer::RBSIndexer.new(@global_state.index).index_ruby_core
769
-
770
768
  @global_state.index.index_all do |percentage|
771
769
  progress("indexing-progress", percentage)
772
770
  true
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.9
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-23 00:00:00.000000000 Z
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