ruby-lsp 0.24.2 → 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 +18 -9
- 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/entry.rb +4 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +9 -0
- data/lib/ruby_indexer/test/configuration_test.rb +1 -2
- data/lib/ruby_indexer/test/index_test.rb +12 -0
- data/lib/ruby_lsp/addon.rb +32 -9
- data/lib/ruby_lsp/base_server.rb +31 -21
- data/lib/ruby_lsp/document.rb +17 -14
- data/lib/ruby_lsp/global_state.rb +21 -0
- data/lib/ruby_lsp/internal.rb +0 -2
- data/lib/ruby_lsp/listeners/completion.rb +5 -2
- data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
- data/lib/ruby_lsp/listeners/test_style.rb +7 -5
- data/lib/ruby_lsp/requests/code_lens.rb +9 -3
- 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/request.rb +3 -1
- data/lib/ruby_lsp/requests/support/formatter.rb +9 -3
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +8 -2
- data/lib/ruby_lsp/response_builders/response_builder.rb +3 -3
- data/lib/ruby_lsp/server.rb +83 -81
- data/lib/ruby_lsp/setup_bundler.rb +56 -22
- data/lib/ruby_lsp/store.rb +0 -10
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +13 -4
- data/lib/ruby_lsp/utils.rb +44 -5
- metadata +1 -16
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
@@ -162,7 +162,7 @@ module RubyLsp
|
|
162
162
|
|
163
163
|
#: (Integer line, Integer character) -> void
|
164
164
|
def move_cursor_to(line, character)
|
165
|
-
return unless /Visual Studio Code|Cursor|VSCodium/.match?(@client_name)
|
165
|
+
return unless /Visual Studio Code|Cursor|VSCodium|Windsurf/.match?(@client_name)
|
166
166
|
|
167
167
|
position = Interface::Position.new(
|
168
168
|
line: line,
|
@@ -9,15 +9,21 @@ module RubyLsp
|
|
9
9
|
module Formatter
|
10
10
|
# @abstract
|
11
11
|
#: (URI::Generic, RubyLsp::RubyDocument) -> String?
|
12
|
-
def run_formatting(uri, document)
|
12
|
+
def run_formatting(uri, document)
|
13
|
+
raise AbstractMethodInvokedError
|
14
|
+
end
|
13
15
|
|
14
16
|
# @abstract
|
15
17
|
#: (URI::Generic, String, Integer) -> String?
|
16
|
-
def run_range_formatting(uri, source, base_indentation)
|
18
|
+
def run_range_formatting(uri, source, base_indentation)
|
19
|
+
raise AbstractMethodInvokedError
|
20
|
+
end
|
17
21
|
|
18
22
|
# @abstract
|
19
23
|
#: (URI::Generic, RubyLsp::RubyDocument) -> Array[Interface::Diagnostic]?
|
20
|
-
def run_diagnostic(uri, document)
|
24
|
+
def run_diagnostic(uri, document)
|
25
|
+
raise AbstractMethodInvokedError
|
26
|
+
end
|
21
27
|
end
|
22
28
|
end
|
23
29
|
end
|
@@ -24,7 +24,7 @@ module RubyLsp
|
|
24
24
|
filename = uri.to_standardized_path || uri.opaque #: as !nil
|
25
25
|
|
26
26
|
# Invoke RuboCop with just this file in `paths`
|
27
|
-
@format_runner.run(filename, document.source)
|
27
|
+
@format_runner.run(filename, document.source, document.parse_result)
|
28
28
|
@format_runner.formatted_source
|
29
29
|
end
|
30
30
|
|
@@ -40,7 +40,7 @@ module RubyLsp
|
|
40
40
|
def run_diagnostic(uri, document)
|
41
41
|
filename = uri.to_standardized_path || uri.opaque #: as !nil
|
42
42
|
# Invoke RuboCop with just this file in `paths`
|
43
|
-
@diagnostic_runner.run(filename, document.source)
|
43
|
+
@diagnostic_runner.run(filename, document.source, document.parse_result)
|
44
44
|
|
45
45
|
@diagnostic_runner.offenses.map do |offense|
|
46
46
|
Support::RuboCopDiagnostic.new(
|
@@ -81,6 +81,7 @@ module RubyLsp
|
|
81
81
|
@offenses = [] #: Array[::RuboCop::Cop::Offense]
|
82
82
|
@errors = [] #: Array[String]
|
83
83
|
@warnings = [] #: Array[String]
|
84
|
+
# @prism_result = nil #: Prism::ParseLexResult?
|
84
85
|
|
85
86
|
args += DEFAULT_ARGS
|
86
87
|
rubocop_options = ::RuboCop::Options.new.parse(args).first
|
@@ -92,8 +93,8 @@ module RubyLsp
|
|
92
93
|
super(rubocop_options, config_store)
|
93
94
|
end
|
94
95
|
|
95
|
-
#: (String
|
96
|
-
def run(path, contents)
|
96
|
+
#: (String, String, Prism::ParseLexResult) -> void
|
97
|
+
def run(path, contents, prism_result)
|
97
98
|
# Clear Runner state between runs since we get a single instance of this class
|
98
99
|
# on every use site.
|
99
100
|
@errors = []
|
@@ -101,6 +102,11 @@ module RubyLsp
|
|
101
102
|
@offenses = []
|
102
103
|
@options[:stdin] = contents
|
103
104
|
|
105
|
+
# Setting the Prism result before running the RuboCop runner makes it reuse the existing AST and avoids
|
106
|
+
# double-parsing. Unfortunately, this leads to a bunch of cops failing to execute properly under LSP mode.
|
107
|
+
# Uncomment this once reusing the Prism result is more stable
|
108
|
+
# @prism_result = prism_result
|
109
|
+
|
104
110
|
super([path])
|
105
111
|
|
106
112
|
# RuboCop rescues interrupts and then sets the `@aborting` variable to true. We don't want them to be rescued,
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -94,7 +94,13 @@ module RubyLsp
|
|
94
94
|
id: message[:id],
|
95
95
|
response:
|
96
96
|
Addon.addons.map do |addon|
|
97
|
-
|
97
|
+
version = begin
|
98
|
+
addon.version
|
99
|
+
rescue AbstractMethodInvokedError
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
{ name: addon.name, version: version, errored: addon.error? }
|
98
104
|
end,
|
99
105
|
),
|
100
106
|
)
|
@@ -161,6 +167,7 @@ module RubyLsp
|
|
161
167
|
return if @setup_error
|
162
168
|
|
163
169
|
errors = Addon.load_addons(@global_state, @outgoing_queue, include_project_addons: include_project_addons)
|
170
|
+
return if test_mode?
|
164
171
|
|
165
172
|
if errors.any?
|
166
173
|
send_log_message(
|
@@ -173,21 +180,13 @@ module RubyLsp
|
|
173
180
|
|
174
181
|
if errored_addons.any?
|
175
182
|
send_message(
|
176
|
-
Notification.
|
177
|
-
|
178
|
-
|
179
|
-
type: Constant::MessageType::WARNING,
|
180
|
-
message: "Error loading add-ons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
|
181
|
-
),
|
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,
|
182
186
|
),
|
183
187
|
)
|
184
188
|
|
185
|
-
|
186
|
-
send_log_message(
|
187
|
-
errored_addons.map(&:errors_details).join("\n\n"),
|
188
|
-
type: Constant::MessageType::WARNING,
|
189
|
-
)
|
190
|
-
end
|
189
|
+
send_log_message(errored_addons.map(&:errors_details).join("\n\n"), type: Constant::MessageType::WARNING)
|
191
190
|
end
|
192
191
|
end
|
193
192
|
|
@@ -203,10 +202,6 @@ module RubyLsp
|
|
203
202
|
|
204
203
|
configured_features = options.dig(:initializationOptions, :enabledFeatures)
|
205
204
|
|
206
|
-
configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
|
207
|
-
@store.features_configuration.dig(:inlayHint) #: as !nil
|
208
|
-
.configuration.merge!(configured_hints) if configured_hints
|
209
|
-
|
210
205
|
enabled_features = case configured_features
|
211
206
|
when Array
|
212
207
|
# If the configuration is using an array, then absent features are disabled and present ones are enabled. That's
|
@@ -360,56 +355,53 @@ module RubyLsp
|
|
360
355
|
|
361
356
|
perform_initial_indexing
|
362
357
|
check_formatter_is_available
|
358
|
+
update_server if @global_state.enabled_feature?(:launcher)
|
363
359
|
end
|
364
360
|
|
365
361
|
#: (Hash[Symbol, untyped] message) -> void
|
366
362
|
def text_document_did_open(message)
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
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
|
377
372
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
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
|
+
)
|
384
379
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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
|
390
385
|
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
),
|
386
|
+
send_message(
|
387
|
+
Notification.new(
|
388
|
+
method: "window/logMessage",
|
389
|
+
params: Interface::LogMessageParams.new(
|
390
|
+
type: Constant::MessageType::WARNING,
|
391
|
+
message: log_message,
|
398
392
|
),
|
399
|
-
)
|
400
|
-
|
393
|
+
),
|
394
|
+
)
|
401
395
|
end
|
402
396
|
end
|
403
397
|
|
404
398
|
#: (Hash[Symbol, untyped] message) -> void
|
405
399
|
def text_document_did_close(message)
|
406
|
-
|
407
|
-
|
408
|
-
@store.delete(uri)
|
400
|
+
uri = message.dig(:params, :textDocument, :uri)
|
401
|
+
@store.delete(uri)
|
409
402
|
|
410
|
-
|
411
|
-
|
412
|
-
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, []))
|
413
405
|
end
|
414
406
|
|
415
407
|
#: (Hash[Symbol, untyped] message) -> void
|
@@ -417,9 +409,7 @@ module RubyLsp
|
|
417
409
|
params = message[:params]
|
418
410
|
text_document = params[:textDocument]
|
419
411
|
|
420
|
-
@
|
421
|
-
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
|
422
|
-
end
|
412
|
+
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
|
423
413
|
end
|
424
414
|
|
425
415
|
#: (Hash[Symbol, untyped] message) -> void
|
@@ -473,8 +463,8 @@ module RubyLsp
|
|
473
463
|
document_symbol = Requests::DocumentSymbol.new(uri, dispatcher)
|
474
464
|
document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
|
475
465
|
inlay_hint = Requests::InlayHints.new(
|
466
|
+
@global_state,
|
476
467
|
document,
|
477
|
-
@store.features_configuration.dig(:inlayHint), #: as !nil
|
478
468
|
dispatcher,
|
479
469
|
)
|
480
470
|
|
@@ -821,7 +811,6 @@ module RubyLsp
|
|
821
811
|
return
|
822
812
|
end
|
823
813
|
|
824
|
-
hints_configurations = @store.features_configuration.dig(:inlayHint) #: as !nil
|
825
814
|
dispatcher = Prism::Dispatcher.new
|
826
815
|
|
827
816
|
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
@@ -829,7 +818,7 @@ module RubyLsp
|
|
829
818
|
return
|
830
819
|
end
|
831
820
|
|
832
|
-
request = Requests::InlayHints.new(
|
821
|
+
request = Requests::InlayHints.new(@global_state, document, dispatcher)
|
833
822
|
dispatcher.visit(document.ast)
|
834
823
|
result = request.perform
|
835
824
|
document.cache_set("textDocument/inlayHint", result)
|
@@ -1103,11 +1092,7 @@ module RubyLsp
|
|
1103
1092
|
@global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
|
1104
1093
|
|
1105
1094
|
# Clear all document caches for pull diagnostics
|
1106
|
-
@
|
1107
|
-
@store.each do |_uri, document|
|
1108
|
-
document.clear_cache("textDocument/diagnostic")
|
1109
|
-
end
|
1110
|
-
end
|
1095
|
+
@store.each { |_uri, document| document.clear_cache("textDocument/diagnostic") }
|
1111
1096
|
|
1112
1097
|
# Request a pull diagnostic refresh from the editor
|
1113
1098
|
if @global_state.client_capabilities.supports_diagnostic_refresh
|
@@ -1222,7 +1207,7 @@ module RubyLsp
|
|
1222
1207
|
}
|
1223
1208
|
end
|
1224
1209
|
end
|
1225
|
-
rescue Bundler::GemNotFound
|
1210
|
+
rescue Bundler::GemNotFound, Bundler::GemfileNotFound
|
1226
1211
|
[]
|
1227
1212
|
end
|
1228
1213
|
|
@@ -1405,8 +1390,38 @@ module RubyLsp
|
|
1405
1390
|
|
1406
1391
|
# We compose the bundle in a thread so that the LSP continues to work while we're checking for its validity. Once
|
1407
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)
|
1408
1423
|
Thread.new do
|
1409
|
-
send_log_message(
|
1424
|
+
send_log_message(log)
|
1410
1425
|
|
1411
1426
|
_stdout, stderr, status = Bundler.with_unbundled_env do
|
1412
1427
|
Open3.capture3(
|
@@ -1421,17 +1436,7 @@ module RubyLsp
|
|
1421
1436
|
)
|
1422
1437
|
end
|
1423
1438
|
|
1424
|
-
|
1425
|
-
# Create a signal for the restart that it can skip composing the bundle and launch directly
|
1426
|
-
FileUtils.touch(already_composed_path)
|
1427
|
-
send_message(Result.new(id: id, response: { success: true }))
|
1428
|
-
else
|
1429
|
-
# This special error code makes the extension avoid restarting in case we already know that the composed
|
1430
|
-
# bundle is not valid
|
1431
|
-
send_message(
|
1432
|
-
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
|
1433
|
-
)
|
1434
|
-
end
|
1439
|
+
block.call(stderr, status)
|
1435
1440
|
end
|
1436
1441
|
end
|
1437
1442
|
|
@@ -1488,10 +1493,7 @@ module RubyLsp
|
|
1488
1493
|
|
1489
1494
|
send_message(Result.new(
|
1490
1495
|
id: message[:id],
|
1491
|
-
response: {
|
1492
|
-
commands: commands,
|
1493
|
-
reporterPaths: [Listeners::TestStyle::MINITEST_REPORTER_PATH, Listeners::TestStyle::TEST_UNIT_REPORTER_PATH],
|
1494
|
-
},
|
1496
|
+
response: { commands: commands },
|
1495
1497
|
))
|
1496
1498
|
end
|
1497
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"
|
@@ -20,11 +19,16 @@ 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/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
|
|
@@ -12,8 +12,17 @@ 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
|
@@ -125,7 +134,7 @@ module RubyLsp
|
|
125
134
|
# ["Foo", :bar, 6, 21, 6, 65] => 0
|
126
135
|
# }
|
127
136
|
# }
|
128
|
-
#: -> Hash[String,
|
137
|
+
#: -> Hash[String, statement_coverage]
|
129
138
|
def gather_coverage_results
|
130
139
|
# Ignore coverage results inside dependencies
|
131
140
|
bundle_path = Bundler.bundle_path.to_s
|
@@ -183,7 +192,7 @@ module RubyLsp
|
|
183
192
|
|
184
193
|
#: -> void
|
185
194
|
def at_exit
|
186
|
-
internal_shutdown unless invoked_shutdown
|
195
|
+
internal_shutdown unless @invoked_shutdown
|
187
196
|
end
|
188
197
|
|
189
198
|
class << self
|
data/lib/ruby_lsp/utils.rb
CHANGED
@@ -5,7 +5,6 @@ module RubyLsp
|
|
5
5
|
# rubocop:disable RubyLsp/UseLanguageServerAliases
|
6
6
|
Interface = LanguageServer::Protocol::Interface
|
7
7
|
Constant = LanguageServer::Protocol::Constant
|
8
|
-
Transport = LanguageServer::Protocol::Transport
|
9
8
|
# rubocop:enable RubyLsp/UseLanguageServerAliases
|
10
9
|
|
11
10
|
# Used to indicate that a request shouldn't return a response
|
@@ -20,6 +19,7 @@ module RubyLsp
|
|
20
19
|
"Gemfile"
|
21
20
|
end #: String
|
22
21
|
GUESSED_TYPES_URL = "https://shopify.github.io/ruby-lsp/#guessed-types"
|
22
|
+
TEST_PATH_PATTERN = "**/{test,spec,features}/**/{*_test.rb,test_*.rb,*_spec.rb,*.feature}"
|
23
23
|
|
24
24
|
# Request delegation for embedded languages is not yet standardized into the language server specification. Here we
|
25
25
|
# use this custom error class as a way to return a signal to the client that the request should be delegated to the
|
@@ -31,6 +31,8 @@ module RubyLsp
|
|
31
31
|
CODE = -32900
|
32
32
|
end
|
33
33
|
|
34
|
+
class AbstractMethodInvokedError < StandardError; end
|
35
|
+
|
34
36
|
BUNDLE_COMPOSE_FAILED_CODE = -33000
|
35
37
|
|
36
38
|
# A notification to be sent to the client
|
@@ -50,7 +52,9 @@ module RubyLsp
|
|
50
52
|
|
51
53
|
# @abstract
|
52
54
|
#: -> Hash[Symbol, untyped]
|
53
|
-
def to_hash
|
55
|
+
def to_hash
|
56
|
+
raise AbstractMethodInvokedError
|
57
|
+
end
|
54
58
|
end
|
55
59
|
|
56
60
|
class Notification < Message
|
@@ -243,9 +247,6 @@ module RubyLsp
|
|
243
247
|
|
244
248
|
# A request configuration, to turn on/off features
|
245
249
|
class RequestConfig
|
246
|
-
#: Hash[Symbol, bool]
|
247
|
-
attr_accessor :configuration
|
248
|
-
|
249
250
|
#: (Hash[Symbol, bool] configuration) -> void
|
250
251
|
def initialize(configuration)
|
251
252
|
@configuration = configuration
|
@@ -255,6 +256,11 @@ module RubyLsp
|
|
255
256
|
def enabled?(feature)
|
256
257
|
@configuration[:enableAll] || @configuration[feature]
|
257
258
|
end
|
259
|
+
|
260
|
+
#: (Hash[Symbol, bool]) -> void
|
261
|
+
def merge!(hash)
|
262
|
+
@configuration.merge!(hash)
|
263
|
+
end
|
258
264
|
end
|
259
265
|
|
260
266
|
class SorbetLevel
|
@@ -299,4 +305,37 @@ module RubyLsp
|
|
299
305
|
#: -> bool
|
300
306
|
def true_or_higher? = @level == :true || @level == :strict
|
301
307
|
end
|
308
|
+
|
309
|
+
# Reads JSON RPC messages from the given IO in a loop
|
310
|
+
class MessageReader
|
311
|
+
#: (IO) -> void
|
312
|
+
def initialize(io)
|
313
|
+
@io = io
|
314
|
+
end
|
315
|
+
|
316
|
+
#: () { (Hash[Symbol, untyped]) -> void } -> void
|
317
|
+
def each_message(&block)
|
318
|
+
while (headers = @io.gets("\r\n\r\n"))
|
319
|
+
raw_message = @io.read(headers[/Content-Length: (\d+)/i, 1].to_i) #: as !nil
|
320
|
+
block.call(JSON.parse(raw_message, symbolize_names: true))
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Writes JSON RPC messages to the given IO
|
326
|
+
class MessageWriter
|
327
|
+
#: (IO) -> void
|
328
|
+
def initialize(io)
|
329
|
+
@io = io
|
330
|
+
end
|
331
|
+
|
332
|
+
#: (Hash[Symbol, untyped]) -> void
|
333
|
+
def write(message)
|
334
|
+
message[:jsonrpc] = "2.0"
|
335
|
+
json_message = message.to_json
|
336
|
+
|
337
|
+
@io.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
|
338
|
+
@io.flush
|
339
|
+
end
|
340
|
+
end
|
302
341
|
end
|