ruby-lsp 0.23.23 → 0.26.0
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/VERSION +1 -1
- data/exe/ruby-lsp +10 -4
- data/exe/ruby-lsp-check +0 -4
- data/exe/ruby-lsp-launcher +25 -11
- data/exe/ruby-lsp-test-exec +3 -15
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +0 -1
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +0 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +7 -1
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +1 -4
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +10 -19
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +27 -7
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +12 -8
- data/lib/ruby_indexer/test/configuration_test.rb +1 -2
- data/lib/ruby_indexer/test/index_test.rb +36 -0
- data/lib/ruby_indexer/test/instance_variables_test.rb +24 -0
- data/lib/ruby_indexer/test/method_test.rb +17 -0
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +2 -2
- data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
- data/lib/ruby_lsp/addon.rb +44 -15
- data/lib/ruby_lsp/base_server.rb +34 -26
- data/lib/ruby_lsp/document.rb +162 -52
- data/lib/ruby_lsp/erb_document.rb +8 -3
- data/lib/ruby_lsp/global_state.rb +21 -0
- data/lib/ruby_lsp/internal.rb +0 -2
- data/lib/ruby_lsp/listeners/completion.rb +14 -3
- data/lib/ruby_lsp/listeners/hover.rb +7 -0
- data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
- data/lib/ruby_lsp/listeners/spec_style.rb +7 -8
- data/lib/ruby_lsp/listeners/test_discovery.rb +18 -15
- data/lib/ruby_lsp/listeners/test_style.rb +14 -13
- data/lib/ruby_lsp/requests/code_action_resolve.rb +3 -3
- data/lib/ruby_lsp/requests/code_lens.rb +9 -3
- data/lib/ruby_lsp/requests/completion.rb +1 -1
- data/lib/ruby_lsp/requests/definition.rb +1 -1
- data/lib/ruby_lsp/requests/discover_tests.rb +2 -2
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +1 -1
- data/lib/ruby_lsp/requests/inlay_hints.rb +3 -3
- data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
- data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
- data/lib/ruby_lsp/requests/references.rb +10 -6
- data/lib/ruby_lsp/requests/rename.rb +8 -6
- data/lib/ruby_lsp/requests/request.rb +6 -7
- data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -1
- data/lib/ruby_lsp/requests/signature_help.rb +1 -1
- data/lib/ruby_lsp/requests/support/common.rb +1 -3
- data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -3
- data/lib/ruby_lsp/response_builders/response_builder.rb +6 -8
- data/lib/ruby_lsp/ruby_document.rb +10 -5
- data/lib/ruby_lsp/server.rb +89 -108
- data/lib/ruby_lsp/setup_bundler.rb +59 -25
- data/lib/ruby_lsp/static_docs.rb +1 -0
- data/lib/ruby_lsp/store.rb +0 -10
- data/lib/ruby_lsp/test_helper.rb +1 -4
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +13 -8
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +17 -4
- data/lib/ruby_lsp/utils.rb +47 -11
- data/static_docs/break.md +103 -0
- metadata +2 -16
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
@@ -2,7 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
|
-
#: [ParseResultType = Prism::
|
5
|
+
#: [ParseResultType = Prism::ParseLexResult]
|
6
6
|
class RubyDocument < Document
|
7
7
|
METHODS_THAT_CHANGE_DECLARATIONS = [
|
8
8
|
:private_constant,
|
@@ -26,7 +26,7 @@ module RubyLsp
|
|
26
26
|
queue = node.child_nodes.compact #: Array[Prism::Node?]
|
27
27
|
closest = node
|
28
28
|
parent = nil #: Prism::Node?
|
29
|
-
nesting_nodes = [] #: Array[(Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode | Prism::DefNode | Prism::BlockNode | Prism::LambdaNode | Prism::ProgramNode)]
|
29
|
+
nesting_nodes = [] #: Array[(Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode | Prism::DefNode | Prism::BlockNode | Prism::LambdaNode | Prism::ProgramNode)]
|
30
30
|
|
31
31
|
nesting_nodes << node if node.is_a?(Prism::ProgramNode)
|
32
32
|
call_node = nil #: Prism::CallNode?
|
@@ -129,11 +129,16 @@ module RubyLsp
|
|
129
129
|
return false unless @needs_parsing
|
130
130
|
|
131
131
|
@needs_parsing = false
|
132
|
-
@parse_result = Prism.
|
132
|
+
@parse_result = Prism.parse_lex(@source)
|
133
133
|
@code_units_cache = @parse_result.code_units_cache(@encoding)
|
134
134
|
true
|
135
135
|
end
|
136
136
|
|
137
|
+
#: -> Prism::ProgramNode
|
138
|
+
def ast
|
139
|
+
@parse_result.value.first
|
140
|
+
end
|
141
|
+
|
137
142
|
# @override
|
138
143
|
#: -> bool
|
139
144
|
def syntax_error?
|
@@ -151,7 +156,7 @@ module RubyLsp
|
|
151
156
|
start_position, end_position = find_index_by_position(range[:start], range[:end])
|
152
157
|
|
153
158
|
desired_range = (start_position...end_position)
|
154
|
-
queue =
|
159
|
+
queue = ast.child_nodes.compact #: Array[Prism::Node?]
|
155
160
|
|
156
161
|
until queue.empty?
|
157
162
|
candidate = queue.shift
|
@@ -179,7 +184,7 @@ module RubyLsp
|
|
179
184
|
char_position, _ = find_index_by_position(position)
|
180
185
|
|
181
186
|
RubyDocument.locate(
|
182
|
-
|
187
|
+
ast,
|
183
188
|
char_position,
|
184
189
|
code_units_cache: @code_units_cache,
|
185
190
|
node_types: node_types,
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -94,13 +94,10 @@ module RubyLsp
|
|
94
94
|
id: message[:id],
|
95
95
|
response:
|
96
96
|
Addon.addons.map do |addon|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
# Therefore, we only call the method if it's defined by the add-on itself
|
102
|
-
if version_method.owner != Addon
|
103
|
-
version = addon.version
|
97
|
+
version = begin
|
98
|
+
addon.version
|
99
|
+
rescue AbstractMethodInvokedError
|
100
|
+
nil
|
104
101
|
end
|
105
102
|
|
106
103
|
{ name: addon.name, version: version, errored: addon.error? }
|
@@ -124,30 +121,18 @@ module RubyLsp
|
|
124
121
|
end
|
125
122
|
rescue DelegateRequestError
|
126
123
|
send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
|
127
|
-
rescue StandardError, LoadError => e
|
124
|
+
rescue StandardError, LoadError, SystemExit => e
|
128
125
|
# If an error occurred in a request, we have to return an error response or else the editor will hang
|
129
126
|
if message[:id]
|
130
127
|
# If a document is deleted before we are able to process all of its enqueued requests, we will try to read it
|
131
128
|
# from disk and it raise this error. This is expected, so we don't include the `data` attribute to avoid
|
132
129
|
# reporting these to our telemetry
|
133
|
-
|
134
|
-
when Store::NonExistingDocumentError
|
130
|
+
if e.is_a?(Store::NonExistingDocumentError)
|
135
131
|
send_message(Error.new(
|
136
132
|
id: message[:id],
|
137
133
|
code: Constant::ErrorCodes::INVALID_PARAMS,
|
138
134
|
message: e.full_message,
|
139
135
|
))
|
140
|
-
when Document::LocationNotFoundError
|
141
|
-
send_message(Error.new(
|
142
|
-
id: message[:id],
|
143
|
-
code: Constant::ErrorCodes::REQUEST_FAILED,
|
144
|
-
message: <<~MESSAGE,
|
145
|
-
Request #{message[:method]} failed to find the target position.
|
146
|
-
The file might have been modified while the server was in the middle of searching for the target.
|
147
|
-
If you experience this regularly, please report any findings and extra information on
|
148
|
-
https://github.com/Shopify/ruby-lsp/issues/2446
|
149
|
-
MESSAGE
|
150
|
-
))
|
151
136
|
else
|
152
137
|
send_message(Error.new(
|
153
138
|
id: message[:id],
|
@@ -182,6 +167,7 @@ module RubyLsp
|
|
182
167
|
return if @setup_error
|
183
168
|
|
184
169
|
errors = Addon.load_addons(@global_state, @outgoing_queue, include_project_addons: include_project_addons)
|
170
|
+
return if test_mode?
|
185
171
|
|
186
172
|
if errors.any?
|
187
173
|
send_log_message(
|
@@ -194,21 +180,13 @@ module RubyLsp
|
|
194
180
|
|
195
181
|
if errored_addons.any?
|
196
182
|
send_message(
|
197
|
-
Notification.
|
198
|
-
|
199
|
-
|
200
|
-
type: Constant::MessageType::WARNING,
|
201
|
-
message: "Error loading add-ons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
|
202
|
-
),
|
183
|
+
Notification.window_show_message(
|
184
|
+
"Error loading add-ons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
|
185
|
+
type: Constant::MessageType::WARNING,
|
203
186
|
),
|
204
187
|
)
|
205
188
|
|
206
|
-
|
207
|
-
send_log_message(
|
208
|
-
errored_addons.map(&:errors_details).join("\n\n"),
|
209
|
-
type: Constant::MessageType::WARNING,
|
210
|
-
)
|
211
|
-
end
|
189
|
+
send_log_message(errored_addons.map(&:errors_details).join("\n\n"), type: Constant::MessageType::WARNING)
|
212
190
|
end
|
213
191
|
end
|
214
192
|
|
@@ -224,10 +202,6 @@ module RubyLsp
|
|
224
202
|
|
225
203
|
configured_features = options.dig(:initializationOptions, :enabledFeatures)
|
226
204
|
|
227
|
-
configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
|
228
|
-
@store.features_configuration.dig(:inlayHint) #: as !nil
|
229
|
-
.configuration.merge!(configured_hints) if configured_hints
|
230
|
-
|
231
205
|
enabled_features = case configured_features
|
232
206
|
when Array
|
233
207
|
# If the configuration is using an array, then absent features are disabled and present ones are enabled. That's
|
@@ -381,56 +355,53 @@ module RubyLsp
|
|
381
355
|
|
382
356
|
perform_initial_indexing
|
383
357
|
check_formatter_is_available
|
358
|
+
update_server if @global_state.enabled_feature?(:launcher)
|
384
359
|
end
|
385
360
|
|
386
361
|
#: (Hash[Symbol, untyped] message) -> void
|
387
362
|
def text_document_did_open(message)
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
end
|
363
|
+
text_document = message.dig(:params, :textDocument)
|
364
|
+
language_id = case text_document[:languageId]
|
365
|
+
when "erb", "eruby"
|
366
|
+
:erb
|
367
|
+
when "rbs"
|
368
|
+
:rbs
|
369
|
+
else
|
370
|
+
:ruby
|
371
|
+
end
|
398
372
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
373
|
+
document = @store.set(
|
374
|
+
uri: text_document[:uri],
|
375
|
+
source: text_document[:text],
|
376
|
+
version: text_document[:version],
|
377
|
+
language_id: language_id,
|
378
|
+
)
|
405
379
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
380
|
+
if document.past_expensive_limit? && text_document[:uri].scheme == "file"
|
381
|
+
log_message = <<~MESSAGE
|
382
|
+
The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
|
383
|
+
diagnostics will be disabled.
|
384
|
+
MESSAGE
|
411
385
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
),
|
386
|
+
send_message(
|
387
|
+
Notification.new(
|
388
|
+
method: "window/logMessage",
|
389
|
+
params: Interface::LogMessageParams.new(
|
390
|
+
type: Constant::MessageType::WARNING,
|
391
|
+
message: log_message,
|
419
392
|
),
|
420
|
-
)
|
421
|
-
|
393
|
+
),
|
394
|
+
)
|
422
395
|
end
|
423
396
|
end
|
424
397
|
|
425
398
|
#: (Hash[Symbol, untyped] message) -> void
|
426
399
|
def text_document_did_close(message)
|
427
|
-
|
428
|
-
|
429
|
-
@store.delete(uri)
|
400
|
+
uri = message.dig(:params, :textDocument, :uri)
|
401
|
+
@store.delete(uri)
|
430
402
|
|
431
|
-
|
432
|
-
|
433
|
-
end
|
403
|
+
# Clear diagnostics for the closed file, so that they no longer appear in the problems tab
|
404
|
+
send_message(Notification.publish_diagnostics(uri.to_s, []))
|
434
405
|
end
|
435
406
|
|
436
407
|
#: (Hash[Symbol, untyped] message) -> void
|
@@ -438,9 +409,7 @@ module RubyLsp
|
|
438
409
|
params = message[:params]
|
439
410
|
text_document = params[:textDocument]
|
440
411
|
|
441
|
-
@
|
442
|
-
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
|
443
|
-
end
|
412
|
+
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
|
444
413
|
end
|
445
414
|
|
446
415
|
#: (Hash[Symbol, untyped] message) -> void
|
@@ -494,8 +463,8 @@ module RubyLsp
|
|
494
463
|
document_symbol = Requests::DocumentSymbol.new(uri, dispatcher)
|
495
464
|
document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
|
496
465
|
inlay_hint = Requests::InlayHints.new(
|
466
|
+
@global_state,
|
497
467
|
document,
|
498
|
-
@store.features_configuration.dig(:inlayHint), #: as !nil
|
499
468
|
dispatcher,
|
500
469
|
)
|
501
470
|
|
@@ -513,12 +482,12 @@ module RubyLsp
|
|
513
482
|
index.delete(uri, skip_require_paths_tree: true)
|
514
483
|
RubyIndexer::DeclarationListener.new(index, dispatcher, parse_result, uri, collect_comments: true)
|
515
484
|
code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
|
516
|
-
dispatcher.dispatch(
|
485
|
+
dispatcher.dispatch(document.ast)
|
517
486
|
end
|
518
487
|
end
|
519
488
|
else
|
520
489
|
code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
|
521
|
-
dispatcher.dispatch(
|
490
|
+
dispatcher.dispatch(document.ast)
|
522
491
|
end
|
523
492
|
|
524
493
|
# Store all responses retrieve in this round of visits in the cache and then return the response for the request
|
@@ -557,7 +526,7 @@ module RubyLsp
|
|
557
526
|
|
558
527
|
dispatcher = Prism::Dispatcher.new
|
559
528
|
semantic_highlighting = Requests::SemanticHighlighting.new(@global_state, dispatcher, document, nil)
|
560
|
-
dispatcher.visit(document.
|
529
|
+
dispatcher.visit(document.ast)
|
561
530
|
|
562
531
|
send_message(Result.new(id: message[:id], response: semantic_highlighting.perform))
|
563
532
|
end
|
@@ -583,7 +552,7 @@ module RubyLsp
|
|
583
552
|
document,
|
584
553
|
message.dig(:params, :previousResultId),
|
585
554
|
)
|
586
|
-
dispatcher.visit(document.
|
555
|
+
dispatcher.visit(document.ast)
|
587
556
|
send_message(Result.new(id: message[:id], response: request.perform))
|
588
557
|
end
|
589
558
|
|
@@ -612,7 +581,7 @@ module RubyLsp
|
|
612
581
|
nil,
|
613
582
|
range: range.dig(:start, :line)..range.dig(:end, :line),
|
614
583
|
)
|
615
|
-
dispatcher.visit(document.
|
584
|
+
dispatcher.visit(document.ast)
|
616
585
|
send_message(Result.new(id: message[:id], response: request.perform))
|
617
586
|
end
|
618
587
|
|
@@ -704,7 +673,7 @@ module RubyLsp
|
|
704
673
|
end
|
705
674
|
|
706
675
|
request = Requests::DocumentHighlight.new(@global_state, document, params[:position], dispatcher)
|
707
|
-
dispatcher.dispatch(document.
|
676
|
+
dispatcher.dispatch(document.ast)
|
708
677
|
send_message(Result.new(id: message[:id], response: request.perform))
|
709
678
|
end
|
710
679
|
|
@@ -842,7 +811,6 @@ module RubyLsp
|
|
842
811
|
return
|
843
812
|
end
|
844
813
|
|
845
|
-
hints_configurations = @store.features_configuration.dig(:inlayHint) #: as !nil
|
846
814
|
dispatcher = Prism::Dispatcher.new
|
847
815
|
|
848
816
|
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
@@ -850,8 +818,8 @@ module RubyLsp
|
|
850
818
|
return
|
851
819
|
end
|
852
820
|
|
853
|
-
request = Requests::InlayHints.new(
|
854
|
-
dispatcher.visit(document.
|
821
|
+
request = Requests::InlayHints.new(@global_state, document, dispatcher)
|
822
|
+
dispatcher.visit(document.ast)
|
855
823
|
result = request.perform
|
856
824
|
document.cache_set("textDocument/inlayHint", result)
|
857
825
|
|
@@ -1124,11 +1092,7 @@ module RubyLsp
|
|
1124
1092
|
@global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
|
1125
1093
|
|
1126
1094
|
# Clear all document caches for pull diagnostics
|
1127
|
-
@
|
1128
|
-
@store.each do |_uri, document|
|
1129
|
-
document.clear_cache("textDocument/diagnostic")
|
1130
|
-
end
|
1131
|
-
end
|
1095
|
+
@store.each { |_uri, document| document.clear_cache("textDocument/diagnostic") }
|
1132
1096
|
|
1133
1097
|
# Request a pull diagnostic refresh from the editor
|
1134
1098
|
if @global_state.client_capabilities.supports_diagnostic_refresh
|
@@ -1243,7 +1207,7 @@ module RubyLsp
|
|
1243
1207
|
}
|
1244
1208
|
end
|
1245
1209
|
end
|
1246
|
-
rescue Bundler::GemNotFound
|
1210
|
+
rescue Bundler::GemNotFound, Bundler::GemfileNotFound
|
1247
1211
|
[]
|
1248
1212
|
end
|
1249
1213
|
|
@@ -1426,8 +1390,38 @@ module RubyLsp
|
|
1426
1390
|
|
1427
1391
|
# We compose the bundle in a thread so that the LSP continues to work while we're checking for its validity. Once
|
1428
1392
|
# we return the response back to the editor, then the restart is triggered
|
1393
|
+
launch_bundle_compose("Recomposing the bundle ahead of restart") do |stderr, status|
|
1394
|
+
if status&.exitstatus == 0
|
1395
|
+
# Create a signal for the restart that it can skip composing the bundle and launch directly
|
1396
|
+
FileUtils.touch(already_composed_path)
|
1397
|
+
send_message(Result.new(id: id, response: { success: true }))
|
1398
|
+
else
|
1399
|
+
# This special error code makes the extension avoid restarting in case we already know that the composed
|
1400
|
+
# bundle is not valid
|
1401
|
+
send_message(
|
1402
|
+
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
|
1403
|
+
)
|
1404
|
+
end
|
1405
|
+
end
|
1406
|
+
end
|
1407
|
+
|
1408
|
+
#: -> void
|
1409
|
+
def update_server
|
1410
|
+
return unless File.exist?(File.join(@global_state.workspace_path, ".ruby-lsp", "needs_update"))
|
1411
|
+
|
1412
|
+
launch_bundle_compose("Trying to update server") do |stderr, status|
|
1413
|
+
if status&.exitstatus == 0
|
1414
|
+
send_log_message("Successfully updated the server")
|
1415
|
+
else
|
1416
|
+
send_log_message("Failed to update server\n#{stderr}", type: Constant::MessageType::ERROR)
|
1417
|
+
end
|
1418
|
+
end
|
1419
|
+
end
|
1420
|
+
|
1421
|
+
#: (String) { (IO, Process::Status?) -> void } -> Thread
|
1422
|
+
def launch_bundle_compose(log, &block)
|
1429
1423
|
Thread.new do
|
1430
|
-
send_log_message(
|
1424
|
+
send_log_message(log)
|
1431
1425
|
|
1432
1426
|
_stdout, stderr, status = Bundler.with_unbundled_env do
|
1433
1427
|
Open3.capture3(
|
@@ -1442,17 +1436,7 @@ module RubyLsp
|
|
1442
1436
|
)
|
1443
1437
|
end
|
1444
1438
|
|
1445
|
-
|
1446
|
-
# Create a signal for the restart that it can skip composing the bundle and launch directly
|
1447
|
-
FileUtils.touch(already_composed_path)
|
1448
|
-
send_message(Result.new(id: id, response: { success: true }))
|
1449
|
-
else
|
1450
|
-
# This special error code makes the extension avoid restarting in case we already know that the composed
|
1451
|
-
# bundle is not valid
|
1452
|
-
send_message(
|
1453
|
-
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
|
1454
|
-
)
|
1455
|
-
end
|
1439
|
+
block.call(stderr, status)
|
1456
1440
|
end
|
1457
1441
|
end
|
1458
1442
|
|
@@ -1509,10 +1493,7 @@ module RubyLsp
|
|
1509
1493
|
|
1510
1494
|
send_message(Result.new(
|
1511
1495
|
id: message[:id],
|
1512
|
-
response: {
|
1513
|
-
commands: commands,
|
1514
|
-
reporterPaths: [Listeners::TestStyle::MINITEST_REPORTER_PATH, Listeners::TestStyle::TEST_UNIT_REPORTER_PATH],
|
1515
|
-
},
|
1496
|
+
response: { commands: commands },
|
1516
1497
|
))
|
1517
1498
|
end
|
1518
1499
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "sorbet-runtime"
|
5
4
|
require "bundler"
|
6
5
|
require "bundler/cli"
|
7
6
|
require "bundler/cli/install"
|
@@ -12,19 +11,24 @@ require "digest"
|
|
12
11
|
require "time"
|
13
12
|
require "uri"
|
14
13
|
|
15
|
-
# This file is a script that will configure a composed bundle for the Ruby LSP. The composed bundle allows developers to
|
16
|
-
# the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to
|
17
|
-
# exact locked versions of dependencies.
|
14
|
+
# This file is a script that will configure a composed bundle for the Ruby LSP. The composed bundle allows developers to
|
15
|
+
# use the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to
|
16
|
+
# the exact locked versions of dependencies.
|
18
17
|
|
19
18
|
Bundler.ui.level = :silent
|
20
19
|
|
21
20
|
module RubyLsp
|
22
21
|
class SetupBundler
|
23
|
-
extend T::Sig
|
24
|
-
|
25
22
|
class BundleNotLocked < StandardError; end
|
26
23
|
class BundleInstallFailure < StandardError; end
|
27
24
|
|
25
|
+
module ThorPatch
|
26
|
+
#: -> IO
|
27
|
+
def stdout
|
28
|
+
$stderr
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
28
32
|
FOUR_HOURS = 4 * 60 * 60 #: Integer
|
29
33
|
|
30
34
|
#: (String project_path, **untyped options) -> void
|
@@ -61,6 +65,7 @@ module RubyLsp
|
|
61
65
|
@bundler_version = bundler_version #: Gem::Version?
|
62
66
|
@rails_app = rails_app? #: bool
|
63
67
|
@retry = false #: bool
|
68
|
+
@needs_update_path = @custom_dir + "needs_update" #: Pathname
|
64
69
|
end
|
65
70
|
|
66
71
|
# Sets up the composed bundle and returns the `BUNDLE_GEMFILE`, `BUNDLE_PATH` and `BUNDLE_APP_CONFIG` that should be
|
@@ -229,6 +234,14 @@ module RubyLsp
|
|
229
234
|
# If no error occurred, then clear previous errors
|
230
235
|
@error_path.delete if @error_path.exist?
|
231
236
|
$stderr.puts("Ruby LSP> Composed bundle installation complete")
|
237
|
+
rescue Errno::EPIPE
|
238
|
+
# If the $stderr pipe was closed by the client, for example when closing the editor during running bundle
|
239
|
+
# install, we don't want to write the error to a file or else we will report to telemetry on the next launch and
|
240
|
+
# it does not represent an actual error.
|
241
|
+
#
|
242
|
+
# This situation may happen because while running bundle install, the server is not yet ready to receive
|
243
|
+
# shutdown requests and we may continue doing work until the process is killed.
|
244
|
+
@error_path.delete if @error_path.exist?
|
232
245
|
rescue => e
|
233
246
|
# Write the error object to a file so that we can read it from the parent process
|
234
247
|
@error_path.write(Marshal.dump(e))
|
@@ -256,19 +269,50 @@ module RubyLsp
|
|
256
269
|
#: (Hash[String, String] env, ?force_install: bool) -> Hash[String, String]
|
257
270
|
def run_bundle_install_directly(env, force_install: false)
|
258
271
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
272
|
+
return update(env) if @needs_update_path.exist?
|
259
273
|
|
260
274
|
# The ENV can only be merged after checking if an update is required because we depend on the original value of
|
261
275
|
# ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
|
262
|
-
|
263
|
-
ENV
|
264
|
-
.merge!(env)
|
276
|
+
FileUtils.touch(@needs_update_path) if should_bundle_update?
|
277
|
+
ENV.merge!(env)
|
265
278
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
279
|
+
$stderr.puts("Ruby LSP> Checking if the composed bundle is satisfied...")
|
280
|
+
missing_gems = bundle_check
|
281
|
+
|
282
|
+
unless missing_gems.empty?
|
283
|
+
$stderr.puts(<<~MESSAGE)
|
284
|
+
Ruby LSP> Running bundle install because the following gems are not installed:
|
285
|
+
#{missing_gems.map { |g| "#{g.name}: #{g.version}" }.join("\n")}
|
286
|
+
MESSAGE
|
287
|
+
|
288
|
+
bundle_install
|
270
289
|
end
|
271
290
|
|
291
|
+
$stderr.puts("Ruby LSP> Bundle already satisfied")
|
292
|
+
env
|
293
|
+
rescue => e
|
294
|
+
$stderr.puts("Ruby LSP> Running bundle install because #{e.message}")
|
295
|
+
bundle_install
|
296
|
+
env
|
297
|
+
end
|
298
|
+
|
299
|
+
# Essentially the same as bundle check, but simplified
|
300
|
+
#: -> Array[Gem::Specification]
|
301
|
+
def bundle_check
|
302
|
+
definition = Bundler.definition
|
303
|
+
definition.validate_runtime!
|
304
|
+
definition.check!
|
305
|
+
definition.missing_specs
|
306
|
+
end
|
307
|
+
|
308
|
+
#: -> void
|
309
|
+
def bundle_install
|
310
|
+
Bundler::CLI::Install.new({ "no-cache" => true }).run
|
311
|
+
correct_relative_remote_paths if @custom_lockfile.exist?
|
312
|
+
end
|
313
|
+
|
314
|
+
#: (Hash[String, String]) -> Hash[String, String]
|
315
|
+
def update(env)
|
272
316
|
# Try to auto upgrade the gems we depend on, unless they are in the Gemfile as that would result in undesired
|
273
317
|
# source control changes
|
274
318
|
gems = ["ruby-lsp", "debug", "prism"].reject { |dep| @dependencies[dep] }
|
@@ -276,11 +320,9 @@ module RubyLsp
|
|
276
320
|
|
277
321
|
Bundler::CLI::Update.new({ conservative: true }, gems).run
|
278
322
|
correct_relative_remote_paths if @custom_lockfile.exist?
|
323
|
+
@needs_update_path.delete
|
279
324
|
@last_updated_path.write(Time.now.iso8601)
|
280
325
|
env
|
281
|
-
rescue Bundler::GemNotFound, Bundler::GitError
|
282
|
-
# If a gem is not installed, skip the upgrade and try to install it with a single retry
|
283
|
-
@retry ? env : run_bundle_install_directly(env, force_install: true)
|
284
326
|
end
|
285
327
|
|
286
328
|
#: (Hash[String, String] env) -> Hash[String, String]
|
@@ -440,15 +482,7 @@ module RubyLsp
|
|
440
482
|
def patch_thor_to_print_progress_to_stderr!
|
441
483
|
return unless defined?(Bundler::Thor::Shell::Basic)
|
442
484
|
|
443
|
-
Bundler::Thor::Shell::Basic.prepend(
|
444
|
-
extend T::Sig
|
445
|
-
|
446
|
-
sig { returns(IO) }
|
447
|
-
def stdout
|
448
|
-
$stderr
|
449
|
-
end
|
450
|
-
end)
|
451
|
-
|
485
|
+
Bundler::Thor::Shell::Basic.prepend(ThorPatch)
|
452
486
|
Bundler.ui.level = :info
|
453
487
|
end
|
454
488
|
end
|
data/lib/ruby_lsp/static_docs.rb
CHANGED
@@ -14,6 +14,7 @@ module RubyLsp
|
|
14
14
|
|
15
15
|
# A map of keyword => short documentation to be displayed on hover or completion
|
16
16
|
KEYWORD_DOCS = {
|
17
|
+
"break" => "Terminates the execution of a block or loop",
|
17
18
|
"yield" => "Invokes the passed block with the given arguments",
|
18
19
|
}.freeze #: Hash[String, String]
|
19
20
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -5,9 +5,6 @@ module RubyLsp
|
|
5
5
|
class Store
|
6
6
|
class NonExistingDocumentError < StandardError; end
|
7
7
|
|
8
|
-
#: Hash[Symbol, RequestConfig]
|
9
|
-
attr_accessor :features_configuration
|
10
|
-
|
11
8
|
#: String
|
12
9
|
attr_accessor :client_name
|
13
10
|
|
@@ -15,13 +12,6 @@ module RubyLsp
|
|
15
12
|
def initialize(global_state)
|
16
13
|
@global_state = global_state
|
17
14
|
@state = {} #: Hash[String, Document[untyped]]
|
18
|
-
@features_configuration = {
|
19
|
-
inlayHint: RequestConfig.new({
|
20
|
-
enableAll: false,
|
21
|
-
implicitRescue: false,
|
22
|
-
implicitHashValue: false,
|
23
|
-
}),
|
24
|
-
} #: Hash[Symbol, RequestConfig]
|
25
15
|
@client_name = "Unknown" #: String
|
26
16
|
end
|
27
17
|
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
@@ -4,13 +4,10 @@
|
|
4
4
|
# NOTE: This module is intended to be used by addons for writing their own tests, so keep that in mind if changing.
|
5
5
|
|
6
6
|
module RubyLsp
|
7
|
+
# @requires_ancestor: Kernel
|
7
8
|
module TestHelper
|
8
9
|
class TestError < StandardError; end
|
9
10
|
|
10
|
-
extend T::Helpers
|
11
|
-
|
12
|
-
requires_ancestor { Kernel }
|
13
|
-
|
14
11
|
#: [T] (?String? source, ?URI::Generic uri, ?stub_no_typechecker: bool, ?load_addons: bool) { (RubyLsp::Server server, URI::Generic uri) -> T } -> T
|
15
12
|
def with_server(source = nil, uri = Kernel.URI("file:///fake.rb"), stub_no_typechecker: false, load_addons: true,
|
16
13
|
&block)
|
@@ -12,16 +12,23 @@ module RubyLsp
|
|
12
12
|
class LspReporter
|
13
13
|
include Singleton
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
# https://code.visualstudio.com/api/references/vscode-api#Position
|
16
|
+
#: type position = { line: Integer, character: Integer }
|
17
|
+
|
18
|
+
# https://code.visualstudio.com/api/references/vscode-api#Range
|
19
|
+
#: type range = { start: position, end: position }
|
20
|
+
|
21
|
+
# https://code.visualstudio.com/api/references/vscode-api#BranchCoverage
|
22
|
+
#: type branch_coverage = { executed: Integer, label: String, location: range }
|
23
|
+
|
24
|
+
# https://code.visualstudio.com/api/references/vscode-api#StatementCoverage
|
25
|
+
#: type statement_coverage = { executed: Integer, location: position, branches: Array[branch_coverage] }
|
17
26
|
|
18
27
|
#: -> void
|
19
28
|
def initialize
|
20
29
|
dir_path = File.join(Dir.tmpdir, "ruby-lsp")
|
21
30
|
FileUtils.mkdir_p(dir_path)
|
22
31
|
|
23
|
-
# Remove in 1 month once updates have rolled out
|
24
|
-
legacy_port_path = File.join(dir_path, "test_reporter_port")
|
25
32
|
port_db_path = File.join(dir_path, "test_reporter_port_db.json")
|
26
33
|
port = ENV["RUBY_LSP_REPORTER_PORT"]
|
27
34
|
|
@@ -32,8 +39,6 @@ module RubyLsp
|
|
32
39
|
elsif File.exist?(port_db_path)
|
33
40
|
db = JSON.load_file(port_db_path)
|
34
41
|
TCPSocket.new("localhost", db[Dir.pwd])
|
35
|
-
elsif File.exist?(legacy_port_path)
|
36
|
-
TCPSocket.new("localhost", File.read(legacy_port_path))
|
37
42
|
else
|
38
43
|
# For tests that don't spawn the TCP server
|
39
44
|
require "stringio"
|
@@ -129,7 +134,7 @@ module RubyLsp
|
|
129
134
|
# ["Foo", :bar, 6, 21, 6, 65] => 0
|
130
135
|
# }
|
131
136
|
# }
|
132
|
-
#: -> Hash[String,
|
137
|
+
#: -> Hash[String, statement_coverage]
|
133
138
|
def gather_coverage_results
|
134
139
|
# Ignore coverage results inside dependencies
|
135
140
|
bundle_path = Bundler.bundle_path.to_s
|
@@ -187,7 +192,7 @@ module RubyLsp
|
|
187
192
|
|
188
193
|
#: -> void
|
189
194
|
def at_exit
|
190
|
-
internal_shutdown unless invoked_shutdown
|
195
|
+
internal_shutdown unless @invoked_shutdown
|
191
196
|
end
|
192
197
|
|
193
198
|
class << self
|