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 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