ruby-lsp 0.26.9 → 0.27.0.beta1

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: 7b12b079fa54de3410e0d0ab2db9bf2241c6413c3c61d3ead21d4bbcc9b4e7d6
4
- data.tar.gz: dede13048ef29f79079271c7d20d505160f8fe24d495863e08be96c95207c6bf
3
+ metadata.gz: 4e5c88483f280d13f2691e131a16e35df07df898e8f380071d077479cd24133e
4
+ data.tar.gz: 7630ce8844fcb532ca63c77e299b692471b145530a623dcbc78ecbb50ce08596
5
5
  SHA512:
6
- metadata.gz: 922edf8dadc38fa037d2c157c8e0b9bbbd3c759931ac6cda3070b615a3039597028672e8632507e80689f6a8f6cc793e7a1006a49bba28d72489f541e75f2fc3
7
- data.tar.gz: 046d2d369c16a743f71750a9a6dfed945d8e21b43bb58ad357ce1098fd731dc4e7060ffebcc96e1b78a43a3b2571ac9e31d94dc6c25c380478cb54a979c0f461
6
+ metadata.gz: 7757882e6e441d49705a389bad6b0b617b7f9a26fb1e5f34ebd3fc93eeb8921b31a703253cff79ed123ae411333fc7d91f0555ae162cafee1d10adfb13b76cf4
7
+ data.tar.gz: c215743ab00113e4dd3186732faabd462fef6a3be425f1c76bb65b045846effd7db0673478244218f1087e3fa9ca96f8668c86970def65bf2e5fbb4ea0728a8d
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.26.9
1
+ 0.27.0.beta1
@@ -129,7 +129,7 @@ module RubyIndexer
129
129
 
130
130
  if current_owner
131
131
  expression = node.expression
132
- name = (expression.is_a?(Prism::SelfNode) ? "<Class:#{last_name_in_stack}>" : "<Class:#{expression.slice}>")
132
+ name = (expression.is_a?(Prism::SelfNode) ? "<#{last_name_in_stack}>" : "<#{expression.slice}>")
133
133
  real_nesting = Index.actual_nesting(@stack, name)
134
134
 
135
135
  existing_entries = @index[real_nesting.join("::")] #: as Array[Entry::SingletonClass]?
@@ -577,7 +577,7 @@ module RubyIndexer
577
577
 
578
578
  # set the class variable's owner to the attached context when defined within a singleton scope.
579
579
  if owner.is_a?(Entry::SingletonClass)
580
- owner = @owner_stack.reverse.find { |entry| !entry.name.include?("<Class:") }
580
+ owner = @owner_stack.reverse.find { |entry| !entry.name.include?("<") }
581
581
  end
582
582
 
