ruby-lsp 0.21.0 → 0.22.1
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/VERSION +1 -1
- data/exe/ruby-lsp +1 -1
- data/exe/ruby-lsp-launcher +0 -3
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +179 -59
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +31 -28
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +10 -10
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +2 -2
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/constant_test.rb +8 -8
- data/lib/ruby_indexer/test/enhancements_test.rb +134 -38
- data/lib/ruby_indexer/test/index_test.rb +39 -0
- data/lib/ruby_indexer/test/method_test.rb +114 -1
- data/lib/ruby_lsp/client_capabilities.rb +8 -1
- data/lib/ruby_lsp/global_state.rb +15 -3
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/listeners/document_highlight.rb +91 -4
- data/lib/ruby_lsp/listeners/document_symbol.rb +37 -4
- data/lib/ruby_lsp/requests/definition.rb +2 -0
- data/lib/ruby_lsp/requests/document_highlight.rb +7 -1
- data/lib/ruby_lsp/requests/hover.rb +2 -0
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +1 -0
- data/lib/ruby_lsp/server.rb +35 -43
- data/lib/ruby_lsp/setup_bundler.rb +46 -25
- data/lib/ruby_lsp/store.rb +0 -4
- data/lib/ruby_lsp/utils.rb +55 -0
- metadata +3 -3
@@ -5,24 +5,28 @@ require_relative "test_case"
|
|
5
5
|
|
6
6
|
module RubyIndexer
|
7
7
|
class EnhancementTest < TestCase
|
8
|
+
def teardown
|
9
|
+
super
|
10
|
+
Enhancement.clear
|
11
|
+
end
|
12
|
+
|
8
13
|
def test_enhancing_indexing_included_hook
|
9
|
-
|
10
|
-
def on_call_node_enter(
|
14
|
+
Class.new(Enhancement) do
|
15
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
16
|
+
owner = @listener.current_owner
|
11
17
|
return unless owner
|
12
|
-
return unless
|
18
|
+
return unless call_node.name == :extend
|
13
19
|
|
14
|
-
arguments =
|
20
|
+
arguments = call_node.arguments&.arguments
|
15
21
|
return unless arguments
|
16
22
|
|
17
|
-
location = Location.from_prism_location(node.location, code_units_cache)
|
18
|
-
|
19
23
|
arguments.each do |node|
|
20
24
|
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
21
25
|
|
22
26
|
module_name = node.full_name
|
23
27
|
next unless module_name == "ActiveSupport::Concern"
|
24
28
|
|
25
|
-
@
|
29
|
+
@listener.register_included_hook do |index, base|
|
26
30
|
class_methods_name = "#{owner.name}::ClassMethods"
|
27
31
|
|
28
32
|
if index.indexed?(class_methods_name)
|
@@ -31,16 +35,11 @@ module RubyIndexer
|
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
34
|
-
@
|
38
|
+
@listener.add_method(
|
35
39
|
"new_method",
|
36
|
-
|
37
|
-
location,
|
38
|
-
location,
|
39
|
-
nil,
|
40
|
+
call_node.location,
|
40
41
|
[Entry::Signature.new([Entry::RequiredParameter.new(name: :a)])],
|
41
|
-
|
42
|
-
owner,
|
43
|
-
))
|
42
|
+
)
|
44
43
|
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
|
45
44
|
Prism::ConstantPathNode::MissingNodesInConstantPathError
|
46
45
|
# Do nothing
|
@@ -48,7 +47,6 @@ module RubyIndexer
|
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
51
|
-
@index.register_enhancement(enhancement_class.new(@index))
|
52
50
|
index(<<~RUBY)
|
53
51
|
module ActiveSupport
|
54
52
|
module Concern
|
@@ -96,9 +94,9 @@ module RubyIndexer
|
|
96
94
|
end
|
97
95
|
|
98
96
|
def test_enhancing_indexing_configuration_dsl
|
99
|
-
|
100
|
-
def on_call_node_enter(
|
101
|
-
return unless
|
97
|
+
Class.new(Enhancement) do
|
98
|
+
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
99
|
+
return unless @listener.current_owner
|
102
100
|
|
103
101
|
name = node.name
|
104
102
|
return unless name == :has_many
|
@@ -109,22 +107,14 @@ module RubyIndexer
|
|
109
107
|
association_name = arguments.first
|
110
108
|
return unless association_name.is_a?(Prism::SymbolNode)
|
111
109
|
|
112
|
-
|
113
|
-
|
114
|
-
@index.add(Entry::Method.new(
|
110
|
+
@listener.add_method(
|
115
111
|
T.must(association_name.value),
|
116
|
-
|
117
|
-
location,
|
118
|
-
location,
|
119
|
-
nil,
|
112
|
+
association_name.location,
|
120
113
|
[],
|
121
|
-
|
122
|
-
owner,
|
123
|
-
))
|
114
|
+
)
|
124
115
|
end
|
125
116
|
end
|
126
117
|
|
127
|
-
@index.register_enhancement(enhancement_class.new(@index))
|
128
118
|
index(<<~RUBY)
|
129
119
|
module ActiveSupport
|
130
120
|
module Concern
|
@@ -157,8 +147,8 @@ module RubyIndexer
|
|
157
147
|
end
|
158
148
|
|
159
149
|
def test_error_handling_in_on_call_node_enter_enhancement
|
160
|
-
|
161
|
-
def on_call_node_enter(
|
150
|
+
Class.new(Enhancement) do
|
151
|
+
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
162
152
|
raise "Error"
|
163
153
|
end
|
164
154
|
|
@@ -169,8 +159,6 @@ module RubyIndexer
|
|
169
159
|
end
|
170
160
|
end
|
171
161
|
|
172
|
-
@index.register_enhancement(enhancement_class.new(@index))
|
173
|
-
|
174
162
|
_stdout, stderr = capture_io do
|
175
163
|
index(<<~RUBY)
|
176
164
|
module ActiveSupport
|
@@ -192,8 +180,8 @@ module RubyIndexer
|
|
192
180
|
end
|
193
181
|
|
194
182
|
def test_error_handling_in_on_call_node_leave_enhancement
|
195
|
-
|
196
|
-
def on_call_node_leave(
|
183
|
+
Class.new(Enhancement) do
|
184
|
+
def on_call_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
197
185
|
raise "Error"
|
198
186
|
end
|
199
187
|
|
@@ -204,8 +192,6 @@ module RubyIndexer
|
|
204
192
|
end
|
205
193
|
end
|
206
194
|
|
207
|
-
@index.register_enhancement(enhancement_class.new(@index))
|
208
|
-
|
209
195
|
_stdout, stderr = capture_io do
|
210
196
|
index(<<~RUBY)
|
211
197
|
module ActiveSupport
|
@@ -225,5 +211,115 @@ module RubyIndexer
|
|
225
211
|
# The module should still be indexed
|
226
212
|
assert_entry("ActiveSupport::Concern", Entry::Module, "/fake/path/foo.rb:1-2:5-5")
|
227
213
|
end
|
214
|
+
|
215
|
+
def test_advancing_namespace_stack_from_enhancement
|
216
|
+
Class.new(Enhancement) do
|
217
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
218
|
+
owner = @listener.current_owner
|
219
|
+
return unless owner
|
220
|
+
|
221
|
+
case call_node.name
|
222
|
+
when :class_methods
|
223
|
+
@listener.add_module("ClassMethods", call_node.location, call_node.location)
|
224
|
+
when :extend
|
225
|
+
arguments = call_node.arguments&.arguments
|
226
|
+
return unless arguments
|
227
|
+
|
228
|
+
arguments.each do |node|
|
229
|
+
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
230
|
+
|
231
|
+
module_name = node.full_name
|
232
|
+
next unless module_name == "ActiveSupport::Concern"
|
233
|
+
|
234
|
+
@listener.register_included_hook do |index, base|
|
235
|
+
class_methods_name = "#{owner.name}::ClassMethods"
|
236
|
+
|
237
|
+
if index.indexed?(class_methods_name)
|
238
|
+
singleton = index.existing_or_new_singleton_class(base.name)
|
239
|
+
singleton.mixin_operations << Entry::Include.new(class_methods_name)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def on_call_node_leave(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
247
|
+
return unless call_node.name == :class_methods
|
248
|
+
|
249
|
+
@listener.pop_namespace_stack
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
index(<<~RUBY)
|
254
|
+
module ActiveSupport
|
255
|
+
module Concern
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
module MyConcern
|
260
|
+
extend ActiveSupport::Concern
|
261
|
+
|
262
|
+
class_methods do
|
263
|
+
def foo; end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class User
|
268
|
+
include MyConcern
|
269
|
+
end
|
270
|
+
RUBY
|
271
|
+
|
272
|
+
assert_equal(
|
273
|
+
[
|
274
|
+
"User::<Class:User>",
|
275
|
+
"MyConcern::ClassMethods",
|
276
|
+
"Object::<Class:Object>",
|
277
|
+
"BasicObject::<Class:BasicObject>",
|
278
|
+
"Class",
|
279
|
+
"Module",
|
280
|
+
"Object",
|
281
|
+
"Kernel",
|
282
|
+
"BasicObject",
|
283
|
+
],
|
284
|
+
@index.linearized_ancestors_of("User::<Class:User>"),
|
285
|
+
)
|
286
|
+
|
287
|
+
refute_nil(@index.resolve_method("foo", "User::<Class:User>"))
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_creating_anonymous_classes_from_enhancement
|
291
|
+
Class.new(Enhancement) do
|
292
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
293
|
+
case call_node.name
|
294
|
+
when :context
|
295
|
+
arguments = call_node.arguments&.arguments
|
296
|
+
first_argument = arguments&.first
|
297
|
+
return unless first_argument.is_a?(Prism::StringNode)
|
298
|
+
|
299
|
+
@listener.add_class(
|
300
|
+
"<RSpec:#{first_argument.content}>",
|
301
|
+
call_node.location,
|
302
|
+
first_argument.location,
|
303
|
+
)
|
304
|
+
when :subject
|
305
|
+
@listener.add_method("subject", call_node.location, [])
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def on_call_node_leave(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
310
|
+
return unless call_node.name == :context
|
311
|
+
|
312
|
+
@listener.pop_namespace_stack
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
index(<<~RUBY)
|
317
|
+
context "does something" do
|
318
|
+
subject { call_whatever }
|
319
|
+
end
|
320
|
+
RUBY
|
321
|
+
|
322
|
+
refute_nil(@index.resolve_method("subject", "<RSpec:does something>"))
|
323
|
+
end
|
228
324
|
end
|
229
325
|
end
|
@@ -1672,6 +1672,38 @@ module RubyIndexer
|
|
1672
1672
|
)
|
1673
1673
|
end
|
1674
1674
|
|
1675
|
+
def test_extend_self
|
1676
|
+
@index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
|
1677
|
+
module Foo
|
1678
|
+
def bar
|
1679
|
+
end
|
1680
|
+
|
1681
|
+
extend self
|
1682
|
+
|
1683
|
+
def baz
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
RUBY
|
1687
|
+
|
1688
|
+
["bar", "baz"].product(["Foo", "Foo::<Class:Foo>"]).each do |method, receiver|
|
1689
|
+
entry = @index.resolve_method(method, receiver)&.first
|
1690
|
+
refute_nil(entry)
|
1691
|
+
assert_equal(method, T.must(entry).name)
|
1692
|
+
end
|
1693
|
+
|
1694
|
+
assert_equal(
|
1695
|
+
[
|
1696
|
+
"Foo::<Class:Foo>",
|
1697
|
+
"Foo",
|
1698
|
+
"Module",
|
1699
|
+
"Object",
|
1700
|
+
"Kernel",
|
1701
|
+
"BasicObject",
|
1702
|
+
],
|
1703
|
+
@index.linearized_ancestors_of("Foo::<Class:Foo>"),
|
1704
|
+
)
|
1705
|
+
end
|
1706
|
+
|
1675
1707
|
def test_linearizing_singleton_ancestors
|
1676
1708
|
@index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
|
1677
1709
|
module First
|
@@ -2023,5 +2055,12 @@ module RubyIndexer
|
|
2023
2055
|
),
|
2024
2056
|
)
|
2025
2057
|
end
|
2058
|
+
|
2059
|
+
def test_prevents_multiple_calls_to_index_all
|
2060
|
+
# For this test class, `index_all` is already called once in `setup`.
|
2061
|
+
assert_raises(Index::IndexNotEmptyError) do
|
2062
|
+
@index.index_all
|
2063
|
+
end
|
2064
|
+
end
|
2026
2065
|
end
|
2027
2066
|
end
|
@@ -141,7 +141,7 @@ module RubyIndexer
|
|
141
141
|
# The first entry points to the location of the module_function call
|
142
142
|
assert_equal("Test", first_entry.owner.name)
|
143
143
|
assert_instance_of(Entry::Module, first_entry.owner)
|
144
|
-
|
144
|
+
assert_predicate(first_entry, :private?)
|
145
145
|
# The second entry points to the public singleton method
|
146
146
|
assert_equal("Test::<Class:Test>", second_entry.owner.name)
|
147
147
|
assert_instance_of(Entry::SingletonClass, second_entry.owner)
|
@@ -149,6 +149,119 @@ module RubyIndexer
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
+
def test_private_class_method_visibility_tracking_string_symbol_arguments
|
153
|
+
index(<<~RUBY)
|
154
|
+
class Test
|
155
|
+
def self.foo
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.bar
|
159
|
+
end
|
160
|
+
|
161
|
+
private_class_method("foo", :bar)
|
162
|
+
|
163
|
+
def self.baz
|
164
|
+
end
|
165
|
+
end
|
166
|
+
RUBY
|
167
|
+
|
168
|
+
["foo", "bar"].each do |keyword|
|
169
|
+
entries = T.must(@index[keyword])
|
170
|
+
assert_equal(1, entries.size)
|
171
|
+
entry = entries.first
|
172
|
+
assert_predicate(entry, :private?)
|
173
|
+
end
|
174
|
+
|
175
|
+
entries = T.must(@index["baz"])
|
176
|
+
assert_equal(1, entries.size)
|
177
|
+
entry = entries.first
|
178
|
+
assert_predicate(entry, :public?)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_private_class_method_visibility_tracking_array_argument
|
182
|
+
index(<<~RUBY)
|
183
|
+
class Test
|
184
|
+
def self.foo
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.bar
|
188
|
+
end
|
189
|
+
|
190
|
+
private_class_method(["foo", :bar])
|
191
|
+
|
192
|
+
def self.baz
|
193
|
+
end
|
194
|
+
end
|
195
|
+
RUBY
|
196
|
+
|
197
|
+
["foo", "bar"].each do |keyword|
|
198
|
+
entries = T.must(@index[keyword])
|
199
|
+
assert_equal(1, entries.size)
|
200
|
+
entry = entries.first
|
201
|
+
assert_predicate(entry, :private?)
|
202
|
+
end
|
203
|
+
|
204
|
+
entries = T.must(@index["baz"])
|
205
|
+
assert_equal(1, entries.size)
|
206
|
+
entry = entries.first
|
207
|
+
assert_predicate(entry, :public?)
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_private_class_method_visibility_tracking_method_argument
|
211
|
+
index(<<~RUBY)
|
212
|
+
class Test
|
213
|
+
private_class_method def self.foo
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.bar
|
217
|
+
end
|
218
|
+
end
|
219
|
+
RUBY
|
220
|
+
|
221
|
+
entries = T.must(@index["foo"])
|
222
|
+
assert_equal(1, entries.size)
|
223
|
+
entry = entries.first
|
224
|
+
assert_predicate(entry, :private?)
|
225
|
+
|
226
|
+
entries = T.must(@index["bar"])
|
227
|
+
assert_equal(1, entries.size)
|
228
|
+
entry = entries.first
|
229
|
+
assert_predicate(entry, :public?)
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_comments_documentation
|
233
|
+
index(<<~RUBY)
|
234
|
+
# Documentation for Foo
|
235
|
+
|
236
|
+
class Foo
|
237
|
+
# ####################
|
238
|
+
# Documentation for bar
|
239
|
+
# ####################
|
240
|
+
#
|
241
|
+
def bar
|
242
|
+
end
|
243
|
+
|
244
|
+
# test
|
245
|
+
|
246
|
+
# Documentation for baz
|
247
|
+
def baz; end
|
248
|
+
def ban; end
|
249
|
+
end
|
250
|
+
RUBY
|
251
|
+
|
252
|
+
foo_comment = @index["Foo"].first.comments
|
253
|
+
assert_equal("Documentation for Foo", foo_comment)
|
254
|
+
|
255
|
+
bar_comment = @index["bar"].first.comments
|
256
|
+
assert_equal("####################\nDocumentation for bar\n####################\n", bar_comment)
|
257
|
+
|
258
|
+
baz_comment = @index["baz"].first.comments
|
259
|
+
assert_equal("Documentation for baz", baz_comment)
|
260
|
+
|
261
|
+
ban_comment = @index["ban"].first.comments
|
262
|
+
assert_empty(ban_comment)
|
263
|
+
end
|
264
|
+
|
152
265
|
def test_method_with_parameters
|
153
266
|
index(<<~RUBY)
|
154
267
|
class Foo
|
@@ -10,7 +10,8 @@ module RubyLsp
|
|
10
10
|
sig { returns(T::Boolean) }
|
11
11
|
attr_reader :supports_watching_files,
|
12
12
|
:supports_request_delegation,
|
13
|
-
:window_show_message_supports_extra_properties
|
13
|
+
:window_show_message_supports_extra_properties,
|
14
|
+
:supports_progress
|
14
15
|
|
15
16
|
sig { void }
|
16
17
|
def initialize
|
@@ -28,6 +29,9 @@ module RubyLsp
|
|
28
29
|
|
29
30
|
# Which resource operations the editor supports, like renaming files
|
30
31
|
@supported_resource_operations = T.let([], T::Array[String])
|
32
|
+
|
33
|
+
# The editor supports displaying progress requests
|
34
|
+
@supports_progress = T.let(false, T::Boolean)
|
31
35
|
end
|
32
36
|
|
33
37
|
sig { params(capabilities: T::Hash[Symbol, T.untyped]).void }
|
@@ -50,6 +54,9 @@ module RubyLsp
|
|
50
54
|
:additionalPropertiesSupport,
|
51
55
|
)
|
52
56
|
@window_show_message_supports_extra_properties = supports_additional_properties || false
|
57
|
+
|
58
|
+
progress = capabilities.dig(:window, :workDoneProgress)
|
59
|
+
@supports_progress = progress if progress
|
53
60
|
end
|
54
61
|
|
55
62
|
sig { returns(T::Boolean) }
|
@@ -21,7 +21,7 @@ module RubyLsp
|
|
21
21
|
attr_reader :encoding
|
22
22
|
|
23
23
|
sig { returns(T::Boolean) }
|
24
|
-
attr_reader :
|
24
|
+
attr_reader :top_level_bundle
|
25
25
|
|
26
26
|
sig { returns(TypeInferrer) }
|
27
27
|
attr_reader :type_inferrer
|
@@ -40,7 +40,6 @@ module RubyLsp
|
|
40
40
|
@has_type_checker = T.let(true, T::Boolean)
|
41
41
|
@index = T.let(RubyIndexer::Index.new, RubyIndexer::Index)
|
42
42
|
@supported_formatters = T.let({}, T::Hash[String, Requests::Support::Formatter])
|
43
|
-
@experimental_features = T.let(false, T::Boolean)
|
44
43
|
@type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
|
45
44
|
@addon_settings = T.let({}, T::Hash[String, T.untyped])
|
46
45
|
@top_level_bundle = T.let(
|
@@ -53,6 +52,7 @@ module RubyLsp
|
|
53
52
|
T::Boolean,
|
54
53
|
)
|
55
54
|
@client_capabilities = T.let(ClientCapabilities.new, ClientCapabilities)
|
55
|
+
@enabled_feature_flags = T.let({}, T::Hash[Symbol, T::Boolean])
|
56
56
|
end
|
57
57
|
|
58
58
|
sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
@@ -130,7 +130,6 @@ module RubyLsp
|
|
130
130
|
end
|
131
131
|
@index.configuration.encoding = @encoding
|
132
132
|
|
133
|
-
@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
|
134
133
|
@client_capabilities.apply_client_capabilities(options[:capabilities]) if options[:capabilities]
|
135
134
|
|
136
135
|
addon_settings = options.dig(:initializationOptions, :addonSettings)
|
@@ -139,9 +138,17 @@ module RubyLsp
|
|
139
138
|
@addon_settings.merge!(addon_settings)
|
140
139
|
end
|
141
140
|
|
141
|
+
enabled_flags = options.dig(:initializationOptions, :enabledFeatureFlags)
|
142
|
+
@enabled_feature_flags = enabled_flags if enabled_flags
|
143
|
+
|
142
144
|
notifications
|
143
145
|
end
|
144
146
|
|
147
|
+
sig { params(flag: Symbol).returns(T.nilable(T::Boolean)) }
|
148
|
+
def enabled_feature?(flag)
|
149
|
+
@enabled_feature_flags[:all] || @enabled_feature_flags[flag]
|
150
|
+
end
|
151
|
+
|
145
152
|
sig { returns(String) }
|
146
153
|
def workspace_path
|
147
154
|
T.must(@workspace_uri.to_standardized_path)
|
@@ -159,6 +166,11 @@ module RubyLsp
|
|
159
166
|
end
|
160
167
|
end
|
161
168
|
|
169
|
+
sig { returns(T::Boolean) }
|
170
|
+
def supports_watching_files
|
171
|
+
@client_capabilities.supports_watching_files
|
172
|
+
end
|
173
|
+
|
162
174
|
private
|
163
175
|
|
164
176
|
sig { params(direct_dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(String) }
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -92,14 +92,15 @@ module RubyLsp
|
|
92
92
|
target: T.nilable(Prism::Node),
|
93
93
|
parent: T.nilable(Prism::Node),
|
94
94
|
dispatcher: Prism::Dispatcher,
|
95
|
+
position: T::Hash[Symbol, T.untyped],
|
95
96
|
).void
|
96
97
|
end
|
97
|
-
def initialize(response_builder, target, parent, dispatcher)
|
98
|
+
def initialize(response_builder, target, parent, dispatcher, position)
|
98
99
|
@response_builder = response_builder
|
99
100
|
|
100
101
|
return unless target && parent
|
101
102
|
|
102
|
-
highlight_target =
|
103
|
+
highlight_target, highlight_target_value =
|
103
104
|
case target
|
104
105
|
when Prism::GlobalVariableReadNode, Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode,
|
105
106
|
Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableTargetNode, Prism::GlobalVariableWriteNode,
|
@@ -116,13 +117,17 @@ module RubyLsp
|
|
116
117
|
Prism::CallNode, Prism::BlockParameterNode, Prism::RequiredKeywordParameterNode,
|
117
118
|
Prism::RequiredKeywordParameterNode, Prism::KeywordRestParameterNode, Prism::OptionalParameterNode,
|
118
119
|
Prism::RequiredParameterNode, Prism::RestParameterNode
|
120
|
+
[target, node_value(target)]
|
121
|
+
when Prism::ModuleNode, Prism::ClassNode, Prism::SingletonClassNode, Prism::DefNode, Prism::CaseNode,
|
122
|
+
Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::IfNode, Prism::UnlessNode
|
119
123
|
target
|
120
124
|
end
|
121
125
|
|
122
126
|
@target = T.let(highlight_target, T.nilable(Prism::Node))
|
123
|
-
@target_value = T.let(
|
127
|
+
@target_value = T.let(highlight_target_value, T.nilable(String))
|
128
|
+
@target_position = position
|
124
129
|
|
125
|
-
if @target
|
130
|
+
if @target
|
126
131
|
dispatcher.register(
|
127
132
|
self,
|
128
133
|
:on_call_node_enter,
|
@@ -172,6 +177,13 @@ module RubyLsp
|
|
172
177
|
:on_global_variable_or_write_node_enter,
|
173
178
|
:on_global_variable_and_write_node_enter,
|
174
179
|
:on_global_variable_operator_write_node_enter,
|
180
|
+
:on_singleton_class_node_enter,
|
181
|
+
:on_case_node_enter,
|
182
|
+
:on_while_node_enter,
|
183
|
+
:on_until_node_enter,
|
184
|
+
:on_for_node_enter,
|
185
|
+
:on_if_node_enter,
|
186
|
+
:on_unless_node_enter,
|
175
187
|
)
|
176
188
|
end
|
177
189
|
end
|
@@ -189,6 +201,8 @@ module RubyLsp
|
|
189
201
|
|
190
202
|
sig { params(node: Prism::DefNode).void }
|
191
203
|
def on_def_node_enter(node)
|
204
|
+
add_matching_end_highlights(node.def_keyword_loc, node.end_keyword_loc) if @target.is_a?(Prism::DefNode)
|
205
|
+
|
192
206
|
return unless matches?(node, [Prism::CallNode, Prism::DefNode])
|
193
207
|
|
194
208
|
add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc)
|
@@ -252,6 +266,8 @@ module RubyLsp
|
|
252
266
|
|
253
267
|
sig { params(node: Prism::ClassNode).void }
|
254
268
|
def on_class_node_enter(node)
|
269
|
+
add_matching_end_highlights(node.class_keyword_loc, node.end_keyword_loc) if @target.is_a?(Prism::ClassNode)
|
270
|
+
|
255
271
|
return unless matches?(node, CONSTANT_NODES + CONSTANT_PATH_NODES + [Prism::ClassNode])
|
256
272
|
|
257
273
|
add_highlight(Constant::DocumentHighlightKind::WRITE, node.constant_path.location)
|
@@ -259,6 +275,8 @@ module RubyLsp
|
|
259
275
|
|
260
276
|
sig { params(node: Prism::ModuleNode).void }
|
261
277
|
def on_module_node_enter(node)
|
278
|
+
add_matching_end_highlights(node.module_keyword_loc, node.end_keyword_loc) if @target.is_a?(Prism::ModuleNode)
|
279
|
+
|
262
280
|
return unless matches?(node, CONSTANT_NODES + CONSTANT_PATH_NODES + [Prism::ModuleNode])
|
263
281
|
|
264
282
|
add_highlight(Constant::DocumentHighlightKind::WRITE, node.constant_path.location)
|
@@ -511,6 +529,55 @@ module RubyLsp
|
|
511
529
|
add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc)
|
512
530
|
end
|
513
531
|
|
532
|
+
sig { params(node: Prism::SingletonClassNode).void }
|
533
|
+
def on_singleton_class_node_enter(node)
|
534
|
+
return unless @target.is_a?(Prism::SingletonClassNode)
|
535
|
+
|
536
|
+
add_matching_end_highlights(node.class_keyword_loc, node.end_keyword_loc)
|
537
|
+
end
|
538
|
+
|
539
|
+
sig { params(node: Prism::CaseNode).void }
|
540
|
+
def on_case_node_enter(node)
|
541
|
+
return unless @target.is_a?(Prism::CaseNode)
|
542
|
+
|
543
|
+
add_matching_end_highlights(node.case_keyword_loc, node.end_keyword_loc)
|
544
|
+
end
|
545
|
+
|
546
|
+
sig { params(node: Prism::WhileNode).void }
|
547
|
+
def on_while_node_enter(node)
|
548
|
+
return unless @target.is_a?(Prism::WhileNode)
|
549
|
+
|
550
|
+
add_matching_end_highlights(node.keyword_loc, node.closing_loc)
|
551
|
+
end
|
552
|
+
|
553
|
+
sig { params(node: Prism::UntilNode).void }
|
554
|
+
def on_until_node_enter(node)
|
555
|
+
return unless @target.is_a?(Prism::UntilNode)
|
556
|
+
|
557
|
+
add_matching_end_highlights(node.keyword_loc, node.closing_loc)
|
558
|
+
end
|
559
|
+
|
560
|
+
sig { params(node: Prism::ForNode).void }
|
561
|
+
def on_for_node_enter(node)
|
562
|
+
return unless @target.is_a?(Prism::ForNode)
|
563
|
+
|
564
|
+
add_matching_end_highlights(node.for_keyword_loc, node.end_keyword_loc)
|
565
|
+
end
|
566
|
+
|
567
|
+
sig { params(node: Prism::IfNode).void }
|
568
|
+
def on_if_node_enter(node)
|
569
|
+
return unless @target.is_a?(Prism::IfNode)
|
570
|
+
|
571
|
+
add_matching_end_highlights(node.if_keyword_loc, node.end_keyword_loc)
|
572
|
+
end
|
573
|
+
|
574
|
+
sig { params(node: Prism::UnlessNode).void }
|
575
|
+
def on_unless_node_enter(node)
|
576
|
+
return unless @target.is_a?(Prism::UnlessNode)
|
577
|
+
|
578
|
+
add_matching_end_highlights(node.keyword_loc, node.end_keyword_loc)
|
579
|
+
end
|
580
|
+
|
514
581
|
private
|
515
582
|
|
516
583
|
sig { params(node: Prism::Node, classes: T::Array[T.class_of(Prism::Node)]).returns(T.nilable(T::Boolean)) }
|
@@ -550,6 +617,26 @@ module RubyLsp
|
|
550
617
|
node.constant_path.slice
|
551
618
|
end
|
552
619
|
end
|
620
|
+
|
621
|
+
sig { params(keyword_loc: T.nilable(Prism::Location), end_loc: T.nilable(Prism::Location)).void }
|
622
|
+
def add_matching_end_highlights(keyword_loc, end_loc)
|
623
|
+
return unless keyword_loc && end_loc && end_loc.length.positive?
|
624
|
+
return unless covers_target_position?(keyword_loc) || covers_target_position?(end_loc)
|
625
|
+
|
626
|
+
add_highlight(Constant::DocumentHighlightKind::TEXT, keyword_loc)
|
627
|
+
add_highlight(Constant::DocumentHighlightKind::TEXT, end_loc)
|
628
|
+
end
|
629
|
+
|
630
|
+
sig { params(location: Prism::Location).returns(T::Boolean) }
|
631
|
+
def covers_target_position?(location)
|
632
|
+
start_line = location.start_line - 1
|
633
|
+
end_line = location.end_line - 1
|
634
|
+
start_covered = start_line < @target_position[:line] ||
|
635
|
+
(start_line == @target_position[:line] && location.start_column <= @target_position[:character])
|
636
|
+
end_covered = end_line > @target_position[:line] ||
|
637
|
+
(end_line == @target_position[:line] && location.end_column >= @target_position[:character])
|
638
|
+
start_covered && end_covered
|
639
|
+
end
|
553
640
|
end
|
554
641
|
end
|
555
642
|
end
|