ruby-lsp 0.20.1 → 0.21.0
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 +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 +20 -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 +108 -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
|
|
@@ -231,14 +230,16 @@ module RubyLsp
|
|
231
230
|
sig { returns(T::Array[String]) }
|
232
231
|
def gather_direct_dependencies
|
233
232
|
Bundler.with_original_env { Bundler.default_gemfile }
|
234
|
-
|
233
|
+
|
234
|
+
dependencies = Bundler.locked_gems&.dependencies&.keys || []
|
235
|
+
dependencies + gemspec_dependencies
|
235
236
|
rescue Bundler::GemfileNotFound
|
236
237
|
[]
|
237
238
|
end
|
238
239
|
|
239
240
|
sig { returns(T::Array[String]) }
|
240
241
|
def gemspec_dependencies
|
241
|
-
Bundler.locked_gems
|
242
|
+
(Bundler.locked_gems&.sources || [])
|
242
243
|
.grep(Bundler::Source::Gemspec)
|
243
244
|
.flat_map { _1.gemspec&.dependencies&.map(&:name) }
|
244
245
|
end
|
@@ -246,7 +247,7 @@ module RubyLsp
|
|
246
247
|
sig { returns(T::Array[String]) }
|
247
248
|
def gather_direct_and_indirect_dependencies
|
248
249
|
Bundler.with_original_env { Bundler.default_gemfile }
|
249
|
-
Bundler.locked_gems
|
250
|
+
Bundler.locked_gems&.specs&.map(&:name) || []
|
250
251
|
rescue Bundler::GemfileNotFound
|
251
252
|
[]
|
252
253
|
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
|
|
@@ -26,7 +26,7 @@ module RubyLsp
|
|
26
26
|
).void
|
27
27
|
end
|
28
28
|
def delegate_request_if_needed!(global_state, document, char_position)
|
29
|
-
if global_state.supports_request_delegation &&
|
29
|
+
if global_state.client_capabilities.supports_request_delegation &&
|
30
30
|
document.is_a?(ERBDocument) &&
|
31
31
|
document.inside_host_language?(char_position)
|
32
32
|
raise DelegateRequestError
|