ruby-lsp 0.23.11 → 0.26.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 +10 -4
- data/exe/ruby-lsp-check +0 -4
- data/exe/ruby-lsp-launcher +45 -22
- data/exe/ruby-lsp-test-exec +6 -0
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -2
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -6
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +140 -183
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +10 -14
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +107 -236
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +166 -281
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +23 -27
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +25 -57
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +58 -68
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +7 -11
- data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
- data/lib/ruby_indexer/test/configuration_test.rb +49 -9
- data/lib/ruby_indexer/test/constant_test.rb +34 -34
- data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +185 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +61 -37
- data/lib/ruby_indexer/test/method_test.rb +166 -123
- data/lib/ruby_indexer/test/prefix_tree_test.rb +21 -21
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +70 -75
- data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
- data/lib/ruby_indexer/test/test_case.rb +9 -3
- data/lib/ruby_indexer/test/uri_test.rb +15 -2
- data/lib/ruby_lsp/addon.rb +88 -86
- data/lib/ruby_lsp/base_server.rb +59 -54
- data/lib/ruby_lsp/client_capabilities.rb +16 -13
- data/lib/ruby_lsp/document.rb +205 -104
- data/lib/ruby_lsp/erb_document.rb +45 -47
- data/lib/ruby_lsp/global_state.rb +73 -57
- data/lib/ruby_lsp/internal.rb +8 -3
- data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
- data/lib/ruby_lsp/listeners/completion.rb +81 -76
- data/lib/ruby_lsp/listeners/definition.rb +44 -58
- data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
- data/lib/ruby_lsp/listeners/document_link.rb +50 -70
- data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
- data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
- data/lib/ruby_lsp/listeners/hover.rb +107 -115
- data/lib/ruby_lsp/listeners/inlay_hints.rb +8 -13
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
- data/lib/ruby_lsp/listeners/signature_help.rb +12 -27
- data/lib/ruby_lsp/listeners/spec_style.rb +214 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +92 -0
- data/lib/ruby_lsp/listeners/test_style.rb +205 -95
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +10 -11
- data/lib/ruby_lsp/requests/code_action_resolve.rb +65 -61
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +31 -21
- data/lib/ruby_lsp/requests/completion.rb +8 -21
- data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
- data/lib/ruby_lsp/requests/definition.rb +8 -20
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +20 -7
- data/lib/ruby_lsp/requests/document_highlight.rb +6 -16
- data/lib/ruby_lsp/requests/document_link.rb +6 -17
- data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
- data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
- data/lib/ruby_lsp/requests/formatting.rb +6 -9
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +85 -0
- data/lib/ruby_lsp/requests/hover.rb +12 -25
- data/lib/ruby_lsp/requests/inlay_hints.rb +8 -19
- data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
- data/lib/ruby_lsp/requests/prepare_rename.rb +5 -10
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
- data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
- data/lib/ruby_lsp/requests/references.rb +17 -57
- data/lib/ruby_lsp/requests/rename.rb +27 -51
- data/lib/ruby_lsp/requests/request.rb +13 -25
- data/lib/ruby_lsp/requests/selection_ranges.rb +7 -7
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
- data/lib/ruby_lsp/requests/signature_help.rb +9 -27
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +16 -58
- data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +13 -16
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +34 -36
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
- data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
- data/lib/ruby_lsp/requests/support/source_uri.rb +20 -32
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
- data/lib/ruby_lsp/requests/support/test_item.rb +16 -14
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
- data/lib/ruby_lsp/response_builders/collection_response_builder.rb +6 -9
- data/lib/ruby_lsp/response_builders/document_symbol.rb +15 -21
- data/lib/ruby_lsp/response_builders/hover.rb +12 -18
- data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +62 -91
- data/lib/ruby_lsp/response_builders/signature_help.rb +6 -8
- data/lib/ruby_lsp/response_builders/test_collection.rb +35 -13
- data/lib/ruby_lsp/ruby_document.rb +32 -98
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +303 -196
- data/lib/ruby_lsp/setup_bundler.rb +121 -82
- data/lib/ruby_lsp/static_docs.rb +12 -7
- data/lib/ruby_lsp/store.rb +21 -49
- data/lib/ruby_lsp/test_helper.rb +3 -16
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +233 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +145 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +92 -0
- data/lib/ruby_lsp/type_inferrer.rb +13 -14
- data/lib/ruby_lsp/utils.rb +138 -93
- data/static_docs/break.md +103 -0
- metadata +14 -20
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
data/lib/ruby_lsp/server.rb
CHANGED
@@ -3,13 +3,12 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
class Server < BaseServer
|
6
|
-
extend T::Sig
|
7
|
-
|
8
6
|
# Only for testing
|
9
|
-
|
7
|
+
#: GlobalState
|
10
8
|
attr_reader :global_state
|
11
9
|
|
12
|
-
|
10
|
+
# @override
|
11
|
+
#: (Hash[Symbol, untyped] message) -> void
|
13
12
|
def process_message(message)
|
14
13
|
case message[:method]
|
15
14
|
when "initialize"
|
@@ -33,6 +32,8 @@ module RubyLsp
|
|
33
32
|
text_document_document_link(message)
|
34
33
|
when "textDocument/codeLens"
|
35
34
|
text_document_code_lens(message)
|
35
|
+
when "codeLens/resolve"
|
36
|
+
code_lens_resolve(message)
|
36
37
|
when "textDocument/semanticTokens/full"
|
37
38
|
text_document_semantic_tokens_full(message)
|
38
39
|
when "textDocument/semanticTokens/full/delta"
|
@@ -93,13 +94,10 @@ module RubyLsp
|
|
93
94
|
id: message[:id],
|
94
95
|
response:
|
95
96
|
Addon.addons.map do |addon|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
# Therefore, we only call the method if it's defined by the add-on itself
|
101
|
-
if version_method.owner != Addon
|
102
|
-
version = addon.version
|
97
|
+
version = begin
|
98
|
+
addon.version
|
99
|
+
rescue AbstractMethodInvokedError
|
100
|
+
nil
|
103
101
|
end
|
104
102
|
|
105
103
|
{ name: addon.name, version: version, errored: addon.error? }
|
@@ -112,6 +110,10 @@ module RubyLsp
|
|
112
110
|
diagnose_state(message)
|
113
111
|
when "rubyLsp/discoverTests"
|
114
112
|
discover_tests(message)
|
113
|
+
when "rubyLsp/resolveTestCommands"
|
114
|
+
resolve_test_commands(message)
|
115
|
+
when "experimental/goToRelevantFile"
|
116
|
+
experimental_go_to_relevant_file(message)
|
115
117
|
when "$/cancelRequest"
|
116
118
|
@global_state.synchronize { @cancelled_requests << message[:params][:id] }
|
117
119
|
when nil
|
@@ -119,30 +121,22 @@ module RubyLsp
|
|
119
121
|
end
|
120
122
|
rescue DelegateRequestError
|
121
123
|
send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
|
122
|
-
rescue StandardError, LoadError => e
|
124
|
+
rescue StandardError, LoadError, SystemExit => e
|
123
125
|
# If an error occurred in a request, we have to return an error response or else the editor will hang
|
124
126
|
if message[:id]
|
125
127
|
# If a document is deleted before we are able to process all of its enqueued requests, we will try to read it
|
126
128
|
# from disk and it raise this error. This is expected, so we don't include the `data` attribute to avoid
|
127
|
-
# reporting these to our telemetry
|
129
|
+
# reporting these to our telemetry.
|
130
|
+
#
|
131
|
+
# Similarly, if we receive a location for an invalid position in the
|
132
|
+
# document, we don't report it to telemetry
|
128
133
|
case e
|
129
|
-
when Store::NonExistingDocumentError
|
134
|
+
when Store::NonExistingDocumentError, Document::InvalidLocationError
|
130
135
|
send_message(Error.new(
|
131
136
|
id: message[:id],
|
132
137
|
code: Constant::ErrorCodes::INVALID_PARAMS,
|
133
138
|
message: e.full_message,
|
134
139
|
))
|
135
|
-
when Document::LocationNotFoundError
|
136
|
-
send_message(Error.new(
|
137
|
-
id: message[:id],
|
138
|
-
code: Constant::ErrorCodes::REQUEST_FAILED,
|
139
|
-
message: <<~MESSAGE,
|
140
|
-
Request #{message[:method]} failed to find the target position.
|
141
|
-
The file might have been modified while the server was in the middle of searching for the target.
|
142
|
-
If you experience this regularly, please report any findings and extra information on
|
143
|
-
https://github.com/Shopify/ruby-lsp/issues/2446
|
144
|
-
MESSAGE
|
145
|
-
))
|
146
140
|
else
|
147
141
|
send_message(Error.new(
|
148
142
|
id: message[:id],
|
@@ -161,7 +155,7 @@ module RubyLsp
|
|
161
155
|
end
|
162
156
|
|
163
157
|
# Process responses to requests that were sent to the client
|
164
|
-
|
158
|
+
#: (Hash[Symbol, untyped] message) -> void
|
165
159
|
def process_response(message)
|
166
160
|
case message.dig(:result, :method)
|
167
161
|
when "window/showMessageRequest"
|
@@ -169,7 +163,7 @@ module RubyLsp
|
|
169
163
|
end
|
170
164
|
end
|
171
165
|
|
172
|
-
|
166
|
+
#: (?include_project_addons: bool) -> void
|
173
167
|
def load_addons(include_project_addons: true)
|
174
168
|
# If invoking Bundler.setup failed, then the load path will not be configured properly and trying to load add-ons
|
175
169
|
# with Gem.find_files will find every single version installed of an add-on, leading to requiring several
|
@@ -177,6 +171,7 @@ module RubyLsp
|
|
177
171
|
return if @setup_error
|
178
172
|
|
179
173
|
errors = Addon.load_addons(@global_state, @outgoing_queue, include_project_addons: include_project_addons)
|
174
|
+
return if test_mode?
|
180
175
|
|
181
176
|
if errors.any?
|
182
177
|
send_log_message(
|
@@ -189,27 +184,19 @@ module RubyLsp
|
|
189
184
|
|
190
185
|
if errored_addons.any?
|
191
186
|
send_message(
|
192
|
-
Notification.
|
193
|
-
|
194
|
-
|
195
|
-
type: Constant::MessageType::WARNING,
|
196
|
-
message: "Error loading add-ons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
|
197
|
-
),
|
187
|
+
Notification.window_show_message(
|
188
|
+
"Error loading add-ons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
|
189
|
+
type: Constant::MessageType::WARNING,
|
198
190
|
),
|
199
191
|
)
|
200
192
|
|
201
|
-
|
202
|
-
send_log_message(
|
203
|
-
errored_addons.map(&:errors_details).join("\n\n"),
|
204
|
-
type: Constant::MessageType::WARNING,
|
205
|
-
)
|
206
|
-
end
|
193
|
+
send_log_message(errored_addons.map(&:errors_details).join("\n\n"), type: Constant::MessageType::WARNING)
|
207
194
|
end
|
208
195
|
end
|
209
196
|
|
210
197
|
private
|
211
198
|
|
212
|
-
|
199
|
+
#: (Hash[Symbol, untyped] message) -> void
|
213
200
|
def run_initialize(message)
|
214
201
|
options = message[:params]
|
215
202
|
global_state_notifications = @global_state.apply_options(options)
|
@@ -219,9 +206,6 @@ module RubyLsp
|
|
219
206
|
|
220
207
|
configured_features = options.dig(:initializationOptions, :enabledFeatures)
|
221
208
|
|
222
|
-
configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
|
223
|
-
T.must(@store.features_configuration.dig(:inlayHint)).configuration.merge!(configured_hints) if configured_hints
|
224
|
-
|
225
209
|
enabled_features = case configured_features
|
226
210
|
when Array
|
227
211
|
# If the configuration is using an array, then absent features are disabled and present ones are enabled. That's
|
@@ -238,7 +222,9 @@ module RubyLsp
|
|
238
222
|
|
239
223
|
bundle_env_path = File.join(".ruby-lsp", "bundle_env")
|
240
224
|
bundle_env = if File.exist?(bundle_env_path)
|
241
|
-
env = File.readlines(bundle_env_path).to_h
|
225
|
+
env = File.readlines(bundle_env_path).to_h do |line|
|
226
|
+
line.chomp.split("=", 2) #: as [String, String]
|
227
|
+
end
|
242
228
|
FileUtils.rm(bundle_env_path)
|
243
229
|
env
|
244
230
|
end
|
@@ -290,6 +276,8 @@ module RubyLsp
|
|
290
276
|
experimental: {
|
291
277
|
addon_detection: true,
|
292
278
|
compose_bundle: true,
|
279
|
+
go_to_relevant_file: true,
|
280
|
+
full_test_discovery: true,
|
293
281
|
},
|
294
282
|
),
|
295
283
|
serverInfo: {
|
@@ -346,7 +334,7 @@ module RubyLsp
|
|
346
334
|
end
|
347
335
|
end
|
348
336
|
|
349
|
-
|
337
|
+
#: -> void
|
350
338
|
def run_initialized
|
351
339
|
load_addons
|
352
340
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
@@ -371,69 +359,64 @@ module RubyLsp
|
|
371
359
|
|
372
360
|
perform_initial_indexing
|
373
361
|
check_formatter_is_available
|
362
|
+
update_server if @global_state.enabled_feature?(:launcher)
|
374
363
|
end
|
375
364
|
|
376
|
-
|
365
|
+
#: (Hash[Symbol, untyped] message) -> void
|
377
366
|
def text_document_did_open(message)
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
end
|
367
|
+
text_document = message.dig(:params, :textDocument)
|
368
|
+
language_id = case text_document[:languageId]
|
369
|
+
when "erb", "eruby"
|
370
|
+
:erb
|
371
|
+
when "rbs"
|
372
|
+
:rbs
|
373
|
+
else
|
374
|
+
:ruby
|
375
|
+
end
|
388
376
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
377
|
+
document = @store.set(
|
378
|
+
uri: text_document[:uri],
|
379
|
+
source: text_document[:text],
|
380
|
+
version: text_document[:version],
|
381
|
+
language_id: language_id,
|
382
|
+
)
|
395
383
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
384
|
+
if document.past_expensive_limit? && text_document[:uri].scheme == "file"
|
385
|
+
log_message = <<~MESSAGE
|
386
|
+
The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
|
387
|
+
diagnostics will be disabled.
|
388
|
+
MESSAGE
|
401
389
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
),
|
390
|
+
send_message(
|
391
|
+
Notification.new(
|
392
|
+
method: "window/logMessage",
|
393
|
+
params: Interface::LogMessageParams.new(
|
394
|
+
type: Constant::MessageType::WARNING,
|
395
|
+
message: log_message,
|
409
396
|
),
|
410
|
-
)
|
411
|
-
|
397
|
+
),
|
398
|
+
)
|
412
399
|
end
|
413
400
|
end
|
414
401
|
|
415
|
-
|
402
|
+
#: (Hash[Symbol, untyped] message) -> void
|
416
403
|
def text_document_did_close(message)
|
417
|
-
|
418
|
-
|
419
|
-
@store.delete(uri)
|
404
|
+
uri = message.dig(:params, :textDocument, :uri)
|
405
|
+
@store.delete(uri)
|
420
406
|
|
421
|
-
|
422
|
-
|
423
|
-
end
|
407
|
+
# Clear diagnostics for the closed file, so that they no longer appear in the problems tab
|
408
|
+
send_message(Notification.publish_diagnostics(uri.to_s, []))
|
424
409
|
end
|
425
410
|
|
426
|
-
|
411
|
+
#: (Hash[Symbol, untyped] message) -> void
|
427
412
|
def text_document_did_change(message)
|
428
413
|
params = message[:params]
|
429
414
|
text_document = params[:textDocument]
|
430
415
|
|
431
|
-
@
|
432
|
-
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
|
433
|
-
end
|
416
|
+
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
|
434
417
|
end
|
435
418
|
|
436
|
-
|
419
|
+
#: (Hash[Symbol, untyped] message) -> void
|
437
420
|
def text_document_selection_range(message)
|
438
421
|
uri = message.dig(:params, :textDocument, :uri)
|
439
422
|
ranges = @store.cache_fetch(uri, "textDocument/selectionRange") do |document|
|
@@ -459,7 +442,7 @@ module RubyLsp
|
|
459
442
|
send_message(Result.new(id: message[:id], response: response))
|
460
443
|
end
|
461
444
|
|
462
|
-
|
445
|
+
#: (Hash[Symbol, untyped] message) -> void
|
463
446
|
def run_combined_requests(message)
|
464
447
|
uri = URI(message.dig(:params, :textDocument, :uri))
|
465
448
|
document = @store.get(uri)
|
@@ -483,8 +466,15 @@ module RubyLsp
|
|
483
466
|
folding_range = Requests::FoldingRanges.new(parse_result.comments, dispatcher)
|
484
467
|
document_symbol = Requests::DocumentSymbol.new(uri, dispatcher)
|
485
468
|
document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
|
486
|
-
|
487
|
-
|
469
|
+
inlay_hint = Requests::InlayHints.new(
|
470
|
+
@global_state,
|
471
|
+
document,
|
472
|
+
dispatcher,
|
473
|
+
)
|
474
|
+
|
475
|
+
# The code lens listener requires the index to be populated, so the DeclarationListener must be inserted first in
|
476
|
+
# the dispatcher's state
|
477
|
+
code_lens = nil #: Requests::CodeLens?
|
488
478
|
|
489
479
|
if document.is_a?(RubyDocument) && document.should_index?
|
490
480
|
# Re-index the file as it is modified. This mode of indexing updates entries only. Require path trees are only
|
@@ -495,11 +485,13 @@ module RubyLsp
|
|
495
485
|
@global_state.index.handle_change(uri) do |index|
|
496
486
|
index.delete(uri, skip_require_paths_tree: true)
|
497
487
|
RubyIndexer::DeclarationListener.new(index, dispatcher, parse_result, uri, collect_comments: true)
|
498
|
-
|
488
|
+
code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
|
489
|
+
dispatcher.dispatch(document.ast)
|
499
490
|
end
|
500
491
|
end
|
501
492
|
else
|
502
|
-
|
493
|
+
code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
|
494
|
+
dispatcher.dispatch(document.ast)
|
503
495
|
end
|
504
496
|
|
505
497
|
# Store all responses retrieve in this round of visits in the cache and then return the response for the request
|
@@ -507,7 +499,11 @@ module RubyLsp
|
|
507
499
|
document.cache_set("textDocument/foldingRange", folding_range.perform)
|
508
500
|
document.cache_set("textDocument/documentSymbol", document_symbol.perform)
|
509
501
|
document.cache_set("textDocument/documentLink", document_link.perform)
|
510
|
-
document.cache_set(
|
502
|
+
document.cache_set(
|
503
|
+
"textDocument/codeLens",
|
504
|
+
code_lens #: as !nil
|
505
|
+
.perform,
|
506
|
+
)
|
511
507
|
document.cache_set("textDocument/inlayHint", inlay_hint.perform)
|
512
508
|
|
513
509
|
send_message(Result.new(id: message[:id], response: document.cache_get(message[:method])))
|
@@ -518,7 +514,7 @@ module RubyLsp
|
|
518
514
|
alias_method :text_document_code_lens, :run_combined_requests
|
519
515
|
alias_method :text_document_folding_range, :run_combined_requests
|
520
516
|
|
521
|
-
|
517
|
+
#: (Hash[Symbol, untyped] message) -> void
|
522
518
|
def text_document_semantic_tokens_full(message)
|
523
519
|
document = @store.get(message.dig(:params, :textDocument, :uri))
|
524
520
|
|
@@ -534,12 +530,12 @@ module RubyLsp
|
|
534
530
|
|
535
531
|
dispatcher = Prism::Dispatcher.new
|
536
532
|
semantic_highlighting = Requests::SemanticHighlighting.new(@global_state, dispatcher, document, nil)
|
537
|
-
dispatcher.visit(document.
|
533
|
+
dispatcher.visit(document.ast)
|
538
534
|
|
539
535
|
send_message(Result.new(id: message[:id], response: semantic_highlighting.perform))
|
540
536
|
end
|
541
537
|
|
542
|
-
|
538
|
+
#: (Hash[Symbol, untyped] message) -> void
|
543
539
|
def text_document_semantic_tokens_delta(message)
|
544
540
|
document = @store.get(message.dig(:params, :textDocument, :uri))
|
545
541
|
|
@@ -560,11 +556,11 @@ module RubyLsp
|
|
560
556
|
document,
|
561
557
|
message.dig(:params, :previousResultId),
|
562
558
|
)
|
563
|
-
dispatcher.visit(document.
|
559
|
+
dispatcher.visit(document.ast)
|
564
560
|
send_message(Result.new(id: message[:id], response: request.perform))
|
565
561
|
end
|
566
562
|
|
567
|
-
|
563
|
+
#: (Hash[Symbol, untyped] message) -> void
|
568
564
|
def text_document_semantic_tokens_range(message)
|
569
565
|
params = message[:params]
|
570
566
|
range = params[:range]
|
@@ -589,11 +585,11 @@ module RubyLsp
|
|
589
585
|
nil,
|
590
586
|
range: range.dig(:start, :line)..range.dig(:end, :line),
|
591
587
|
)
|
592
|
-
dispatcher.visit(document.
|
588
|
+
dispatcher.visit(document.ast)
|
593
589
|
send_message(Result.new(id: message[:id], response: request.perform))
|
594
590
|
end
|
595
591
|
|
596
|
-
|
592
|
+
#: (Hash[Symbol, untyped] message) -> void
|
597
593
|
def text_document_range_formatting(message)
|
598
594
|
# If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
|
599
595
|
if @global_state.formatter == "none"
|
@@ -621,7 +617,7 @@ module RubyLsp
|
|
621
617
|
send_message(Result.new(id: message[:id], response: response))
|
622
618
|
end
|
623
619
|
|
624
|
-
|
620
|
+
#: (Hash[Symbol, untyped] message) -> void
|
625
621
|
def text_document_formatting(message)
|
626
622
|
# If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
|
627
623
|
if @global_state.formatter == "none"
|
@@ -662,10 +658,14 @@ module RubyLsp
|
|
662
658
|
"Formatting error: #{error.message}",
|
663
659
|
type: Constant::MessageType::ERROR,
|
664
660
|
))
|
661
|
+
send_message(Notification.window_log_message(
|
662
|
+
"Formatting failed with\r\n: #{error.full_message}",
|
663
|
+
type: Constant::MessageType::ERROR,
|
664
|
+
))
|
665
665
|
send_empty_response(message[:id])
|
666
666
|
end
|
667
667
|
|
668
|
-
|
668
|
+
#: (Hash[Symbol, untyped] message) -> void
|
669
669
|
def text_document_document_highlight(message)
|
670
670
|
params = message[:params]
|
671
671
|
dispatcher = Prism::Dispatcher.new
|
@@ -677,11 +677,11 @@ module RubyLsp
|
|
677
677
|
end
|
678
678
|
|
679
679
|
request = Requests::DocumentHighlight.new(@global_state, document, params[:position], dispatcher)
|
680
|
-
dispatcher.dispatch(document.
|
680
|
+
dispatcher.dispatch(document.ast)
|
681
681
|
send_message(Result.new(id: message[:id], response: request.perform))
|
682
682
|
end
|
683
683
|
|
684
|
-
|
684
|
+
#: (Hash[Symbol, untyped] message) -> void
|
685
685
|
def text_document_on_type_formatting(message)
|
686
686
|
params = message[:params]
|
687
687
|
document = @store.get(params.dig(:textDocument, :uri))
|
@@ -704,7 +704,7 @@ module RubyLsp
|
|
704
704
|
)
|
705
705
|
end
|
706
706
|
|
707
|
-
|
707
|
+
#: (Hash[Symbol, untyped] message) -> void
|
708
708
|
def text_document_hover(message)
|
709
709
|
params = message[:params]
|
710
710
|
dispatcher = Prism::Dispatcher.new
|
@@ -729,7 +729,7 @@ module RubyLsp
|
|
729
729
|
)
|
730
730
|
end
|
731
731
|
|
732
|
-
|
732
|
+
#: (Hash[Symbol, untyped] message) -> void
|
733
733
|
def text_document_rename(message)
|
734
734
|
params = message[:params]
|
735
735
|
document = @store.get(params.dig(:textDocument, :uri))
|
@@ -749,7 +749,7 @@ module RubyLsp
|
|
749
749
|
send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
|
750
750
|
end
|
751
751
|
|
752
|
-
|
752
|
+
#: (Hash[Symbol, untyped] message) -> void
|
753
753
|
def text_document_prepare_rename(message)
|
754
754
|
params = message[:params]
|
755
755
|
document = @store.get(params.dig(:textDocument, :uri))
|
@@ -767,7 +767,7 @@ module RubyLsp
|
|
767
767
|
)
|
768
768
|
end
|
769
769
|
|
770
|
-
|
770
|
+
#: (Hash[Symbol, untyped] message) -> void
|
771
771
|
def text_document_references(message)
|
772
772
|
params = message[:params]
|
773
773
|
document = @store.get(params.dig(:textDocument, :uri))
|
@@ -785,15 +785,19 @@ module RubyLsp
|
|
785
785
|
)
|
786
786
|
end
|
787
787
|
|
788
|
-
|
788
|
+
#: (Document[untyped] document) -> SorbetLevel
|
789
789
|
def sorbet_level(document)
|
790
|
-
return
|
791
|
-
return
|
790
|
+
return SorbetLevel.ignore unless document.is_a?(RubyDocument)
|
791
|
+
return SorbetLevel.ignore unless @global_state.has_type_checker
|
792
|
+
|
793
|
+
sigil = document.parse_result.magic_comments.find do |comment|
|
794
|
+
comment.key == "typed"
|
795
|
+
end&.value
|
792
796
|
|
793
|
-
|
797
|
+
SorbetLevel.new(sigil)
|
794
798
|
end
|
795
799
|
|
796
|
-
|
800
|
+
#: (Hash[Symbol, untyped] message) -> void
|
797
801
|
def text_document_inlay_hint(message)
|
798
802
|
params = message[:params]
|
799
803
|
document = @store.get(params.dig(:textDocument, :uri))
|
@@ -811,7 +815,6 @@ module RubyLsp
|
|
811
815
|
return
|
812
816
|
end
|
813
817
|
|
814
|
-
hints_configurations = T.must(@store.features_configuration.dig(:inlayHint))
|
815
818
|
dispatcher = Prism::Dispatcher.new
|
816
819
|
|
817
820
|
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
@@ -819,15 +822,15 @@ module RubyLsp
|
|
819
822
|
return
|
820
823
|
end
|
821
824
|
|
822
|
-
request = Requests::InlayHints.new(
|
823
|
-
dispatcher.visit(document.
|
825
|
+
request = Requests::InlayHints.new(@global_state, document, dispatcher)
|
826
|
+
dispatcher.visit(document.ast)
|
824
827
|
result = request.perform
|
825
828
|
document.cache_set("textDocument/inlayHint", result)
|
826
829
|
|
827
830
|
send_message(Result.new(id: message[:id], response: result.select { |hint| range.cover?(hint.position[:line]) }))
|
828
831
|
end
|
829
832
|
|
830
|
-
|
833
|
+
#: (Hash[Symbol, untyped] message) -> void
|
831
834
|
def text_document_code_action(message)
|
832
835
|
params = message[:params]
|
833
836
|
document = @store.get(params.dig(:textDocument, :uri))
|
@@ -849,7 +852,7 @@ module RubyLsp
|
|
849
852
|
)
|
850
853
|
end
|
851
854
|
|
852
|
-
|
855
|
+
#: (Hash[Symbol, untyped] message) -> void
|
853
856
|
def code_action_resolve(message)
|
854
857
|
params = message[:params]
|
855
858
|
uri = URI(params.dig(:data, :uri))
|
@@ -861,20 +864,12 @@ module RubyLsp
|
|
861
864
|
end
|
862
865
|
|
863
866
|
result = Requests::CodeActionResolve.new(document, @global_state, params).perform
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
fail_request_and_notify(message[:id], "Invalid selection for extract variable refactor")
|
868
|
-
when Requests::CodeActionResolve::Error::InvalidTargetRange
|
869
|
-
fail_request_and_notify(message[:id], "Couldn't find an appropriate location to place extracted refactor")
|
870
|
-
when Requests::CodeActionResolve::Error::UnknownCodeAction
|
871
|
-
fail_request_and_notify(message[:id], "Unknown code action")
|
872
|
-
else
|
873
|
-
send_message(Result.new(id: message[:id], response: result))
|
874
|
-
end
|
867
|
+
send_message(Result.new(id: message[:id], response: result))
|
868
|
+
rescue Requests::CodeActionResolve::CodeActionError => e
|
869
|
+
fail_request_and_notify(message[:id], e.message)
|
875
870
|
end
|
876
871
|
|
877
|
-
|
872
|
+
#: (Hash[Symbol, untyped] message) -> void
|
878
873
|
def text_document_diagnostic(message)
|
879
874
|
# Do not compute diagnostics for files outside of the workspace. For example, if someone is looking at a gem's
|
880
875
|
# source code, we don't want to show diagnostics for it
|
@@ -911,10 +906,14 @@ module RubyLsp
|
|
911
906
|
"Error running diagnostics: #{error.message}",
|
912
907
|
type: Constant::MessageType::ERROR,
|
913
908
|
))
|
909
|
+
send_message(Notification.window_log_message(
|
910
|
+
"Diagnostics failed with\r\n: #{error.full_message}",
|
911
|
+
type: Constant::MessageType::ERROR,
|
912
|
+
))
|
914
913
|
send_empty_response(message[:id])
|
915
914
|
end
|
916
915
|
|
917
|
-
|
916
|
+
#: (Hash[Symbol, untyped] message) -> void
|
918
917
|
def text_document_completion(message)
|
919
918
|
params = message[:params]
|
920
919
|
dispatcher = Prism::Dispatcher.new
|
@@ -939,7 +938,7 @@ module RubyLsp
|
|
939
938
|
)
|
940
939
|
end
|
941
940
|
|
942
|
-
|
941
|
+
#: (Hash[Symbol, untyped] message) -> void
|
943
942
|
def text_document_completion_item_resolve(message)
|
944
943
|
# When responding to a delegated completion request, it means we're handling a completion item that isn't related
|
945
944
|
# to Ruby (probably related to an ERB host language like HTML). We need to return the original completion item
|
@@ -958,7 +957,7 @@ module RubyLsp
|
|
958
957
|
))
|
959
958
|
end
|
960
959
|
|
961
|
-
|
960
|
+
#: (Hash[Symbol, untyped] message) -> void
|
962
961
|
def text_document_signature_help(message)
|
963
962
|
params = message[:params]
|
964
963
|
dispatcher = Prism::Dispatcher.new
|
@@ -984,7 +983,7 @@ module RubyLsp
|
|
984
983
|
)
|
985
984
|
end
|
986
985
|
|
987
|
-
|
986
|
+
#: (Hash[Symbol, untyped] message) -> void
|
988
987
|
def text_document_definition(message)
|
989
988
|
params = message[:params]
|
990
989
|
dispatcher = Prism::Dispatcher.new
|
@@ -1009,8 +1008,23 @@ module RubyLsp
|
|
1009
1008
|
)
|
1010
1009
|
end
|
1011
1010
|
|
1012
|
-
|
1011
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1013
1012
|
def workspace_did_change_watched_files(message)
|
1013
|
+
# If indexing is not complete yet, delay processing did change watched file notifications. We need initial
|
1014
|
+
# indexing to be in place so that we can handle file changes appropriately without risking duplicates. We also
|
1015
|
+
# have to sleep before re-inserting the notification in the queue otherwise the worker can get stuck in its own
|
1016
|
+
# loop of pushing and popping the same notification
|
1017
|
+
unless @global_state.index.initial_indexing_completed
|
1018
|
+
Thread.new do
|
1019
|
+
sleep(2)
|
1020
|
+
# We have to ensure that the queue is not closed yet, since nothing stops the user from saving a file and then
|
1021
|
+
# immediately telling the LSP to shutdown
|
1022
|
+
@incoming_queue << message unless @incoming_queue.closed?
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
return
|
1026
|
+
end
|
1027
|
+
|
1014
1028
|
changes = message.dig(:params, :changes)
|
1015
1029
|
# We allow add-ons to register for watching files and we have no restrictions for what they register for. If the
|
1016
1030
|
# same pattern is registered more than once, the LSP will receive duplicate change notifications. Receiving them
|
@@ -1037,7 +1051,8 @@ module RubyLsp
|
|
1037
1051
|
end
|
1038
1052
|
|
1039
1053
|
Addon.file_watcher_addons.each do |addon|
|
1040
|
-
|
1054
|
+
addon #: as untyped
|
1055
|
+
.workspace_did_change_watched_files(changes)
|
1041
1056
|
rescue => e
|
1042
1057
|
send_log_message(
|
1043
1058
|
"Error in #{addon.name} add-on while processing watched file notifications: #{e.full_message}",
|
@@ -1046,32 +1061,34 @@ module RubyLsp
|
|
1046
1061
|
end
|
1047
1062
|
end
|
1048
1063
|
|
1049
|
-
|
1064
|
+
#: (RubyIndexer::Index index, String file_path, Integer change_type) -> void
|
1050
1065
|
def handle_ruby_file_change(index, file_path, change_type)
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1066
|
+
@global_state.synchronize do
|
1067
|
+
load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
|
1068
|
+
uri = URI::Generic.from_path(load_path_entry: load_path_entry, path: file_path)
|
1069
|
+
|
1070
|
+
case change_type
|
1071
|
+
when Constant::FileChangeType::CREATED
|
1072
|
+
content = File.read(file_path)
|
1073
|
+
# If we receive a late created notification for a file that has already been claimed by the client, we want to
|
1074
|
+
# handle change for that URI so that the require path tree is updated
|
1075
|
+
@store.key?(uri) ? index.handle_change(uri, content) : index.index_single(uri, content)
|
1076
|
+
when Constant::FileChangeType::CHANGED
|
1077
|
+
content = File.read(file_path)
|
1078
|
+
# We only handle changes on file watched notifications if the client is not the one managing this URI.
|
1079
|
+
# Otherwise, these changes are handled when running the combined requests
|
1080
|
+
index.handle_change(uri, content) unless @store.key?(uri)
|
1081
|
+
when Constant::FileChangeType::DELETED
|
1082
|
+
index.delete(uri)
|
1083
|
+
end
|
1084
|
+
rescue Errno::ENOENT
|
1085
|
+
# If a file is created and then delete immediately afterwards, we will process the created notification before
|
1086
|
+
# we receive the deleted one, but the file no longer exists. This may happen when running a test suite that
|
1087
|
+
# creates and deletes files automatically.
|
1088
|
+
end
|
1072
1089
|
end
|
1073
1090
|
|
1074
|
-
|
1091
|
+
#: (URI::Generic uri) -> void
|
1075
1092
|
def handle_rubocop_config_change(uri)
|
1076
1093
|
return unless defined?(Requests::Support::RuboCopFormatter)
|
1077
1094
|
|
@@ -1079,11 +1096,7 @@ module RubyLsp
|
|
1079
1096
|
@global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
|
1080
1097
|
|
1081
1098
|
# Clear all document caches for pull diagnostics
|
1082
|
-
@
|
1083
|
-
@store.each do |_uri, document|
|
1084
|
-
document.cache_set("textDocument/diagnostic", Document::EMPTY_CACHE)
|
1085
|
-
end
|
1086
|
-
end
|
1099
|
+
@store.each { |_uri, document| document.clear_cache("textDocument/diagnostic") }
|
1087
1100
|
|
1088
1101
|
# Request a pull diagnostic refresh from the editor
|
1089
1102
|
if @global_state.client_capabilities.supports_diagnostic_refresh
|
@@ -1091,7 +1104,7 @@ module RubyLsp
|
|
1091
1104
|
end
|
1092
1105
|
end
|
1093
1106
|
|
1094
|
-
|
1107
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1095
1108
|
def workspace_symbol(message)
|
1096
1109
|
send_message(
|
1097
1110
|
Result.new(
|
@@ -1104,7 +1117,7 @@ module RubyLsp
|
|
1104
1117
|
)
|
1105
1118
|
end
|
1106
1119
|
|
1107
|
-
|
1120
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1108
1121
|
def text_document_show_syntax_tree(message)
|
1109
1122
|
params = message[:params]
|
1110
1123
|
document = @store.get(params.dig(:textDocument, :uri))
|
@@ -1123,7 +1136,26 @@ module RubyLsp
|
|
1123
1136
|
send_message(Result.new(id: message[:id], response: response))
|
1124
1137
|
end
|
1125
1138
|
|
1126
|
-
|
1139
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1140
|
+
def experimental_go_to_relevant_file(message)
|
1141
|
+
path = message.dig(:params, :textDocument, :uri).to_standardized_path
|
1142
|
+
unless path.nil? || path.start_with?(@global_state.workspace_path)
|
1143
|
+
send_empty_response(message[:id])
|
1144
|
+
return
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
unless path
|
1148
|
+
send_empty_response(message[:id])
|
1149
|
+
return
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
response = {
|
1153
|
+
locations: Requests::GoToRelevantFile.new(path, @global_state.workspace_path).perform,
|
1154
|
+
}
|
1155
|
+
send_message(Result.new(id: message[:id], response: response))
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1127
1159
|
def text_document_prepare_type_hierarchy(message)
|
1128
1160
|
params = message[:params]
|
1129
1161
|
document = @store.get(params.dig(:textDocument, :uri))
|
@@ -1142,7 +1174,7 @@ module RubyLsp
|
|
1142
1174
|
send_message(Result.new(id: message[:id], response: response))
|
1143
1175
|
end
|
1144
1176
|
|
1145
|
-
|
1177
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1146
1178
|
def type_hierarchy_supertypes(message)
|
1147
1179
|
response = Requests::TypeHierarchySupertypes.new(
|
1148
1180
|
@global_state.index,
|
@@ -1151,14 +1183,14 @@ module RubyLsp
|
|
1151
1183
|
send_message(Result.new(id: message[:id], response: response))
|
1152
1184
|
end
|
1153
1185
|
|
1154
|
-
|
1186
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1155
1187
|
def type_hierarchy_subtypes(message)
|
1156
1188
|
# TODO: implement subtypes
|
1157
1189
|
# The current index representation doesn't allow us to find the children of an entry.
|
1158
1190
|
send_message(Result.new(id: message[:id], response: nil))
|
1159
1191
|
end
|
1160
1192
|
|
1161
|
-
|
1193
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1162
1194
|
def workspace_dependencies(message)
|
1163
1195
|
unless @global_state.top_level_bundle
|
1164
1196
|
send_message(Result.new(id: message[:id], response: []))
|
@@ -1179,19 +1211,20 @@ module RubyLsp
|
|
1179
1211
|
}
|
1180
1212
|
end
|
1181
1213
|
end
|
1182
|
-
rescue Bundler::GemNotFound
|
1214
|
+
rescue Bundler::GemNotFound, Bundler::GemfileNotFound
|
1183
1215
|
[]
|
1184
1216
|
end
|
1185
1217
|
|
1186
1218
|
send_message(Result.new(id: message[:id], response: response))
|
1187
1219
|
end
|
1188
1220
|
|
1189
|
-
|
1221
|
+
# @override
|
1222
|
+
#: -> void
|
1190
1223
|
def shutdown
|
1191
1224
|
Addon.unload_addons
|
1192
1225
|
end
|
1193
1226
|
|
1194
|
-
|
1227
|
+
#: -> void
|
1195
1228
|
def perform_initial_indexing
|
1196
1229
|
# The begin progress invocation happens during `initialize`, so that the notification is sent before we are
|
1197
1230
|
# stuck indexing files
|
@@ -1217,13 +1250,29 @@ module RubyLsp
|
|
1217
1250
|
# allocations and garbage collections are faster
|
1218
1251
|
GC.compact unless @test_mode
|
1219
1252
|
|
1253
|
+
@global_state.synchronize do
|
1254
|
+
# If we linearize ancestors while the index is not fully populated, we may end up caching incorrect results
|
1255
|
+
# that were missing namespaces. After indexing is complete, we need to clear the ancestors cache and start
|
1256
|
+
# again
|
1257
|
+
@global_state.index.clear_ancestors
|
1258
|
+
|
1259
|
+
# The results for code lens depend on ancestor linearization, so we need to clear any previously computed
|
1260
|
+
# responses
|
1261
|
+
@store.each { |_uri, document| document.clear_cache("textDocument/codeLens") }
|
1262
|
+
end
|
1263
|
+
|
1220
1264
|
# Always end the progress notification even if indexing failed or else it never goes away and the user has no
|
1221
1265
|
# way of dismissing it
|
1222
1266
|
end_progress("indexing-progress")
|
1267
|
+
|
1268
|
+
# Request a code lens refresh if we populated them before all test parent classes were indexed
|
1269
|
+
if @global_state.client_capabilities.supports_code_lens_refresh
|
1270
|
+
send_message(Request.new(id: @current_request_id, method: "workspace/codeLens/refresh", params: nil))
|
1271
|
+
end
|
1223
1272
|
end
|
1224
1273
|
end
|
1225
1274
|
|
1226
|
-
|
1275
|
+
#: (String id, String title, ?percentage: Integer) -> void
|
1227
1276
|
def begin_progress(id, title, percentage: 0)
|
1228
1277
|
return unless @global_state.client_capabilities.supports_progress
|
1229
1278
|
|
@@ -1236,14 +1285,14 @@ module RubyLsp
|
|
1236
1285
|
send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
|
1237
1286
|
end
|
1238
1287
|
|
1239
|
-
|
1288
|
+
#: (String id, Integer percentage) -> void
|
1240
1289
|
def progress(id, percentage)
|
1241
1290
|
return unless @global_state.client_capabilities.supports_progress
|
1242
1291
|
|
1243
1292
|
send_message(Notification.progress_report(id, percentage: percentage, message: "#{percentage}% completed"))
|
1244
1293
|
end
|
1245
1294
|
|
1246
|
-
|
1295
|
+
#: (String id) -> void
|
1247
1296
|
def end_progress(id)
|
1248
1297
|
return unless @global_state.client_capabilities.supports_progress
|
1249
1298
|
|
@@ -1253,7 +1302,7 @@ module RubyLsp
|
|
1253
1302
|
# notification
|
1254
1303
|
end
|
1255
1304
|
|
1256
|
-
|
1305
|
+
#: -> void
|
1257
1306
|
def check_formatter_is_available
|
1258
1307
|
return if @setup_error
|
1259
1308
|
# Warn of an unavailable `formatter` setting, e.g. `rubocop_internal` on a project which doesn't have RuboCop.
|
@@ -1271,7 +1320,7 @@ module RubyLsp
|
|
1271
1320
|
end
|
1272
1321
|
end
|
1273
1322
|
|
1274
|
-
|
1323
|
+
#: (Hash[Symbol, untyped]? indexing_options) -> void
|
1275
1324
|
def process_indexing_configuration(indexing_options)
|
1276
1325
|
# Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
|
1277
1326
|
index_path = File.join(@global_state.workspace_path, ".index.yml")
|
@@ -1312,7 +1361,7 @@ module RubyLsp
|
|
1312
1361
|
configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
|
1313
1362
|
end
|
1314
1363
|
|
1315
|
-
|
1364
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1316
1365
|
def window_show_message_request(message)
|
1317
1366
|
result = message[:result]
|
1318
1367
|
return unless result
|
@@ -1327,7 +1376,7 @@ module RubyLsp
|
|
1327
1376
|
# NOTE: all servers methods are void because they can produce several messages for the client. The only reason this
|
1328
1377
|
# method returns the created thread is to that we can join it in tests and avoid flakiness. The implementation is
|
1329
1378
|
# not supposed to rely on the return of this method
|
1330
|
-
|
1379
|
+
#: (Hash[Symbol, untyped] message) -> Thread?
|
1331
1380
|
def compose_bundle(message)
|
1332
1381
|
already_composed_path = File.join(@global_state.workspace_path, ".ruby-lsp", "bundle_is_composed")
|
1333
1382
|
id = message[:id]
|
@@ -1345,36 +1394,58 @@ module RubyLsp
|
|
1345
1394
|
|
1346
1395
|
# We compose the bundle in a thread so that the LSP continues to work while we're checking for its validity. Once
|
1347
1396
|
# we return the response back to the editor, then the restart is triggered
|
1397
|
+
launch_bundle_compose("Recomposing the bundle ahead of restart") do |stderr, status|
|
1398
|
+
if status&.exitstatus == 0
|
1399
|
+
# Create a signal for the restart that it can skip composing the bundle and launch directly
|
1400
|
+
FileUtils.touch(already_composed_path)
|
1401
|
+
send_message(Result.new(id: id, response: { success: true }))
|
1402
|
+
else
|
1403
|
+
# This special error code makes the extension avoid restarting in case we already know that the composed
|
1404
|
+
# bundle is not valid
|
1405
|
+
send_message(
|
1406
|
+
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
|
1407
|
+
)
|
1408
|
+
end
|
1409
|
+
end
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
#: -> void
|
1413
|
+
def update_server
|
1414
|
+
return unless File.exist?(File.join(@global_state.workspace_path, ".ruby-lsp", "needs_update"))
|
1415
|
+
|
1416
|
+
launch_bundle_compose("Trying to update server") do |stderr, status|
|
1417
|
+
if status&.exitstatus == 0
|
1418
|
+
send_log_message("Successfully updated the server")
|
1419
|
+
else
|
1420
|
+
send_log_message("Failed to update server\n#{stderr}", type: Constant::MessageType::ERROR)
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
#: (String) { (IO, Process::Status?) -> void } -> Thread
|
1426
|
+
def launch_bundle_compose(log, &block)
|
1348
1427
|
Thread.new do
|
1349
|
-
send_log_message(
|
1428
|
+
send_log_message(log)
|
1350
1429
|
|
1351
1430
|
_stdout, stderr, status = Bundler.with_unbundled_env do
|
1352
1431
|
Open3.capture3(
|
1353
1432
|
Gem.ruby,
|
1354
1433
|
"-I",
|
1355
|
-
File.dirname(
|
1434
|
+
File.dirname(
|
1435
|
+
__dir__, #: as !nil
|
1436
|
+
),
|
1356
1437
|
File.expand_path("../../exe/ruby-lsp-launcher", __dir__),
|
1357
1438
|
@global_state.workspace_uri.to_s,
|
1358
1439
|
chdir: @global_state.workspace_path,
|
1359
1440
|
)
|
1360
1441
|
end
|
1361
1442
|
|
1362
|
-
|
1363
|
-
# Create a signal for the restart that it can skip composing the bundle and launch directly
|
1364
|
-
FileUtils.touch(already_composed_path)
|
1365
|
-
send_message(Result.new(id: id, response: { success: true }))
|
1366
|
-
else
|
1367
|
-
# This special error code makes the extension avoid restarting in case we already know that the composed
|
1368
|
-
# bundle is not valid
|
1369
|
-
send_message(
|
1370
|
-
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
|
1371
|
-
)
|
1372
|
-
end
|
1443
|
+
block.call(stderr, status)
|
1373
1444
|
end
|
1374
1445
|
end
|
1375
1446
|
|
1376
1447
|
# Returns internal state information for debugging purposes
|
1377
|
-
|
1448
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1378
1449
|
def diagnose_state(message)
|
1379
1450
|
documents = {}
|
1380
1451
|
@store.each { |uri, document| documents[uri] = document.source }
|
@@ -1394,7 +1465,7 @@ module RubyLsp
|
|
1394
1465
|
|
1395
1466
|
# Discovers all available test groups and examples in a given file taking into consideration the merged response of
|
1396
1467
|
# all add-ons
|
1397
|
-
|
1468
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1398
1469
|
def discover_tests(message)
|
1399
1470
|
document = @store.get(message.dig(:params, :textDocument, :uri))
|
1400
1471
|
|
@@ -1414,5 +1485,41 @@ module RubyLsp
|
|
1414
1485
|
|
1415
1486
|
send_message(Result.new(id: message[:id], response: items.map(&:to_hash)))
|
1416
1487
|
end
|
1488
|
+
|
1489
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1490
|
+
def resolve_test_commands(message)
|
1491
|
+
items = message.dig(:params, :items)
|
1492
|
+
commands = Listeners::TestStyle.resolve_test_commands(items)
|
1493
|
+
|
1494
|
+
Addon.addons.each do |addon|
|
1495
|
+
commands.concat(addon.resolve_test_commands(items))
|
1496
|
+
end
|
1497
|
+
|
1498
|
+
send_message(Result.new(
|
1499
|
+
id: message[:id],
|
1500
|
+
response: { commands: commands },
|
1501
|
+
))
|
1502
|
+
end
|
1503
|
+
|
1504
|
+
#: (Hash[Symbol, untyped] message) -> void
|
1505
|
+
def code_lens_resolve(message)
|
1506
|
+
code_lens = message[:params]
|
1507
|
+
args = code_lens.dig(:data, :arguments)
|
1508
|
+
|
1509
|
+
case code_lens.dig(:data, :kind)
|
1510
|
+
when "run_test"
|
1511
|
+
code_lens[:command] = Interface::Command.new(title: "▶ Run", command: "rubyLsp.runTest", arguments: args)
|
1512
|
+
when "run_test_in_terminal"
|
1513
|
+
code_lens[:command] =
|
1514
|
+
Interface::Command.new(title: "▶ Run in terminal", command: "rubyLsp.runTestInTerminal", arguments: args)
|
1515
|
+
when "debug_test"
|
1516
|
+
code_lens[:command] = Interface::Command.new(title: "⚙ Debug", command: "rubyLsp.debugTest", arguments: args)
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
send_message(Result.new(
|
1520
|
+
id: message[:id],
|
1521
|
+
response: code_lens,
|
1522
|
+
))
|
1523
|
+
end
|
1417
1524
|
end
|
1418
1525
|
end
|