ruby-lsp 0.20.1 → 0.21.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 +18 -3
- data/exe/ruby-lsp-launcher +127 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +56 -2
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +21 -6
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +5 -5
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +2 -2
- data/lib/ruby_indexer/test/enhancements_test.rb +51 -19
- data/lib/ruby_indexer/test/index_test.rb +2 -2
- data/lib/ruby_indexer/test/instance_variables_test.rb +1 -1
- data/lib/ruby_indexer/test/method_test.rb +26 -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 +60 -0
- data/lib/ruby_lsp/document.rb +1 -1
- data/lib/ruby_lsp/global_state.rb +25 -19
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/completion.rb +62 -0
- data/lib/ruby_lsp/listeners/definition.rb +48 -13
- 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 +26 -11
- data/lib/ruby_lsp/requests/document_symbol.rb +2 -1
- data/lib/ruby_lsp/requests/hover.rb +24 -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 +11 -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 +54 -16
- data/lib/ruby_lsp/setup_bundler.rb +111 -22
- data/lib/ruby_lsp/utils.rb +8 -0
- metadata +8 -3
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,60 @@
|
|
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
|
+
|
15
|
+
sig { void }
|
16
|
+
def initialize
|
17
|
+
# The editor supports watching files. This requires two capabilities: dynamic registration and relative pattern
|
18
|
+
# support
|
19
|
+
@supports_watching_files = T.let(false, T::Boolean)
|
20
|
+
|
21
|
+
# The editor supports request delegation. This is an experimental capability since request delegation has not been
|
22
|
+
# standardized into the LSP spec yet
|
23
|
+
@supports_request_delegation = T.let(false, T::Boolean)
|
24
|
+
|
25
|
+
# The editor supports extra arbitrary properties for `window/showMessageRequest`. Necessary for add-ons to show
|
26
|
+
# dialogs with user interactions
|
27
|
+
@window_show_message_supports_extra_properties = T.let(false, T::Boolean)
|
28
|
+
|
29
|
+
# Which resource operations the editor supports, like renaming files
|
30
|
+
@supported_resource_operations = T.let([], T::Array[String])
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { params(capabilities: T::Hash[Symbol, T.untyped]).void }
|
34
|
+
def apply_client_capabilities(capabilities)
|
35
|
+
workspace_capabilities = capabilities[:workspace] || {}
|
36
|
+
|
37
|
+
file_watching_caps = workspace_capabilities[:didChangeWatchedFiles]
|
38
|
+
if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
|
39
|
+
@supports_watching_files = true
|
40
|
+
end
|
41
|
+
|
42
|
+
@supports_request_delegation = capabilities.dig(:experimental, :requestDelegation) || false
|
43
|
+
supported_resource_operations = workspace_capabilities.dig(:workspaceEdit, :resourceOperations)
|
44
|
+
@supported_resource_operations = supported_resource_operations if supported_resource_operations
|
45
|
+
|
46
|
+
supports_additional_properties = capabilities.dig(
|
47
|
+
:window,
|
48
|
+
:showMessage,
|
49
|
+
:messageActionItem,
|
50
|
+
:additionalPropertiesSupport,
|
51
|
+
)
|
52
|
+
@window_show_message_supports_extra_properties = supports_additional_properties || false
|
53
|
+
end
|
54
|
+
|
55
|
+
sig { returns(T::Boolean) }
|
56
|
+
def supports_rename?
|
57
|
+
@supported_resource_operations.include?("rename")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
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 :experimental_features, :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
43
|
@experimental_features = T.let(false, T::Boolean)
|
45
44
|
@type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
|
46
45
|
@addon_settings = T.let({}, T::Hash[String, T.untyped])
|
47
|
-
@
|
48
|
-
|
46
|
+
@top_level_bundle = T.let(
|
47
|
+
begin
|
48
|
+
Bundler.with_original_env { Bundler.default_gemfile }
|
49
|
+
true
|
50
|
+
rescue Bundler::GemfileNotFound, Bundler::GitError
|
51
|
+
false
|
52
|
+
end,
|
53
|
+
T::Boolean,
|
54
|
+
)
|
55
|
+
@client_capabilities = T.let(ClientCapabilities.new, ClientCapabilities)
|
49
56
|
end
|
50
57
|
|
51
58
|
sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
@@ -123,12 +130,8 @@ module RubyLsp
|
|
123
130
|
end
|
124
131
|
@index.configuration.encoding = @encoding
|
125
132
|
|
126
|
-
file_watching_caps = options.dig(:capabilities, :workspace, :didChangeWatchedFiles)
|
127
|
-
if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
|
128
|
-
@supports_watching_files = true
|
129
|
-
end
|
130
|
-
|
131
133
|
@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
|
134
|
+
@client_capabilities.apply_client_capabilities(options[:capabilities]) if options[:capabilities]
|
132
135
|
|
133
136
|
addon_settings = options.dig(:initializationOptions, :addonSettings)
|
134
137
|
if addon_settings
|
@@ -136,10 +139,6 @@ module RubyLsp
|
|
136
139
|
@addon_settings.merge!(addon_settings)
|
137
140
|
end
|
138
141
|
|
139
|
-
@supports_request_delegation = options.dig(:capabilities, :experimental, :requestDelegation) || false
|
140
|
-
supported_resource_operations = options.dig(:capabilities, :workspace, :workspaceEdit, :resourceOperations)
|
141
|
-
@supported_resource_operations = supported_resource_operations if supported_resource_operations
|
142
|
-
|
143
142
|
notifications
|
144
143
|
end
|
145
144
|
|
@@ -160,6 +159,11 @@ module RubyLsp
|
|
160
159
|
end
|
161
160
|
end
|
162
161
|
|
162
|
+
sig { returns(T::Boolean) }
|
163
|
+
def supports_watching_files
|
164
|
+
@client_capabilities.supports_watching_files
|
165
|
+
end
|
166
|
+
|
163
167
|
private
|
164
168
|
|
165
169
|
sig { params(direct_dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(String) }
|
@@ -231,14 +235,16 @@ module RubyLsp
|
|
231
235
|
sig { returns(T::Array[String]) }
|
232
236
|
def gather_direct_dependencies
|
233
237
|
Bundler.with_original_env { Bundler.default_gemfile }
|
234
|
-
|
238
|
+
|
239
|
+
dependencies = Bundler.locked_gems&.dependencies&.keys || []
|
240
|
+
dependencies + gemspec_dependencies
|
235
241
|
rescue Bundler::GemfileNotFound
|
236
242
|
[]
|
237
243
|
end
|
238
244
|
|
239
245
|
sig { returns(T::Array[String]) }
|
240
246
|
def gemspec_dependencies
|
241
|
-
Bundler.locked_gems
|
247
|
+
(Bundler.locked_gems&.sources || [])
|
242
248
|
.grep(Bundler::Source::Gemspec)
|
243
249
|
.flat_map { _1.gemspec&.dependencies&.map(&:name) }
|
244
250
|
end
|
@@ -246,7 +252,7 @@ module RubyLsp
|
|
246
252
|
sig { returns(T::Array[String]) }
|
247
253
|
def gather_direct_and_indirect_dependencies
|
248
254
|
Bundler.with_original_env { Bundler.default_gemfile }
|
249
|
-
Bundler.locked_gems
|
255
|
+
Bundler.locked_gems&.specs&.map(&:name) || []
|
250
256
|
rescue Bundler::GemfileNotFound
|
251
257
|
[]
|
252
258
|
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"
|
@@ -28,6 +29,7 @@ require "core_ext/uri"
|
|
28
29
|
require "ruby_lsp/utils"
|
29
30
|
require "ruby_lsp/static_docs"
|
30
31
|
require "ruby_lsp/scope"
|
32
|
+
require "ruby_lsp/client_capabilities"
|
31
33
|
require "ruby_lsp/global_state"
|
32
34
|
require "ruby_lsp/server"
|
33
35
|
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
|
@@ -13,6 +13,12 @@ module RubyLsp
|
|
13
13
|
Prism::ConstantReadNode,
|
14
14
|
Prism::ConstantWriteNode,
|
15
15
|
Prism::ConstantPathNode,
|
16
|
+
Prism::GlobalVariableAndWriteNode,
|
17
|
+
Prism::GlobalVariableOperatorWriteNode,
|
18
|
+
Prism::GlobalVariableOrWriteNode,
|
19
|
+
Prism::GlobalVariableReadNode,
|
20
|
+
Prism::GlobalVariableTargetNode,
|
21
|
+
Prism::GlobalVariableWriteNode,
|
16
22
|
Prism::InstanceVariableReadNode,
|
17
23
|
Prism::InstanceVariableAndWriteNode,
|
18
24
|
Prism::InstanceVariableOperatorWriteNode,
|
@@ -62,6 +68,12 @@ module RubyLsp
|
|
62
68
|
:on_constant_write_node_enter,
|
63
69
|
:on_constant_path_node_enter,
|
64
70
|
:on_call_node_enter,
|
71
|
+
:on_global_variable_and_write_node_enter,
|
72
|
+
:on_global_variable_operator_write_node_enter,
|
73
|
+
:on_global_variable_or_write_node_enter,
|
74
|
+
:on_global_variable_read_node_enter,
|
75
|
+
:on_global_variable_target_node_enter,
|
76
|
+
:on_global_variable_write_node_enter,
|
65
77
|
:on_instance_variable_read_node_enter,
|
66
78
|
:on_instance_variable_write_node_enter,
|
67
79
|
:on_instance_variable_and_write_node_enter,
|
@@ -128,6 +140,36 @@ module RubyLsp
|
|
128
140
|
handle_method_hover(message)
|
129
141
|
end
|
130
142
|
|
143
|
+
sig { params(node: Prism::GlobalVariableAndWriteNode).void }
|
144
|
+
def on_global_variable_and_write_node_enter(node)
|
145
|
+
handle_global_variable_hover(node.name.to_s)
|
146
|
+
end
|
147
|
+
|
148
|
+
sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
|
149
|
+
def on_global_variable_operator_write_node_enter(node)
|
150
|
+
handle_global_variable_hover(node.name.to_s)
|
151
|
+
end
|
152
|
+
|
153
|
+
sig { params(node: Prism::GlobalVariableOrWriteNode).void }
|
154
|
+
def on_global_variable_or_write_node_enter(node)
|
155
|
+
handle_global_variable_hover(node.name.to_s)
|
156
|
+
end
|
157
|
+
|
158
|
+
sig { params(node: Prism::GlobalVariableReadNode).void }
|
159
|
+
def on_global_variable_read_node_enter(node)
|
160
|
+
handle_global_variable_hover(node.name.to_s)
|
161
|
+
end
|
162
|
+
|
163
|
+
sig { params(node: Prism::GlobalVariableTargetNode).void }
|
164
|
+
def on_global_variable_target_node_enter(node)
|
165
|
+
handle_global_variable_hover(node.name.to_s)
|
166
|
+
end
|
167
|
+
|
168
|
+
sig { params(node: Prism::GlobalVariableWriteNode).void }
|
169
|
+
def on_global_variable_write_node_enter(node)
|
170
|
+
handle_global_variable_hover(node.name.to_s)
|
171
|
+
end
|
172
|
+
|
131
173
|
sig { params(node: Prism::InstanceVariableReadNode).void }
|
132
174
|
def on_instance_variable_read_node_enter(node)
|
133
175
|
handle_instance_variable_hover(node.name.to_s)
|
@@ -265,6 +307,16 @@ module RubyLsp
|
|
265
307
|
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
266
308
|
end
|
267
309
|
|
310
|
+
sig { params(name: String).void }
|
311
|
+
def handle_global_variable_hover(name)
|
312
|
+
entries = @index[name]
|
313
|
+
return unless entries
|
314
|
+
|
315
|
+
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
316
|
+
@response_builder.push(content, category: category)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
268
320
|
sig { params(name: String, location: Prism::Location).void }
|
269
321
|
def generate_hover(name, location)
|
270
322
|
entries = @index.resolve(name, @node_context.nesting)
|
@@ -121,7 +121,7 @@ module RubyLsp
|
|
121
121
|
return Error::InvalidTargetRange if closest_node.is_a?(Prism::MissingNode)
|
122
122
|
|
123
123
|
closest_node_loc = closest_node.location
|
124
|
-
# If the parent expression is a single line block, then we have to extract it inside of the
|
124
|
+
# If the parent expression is a single line block, then we have to extract it inside of the one-line block
|
125
125
|
if parent_statements.is_a?(Prism::BlockNode) &&
|
126
126
|
parent_statements.location.start_line == parent_statements.location.end_line
|
127
127
|
|
@@ -17,7 +17,7 @@ module RubyLsp
|
|
17
17
|
def provider
|
18
18
|
Interface::CompletionOptions.new(
|
19
19
|
resolve_provider: true,
|
20
|
-
trigger_characters: ["/", "\"", "'", ":", "@", ".", "=", "<"],
|
20
|
+
trigger_characters: ["/", "\"", "'", ":", "@", ".", "=", "<", "$"],
|
21
21
|
completion_item: {
|
22
22
|
labelDetailsSupport: true,
|
23
23
|
},
|
@@ -50,6 +50,12 @@ module RubyLsp
|
|
50
50
|
Prism::CallNode,
|
51
51
|
Prism::ConstantReadNode,
|
52
52
|
Prism::ConstantPathNode,
|
53
|
+
Prism::GlobalVariableAndWriteNode,
|
54
|
+
Prism::GlobalVariableOperatorWriteNode,
|
55
|
+
Prism::GlobalVariableOrWriteNode,
|
56
|
+
Prism::GlobalVariableReadNode,
|
57
|
+
Prism::GlobalVariableTargetNode,
|
58
|
+
Prism::GlobalVariableWriteNode,
|
53
59
|
Prism::InstanceVariableReadNode,
|
54
60
|
Prism::InstanceVariableAndWriteNode,
|
55
61
|
Prism::InstanceVariableOperatorWriteNode,
|
@@ -34,7 +34,7 @@ module RubyLsp
|
|
34
34
|
|
35
35
|
# Based on the spec https://microsoft.github.io/language-server-protocol/specification#textDocument_completion,
|
36
36
|
# a completion resolve request must always return the original completion item without modifying ANY fields
|
37
|
-
# other than detail and documentation (NOT labelDetails). If we modify anything, the completion
|
37
|
+
# other than detail and documentation (NOT labelDetails). If we modify anything, the completion behavior might
|
38
38
|
# be broken.
|
39
39
|
#
|
40
40
|
# For example, forgetting to return the `insertText` included in the original item will make the editor use the
|
@@ -12,12 +12,6 @@ module RubyLsp
|
|
12
12
|
extend T::Sig
|
13
13
|
extend T::Generic
|
14
14
|
|
15
|
-
SPECIAL_METHOD_CALLS = [
|
16
|
-
:require,
|
17
|
-
:require_relative,
|
18
|
-
:autoload,
|
19
|
-
].freeze
|
20
|
-
|
21
15
|
sig do
|
22
16
|
params(
|
23
17
|
document: T.any(RubyDocument, ERBDocument),
|
@@ -46,7 +40,12 @@ module RubyLsp
|
|
46
40
|
Prism::ConstantReadNode,
|
47
41
|
Prism::ConstantPathNode,
|
48
42
|
Prism::BlockArgumentNode,
|
43
|
+
Prism::GlobalVariableAndWriteNode,
|
44
|
+
Prism::GlobalVariableOperatorWriteNode,
|
45
|
+
Prism::GlobalVariableOrWriteNode,
|
49
46
|
Prism::GlobalVariableReadNode,
|
47
|
+
Prism::GlobalVariableTargetNode,
|
48
|
+
Prism::GlobalVariableWriteNode,
|
50
49
|
Prism::InstanceVariableReadNode,
|
51
50
|
Prism::InstanceVariableAndWriteNode,
|
52
51
|
Prism::InstanceVariableOperatorWriteNode,
|
@@ -72,11 +71,7 @@ module RubyLsp
|
|
72
71
|
parent,
|
73
72
|
position,
|
74
73
|
)
|
75
|
-
elsif
|
76
|
-
target.message_loc, position
|
77
|
-
)
|
78
|
-
# If the target is a method call, we need to ensure that the requested position is exactly on top of the
|
79
|
-
# method identifier. Otherwise, we risk showing definitions for unrelated things
|
74
|
+
elsif position_outside_target?(position, target)
|
80
75
|
target = nil
|
81
76
|
# For methods with block arguments using symbol-to-proc
|
82
77
|
elsif target.is_a?(Prism::SymbolNode) && parent.is_a?(Prism::BlockArgumentNode)
|
@@ -107,6 +102,26 @@ module RubyLsp
|
|
107
102
|
@dispatcher.dispatch_once(@target) if @target
|
108
103
|
@response_builder.response
|
109
104
|
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
sig { params(position: T::Hash[Symbol, T.untyped], target: T.nilable(Prism::Node)).returns(T::Boolean) }
|
109
|
+
def position_outside_target?(position, target)
|
110
|
+
case target
|
111
|
+
when Prism::GlobalVariableAndWriteNode,
|
112
|
+
Prism::GlobalVariableOperatorWriteNode,
|
113
|
+
Prism::GlobalVariableOrWriteNode,
|
114
|
+
Prism::GlobalVariableWriteNode,
|
115
|
+
Prism::InstanceVariableAndWriteNode,
|
116
|
+
Prism::InstanceVariableOperatorWriteNode,
|
117
|
+
Prism::InstanceVariableOrWriteNode,
|
118
|
+
Prism::InstanceVariableWriteNode
|
119
|
+
|
120
|
+
!covers_position?(target.name_loc, position)
|
121
|
+
else
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
110
125
|
end
|
111
126
|
end
|
112
127
|
end
|
@@ -10,7 +10,8 @@ module RubyLsp
|
|
10
10
|
# informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
|
11
11
|
# this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.
|
12
12
|
#
|
13
|
-
# In VS Code,
|
13
|
+
# In VS Code, symbol search known as 'Go To Symbol in Editor' and can be accessed with Ctrl/Cmd-Shift-O,
|
14
|
+
# or by opening the command palette and inserting an `@` symbol.
|
14
15
|
class DocumentSymbol < Request
|
15
16
|
extend T::Sig
|
16
17
|
|
@@ -46,17 +46,13 @@ module RubyLsp
|
|
46
46
|
target = node_context.node
|
47
47
|
parent = node_context.parent
|
48
48
|
|
49
|
-
if
|
50
|
-
!Listeners::Hover::ALLOWED_TARGETS.include?(target.class)) ||
|
51
|
-
(parent.is_a?(Prism::ConstantPathNode) && target.is_a?(Prism::ConstantReadNode))
|
49
|
+
if should_refine_target?(parent, target)
|
52
50
|
target = determine_target(
|
53
51
|
T.must(target),
|
54
52
|
T.must(parent),
|
55
53
|
position,
|
56
54
|
)
|
57
|
-
elsif
|
58
|
-
!covers_position?(target.message_loc, position)
|
59
|
-
|
55
|
+
elsif position_outside_target?(position, target)
|
60
56
|
target = nil
|
61
57
|
end
|
62
58
|
|
@@ -89,6 +85,28 @@ module RubyLsp
|
|
89
85
|
),
|
90
86
|
)
|
91
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
sig { params(parent: T.nilable(Prism::Node), target: T.nilable(Prism::Node)).returns(T::Boolean) }
|
92
|
+
def should_refine_target?(parent, target)
|
93
|
+
(Listeners::Hover::ALLOWED_TARGETS.include?(parent.class) &&
|
94
|
+
!Listeners::Hover::ALLOWED_TARGETS.include?(target.class)) ||
|
95
|
+
(parent.is_a?(Prism::ConstantPathNode) && target.is_a?(Prism::ConstantReadNode))
|
96
|
+
end
|
97
|
+
|
98
|
+
sig { params(position: T::Hash[Symbol, T.untyped], target: T.nilable(Prism::Node)).returns(T::Boolean) }
|
99
|
+
def position_outside_target?(position, target)
|
100
|
+
case target
|
101
|
+
when Prism::GlobalVariableAndWriteNode,
|
102
|
+
Prism::GlobalVariableOperatorWriteNode,
|
103
|
+
Prism::GlobalVariableOrWriteNode,
|
104
|
+
Prism::GlobalVariableWriteNode
|
105
|
+
!covers_position?(target.name_loc, position)
|
106
|
+
else
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
92
110
|
end
|
93
111
|
end
|
94
112
|
end
|
@@ -72,7 +72,7 @@ module RubyLsp
|
|
72
72
|
|
73
73
|
# If the client doesn't support resource operations, such as renaming files, then we can only return the basic
|
74
74
|
# text changes
|
75
|
-
unless @global_state.
|
75
|
+
unless @global_state.client_capabilities.supports_rename?
|
76
76
|
return Interface::WorkspaceEdit.new(changes: changes)
|
77
77
|
end
|
78
78
|
|