ruby-lsp 0.20.1 → 0.22.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +19 -4
- data/exe/ruby-lsp-launcher +124 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +233 -59
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +34 -16
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +15 -15
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +4 -4
- 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 +169 -41
- data/lib/ruby_indexer/test/index_test.rb +41 -2
- data/lib/ruby_indexer/test/instance_variables_test.rb +1 -1
- data/lib/ruby_indexer/test/method_test.rb +139 -0
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_lsp/addon.rb +9 -2
- data/lib/ruby_lsp/base_server.rb +14 -5
- data/lib/ruby_lsp/client_capabilities.rb +67 -0
- data/lib/ruby_lsp/document.rb +1 -1
- data/lib/ruby_lsp/global_state.rb +33 -20
- data/lib/ruby_lsp/internal.rb +3 -0
- data/lib/ruby_lsp/listeners/completion.rb +62 -0
- data/lib/ruby_lsp/listeners/definition.rb +48 -13
- data/lib/ruby_lsp/listeners/document_highlight.rb +91 -4
- data/lib/ruby_lsp/listeners/document_symbol.rb +37 -4
- data/lib/ruby_lsp/listeners/hover.rb +52 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/completion.rb +7 -1
- data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/definition.rb +28 -11
- data/lib/ruby_lsp/requests/document_highlight.rb +7 -1
- data/lib/ruby_lsp/requests/document_symbol.rb +2 -1
- data/lib/ruby_lsp/requests/hover.rb +26 -6
- data/lib/ruby_lsp/requests/rename.rb +1 -1
- data/lib/ruby_lsp/requests/request.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +12 -1
- data/lib/ruby_lsp/scripts/compose_bundle.rb +20 -0
- data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +8 -0
- data/lib/ruby_lsp/server.rb +85 -55
- data/lib/ruby_lsp/setup_bundler.rb +154 -47
- data/lib/ruby_lsp/store.rb +0 -4
- data/lib/ruby_lsp/utils.rb +63 -0
- metadata +8 -3
@@ -100,7 +100,7 @@ module RubyIndexer
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def test_location_and_name_location_are_the_same
|
103
|
-
# NOTE: RBS does not store the name location for classes, modules or methods. This
|
103
|
+
# NOTE: RBS does not store the name location for classes, modules or methods. This behavior is not exactly what
|
104
104
|
# we would like, but for now we assign the same location to both
|
105
105
|
|
106
106
|
entries = @index["Array"]
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -46,7 +46,7 @@ module RubyLsp
|
|
46
46
|
sig { returns(T::Array[T.class_of(Addon)]) }
|
47
47
|
attr_reader :addon_classes
|
48
48
|
|
49
|
-
# Automatically track and instantiate
|
49
|
+
# Automatically track and instantiate add-on classes
|
50
50
|
sig { params(child_class: T.class_of(Addon)).void }
|
51
51
|
def inherited(child_class)
|
52
52
|
addon_classes << child_class
|
@@ -82,7 +82,7 @@ module RubyLsp
|
|
82
82
|
e
|
83
83
|
end
|
84
84
|
|
85
|
-
# Instantiate all discovered
|
85
|
+
# Instantiate all discovered add-on classes
|
86
86
|
self.addons = addon_classes.map(&:new)
|
87
87
|
self.file_watcher_addons = addons.select { |addon| addon.respond_to?(:workspace_did_change_watched_files) }
|
88
88
|
|
@@ -194,6 +194,13 @@ module RubyLsp
|
|
194
194
|
sig { abstract.returns(String) }
|
195
195
|
def version; end
|
196
196
|
|
197
|
+
# Handle a response from a window/showMessageRequest request. Add-ons must include the addon_name as part of the
|
198
|
+
# original request so that the response is delegated to the correct add-on and must override this method to handle
|
199
|
+
# the response
|
200
|
+
# https://microsoft.github.io/language-server-protocol/specification#window_showMessageRequest
|
201
|
+
sig { overridable.params(title: String).void }
|
202
|
+
def handle_window_show_message_response(title); end
|
203
|
+
|
197
204
|
# Creates a new CodeLens listener. This method is invoked on every CodeLens request
|
198
205
|
sig do
|
199
206
|
overridable.params(
|
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -8,9 +8,11 @@ module RubyLsp
|
|
8
8
|
|
9
9
|
abstract!
|
10
10
|
|
11
|
-
sig { params(
|
12
|
-
def initialize(
|
13
|
-
@test_mode = T.let(test_mode, T::Boolean)
|
11
|
+
sig { params(options: T.untyped).void }
|
12
|
+
def initialize(**options)
|
13
|
+
@test_mode = T.let(options[:test_mode], T.nilable(T::Boolean))
|
14
|
+
@setup_error = T.let(options[:setup_error], T.nilable(StandardError))
|
15
|
+
@install_error = T.let(options[:install_error], T.nilable(StandardError))
|
14
16
|
@writer = T.let(Transport::Stdio::Writer.new, Transport::Stdio::Writer)
|
15
17
|
@reader = T.let(Transport::Stdio::Reader.new, Transport::Stdio::Reader)
|
16
18
|
@incoming_queue = T.let(Thread::Queue.new, Thread::Queue)
|
@@ -22,7 +24,7 @@ module RubyLsp
|
|
22
24
|
@store = T.let(Store.new, Store)
|
23
25
|
@outgoing_dispatcher = T.let(
|
24
26
|
Thread.new do
|
25
|
-
unless test_mode
|
27
|
+
unless @test_mode
|
26
28
|
while (message = @outgoing_queue.pop)
|
27
29
|
@mutex.synchronize { @writer.write(message.to_hash) }
|
28
30
|
end
|
@@ -33,6 +35,11 @@ module RubyLsp
|
|
33
35
|
|
34
36
|
@global_state = T.let(GlobalState.new, GlobalState)
|
35
37
|
Thread.main.priority = 1
|
38
|
+
|
39
|
+
# We read the initialize request in `exe/ruby-lsp` to be able to determine the workspace URI where Bundler should
|
40
|
+
# be set up
|
41
|
+
initialize_request = options[:initialize_request]
|
42
|
+
process_message(initialize_request) if initialize_request
|
36
43
|
end
|
37
44
|
|
38
45
|
sig { void }
|
@@ -59,7 +66,9 @@ module RubyLsp
|
|
59
66
|
# If the client supports request delegation and we're working with an ERB document and there was
|
60
67
|
# something to parse, then we have to maintain the client updated about the virtual state of the host
|
61
68
|
# language source
|
62
|
-
if document.parse! && @global_state.supports_request_delegation &&
|
69
|
+
if document.parse! && @global_state.client_capabilities.supports_request_delegation &&
|
70
|
+
document.is_a?(ERBDocument)
|
71
|
+
|
63
72
|
send_message(
|
64
73
|
Notification.new(
|
65
74
|
method: "delegate/textDocument/virtualState",
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
# This class stores all client capabilities that the Ruby LSP and its add-ons depend on to ensure that we're
|
6
|
+
# not enabling functionality unsupported by the editor connecting to the server
|
7
|
+
class ClientCapabilities
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { returns(T::Boolean) }
|
11
|
+
attr_reader :supports_watching_files,
|
12
|
+
:supports_request_delegation,
|
13
|
+
:window_show_message_supports_extra_properties,
|
14
|
+
:supports_progress
|
15
|
+
|
16
|
+
sig { void }
|
17
|
+
def initialize
|
18
|
+
# The editor supports watching files. This requires two capabilities: dynamic registration and relative pattern
|
19
|
+
# support
|
20
|
+
@supports_watching_files = T.let(false, T::Boolean)
|
21
|
+
|
22
|
+
# The editor supports request delegation. This is an experimental capability since request delegation has not been
|
23
|
+
# standardized into the LSP spec yet
|
24
|
+
@supports_request_delegation = T.let(false, T::Boolean)
|
25
|
+
|
26
|
+
# The editor supports extra arbitrary properties for `window/showMessageRequest`. Necessary for add-ons to show
|
27
|
+
# dialogs with user interactions
|
28
|
+
@window_show_message_supports_extra_properties = T.let(false, T::Boolean)
|
29
|
+
|
30
|
+
# Which resource operations the editor supports, like renaming files
|
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)
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { params(capabilities: T::Hash[Symbol, T.untyped]).void }
|
38
|
+
def apply_client_capabilities(capabilities)
|
39
|
+
workspace_capabilities = capabilities[:workspace] || {}
|
40
|
+
|
41
|
+
file_watching_caps = workspace_capabilities[:didChangeWatchedFiles]
|
42
|
+
if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
|
43
|
+
@supports_watching_files = true
|
44
|
+
end
|
45
|
+
|
46
|
+
@supports_request_delegation = capabilities.dig(:experimental, :requestDelegation) || false
|
47
|
+
supported_resource_operations = workspace_capabilities.dig(:workspaceEdit, :resourceOperations)
|
48
|
+
@supported_resource_operations = supported_resource_operations if supported_resource_operations
|
49
|
+
|
50
|
+
supports_additional_properties = capabilities.dig(
|
51
|
+
:window,
|
52
|
+
:showMessage,
|
53
|
+
:messageActionItem,
|
54
|
+
:additionalPropertiesSupport,
|
55
|
+
)
|
56
|
+
@window_show_message_supports_extra_properties = supports_additional_properties || false
|
57
|
+
|
58
|
+
progress = capabilities.dig(:window, :workDoneProgress)
|
59
|
+
@supports_progress = progress if progress
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { returns(T::Boolean) }
|
63
|
+
def supports_rename?
|
64
|
+
@supported_resource_operations.include?("rename")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -63,7 +63,7 @@ module RubyLsp
|
|
63
63
|
sig { abstract.returns(LanguageId) }
|
64
64
|
def language_id; end
|
65
65
|
|
66
|
-
# TODO: remove this method once all
|
66
|
+
# TODO: remove this method once all non-positional requests have been migrated to the listener pattern
|
67
67
|
sig do
|
68
68
|
type_parameters(:T)
|
69
69
|
.params(
|
@@ -21,14 +21,14 @@ module RubyLsp
|
|
21
21
|
attr_reader :encoding
|
22
22
|
|
23
23
|
sig { returns(T::Boolean) }
|
24
|
-
attr_reader :
|
25
|
-
|
26
|
-
sig { returns(T::Array[String]) }
|
27
|
-
attr_reader :supported_resource_operations
|
24
|
+
attr_reader :top_level_bundle
|
28
25
|
|
29
26
|
sig { returns(TypeInferrer) }
|
30
27
|
attr_reader :type_inferrer
|
31
28
|
|
29
|
+
sig { returns(ClientCapabilities) }
|
30
|
+
attr_reader :client_capabilities
|
31
|
+
|
32
32
|
sig { void }
|
33
33
|
def initialize
|
34
34
|
@workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
|
@@ -40,12 +40,19 @@ 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
|
-
@supports_watching_files = T.let(false, T::Boolean)
|
44
|
-
@experimental_features = T.let(false, T::Boolean)
|
45
43
|
@type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
|
46
44
|
@addon_settings = T.let({}, T::Hash[String, T.untyped])
|
47
|
-
@
|
48
|
-
|
45
|
+
@top_level_bundle = T.let(
|
46
|
+
begin
|
47
|
+
Bundler.with_original_env { Bundler.default_gemfile }
|
48
|
+
true
|
49
|
+
rescue Bundler::GemfileNotFound, Bundler::GitError
|
50
|
+
false
|
51
|
+
end,
|
52
|
+
T::Boolean,
|
53
|
+
)
|
54
|
+
@client_capabilities = T.let(ClientCapabilities.new, ClientCapabilities)
|
55
|
+
@enabled_feature_flags = T.let({}, T::Hash[Symbol, T::Boolean])
|
49
56
|
end
|
50
57
|
|
51
58
|
sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
@@ -123,12 +130,7 @@ module RubyLsp
|
|
123
130
|
end
|
124
131
|
@index.configuration.encoding = @encoding
|
125
132
|
|
126
|
-
|
127
|
-
if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
|
128
|
-
@supports_watching_files = true
|
129
|
-
end
|
130
|
-
|
131
|
-
@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
|
133
|
+
@client_capabilities.apply_client_capabilities(options[:capabilities]) if options[:capabilities]
|
132
134
|
|
133
135
|
addon_settings = options.dig(:initializationOptions, :addonSettings)
|
134
136
|
if addon_settings
|
@@ -136,13 +138,17 @@ module RubyLsp
|
|
136
138
|
@addon_settings.merge!(addon_settings)
|
137
139
|
end
|
138
140
|
|
139
|
-
|
140
|
-
|
141
|
-
@supported_resource_operations = supported_resource_operations if supported_resource_operations
|
141
|
+
enabled_flags = options.dig(:initializationOptions, :enabledFeatureFlags)
|
142
|
+
@enabled_feature_flags = enabled_flags if enabled_flags
|
142
143
|
|
143
144
|
notifications
|
144
145
|
end
|
145
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
|
+
|
146
152
|
sig { returns(String) }
|
147
153
|
def workspace_path
|
148
154
|
T.must(@workspace_uri.to_standardized_path)
|
@@ -160,6 +166,11 @@ module RubyLsp
|
|
160
166
|
end
|
161
167
|
end
|
162
168
|
|
169
|
+
sig { returns(T::Boolean) }
|
170
|
+
def supports_watching_files
|
171
|
+
@client_capabilities.supports_watching_files
|
172
|
+
end
|
173
|
+
|
163
174
|
private
|
164
175
|
|
165
176
|
sig { params(direct_dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(String) }
|
@@ -231,14 +242,16 @@ module RubyLsp
|
|
231
242
|
sig { returns(T::Array[String]) }
|
232
243
|
def gather_direct_dependencies
|
233
244
|
Bundler.with_original_env { Bundler.default_gemfile }
|
234
|
-
|
245
|
+
|
246
|
+
dependencies = Bundler.locked_gems&.dependencies&.keys || []
|
247
|
+
dependencies + gemspec_dependencies
|
235
248
|
rescue Bundler::GemfileNotFound
|
236
249
|
[]
|
237
250
|
end
|
238
251
|
|
239
252
|
sig { returns(T::Array[String]) }
|
240
253
|
def gemspec_dependencies
|
241
|
-
Bundler.locked_gems
|
254
|
+
(Bundler.locked_gems&.sources || [])
|
242
255
|
.grep(Bundler::Source::Gemspec)
|
243
256
|
.flat_map { _1.gemspec&.dependencies&.map(&:name) }
|
244
257
|
end
|
@@ -246,7 +259,7 @@ module RubyLsp
|
|
246
259
|
sig { returns(T::Array[String]) }
|
247
260
|
def gather_direct_and_indirect_dependencies
|
248
261
|
Bundler.with_original_env { Bundler.default_gemfile }
|
249
|
-
Bundler.locked_gems
|
262
|
+
Bundler.locked_gems&.specs&.map(&:name) || []
|
250
263
|
rescue Bundler::GemfileNotFound
|
251
264
|
[]
|
252
265
|
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -12,6 +12,7 @@ require "sorbet-runtime"
|
|
12
12
|
require "bundler"
|
13
13
|
Bundler.ui.level = :silent
|
14
14
|
|
15
|
+
require "json"
|
15
16
|
require "uri"
|
16
17
|
require "cgi"
|
17
18
|
require "set"
|
@@ -20,6 +21,7 @@ require "prism"
|
|
20
21
|
require "prism/visitor"
|
21
22
|
require "language_server-protocol"
|
22
23
|
require "rbs"
|
24
|
+
require "fileutils"
|
23
25
|
|
24
26
|
require "ruby-lsp"
|
25
27
|
require "ruby_lsp/base_server"
|
@@ -28,6 +30,7 @@ require "core_ext/uri"
|
|
28
30
|
require "ruby_lsp/utils"
|
29
31
|
require "ruby_lsp/static_docs"
|
30
32
|
require "ruby_lsp/scope"
|
33
|
+
require "ruby_lsp/client_capabilities"
|
31
34
|
require "ruby_lsp/global_state"
|
32
35
|
require "ruby_lsp/server"
|
33
36
|
require "ruby_lsp/type_inferrer"
|
@@ -85,6 +85,12 @@ module RubyLsp
|
|
85
85
|
:on_constant_path_node_enter,
|
86
86
|
:on_constant_read_node_enter,
|
87
87
|
:on_call_node_enter,
|
88
|
+
:on_global_variable_and_write_node_enter,
|
89
|
+
:on_global_variable_operator_write_node_enter,
|
90
|
+
:on_global_variable_or_write_node_enter,
|
91
|
+
:on_global_variable_read_node_enter,
|
92
|
+
:on_global_variable_target_node_enter,
|
93
|
+
:on_global_variable_write_node_enter,
|
88
94
|
:on_instance_variable_read_node_enter,
|
89
95
|
:on_instance_variable_write_node_enter,
|
90
96
|
:on_instance_variable_and_write_node_enter,
|
@@ -180,6 +186,36 @@ module RubyLsp
|
|
180
186
|
end
|
181
187
|
end
|
182
188
|
|
189
|
+
sig { params(node: Prism::GlobalVariableAndWriteNode).void }
|
190
|
+
def on_global_variable_and_write_node_enter(node)
|
191
|
+
handle_global_variable_completion(node.name.to_s, node.name_loc)
|
192
|
+
end
|
193
|
+
|
194
|
+
sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
|
195
|
+
def on_global_variable_operator_write_node_enter(node)
|
196
|
+
handle_global_variable_completion(node.name.to_s, node.name_loc)
|
197
|
+
end
|
198
|
+
|
199
|
+
sig { params(node: Prism::GlobalVariableOrWriteNode).void }
|
200
|
+
def on_global_variable_or_write_node_enter(node)
|
201
|
+
handle_global_variable_completion(node.name.to_s, node.name_loc)
|
202
|
+
end
|
203
|
+
|
204
|
+
sig { params(node: Prism::GlobalVariableReadNode).void }
|
205
|
+
def on_global_variable_read_node_enter(node)
|
206
|
+
handle_global_variable_completion(node.name.to_s, node.location)
|
207
|
+
end
|
208
|
+
|
209
|
+
sig { params(node: Prism::GlobalVariableTargetNode).void }
|
210
|
+
def on_global_variable_target_node_enter(node)
|
211
|
+
handle_global_variable_completion(node.name.to_s, node.location)
|
212
|
+
end
|
213
|
+
|
214
|
+
sig { params(node: Prism::GlobalVariableWriteNode).void }
|
215
|
+
def on_global_variable_write_node_enter(node)
|
216
|
+
handle_global_variable_completion(node.name.to_s, node.name_loc)
|
217
|
+
end
|
218
|
+
|
183
219
|
sig { params(node: Prism::InstanceVariableReadNode).void }
|
184
220
|
def on_instance_variable_read_node_enter(node)
|
185
221
|
handle_instance_variable_completion(node.name.to_s, node.location)
|
@@ -267,6 +303,29 @@ module RubyLsp
|
|
267
303
|
end
|
268
304
|
end
|
269
305
|
|
306
|
+
sig { params(name: String, location: Prism::Location).void }
|
307
|
+
def handle_global_variable_completion(name, location)
|
308
|
+
candidates = @index.prefix_search(name)
|
309
|
+
|
310
|
+
return if candidates.none?
|
311
|
+
|
312
|
+
range = range_from_location(location)
|
313
|
+
|
314
|
+
candidates.flatten.uniq(&:name).each do |entry|
|
315
|
+
entry_name = entry.name
|
316
|
+
|
317
|
+
@response_builder << Interface::CompletionItem.new(
|
318
|
+
label: entry_name,
|
319
|
+
filter_text: entry_name,
|
320
|
+
label_details: Interface::CompletionItemLabelDetails.new(
|
321
|
+
description: entry.file_name,
|
322
|
+
),
|
323
|
+
text_edit: Interface::TextEdit.new(range: range, new_text: entry_name),
|
324
|
+
kind: Constant::CompletionItemKind::VARIABLE,
|
325
|
+
)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
270
329
|
sig { params(name: String, location: Prism::Location).void }
|
271
330
|
def handle_instance_variable_completion(name, location)
|
272
331
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
@@ -381,8 +440,11 @@ module RubyLsp
|
|
381
440
|
return unless range
|
382
441
|
|
383
442
|
guessed_type = type.is_a?(TypeInferrer::GuessedType) && type.name
|
443
|
+
external_references = @node_context.fully_qualified_name != type.name
|
384
444
|
|
385
445
|
@index.method_completion_candidates(method_name, type.name).each do |entry|
|
446
|
+
next if entry.visibility != RubyIndexer::Entry::Visibility::PUBLIC && external_references
|
447
|
+
|
386
448
|
entry_name = entry.name
|
387
449
|
owner_name = entry.owner&.name
|
388
450
|
|
@@ -39,7 +39,12 @@ module RubyLsp
|
|
39
39
|
:on_block_argument_node_enter,
|
40
40
|
:on_constant_read_node_enter,
|
41
41
|
:on_constant_path_node_enter,
|
42
|
+
:on_global_variable_and_write_node_enter,
|
43
|
+
:on_global_variable_operator_write_node_enter,
|
44
|
+
:on_global_variable_or_write_node_enter,
|
42
45
|
:on_global_variable_read_node_enter,
|
46
|
+
:on_global_variable_target_node_enter,
|
47
|
+
:on_global_variable_write_node_enter,
|
43
48
|
:on_instance_variable_read_node_enter,
|
44
49
|
:on_instance_variable_write_node_enter,
|
45
50
|
:on_instance_variable_and_write_node_enter,
|
@@ -121,23 +126,34 @@ module RubyLsp
|
|
121
126
|
find_in_index(name)
|
122
127
|
end
|
123
128
|
|
129
|
+
sig { params(node: Prism::GlobalVariableAndWriteNode).void }
|
130
|
+
def on_global_variable_and_write_node_enter(node)
|
131
|
+
handle_global_variable_definition(node.name.to_s)
|
132
|
+
end
|
133
|
+
|
134
|
+
sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
|
135
|
+
def on_global_variable_operator_write_node_enter(node)
|
136
|
+
handle_global_variable_definition(node.name.to_s)
|
137
|
+
end
|
138
|
+
|
139
|
+
sig { params(node: Prism::GlobalVariableOrWriteNode).void }
|
140
|
+
def on_global_variable_or_write_node_enter(node)
|
141
|
+
handle_global_variable_definition(node.name.to_s)
|
142
|
+
end
|
143
|
+
|
124
144
|
sig { params(node: Prism::GlobalVariableReadNode).void }
|
125
145
|
def on_global_variable_read_node_enter(node)
|
126
|
-
|
127
|
-
|
128
|
-
return unless entries
|
146
|
+
handle_global_variable_definition(node.name.to_s)
|
147
|
+
end
|
129
148
|
|
130
|
-
|
131
|
-
|
149
|
+
sig { params(node: Prism::GlobalVariableTargetNode).void }
|
150
|
+
def on_global_variable_target_node_enter(node)
|
151
|
+
handle_global_variable_definition(node.name.to_s)
|
152
|
+
end
|
132
153
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
137
|
-
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
138
|
-
),
|
139
|
-
)
|
140
|
-
end
|
154
|
+
sig { params(node: Prism::GlobalVariableWriteNode).void }
|
155
|
+
def on_global_variable_write_node_enter(node)
|
156
|
+
handle_global_variable_definition(node.name.to_s)
|
141
157
|
end
|
142
158
|
|
143
159
|
sig { params(node: Prism::InstanceVariableReadNode).void }
|
@@ -197,6 +213,25 @@ module RubyLsp
|
|
197
213
|
)
|
198
214
|
end
|
199
215
|
|
216
|
+
sig { params(name: String).void }
|
217
|
+
def handle_global_variable_definition(name)
|
218
|
+
entries = @index[name]
|
219
|
+
|
220
|
+
return unless entries
|
221
|
+
|
222
|
+
entries.each do |entry|
|
223
|
+
location = entry.location
|
224
|
+
|
225
|
+
@response_builder << Interface::Location.new(
|
226
|
+
uri: URI::Generic.from_path(path: entry.file_path).to_s,
|
227
|
+
range: Interface::Range.new(
|
228
|
+
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
229
|
+
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
230
|
+
),
|
231
|
+
)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
200
235
|
sig { params(name: String).void }
|
201
236
|
def handle_instance_variable_definition(name)
|
202
237
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
@@ -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
|