ruby-lsp 0.17.3 → 0.17.5

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/VERSION +1 -1
  4. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +251 -100
  5. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +173 -114
  6. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +337 -77
  7. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +43 -14
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +79 -3
  9. data/lib/ruby_indexer/test/index_test.rb +563 -29
  10. data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
  11. data/lib/ruby_indexer/test/method_test.rb +75 -25
  12. data/lib/ruby_indexer/test/rbs_indexer_test.rb +38 -2
  13. data/lib/ruby_indexer/test/test_case.rb +1 -5
  14. data/lib/ruby_lsp/addon.rb +13 -1
  15. data/lib/ruby_lsp/document.rb +50 -23
  16. data/lib/ruby_lsp/erb_document.rb +125 -0
  17. data/lib/ruby_lsp/global_state.rb +11 -4
  18. data/lib/ruby_lsp/internal.rb +3 -0
  19. data/lib/ruby_lsp/listeners/completion.rb +69 -34
  20. data/lib/ruby_lsp/listeners/definition.rb +34 -23
  21. data/lib/ruby_lsp/listeners/hover.rb +14 -7
  22. data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
  23. data/lib/ruby_lsp/node_context.rb +6 -1
  24. data/lib/ruby_lsp/requests/code_action_resolve.rb +2 -2
  25. data/lib/ruby_lsp/requests/completion.rb +6 -5
  26. data/lib/ruby_lsp/requests/completion_resolve.rb +7 -4
  27. data/lib/ruby_lsp/requests/definition.rb +4 -3
  28. data/lib/ruby_lsp/requests/formatting.rb +2 -0
  29. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
  30. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  31. data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -2
  32. data/lib/ruby_lsp/requests/support/common.rb +19 -1
  33. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
  34. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +87 -0
  35. data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
  36. data/lib/ruby_lsp/requests.rb +2 -0
  37. data/lib/ruby_lsp/ruby_document.rb +10 -0
  38. data/lib/ruby_lsp/server.rb +95 -26
  39. data/lib/ruby_lsp/store.rb +23 -8
  40. data/lib/ruby_lsp/test_helper.rb +3 -1
  41. data/lib/ruby_lsp/type_inferrer.rb +86 -0
  42. metadata +10 -6
@@ -20,7 +20,9 @@ module RubyIndexer
20
20
  assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
21
21
 
22
22
  entry = T.must(@index["@a"]&.first)
23
- assert_equal("Foo::Bar", T.must(entry.owner).name)
23
+ owner = T.must(entry.owner)
24
+ assert_instance_of(Entry::Class, owner)
25
+ assert_equal("Foo::Bar", owner.name)
24
26
  end
25
27
 
26
28
  def test_instance_variable_and_write
@@ -38,7 +40,9 @@ module RubyIndexer
38
40
  assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
39
41
 
40
42
  entry = T.must(@index["@a"]&.first)
41
- assert_equal("Foo::Bar", T.must(entry.owner).name)
43
+ owner = T.must(entry.owner)
44
+ assert_instance_of(Entry::Class, owner)
45
+ assert_equal("Foo::Bar", owner.name)
42
46
  end
43
47
 
44
48
  def test_instance_variable_operator_write
@@ -56,7 +60,9 @@ module RubyIndexer
56
60
  assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
57
61
 
58
62
  entry = T.must(@index["@a"]&.first)
59
- assert_equal("Foo::Bar", T.must(entry.owner).name)
63
+ owner = T.must(entry.owner)
64
+ assert_instance_of(Entry::Class, owner)
65
+ assert_equal("Foo::Bar", owner.name)
60
66
  end
61
67
 
62
68
  def test_instance_variable_or_write
@@ -74,7 +80,9 @@ module RubyIndexer
74
80
  assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
75
81
 
76
82
  entry = T.must(@index["@a"]&.first)
77
- assert_equal("Foo::Bar", T.must(entry.owner).name)
83
+ owner = T.must(entry.owner)
84
+ assert_instance_of(Entry::Class, owner)
85
+ assert_equal("Foo::Bar", owner.name)
78
86
  end
79
87
 
80
88
  def test_instance_variable_target
@@ -93,10 +101,14 @@ module RubyIndexer
93
101
  assert_entry("@b", Entry::InstanceVariable, "/fake/path/foo.rb:4-10:4-12")
94
102
 
95
103
  entry = T.must(@index["@a"]&.first)