583
583
  @index.add(Entry::ClassVariable.new(
@@ -507,7 +507,7 @@ module RubyIndexer
507
507
  singleton_levels = 0
508
508
 
509
509
  parts.reverse_each do |part|
510
- break unless part.include?("<Class:")
510
+ break unless part.start_with?("<")
511
511
 
512
512
  singleton_levels += 1
513
513
  parts.pop
@@ -551,7 +551,7 @@ module RubyIndexer
551
551
 
552
552
  if nesting.any?
553
553
  singleton_levels.times do
554
- nesting << "<Class:#{nesting.last}>"
554
+ nesting << "<#{nesting.last}>"
555
555
  end
556
556
  end
557
557
 
@@ -616,7 +616,7 @@ module RubyIndexer
616
616
  if class_variables.any?
617
617
  name_parts = owner_name.split("::")
618
618
 
619
- if name_parts.last&.start_with?("<Class:")
619
+ if name_parts.last&.start_with?("<")
620
620
  attached_name = name_parts[0..-2] #: as !nil
621
621
  .join("::")
622
622
  attached_ancestors = linearized_ancestors_of(attached_name)
@@ -707,7 +707,7 @@ module RubyIndexer
707
707
  #: (String name) -> Entry::SingletonClass
708
708
  def existing_or_new_singleton_class(name)
709
709
  *_namespace, unqualified_name = name.split("::")
710
- full_singleton_name = "#{name}::<Class:#{unqualified_name}>"
710
+ full_singleton_name = "#{name}::<#{unqualified_name}>"
711
711
  singleton = self[full_singleton_name]&.first #: as Entry::SingletonClass?
712
712
 
713
713
  unless singleton
@@ -744,7 +744,7 @@ module RubyIndexer
744
744
  def linearized_attached_ancestors(name)
745
745
  name_parts = name.split("::")
746
746
 
747
- if name_parts.last&.start_with?("<Class:")
747
+ if name_parts.last&.start_with?("<")
748
748
  attached_name = name_parts[0..-2] #: as !nil
749
749
  .join("::")
750
750
  linearized_ancestors_of(attached_name)
@@ -866,7 +866,7 @@ module RubyIndexer
866
866
 
867
867
  parent_name_parts = parent_class_name.split("::")
868
868
  singleton_levels.times do
869
- parent_name_parts << "<Class:#{parent_name_parts.last}>"
869
+ parent_name_parts << "<#{parent_name_parts.last}>"
870
870
  end
871
871
 
872
872
  ancestors.concat(linearized_ancestors_of(parent_name_parts.join("::")))
@@ -878,7 +878,7 @@ module RubyIndexer
878
878
  class_class_name_parts = ["Class"]
879
879
 
880
880
  (singleton_levels - 1).times do
881
- class_class_name_parts << "<Class:#{class_class_name_parts.last}>"
881
+ class_class_name_parts << "<#{class_class_name_parts.last}>"
882
882
  end
883
883
 
884
884
  ancestors.concat(linearized_ancestors_of(class_class_name_parts.join("::")))
@@ -892,7 +892,7 @@ module RubyIndexer
892
892
  module_class_name_parts = ["Module"]
893
893
 
894
894
  (singleton_levels - 1).times do
895
- module_class_name_parts << "<Class:#{module_class_name_parts.last}>"
895
+ module_class_name_parts << "<#{module_class_name_parts.last}>"
896
896
  end
897
897
 
898
898
  ancestors.concat(linearized_ancestors_of(module_class_name_parts.join("::")))
@@ -134,7 +134,7 @@ module RubyIndexer
134
134
  expression = node.expression
135
135
  return unless expression.is_a?(Prism::SelfNode)
136
136
 
137
- @stack << "<Class:#{@stack.last}>"
137
+ @stack << "<#{@stack.last}>"
138
138
  end
139
139
 
140
140
  #: (Prism::SingletonClassNode node) -> void
@@ -239,7 +239,7 @@ module RubyIndexer
239
239
  end
240
240
 
241
241
  if node.receiver.is_a?(Prism::SelfNode)
242
- @stack << "<Class:#{@stack.last}>"
242
+ @stack << "<#{@stack.last}>"
243
243
  end
244
244
  end
245
245
 
@@ -30,6 +30,9 @@ module RubyLsp
30
30
  #: RubyIndexer::Index
31
31
  attr_reader :index
32
32
 
33
+ #: Rubydex::Graph
34
+ attr_reader :graph
35
+
33
36
  #: Encoding
34
37
  attr_reader :encoding
35
38
 
@@ -58,8 +61,9 @@ module RubyLsp
58
61
  @test_library = "minitest" #: String
59
62
  @has_type_checker = true #: bool
60
63
  @index = RubyIndexer::Index.new #: RubyIndexer::Index
64
+ @graph = Rubydex::Graph.new #: Rubydex::Graph
61
65
  @supported_formatters = {} #: Hash[String, Requests::Support::Formatter]
62
- @type_inferrer = TypeInferrer.new(@index) #: TypeInferrer
66
+ @type_inferrer = TypeInferrer.new(@graph) #: TypeInferrer
63
67
  @addon_settings = {} #: Hash[String, untyped]
64
68
  @top_level_bundle = begin
65
69
  Bundler.with_original_env { Bundler.default_gemfile }
@@ -117,6 +121,7 @@ module RubyLsp
117
121
  all_dependencies = gather_direct_and_indirect_dependencies
118
122
  workspace_uri = options.dig(:workspaceFolders, 0, :uri)
119
123
  @workspace_uri = URI(workspace_uri) if workspace_uri
124
+ @graph.workspace_path = workspace_path
120
125
 
121
126
  specified_formatter = options.dig(:initializationOptions, :formatter)
122
127
  rubocop_has_addon = defined?(::RuboCop::Version::STRING) &&
@@ -189,12 +194,16 @@ module RubyLsp
189
194
 
190
195
  encodings = options.dig(:capabilities, :general, :positionEncodings)
191
196
  @encoding = if !encodings || encodings.empty?
197
+ @graph.encoding = "utf16"
192
198
  Encoding::UTF_16LE
193
199
  elsif encodings.include?(Constant::PositionEncodingKind::UTF8)
200
+ @graph.encoding = "utf8"
194
201
  Encoding::UTF_8
195
202
  elsif encodings.include?(Constant::PositionEncodingKind::UTF16)
203
+ @graph.encoding = "utf16"
196
204
  Encoding::UTF_16LE
197
205
  else
206
+ @graph.encoding = "utf32"
198
207
  Encoding::UTF_32
199
208
  end
200
209
  @index.configuration.encoding = @encoding
@@ -20,6 +20,7 @@ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.5")
20
20
  end
21
21
  require "set"
22
22
  require "strscan"
23
+ require "rubydex"
23
24
  require "prism"
24
25
  require "prism/visitor"
25
26
  require "language_server-protocol"
@@ -30,6 +31,10 @@ require "securerandom"
30
31
  require "shellwords"
31
32
  require "set"
32
33
 
34
+ # Rubydex LSP additions
35
+ require "ruby_lsp/rubydex/definition"
36
+ require "ruby_lsp/rubydex/reference"
37
+
33
38
  require "ruby-lsp"
34
39
  require "ruby_lsp/base_server"
35
40
  require "ruby_indexer/ruby_indexer"
@@ -12,7 +12,7 @@ module RubyLsp
12
12
  def initialize(response_builder, global_state, language_id, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
13
13
  @response_builder = response_builder
14
14
  @global_state = global_state
15
- @index = global_state.index #: RubyIndexer::Index
15
+ @graph = global_state.graph #: Rubydex::Graph
16
16
  @type_inferrer = global_state.type_inferrer #: TypeInferrer
17
17
  @language_id = language_id
18
18
  @uri = uri
@@ -109,7 +109,7 @@ module RubyLsp
109
109
  name = RubyIndexer::Index.constant_name(node)
110
110
  return if name.nil?
111
111
 
112
- find_in_index(name)
112
+ handle_constant_definition(name)
113
113
  end
114
114
 
115
115
  #: (Prism::ConstantReadNode node) -> void
@@ -117,7 +117,7 @@ module RubyLsp
117
117
  name = RubyIndexer::Index.constant_name(node)
118
118
  return if name.nil?
119
119
 
120
- find_in_index(name)
120
+ handle_constant_definition(name)
121
121
  end
122
122
 
123
123
  #: (Prism::GlobalVariableAndWriteNode node) -> void
@@ -152,32 +152,32 @@ module RubyLsp
152
152
 
153
153
  #: (Prism::InstanceVariableReadNode node) -> void
154
154
  def on_instance_variable_read_node_enter(node)
155
- handle_instance_variable_definition(node.name.to_s)
155
+ handle_variable_definition(node.name.to_s)
156
156
  end
157
157
 
158
158
  #: (Prism::InstanceVariableWriteNode node) -> void
159
159
  def on_instance_variable_write_node_enter(node)
160
- handle_instance_variable_definition(node.name.to_s)
160
+ handle_variable_definition(node.name.to_s)
161
161
  end
162
162
 
163
163
  #: (Prism::InstanceVariableAndWriteNode node) -> void
164
164
  def on_instance_variable_and_write_node_enter(node)
165
- handle_instance_variable_definition(node.name.to_s)
165
+ handle_variable_definition(node.name.to_s)
166
166
  end
167
167
 
168
168
  #: (Prism::InstanceVariableOperatorWriteNode node) -> void
169
169
  def on_instance_variable_operator_write_node_enter(node)
170
- handle_instance_variable_definition(node.name.to_s)
170
+ handle_variable_definition(node.name.to_s)
171
171
  end
172
172
 
173
173
  #: (Prism::InstanceVariableOrWriteNode node) -> void
174
174
  def on_instance_variable_or_write_node_enter(node)
175
- handle_instance_variable_definition(node.name.to_s)
175
+ handle_variable_definition(node.name.to_s)
176
176
  end
177
177
 
178
178
  #: (Prism::InstanceVariableTargetNode node) -> void
179
179
  def on_instance_variable_target_node_enter(node)
180
- handle_instance_variable_definition(node.name.to_s)
180
+ handle_variable_definition(node.name.to_s)
181
181
  end
182
182
 
183
183
  #: (Prism::SuperNode node) -> void
@@ -192,32 +192,32 @@ module RubyLsp
192
192
 
193
193
  #: (Prism::ClassVariableAndWriteNode node) -> void
194
194
  def on_class_variable_and_write_node_enter(node)
195
- handle_class_variable_definition(node.name.to_s)
195
+ handle_variable_definition(node.name.to_s)
196
196
  end
197
197
 
198
198
  #: (Prism::ClassVariableOperatorWriteNode node) -> void
199
199
  def on_class_variable_operator_write_node_enter(node)
200
- handle_class_variable_definition(node.name.to_s)
200
+ handle_variable_definition(node.name.to_s)
201
201
  end
202
202
 
203
203
  #: (Prism::ClassVariableOrWriteNode node) -> void
204
204
  def on_class_variable_or_write_node_enter(node)
205
- handle_class_variable_definition(node.name.to_s)
205
+ handle_variable_definition(node.name.to_s)
206
206
  end
207
207
 
208
208
  #: (Prism::ClassVariableTargetNode node) -> void
209
209
  def on_class_variable_target_node_enter(node)
210
- handle_class_variable_definition(node.name.to_s)
210
+ handle_variable_definition(node.name.to_s)
211
211
  end
212
212
 
213
213
  #: (Prism::ClassVariableReadNode node) -> void
214
214
  def on_class_variable_read_node_enter(node)
215
- handle_class_variable_definition(node.name.to_s)
215
+ handle_variable_definition(node.name.to_s)
216
216
  end
217
217
 
218
218
  #: (Prism::ClassVariableWriteNode node) -> void
219
219
  def on_class_variable_write_node_enter(node)
220
- handle_class_variable_definition(node.name.to_s)
220
+ handle_variable_definition(node.name.to_s)
221
221
  end
222
222
 
223
223
  private
@@ -257,93 +257,63 @@ module RubyLsp
257
257
 
258
258
  #: (String name) -> void
259
259
  def handle_global_variable_definition(name)
260
- entries = @index[name]
260
+ declaration = @graph[name]
261
+ return unless declaration
261
262
 
262
- return unless entries
263
-
264
- entries.each do |entry|
265
- location = entry.location
266
-
267
- @response_builder << Interface::Location.new(
268
- uri: entry.uri.to_s,
269
- range: Interface::Range.new(
270
- start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
271
- end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
272
- ),
273
- )
274
- end
275
- end
276
-
277
- #: (String name) -> void
278
- def handle_class_variable_definition(name)
279
- type = @type_inferrer.infer_receiver_type(@node_context)
280
- return unless type
281
-
282
- entries = @index.resolve_class_variable(name, type.name)
283
- return unless entries
284
-
285
- entries.each do |entry|
286
- @response_builder << Interface::Location.new(
287
- uri: entry.uri.to_s,
288
- range: range_from_location(entry.location),
289
- )
290
- end
291
- rescue RubyIndexer::Index::NonExistingNamespaceError
292
- # If by any chance we haven't indexed the owner, then there's no way to find the right declaration
263
+ declaration.definitions.each { |definition| @response_builder << definition.to_lsp_selection_location }
293
264
  end
294
265
 
266
+ # Handle class or instance variables. We collect all definitions across the ancestors of the type
267
+ #
295
268
  #: (String name) -> void
296
- def handle_instance_variable_definition(name)
297
- # Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
298
- # to provide all features for them
269
+ def handle_variable_definition(name)
270
+ # Sorbet enforces that all variables be declared on typed strict or higher, which means it will be able to
271
+ # provide all features for them
299
272
  return if @sorbet_level.strict?
300
273
 
301
274
  type = @type_inferrer.infer_receiver_type(@node_context)
302
275
  return unless type
303
276
 
304
- entries = @index.resolve_instance_variable(name, type.name)
305
- return unless entries
277
+ owner = @graph[type.name]
278
+ return unless owner.is_a?(Rubydex::Namespace)
306
279
 
307
- entries.each do |entry|
308
- location = entry.location
280
+ owner.ancestors.each do |ancestor|
281
+ member = ancestor.member(name)
282
+ next unless member
309
283
 
310
- @response_builder << Interface::Location.new(
311
- uri: entry.uri.to_s,
312
- range: Interface::Range.new(
313
- start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
314
- end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
315
- ),
316
- )
284
+ member.definitions.each { |definition| @response_builder << definition.to_lsp_selection_location }
317
285
  end
318
- rescue RubyIndexer::Index::NonExistingNamespaceError
319
- # If by any chance we haven't indexed the owner, then there's no way to find the right declaration
320
286
  end
321
287
 
322
288
  #: (String message, TypeInferrer::Type? receiver_type, ?inherited_only: bool) -> void
323
289
  def handle_method_definition(message, receiver_type, inherited_only: false)
324
- methods = if receiver_type
325
- @index.resolve_method(message, receiver_type.name, inherited_only: inherited_only)
290
+ declaration = if receiver_type
291
+ owner = @graph[receiver_type.name]
292
+ owner.find_member("#{message}()", only_inherited: inherited_only) if owner.is_a?(Rubydex::Namespace)
326
293
  end
327
294
 
328
- # If the method doesn't have a receiver, or the guessed receiver doesn't have any matched candidates,
329
- # then we provide a few candidates to jump to
330
- # But we don't want to provide too many candidates, as it can be overwhelming
331
- if receiver_type.nil? || (receiver_type.is_a?(TypeInferrer::GuessedType) && methods.nil?)
332
- methods = @index[message]&.take(MAX_NUMBER_OF_DEFINITION_CANDIDATES_WITHOUT_RECEIVER)
295
+ # If the method doesn't have a receiver, or the guessed receiver doesn't have any matched candidates, then we
296
+ # provide a few candidates to jump to. However, we don't want to provide too many candidates, as it can be
297
+ # overwhelming
298
+ if receiver_type.nil? || (receiver_type.is_a?(TypeInferrer::GuessedType) && declaration.nil?)
299
+ declaration = @graph.search("##{message}()").take(MAX_NUMBER_OF_DEFINITION_CANDIDATES_WITHOUT_RECEIVER)
333
300
  end
334
301
 
335
- return unless methods
302
+ return unless declaration
336
303
 
337
- methods.each do |target_method|
338
- uri = target_method.uri
339
- full_path = uri.full_path
340
- next if @sorbet_level.true_or_higher? && (!full_path || not_in_dependencies?(full_path))
304
+ Array(declaration).each do |decl|
305
+ decl.definitions.each do |definition|
306
+ location = definition.location
307
+ uri = URI(location.uri)
308
+ full_path = uri.full_path
309
+ next if @sorbet_level.true_or_higher? && (!full_path || not_in_dependencies?(full_path))
341
310
 
342
- @response_builder << Interface::LocationLink.new(
343
- target_uri: uri.to_s,
344
- target_range: range_from_location(target_method.location),
345
- target_selection_range: range_from_location(target_method.name_location),
346
- )
311
+ @response_builder << Interface::LocationLink.new(
312
+ target_uri: uri.to_s,
313
+ target_range: definition.to_lsp_selection_range,
314
+ target_selection_range: definition.to_lsp_name_range || definition.to_lsp_selection_range,
315
+ )
316
+ end
347
317
  end
348
318
  end
349
319
 
@@ -351,12 +321,10 @@ module RubyLsp
351
321
  def handle_require_definition(node, message)
352
322
  case message
353
323
  when :require
354
- entry = @index.search_require_paths(node.content).find do |uri|
355
- uri.require_path == node.content
356
- end
324
+ document = @graph.resolve_require_path(node.content, $LOAD_PATH)
357
325
 
358
- if entry
359
- candidate = entry.full_path
326
+ if document
327
+ candidate = URI(document.uri).full_path
360
328
 
361
329
  if candidate
362
330
  @response_builder << Interface::Location.new(
@@ -392,35 +360,33 @@ module RubyLsp
392
360
  constant_name = argument.value
393
361
  return unless constant_name
394
362
 
395
- find_in_index(constant_name)
363
+ handle_constant_definition(constant_name)
396
364
  end
397
365
 
398
366
  #: (String value) -> void
399
- def find_in_index(value)
400
- entries = @index.resolve(value, @node_context.nesting)
401
- return unless entries
367
+ def handle_constant_definition(value)
368
+ declaration = @graph.resolve_constant(value, @node_context.nesting)
369
+ return unless declaration
402
370
 
371
+ # [RUBYDEX] TODO: temporarily commented out until we have the visibility API
372
+ #
403
373
  # We should only allow jumping to the definition of private constants if the constant is defined in the same
404
374
  # namespace as the reference
405
- first_entry = entries.first #: as !nil
406
- return if first_entry.private? && first_entry.name != "#{@node_context.fully_qualified_name}::#{value}"
375
+ #
376
+ # return if declaration.private? && declaration.name != "#{@node_context.fully_qualified_name}::#{value}"
407
377
 
408
- entries.each do |entry|
378
+ declaration.definitions.each do |definition|
409
379
  # If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
410
380
  # additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
411
381
  # ignore
412
- uri = entry.uri
382
+ uri = URI(definition.location.uri)
413
383
  full_path = uri.full_path
414
384
 
415
385
  if !@sorbet_level.ignore? && (!full_path || not_in_dependencies?(full_path))
416
386
  next
417
387
  end
418
388
 
419
- @response_builder << Interface::LocationLink.new(
420
- target_uri: uri.to_s,
421
- target_range: range_from_location(entry.location),
422
- target_selection_range: range_from_location(entry.name_location),
423
- )
389
+ @response_builder << definition.to_lsp_location_link
424
390
  end
425
391
  end
426
392
  end
@@ -48,6 +48,7 @@ module RubyLsp
48
48
  @response_builder = response_builder
49
49
  @global_state = global_state
50
50
  @index = global_state.index #: RubyIndexer::Index
51
+ @graph = global_state.graph #: Rubydex::Graph
51
52
  @type_inferrer = global_state.type_inferrer #: TypeInferrer
52
53
  @path = uri.to_standardized_path #: String?
53
54
  @node_context = node_context
@@ -178,32 +179,32 @@ module RubyLsp
178
179
 
179
180
  #: (Prism::InstanceVariableReadNode node) -> void
180
181
  def on_instance_variable_read_node_enter(node)
181
- handle_instance_variable_hover(node.name.to_s)
182
+ handle_variable_hover(node.name.to_s)
182
183
  end
183
184
 
184
185
  #: (Prism::InstanceVariableWriteNode node) -> void
185
186
  def on_instance_variable_write_node_enter(node)
186
- handle_instance_variable_hover(node.name.to_s)
187
+ handle_variable_hover(node.name.to_s)
187
188
  end
188
189
 
189
190
  #: (Prism::InstanceVariableAndWriteNode node) -> void
190
191
  def on_instance_variable_and_write_node_enter(node)
191
- handle_instance_variable_hover(node.name.to_s)
192
+ handle_variable_hover(node.name.to_s)
192
193
  end
193
194
 
194
195
  #: (Prism::InstanceVariableOperatorWriteNode node) -> void
195
196
  def on_instance_variable_operator_write_node_enter(node)
196
- handle_instance_variable_hover(node.name.to_s)
197
+ handle_variable_hover(node.name.to_s)
197
198
  end
198
199
 
199
200
  #: (Prism::InstanceVariableOrWriteNode node) -> void
200
201
  def on_instance_variable_or_write_node_enter(node)
201
- handle_instance_variable_hover(node.name.to_s)
202
+ handle_variable_hover(node.name.to_s)
202
203
  end
203
204
 
204
205
  #: (Prism::InstanceVariableTargetNode node) -> void
205
206
  def on_instance_variable_target_node_enter(node)
206
- handle_instance_variable_hover(node.name.to_s)
207
+ handle_variable_hover(node.name.to_s)
207
208
  end
208
209
 
209
210
  #: (Prism::SuperNode node) -> void
@@ -223,32 +224,32 @@ module RubyLsp
223
224
 
224
225
  #: (Prism::ClassVariableAndWriteNode node) -> void
225
226
  def on_class_variable_and_write_node_enter(node)
226
- handle_class_variable_hover(node.name.to_s)
227
+ handle_variable_hover(node.name.to_s)
227
228
  end
228
229
 
229
230
  #: (Prism::ClassVariableOperatorWriteNode node) -> void
230
231
  def on_class_variable_operator_write_node_enter(node)
231
- handle_class_variable_hover(node.name.to_s)
232
+ handle_variable_hover(node.name.to_s)
232
233
  end
233
234
 
234
235
  #: (Prism::ClassVariableOrWriteNode node) -> void
235
236
  def on_class_variable_or_write_node_enter(node)
236
- handle_class_variable_hover(node.name.to_s)
237
+ handle_variable_hover(node.name.to_s)
237
238
  end
238
239
 
239
240
  #: (Prism::ClassVariableTargetNode node) -> void
240
241
  def on_class_variable_target_node_enter(node)
241
- handle_class_variable_hover(node.name.to_s)
242
+ handle_variable_hover(node.name.to_s)
242
243
  end
243
244
 
244
245
  #: (Prism::ClassVariableReadNode node) -> void
245
246
  def on_class_variable_read_node_enter(node)
246
- handle_class_variable_hover(node.name.to_s)
247
+ handle_variable_hover(node.name.to_s)
247
248
  end
248
249
 
249
250
  #: (Prism::ClassVariableWriteNode node) -> void
250
251
  def on_class_variable_write_node_enter(node)
251
- handle_class_variable_hover(node.name.to_s)
252
+ handle_variable_hover(node.name.to_s)
252
253
  end
253
254
 
254
255
  private
@@ -324,62 +325,46 @@ module RubyLsp
324
325
  end
325
326
  end
326
327
 
327
- #: (String name) -> void
328
- def handle_instance_variable_hover(name)
329
- # Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
330
- # to provide all features for them
331
- return if @sorbet_level.strict?
332
-
333
- type = @type_inferrer.infer_receiver_type(@node_context)
334
- return unless type
335
-
336
- entries = @index.resolve_instance_variable(name, type.name)
337
- return unless entries
338
-
339
- categorized_markdown_from_index_entries(name, entries).each do |category, content|
340
- @response_builder.push(content, category: category)
341
- end
342
- rescue RubyIndexer::Index::NonExistingNamespaceError
343
- # If by any chance we haven't indexed the owner, then there's no way to find the right declaration
344
- end
345
-
346
328
  #: (String name) -> void
347
329
  def handle_global_variable_hover(name)
348
- entries = @index[name]
349
- return unless entries
330
+ declaration = @graph[name]
331
+ return unless declaration
350
332
 
351
- categorized_markdown_from_index_entries(name, entries).each do |category, content|
333
+ categorized_markdown_from_definitions(name, declaration.definitions).each do |category, content|
352
334
  @response_builder.push(content, category: category)
353
335
  end
354
336
  end
355
337
 
338
+ # Handle class or instance variables. We collect all definitions across the ancestors of the type
339
+ #
356
340
  #: (String name) -> void
357
- def handle_class_variable_hover(name)
341
+ def handle_variable_hover(name)
342
+ # Sorbet enforces that all variables be declared on typed strict or higher, which means it will be able to
343
+ # provide all features for them
344
+ return if @sorbet_level.strict?
345
+
358
346
  type = @type_inferrer.infer_receiver_type(@node_context)
359
347
  return unless type
360
348
 
361
- entries = @index.resolve_class_variable(name, type.name)
362
- return unless entries
349
+ owner = @graph[type.name]
350
+ return unless owner.is_a?(Rubydex::Namespace)
363
351
 
364
- categorized_markdown_from_index_entries(name, entries).each do |category, content|
365
- @response_builder.push(content, category: category)
352
+ owner.ancestors.each do |ancestor|
353
+ member = ancestor.member(name)
354
+ next unless member
355
+
356
+ categorized_markdown_from_definitions(member.name, member.definitions).each do |category, content|
357
+ @response_builder.push(content, category: category)
358
+ end
366
359
  end
367
- rescue RubyIndexer::Index::NonExistingNamespaceError
368
- # If by any chance we haven't indexed the owner, then there's no way to find the right declaration
369
360
  end
370
361
 
371
362
  #: (String name, Prism::Location location) -> void
372
363
  def generate_hover(name, location)
373
- entries = @index.resolve(name, @node_context.nesting)
374
- return unless entries
375
-
376
- # We should only show hover for private constants if the constant is defined in the same namespace as the
377
- # reference
378
- first_entry = entries.first #: as !nil
379
- full_name = first_entry.name
380
- return if first_entry.private? && full_name != "#{@node_context.fully_qualified_name}::#{name}"
364
+ declaration = @graph.resolve_constant(name, @node_context.nesting)
365
+ return unless declaration
381
366
 
382
- categorized_markdown_from_index_entries(full_name, entries).each do |category, content|
367
+ categorized_markdown_from_definitions(declaration.name, declaration.definitions).each do |category, content|
383
368
  @response_builder.push(content, category: category)
384
369
  end
385
370
  end
@@ -266,7 +266,7 @@ module RubyLsp
266
266
  # We only support regular Minitest tests. The declarative syntax provided by ActiveSupport is handled by the
267
267
  # Rails add-on
268
268
  name_parts = fully_qualified_name.split("::")
269
- singleton_name = "#{name_parts.join("::")}::<Class:#{name_parts.last}>"
269
+ singleton_name = "#{name_parts.join("::")}::<#{name_parts.last}>"
270
270
  !@index.linearized_ancestors_of(singleton_name).include?("ActiveSupport::Testing::Declarative")
271
271
  rescue RubyIndexer::Index::NonExistingNamespaceError
272
272
  true