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.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/VERSION +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +251 -100
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +173 -114
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +337 -77
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +43 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +79 -3
- data/lib/ruby_indexer/test/index_test.rb +563 -29
- data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
- data/lib/ruby_indexer/test/method_test.rb +75 -25
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +38 -2
- data/lib/ruby_indexer/test/test_case.rb +1 -5
- data/lib/ruby_lsp/addon.rb +13 -1
- data/lib/ruby_lsp/document.rb +50 -23
- data/lib/ruby_lsp/erb_document.rb +125 -0
- data/lib/ruby_lsp/global_state.rb +11 -4
- data/lib/ruby_lsp/internal.rb +3 -0
- data/lib/ruby_lsp/listeners/completion.rb +69 -34
- data/lib/ruby_lsp/listeners/definition.rb +34 -23
- data/lib/ruby_lsp/listeners/hover.rb +14 -7
- data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
- data/lib/ruby_lsp/node_context.rb +6 -1
- data/lib/ruby_lsp/requests/code_action_resolve.rb +2 -2
- data/lib/ruby_lsp/requests/completion.rb +6 -5
- data/lib/ruby_lsp/requests/completion_resolve.rb +7 -4
- data/lib/ruby_lsp/requests/definition.rb +4 -3
- data/lib/ruby_lsp/requests/formatting.rb +2 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
- data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -2
- data/lib/ruby_lsp/requests/support/common.rb +19 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +87 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
- data/lib/ruby_lsp/requests.rb +2 -0
- data/lib/ruby_lsp/ruby_document.rb +10 -0
- data/lib/ruby_lsp/server.rb +95 -26
- data/lib/ruby_lsp/store.rb +23 -8
- data/lib/ruby_lsp/test_helper.rb +3 -1
- data/lib/ruby_lsp/type_inferrer.rb +86 -0
- 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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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::
|
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::
|
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::
|
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::
|
87
|
-
assert_entry("bar", Entry::
|
88
|
-
assert_entry("baz", Entry::
|
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::
|
107
|
-
assert_entry("bar", Entry::
|
108
|
-
assert_entry("baz", Entry::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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
|
367
|
+
def first_method; end
|
363
368
|
|
364
369
|
module Bar
|
365
|
-
def
|
370
|
+
def second_method; end
|
366
371
|
end
|
367
372
|
|
368
|
-
def
|
373
|
+
def third_method; end
|
369
374
|
end
|
370
375
|
RUBY
|
371
376
|
|
372
|
-
entry = T.
|
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.
|
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.
|
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.
|
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
|
-
|
12
|
-
|
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.
|
48
|
+
refute(@index.indexed?(entry), "Expected '#{entry}' to not be indexed")
|
53
49
|
end
|
54
50
|
end
|
55
51
|
end
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -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[
|
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,
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -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
|
-
|
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 =
|
155
|
-
|
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
|
-
|
160
|
-
|
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 =
|
193
|
-
|
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
|
223
|
+
NodeContext.new(closest, parent, nesting, call_node, surrounding_method)
|
197
224
|
end
|
198
225
|
|
199
226
|
sig { returns(T::Boolean) }
|