96
- assert_equal("Foo::Bar", T.must(entry.owner).name)
104
+ owner = T.must(entry.owner)
105
+ assert_instance_of(Entry::Class, owner)
106
+ assert_equal("Foo::Bar", owner.name)
97
107
 
98
108
  entry = T.must(@index["@b"]&.first)
99
- assert_equal("Foo::Bar", T.must(entry.owner).name)
109
+ owner = T.must(entry.owner)
110
+ assert_instance_of(Entry::Class, owner)
111
+ assert_equal("Foo::Bar", owner.name)
100
112
  end
101
113
 
102
114
  def test_empty_name_instance_variables
@@ -118,6 +130,14 @@ module RubyIndexer
118
130
  module Foo
119
131
  class Bar
120
132
  @a = 123
133
+
134
+ class << self
135
+ def hello
136
+ @b = 123
137
+ end
138
+
139
+ @c = 123
140
+ end
121
141
  end
122
142
  end
123
143
  RUBY
@@ -125,7 +145,64 @@ module RubyIndexer
125
145
  assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:2-4:2-6")
126
146
 
127
147
  entry = T.must(@index["@a"]&.first)
128
- assert_equal("Foo::Bar", T.must(entry.owner).name)
148
+ owner = T.must(entry.owner)
149
+ assert_instance_of(Entry::SingletonClass, owner)
150
+ assert_equal("Foo::Bar::<Class:Bar>", owner.name)
151
+
152
+ assert_entry("@b", Entry::InstanceVariable, "/fake/path/foo.rb:6-8:6-10")
153
+
154
+ entry = T.must(@index["@b"]&.first)
155
+ owner = T.must(entry.owner)
156
+ assert_instance_of(Entry::SingletonClass, owner)
157
+ assert_equal("Foo::Bar::<Class:Bar>", owner.name)
158
+
159
+ assert_entry("@c", Entry::InstanceVariable, "/fake/path/foo.rb:9-6:9-8")
160
+
161
+ entry = T.must(@index["@c"]&.first)
162
+ owner = T.must(entry.owner)
163
+ assert_instance_of(Entry::SingletonClass, owner)
164
+ assert_equal("Foo::Bar::<Class:Bar>::<Class:<Class:Bar>>", owner.name)
165
+ end
166
+
167
+ def test_top_level_instance_variables
168
+ index(<<~RUBY)
169
+ @a = 123
170
+ RUBY
171
+
172
+ entry = T.must(@index["@a"]&.first)
173
+ assert_nil(entry.owner)
174
+ end
175
+
176
+ def test_class_instance_variables_inside_self_method
177
+ index(<<~RUBY)
178
+ class Foo
179
+ def self.bar
180
+ @a = 123
181
+ end
182
+ end
183
+ RUBY
184
+
185
+ entry = T.must(@index["@a"]&.first)
186
+ owner = T.must(entry.owner)
187
+ assert_instance_of(Entry::SingletonClass, owner)
188
+ assert_equal("Foo::<Class:Foo>", owner.name)
189
+ end
190
+
191
+ def test_instance_variable_inside_dynamic_method_declaration
192
+ index(<<~RUBY)
193
+ class Foo
194
+ def something.bar
195
+ @a = 123
196
+ end
197
+ end
198
+ RUBY
199
+
200
+ # If the surrounding method is beind defined on any dynamic value that isn't `self`, then we attribute the
201
+ # instance variable to the wrong owner since there's no way to understand that statically
202
+ entry = T.must(@index["@a"]&.first)
203
+ owner = T.must(entry.owner)
204
+ assert_instance_of(Entry::Class, owner)
205
+ assert_equal("Foo", owner.name)
129
206
  end
130
207
  end
131
208
  end
@@ -13,7 +13,7 @@ module RubyIndexer
13
13
  end
14
14
  RUBY
15
15
 
16
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
16
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
17
17
  end
18
18
 
19
19
  def test_conditional_method
@@ -24,7 +24,7 @@ module RubyIndexer
24
24
  end
25
25
  RUBY
26
26
 
27
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
27
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
28
28
  end
29
29
 
30
30
  def test_singleton_method_using_self_receiver
@@ -35,7 +35,12 @@ module RubyIndexer
35
35
  end
36
36
  RUBY
37
37
 
