ruby-lsp 0.20.1 → 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/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
|