38
- assert_entry("bar", Entry::SingletonMethod, "/fake/path/foo.rb:1-2:2-5")
38
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
39
+
40
+ entry = T.must(@index["bar"].first)
41
+ owner = T.must(entry.owner)
42
+ assert_equal("Foo::<Class:Foo>", owner.name)
43
+ assert_instance_of(Entry::SingletonClass, owner)
39
44
  end
40
45
 
41
46
  def test_singleton_method_using_other_receiver_is_not_indexed
@@ -83,9 +88,9 @@ module RubyIndexer
83
88
  def baz; end
84
89
  RUBY
85
90
 
86
- assert_entry("foo", Entry::InstanceMethod, "/fake/path/foo.rb:0-8:1-3", visibility: Entry::Visibility::PRIVATE)
87
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:3-0:3-12", visibility: Entry::Visibility::PUBLIC)
88
- assert_entry("baz", Entry::InstanceMethod, "/fake/path/foo.rb:7-0:7-12", visibility: Entry::Visibility::PROTECTED)
91
+ assert_entry("foo", Entry::Method, "/fake/path/foo.rb:0-8:1-3", visibility: Entry::Visibility::PRIVATE)
92
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:3-0:3-12", visibility: Entry::Visibility::PUBLIC)
93
+ assert_entry("baz", Entry::Method, "/fake/path/foo.rb:7-0:7-12", visibility: Entry::Visibility::PROTECTED)
89
94
  end
90
95
 
91
96
  def test_visibility_tracking_with_nested_class_or_modules
@@ -103,9 +108,9 @@ module RubyIndexer
103
108
  end
104
109
  RUBY
105
110
 
106
- assert_entry("foo", Entry::InstanceMethod, "/fake/path/foo.rb:3-2:3-14", visibility: Entry::Visibility::PRIVATE)
107
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:6-4:6-16", visibility: Entry::Visibility::PUBLIC)
108
- assert_entry("baz", Entry::InstanceMethod, "/fake/path/foo.rb:9-2:9-14", visibility: Entry::Visibility::PRIVATE)
111
+ assert_entry("foo", Entry::Method, "/fake/path/foo.rb:3-2:3-14", visibility: Entry::Visibility::PRIVATE)
112
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:6-4:6-16", visibility: Entry::Visibility::PUBLIC)
113
+ assert_entry("baz", Entry::Method, "/fake/path/foo.rb:9-2:9-14", visibility: Entry::Visibility::PRIVATE)
109
114
  end
110
115
 
111
116
  def test_method_with_parameters
@@ -116,7 +121,7 @@ module RubyIndexer
116
121
  end
117
122
  RUBY
118
123
 
119
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
124
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
120
125
  entry = T.must(@index["bar"].first)
121
126
  assert_equal(1, entry.parameters.length)
122
127
  parameter = entry.parameters.first
@@ -132,7 +137,7 @@ module RubyIndexer
132
137
  end
133
138
  RUBY
134
139
 
135
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
140
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
136
141
  entry = T.must(@index["bar"].first)
137
142
  assert_equal(1, entry.parameters.length)
138
143
  parameter = entry.parameters.first
@@ -148,7 +153,7 @@ module RubyIndexer
148
153
  end
149
154
  RUBY
150
155
 
151
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
156
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
152
157
  entry = T.must(@index["bar"].first)
153
158
  assert_equal(1, entry.parameters.length)
154
159
  parameter = entry.parameters.first
@@ -164,7 +169,7 @@ module RubyIndexer
164
169
  end
165
170
  RUBY
166
171
 
167
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
172
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
168
173
  entry = T.must(@index["bar"].first)
169
174
  assert_equal(2, entry.parameters.length)
170
175
  a, b = entry.parameters
@@ -184,7 +189,7 @@ module RubyIndexer
184
189
  end
185
190
  RUBY
186
191
 
187
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
192
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
188
193
  entry = T.must(@index["bar"].first)
189
194
  assert_equal(2, entry.parameters.length)
190
195
  a, b = entry.parameters
@@ -209,7 +214,7 @@ module RubyIndexer
209
214
  end
210
215
  RUBY
211
216
 
212
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
217
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
213
218
  entry = T.must(@index["bar"].first)
214
219
  assert_equal(2, entry.parameters.length)
215
220
  a, b = entry.parameters
@@ -246,7 +251,7 @@ module RubyIndexer
246
251
  end
247
252
  RUBY
248
253
 
249
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
254
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
250
255
  entry = T.must(@index["bar"].first)
251
256
  assert_equal(1, entry.parameters.length)
252
257
  param = entry.parameters.first
@@ -287,7 +292,7 @@ module RubyIndexer
287
292
  end
288
293
  RUBY
289
294
 
290
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
295
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
291
296
  entry = T.must(@index["bar"].first)
292
297
  assert_equal(2, entry.parameters.length)
293
298
  first, second = entry.parameters
@@ -307,7 +312,7 @@ module RubyIndexer
307
312
  end
308
313
  RUBY
309
314
 
310
- assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
315
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
311
316
  entry = T.must(@index["bar"].first)
312
317
  assert_empty(entry.parameters)
313
318
  end
@@ -359,23 +364,23 @@ module RubyIndexer
359
364
  def test_properly_tracks_multiple_levels_of_nesting
360
365
  index(<<~RUBY)
361
366
  module Foo
362
- def first; end
367
+ def first_method; end
363
368
 
364
369
  module Bar
365
- def second; end
370
+ def second_method; end
366
371
  end
367
372
 
368
- def third; end
373
+ def third_method; end
369
374
  end
370
375
  RUBY
371
376
 
372
- entry = T.must(@index["first"]&.first)
377
+ entry = T.cast(@index["first_method"]&.first, Entry::Method)
373
378
  assert_equal("Foo", T.must(entry.owner).name)
374
379
 
375
- entry = T.must(@index["second"]&.first)
380
+ entry = T.cast(@index["second_method"]&.first, Entry::Method)
376
381
  assert_equal("Foo::Bar", T.must(entry.owner).name)
377
382
 
378
- entry = T.must(@index["third"]&.first)
383
+ entry = T.cast(@index["third_method"]&.first, Entry::Method)
379
384
  assert_equal("Foo", T.must(entry.owner).name)
380
385
  end
381
386
 
@@ -396,7 +401,52 @@ module RubyIndexer
396
401
  assert_entry("foo", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:2-15:2-19")
397
402
  assert_entry("bar", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:3-15:3-20")
398
403
  # Foo plus 3 valid aliases
399
- assert_equal(4, @index.instance_variable_get(:@entries).length - @default_indexed_entries.length)
404
+ assert_equal(4, @index.length - @default_indexed_entries.length)
405
+ end
406
+
407
+ def test_singleton_methods
408
+ index(<<~RUBY)
409
+ class Foo
410
+ def self.bar; end
411
+
412
+ class << self
413
+ def baz; end
414
+ end
415
+ end
416
+ RUBY
417
+
418
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:1-19")
419
+ assert_entry("baz", Entry::Method, "/fake/path/foo.rb:4-4:4-16")
420
+
421
+ bar_owner = T.must(T.must(@index["bar"].first).owner)
422
+ baz_owner = T.must(T.must(@index["baz"].first).owner)
423
+
424
+ assert_instance_of(Entry::SingletonClass, bar_owner)
425
+ assert_instance_of(Entry::SingletonClass, baz_owner)
426
+
427
+ # Regardless of whether the method was added through `self.something` or `class << self`, the owner object must be
428
+ # the exact same
429
+ assert_same(bar_owner, baz_owner)
430
+ end
431
+
432
+ def test_name_location_points_to_method_identifier_location
433
+ index(<<~RUBY)
434
+ class Foo
435
+ def bar
436
+ a = 123
437
+ a + 456
438
+ end
439
+ end
440
+ RUBY
441
+
442
+ entry = T.must(@index["bar"].first)
443
+ refute_equal(entry.location, entry.name_location)
444
+
445
+ name_location = entry.name_location
446
+ assert_equal(2, name_location.start_line)
447
+ assert_equal(2, name_location.end_line)
448
+ assert_equal(6, name_location.start_column)
449
+ assert_equal(9, name_location.end_column)
400
450
  end
401
451
  end
402
452
  end
@@ -8,8 +8,9 @@ module RubyIndexer
8
8
  def test_index_core_classes
9
9
  entries = @index["Array"]
10
10
  refute_nil(entries)
11
- assert_equal(1, entries.length)
12
- entry = entries.first
11
+ # Array is a class but also an instance method on Kernel
12
+ assert_equal(2, entries.length)
13
+ entry = entries.find { |entry| entry.is_a?(RubyIndexer::Entry::Class) }
13
14
  assert_match(%r{/gems/rbs-.*/core/array.rbs}, entry.file_path)
14
15
  assert_equal("array.rbs", entry.file_name)
15
16
  assert_equal("Object", entry.parent_class)
@@ -38,5 +39,40 @@ module RubyIndexer
38
39
  assert_equal(0, entry.location.start_column)
39
40
  assert_operator(entry.location.end_column, :>, 0)
40
41
  end
42
+
43
+ def test_index_methods
44
+ entries = @index["initialize"]
45
+ refute_nil(entries)
46
+ entry = entries.find { |entry| entry.owner.name == "Array" }
47
+ assert_match(%r{/gems/rbs-.*/core/array.rbs}, entry.file_path)
48
+ assert_equal("array.rbs", entry.file_name)
49
+ assert_equal(Entry::Visibility::PUBLIC, entry.visibility)
50
+
51
+ # Using fixed positions would be fragile, so let's just check some basics.
52
+ assert_operator(entry.location.start_line, :>, 0)
53
+ assert_operator(entry.location.end_line, :>, entry.location.start_line)
54
+ assert_equal(2, entry.location.start_column)
55
+ assert_operator(entry.location.end_column, :>, 0)
56
+ end
57
+
58
+ def test_attaches_correct_owner_to_singleton_methods
59
+ entries = @index["basename"]
60
+ refute_nil(entries)
61
+
62
+ owner = entries.first.owner
63
+ assert_instance_of(Entry::SingletonClass, owner)
64
+ assert_equal("File::<Class:File>", owner.name)
65
+ end
66
+
67
+ def test_location_and_name_location_are_the_same
68
+ # NOTE: RBS does not store the name location for classes, modules or methods. This behaviour is not exactly what
69
+ # we would like, but for now we assign the same location to both
70
+
71
+ entries = @index["Array"]
72
+ refute_nil(entries)
73
+ entry = entries.find { |entry| entry.is_a?(Entry::Class) }
74
+
75
+ assert_same(entry.location, entry.name_location)
76
+ end
41
77
  end
42
78
  end
@@ -40,16 +40,12 @@ module RubyIndexer
40
40
  assert_nil(entries, "Expected #{expected_name} to not be indexed")
41
41
  end
42
42
 
43
- def assert_no_entries
44
- assert_empty(@index.instance_variable_get(:@entries), "Expected nothing to be indexed")
45
- end
46
-
47
43
  def assert_no_indexed_entries
48
44
  assert_equal(@default_indexed_entries, @index.instance_variable_get(:@entries))
49
45
  end
50
46
 
51
47
  def assert_no_entry(entry)
52
- refute(@index.instance_variable_get(:@entries).key?(entry), "Expected '#{entry}' to not be indexed")
48
+ refute(@index.indexed?(entry), "Expected '#{entry}' to not be indexed")
53
49
  end
54
50
  end
55
51
  end
@@ -72,6 +72,15 @@ module RubyLsp
72
72
  addon.add_error(e)
73
73
  end
74
74
  end
75
+
76
+ # Intended for use by tests for addons
77
+ sig { params(addon_name: String).returns(Addon) }
78
+ def get(addon_name)
79
+ addon = addons.find { |addon| addon.name == addon_name }
80
+ raise "Could not find addon '#{addon_name}'" unless addon
81
+
82
+ addon
83
+ end
75
84
  end
76
85
 
77
86
  sig { void }
@@ -157,7 +166,10 @@ module RubyLsp
157
166
  # Creates a new Definition listener. This method is invoked on every Definition request
158
167
  sig do
159
168
  overridable.params(
160
- response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
169
+ response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
170
+ Interface::Location,
171
+ Interface::LocationLink,
172
+ )],
161
173
  uri: URI::Generic,
162
174
  node_context: NodeContext,
163
175
  dispatcher: Prism::Dispatcher,
@@ -3,6 +3,13 @@
3
3
 
4
4
  module RubyLsp
5
5
  class Document
6
+ class LanguageId < T::Enum
7
+ enums do
8
+ Ruby = new("ruby")
9
+ ERB = new("erb")
10
+ end
11
+ end
12
+
6
13
  extend T::Sig
7
14
  extend T::Helpers
8
15
 
@@ -34,21 +41,14 @@ module RubyLsp
34
41
  @parse_result = T.let(parse, Prism::ParseResult)
35
42
  end
36
43
 
37
- sig { returns(Prism::ProgramNode) }
38
- def tree
39
- @parse_result.value
40
- end
41
-
42
- sig { returns(T::Array[Prism::Comment]) }
43
- def comments
44
- @parse_result.comments
45
- end
46
-
47
44
  sig { params(other: Document).returns(T::Boolean) }
48
45
  def ==(other)
49
- @source == other.source
46
+ self.class == other.class && uri == other.uri && @source == other.source
50
47
  end
51
48
 
49
+ sig { abstract.returns(LanguageId) }
50
+ def language_id; end
51
+
52
52
  # TODO: remove this method once all nonpositional requests have been migrated to the listener pattern
53
53
  sig do
54
54
  type_parameters(:T)
@@ -96,10 +96,8 @@ module RubyLsp
96
96
  sig { abstract.returns(Prism::ParseResult) }
97
97
  def parse; end
98
98
 
99
- sig { returns(T::Boolean) }
100
- def syntax_error?
101
- @parse_result.failure?
102
- end
99
+ sig { abstract.returns(T::Boolean) }
100
+ def syntax_error?; end
103
101
 
104
102
  sig { returns(Scanner) }
105
103
  def create_scanner
@@ -127,7 +125,10 @@ module RubyLsp
127
125
  queue = T.let(node.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
128
126
  closest = node
129
127
  parent = T.let(nil, T.nilable(Prism::Node))
130
- nesting = T.let([], T::Array[T.any(Prism::ClassNode, Prism::ModuleNode)])
128
+ nesting_nodes = T.let(
129
+ [],
130
+ T::Array[T.any(Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode, Prism::DefNode)],
131
+ )
131
132
  call_node = T.let(nil, T.nilable(Prism::CallNode))
132
133
 
133
134
  until queue.empty?
@@ -151,13 +152,18 @@ module RubyLsp
151
152
 
152
153
  # If the candidate starts after the end of the previous nesting level, then we've exited that nesting level and
153
154
  # need to pop the stack
154
- previous_level = nesting.last
155
- nesting.pop if previous_level && loc.start_offset > previous_level.location.end_offset
155
+ previous_level = nesting_nodes.last
156
+ nesting_nodes.pop if previous_level && loc.start_offset > previous_level.location.end_offset
156
157
 
157
158
  # Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
158
159
  # target when it is a constant
159
- if candidate.is_a?(Prism::ClassNode) || candidate.is_a?(Prism::ModuleNode)
160
- nesting << candidate
160
+ case candidate
161
+ when Prism::ClassNode, Prism::ModuleNode
162
+ nesting_nodes << candidate
163
+ when Prism::SingletonClassNode
164
+ nesting_nodes << candidate
165
+ when Prism::DefNode
166
+ nesting_nodes << candidate
161
167
  end
162
168
 
163
169
  if candidate.is_a?(Prism::CallNode)
@@ -189,11 +195,32 @@ module RubyLsp
189
195
  # The correct target is `Foo::Bar` with an empty nesting. `Foo::Bar` should not appear in the nesting stack, even
190
196
  # though the class/module node does indeed enclose the target, because it would lead to incorrect behavior
191
197
  if closest.is_a?(Prism::ConstantReadNode) || closest.is_a?(Prism::ConstantPathNode)
192
- last_level = nesting.last
193
- nesting.pop if last_level && last_level.constant_path == closest
198
+ last_level = nesting_nodes.last
199
+
200
+ if (last_level.is_a?(Prism::ModuleNode) || last_level.is_a?(Prism::ClassNode)) &&
201
+ last_level.constant_path == closest
202
+ nesting_nodes.pop
203
+ end
204
+ end
205
+
206
+ nesting = []
207
+ surrounding_method = T.let(nil, T.nilable(String))
208
+
209
+ nesting_nodes.each do |node|
210
+ case node
211
+ when Prism::ClassNode, Prism::ModuleNode
212
+ nesting << node.constant_path.slice
213
+ when Prism::SingletonClassNode
214
+ nesting << "<Class:#{nesting.last}>"
215
+ when Prism::DefNode
216
+ surrounding_method = node.name.to_s
217
+ next unless node.receiver.is_a?(Prism::SelfNode)
218
+
219
+ nesting << "<Class:#{nesting.last}>"
220
+ end
194
221
  end
195
222
 
196
- NodeContext.new(closest, parent, nesting.map { |n| n.constant_path.location.slice }, call_node)
223
+ NodeContext.new(closest, parent, nesting, call_node, surrounding_method)
197
224
  end
198
225
 
199
226
  sig { returns(T::Boolean) }