ruby-lsp 0.23.11 → 0.26.2
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 +183 -284
- 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 +225 -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 +149 -151
- 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 +231 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +107 -0
- data/lib/ruby_lsp/listeners/test_style.rb +207 -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 +139 -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 +23 -61
- 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 +38 -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 +305 -198
- data/lib/ruby_lsp/setup_bundler.rb +122 -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
|
@@ -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,63 +11,66 @@ 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
|
|
|
28
|
-
|
|
25
|
+
module ThorPatch
|
|
26
|
+
#: -> IO
|
|
27
|
+
def stdout
|
|
28
|
+
$stderr
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
FOUR_HOURS = 4 * 60 * 60 #: Integer
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
#: (String project_path, **untyped options) -> void
|
|
31
35
|
def initialize(project_path, **options)
|
|
32
36
|
@project_path = project_path
|
|
33
|
-
@branch =
|
|
34
|
-
@launcher =
|
|
37
|
+
@branch = options[:branch] #: String?
|
|
38
|
+
@launcher = options[:launcher] #: bool?
|
|
35
39
|
patch_thor_to_print_progress_to_stderr! if @launcher
|
|
36
40
|
|
|
37
41
|
# Regular bundle paths
|
|
38
|
-
@gemfile =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
T.nilable(Pathname),
|
|
45
|
-
)
|
|
46
|
-
@lockfile = T.let(@gemfile ? Bundler.default_lockfile : nil, T.nilable(Pathname))
|
|
42
|
+
@gemfile = begin
|
|
43
|
+
Bundler.default_gemfile
|
|
44
|
+
rescue Bundler::GemfileNotFound
|
|
45
|
+
nil
|
|
46
|
+
end #: Pathname?
|
|
47
|
+
@lockfile = @gemfile ? Bundler.default_lockfile : nil #: Pathname?
|
|
47
48
|
|
|
48
|
-
@gemfile_hash =
|
|
49
|
-
@lockfile_hash =
|
|
49
|
+
@gemfile_hash = @gemfile ? Digest::SHA256.hexdigest(@gemfile.read) : nil #: String?
|
|
50
|
+
@lockfile_hash = @lockfile&.exist? ? Digest::SHA256.hexdigest(@lockfile.read) : nil #: String?
|
|
50
51
|
|
|
51
|
-
@gemfile_name =
|
|
52
|
+
@gemfile_name = @gemfile&.basename&.to_s || "Gemfile" #: String
|
|
52
53
|
|
|
53
54
|
# Custom bundle paths
|
|
54
|
-
@custom_dir =
|
|
55
|
-
@custom_gemfile =
|
|
56
|
-
@custom_lockfile =
|
|
57
|
-
@lockfile_hash_path =
|
|
58
|
-
@last_updated_path =
|
|
59
|
-
@error_path =
|
|
60
|
-
@already_composed_path =
|
|
55
|
+
@custom_dir = Pathname.new(".ruby-lsp").expand_path(@project_path) #: Pathname
|
|
56
|
+
@custom_gemfile = @custom_dir + @gemfile_name #: Pathname
|
|
57
|
+
@custom_lockfile = @custom_dir + (@lockfile&.basename || "Gemfile.lock") #: Pathname
|
|
58
|
+
@lockfile_hash_path = @custom_dir + "main_lockfile_hash" #: Pathname
|
|
59
|
+
@last_updated_path = @custom_dir + "last_updated" #: Pathname
|
|
60
|
+
@error_path = @custom_dir + "install_error" #: Pathname
|
|
61
|
+
@already_composed_path = @custom_dir + "bundle_is_composed" #: Pathname
|
|
61
62
|
|
|
62
63
|
dependencies, bundler_version = load_dependencies
|
|
63
|
-
@dependencies =
|
|
64
|
-
@bundler_version =
|
|
65
|
-
@rails_app =
|
|
66
|
-
@retry =
|
|
64
|
+
@dependencies = dependencies #: Hash[String, untyped]
|
|
65
|
+
@bundler_version = bundler_version #: Gem::Version?
|
|
66
|
+
@rails_app = rails_app? #: bool
|
|
67
|
+
@retry = false #: bool
|
|
68
|
+
@needs_update_path = @custom_dir + "needs_update" #: Pathname
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
# Sets up the composed bundle and returns the `BUNDLE_GEMFILE`, `BUNDLE_PATH` and `BUNDLE_APP_CONFIG` that should be
|
|
70
72
|
# used for running the server
|
|
71
|
-
|
|
73
|
+
#: -> Hash[String, String]
|
|
72
74
|
def setup!
|
|
73
75
|
raise BundleNotLocked if !@launcher && @gemfile&.exist? && !@lockfile&.exist?
|
|
74
76
|
|
|
@@ -128,26 +130,23 @@ module RubyLsp
|
|
|
128
130
|
|
|
129
131
|
private
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
#: -> Hash[String, untyped]
|
|
132
134
|
def composed_bundle_dependencies
|
|
133
|
-
@composed_bundle_dependencies ||=
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
end,
|
|
146
|
-
T.nilable(T::Hash[String, T.untyped]),
|
|
147
|
-
)
|
|
135
|
+
@composed_bundle_dependencies ||= begin
|
|
136
|
+
original_bundle_gemfile = ENV["BUNDLE_GEMFILE"]
|
|
137
|
+
|
|
138
|
+
if @custom_lockfile.exist?
|
|
139
|
+
ENV["BUNDLE_GEMFILE"] = @custom_gemfile.to_s
|
|
140
|
+
Bundler::LockfileParser.new(@custom_lockfile.read).dependencies
|
|
141
|
+
else
|
|
142
|
+
{}
|
|
143
|
+
end
|
|
144
|
+
ensure
|
|
145
|
+
ENV["BUNDLE_GEMFILE"] = original_bundle_gemfile
|
|
146
|
+
end #: Hash[String, untyped]?
|
|
148
147
|
end
|
|
149
148
|
|
|
150
|
-
|
|
149
|
+
#: -> void
|
|
151
150
|
def write_custom_gemfile
|
|
152
151
|
parts = [
|
|
153
152
|
"# This custom gemfile is automatically generated by the Ruby LSP.",
|
|
@@ -188,7 +187,7 @@ module RubyLsp
|
|
|
188
187
|
@custom_gemfile.write(content) unless @custom_gemfile.exist? && @custom_gemfile.read == content
|
|
189
188
|
end
|
|
190
189
|
|
|
191
|
-
|
|
190
|
+
#: -> [Hash[String, untyped], Gem::Version?]
|
|
192
191
|
def load_dependencies
|
|
193
192
|
return [{}, nil] unless @lockfile&.exist?
|
|
194
193
|
|
|
@@ -208,7 +207,7 @@ module RubyLsp
|
|
|
208
207
|
[dependencies, lockfile_parser.bundler_version]
|
|
209
208
|
end
|
|
210
209
|
|
|
211
|
-
|
|
210
|
+
#: (?Pathname? bundle_gemfile) -> Hash[String, String]
|
|
212
211
|
def run_bundle_install(bundle_gemfile = @gemfile)
|
|
213
212
|
env = bundler_settings_as_env
|
|
214
213
|
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
|
|
@@ -235,6 +234,15 @@ module RubyLsp
|
|
|
235
234
|
# If no error occurred, then clear previous errors
|
|
236
235
|
@error_path.delete if @error_path.exist?
|
|
237
236
|
$stderr.puts("Ruby LSP> Composed bundle installation complete")
|
|
237
|
+
rescue Errno::EPIPE, Bundler::HTTPError
|
|
238
|
+
# There are cases where we expect certain errors to happen occasionally, and we don't want to write them to
|
|
239
|
+
# a file, which would report to telemetry on the next launch.
|
|
240
|
+
#
|
|
241
|
+
# - The $stderr pipe might be closed by the client, for example when closing the editor during running bundle
|
|
242
|
+
# install. This situation may happen because, while running bundle install, the server is not yet ready to
|
|
243
|
+
# receive shutdown requests and we may continue doing work until the process is killed.
|
|
244
|
+
# - Bundler might also encounter a network error.
|
|
245
|
+
@error_path.delete if @error_path.exist?
|
|
238
246
|
rescue => e
|
|
239
247
|
# Write the error object to a file so that we can read it from the parent process
|
|
240
248
|
@error_path.write(Marshal.dump(e))
|
|
@@ -259,21 +267,53 @@ module RubyLsp
|
|
|
259
267
|
env
|
|
260
268
|
end
|
|
261
269
|
|
|
262
|
-
|
|
270
|
+
#: (Hash[String, String] env, ?force_install: bool) -> Hash[String, String]
|
|
263
271
|
def run_bundle_install_directly(env, force_install: false)
|
|
264
272
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
|
273
|
+
return update(env) if @needs_update_path.exist?
|
|
265
274
|
|
|
266
275
|
# The ENV can only be merged after checking if an update is required because we depend on the original value of
|
|
267
276
|
# ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
|
|
268
|
-
|
|
269
|
-
|
|
277
|
+
FileUtils.touch(@needs_update_path) if should_bundle_update?
|
|
278
|
+
ENV.merge!(env)
|
|
270
279
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
280
|
+
$stderr.puts("Ruby LSP> Checking if the composed bundle is satisfied...")
|
|
281
|
+
missing_gems = bundle_check
|
|
282
|
+
|
|
283
|
+
unless missing_gems.empty?
|
|
284
|
+
$stderr.puts(<<~MESSAGE)
|
|
285
|
+
Ruby LSP> Running bundle install because the following gems are not installed:
|
|
286
|
+
#{missing_gems.map { |g| "#{g.name}: #{g.version}" }.join("\n")}
|
|
287
|
+
MESSAGE
|
|
288
|
+
|
|
289
|
+
bundle_install
|
|
275
290
|
end
|
|
276
291
|
|
|
292
|
+
$stderr.puts("Ruby LSP> Bundle already satisfied")
|
|
293
|
+
env
|
|
294
|
+
rescue => e
|
|
295
|
+
$stderr.puts("Ruby LSP> Running bundle install because #{e.message}")
|
|
296
|
+
bundle_install
|
|
297
|
+
env
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Essentially the same as bundle check, but simplified
|
|
301
|
+
#: -> Array[Gem::Specification]
|
|
302
|
+
def bundle_check
|
|
303
|
+
definition = Bundler.definition
|
|
304
|
+
definition.validate_runtime!
|
|
305
|
+
definition.check!
|
|
306
|
+
definition.missing_specs
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
#: -> void
|
|
310
|
+
def bundle_install
|
|
311
|
+
Bundler::CLI::Install.new({ "no-cache" => true }).run
|
|
312
|
+
correct_relative_remote_paths if @custom_lockfile.exist?
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
#: (Hash[String, String]) -> Hash[String, String]
|
|
316
|
+
def update(env)
|
|
277
317
|
# Try to auto upgrade the gems we depend on, unless they are in the Gemfile as that would result in undesired
|
|
278
318
|
# source control changes
|
|
279
319
|
gems = ["ruby-lsp", "debug", "prism"].reject { |dep| @dependencies[dep] }
|
|
@@ -281,14 +321,12 @@ module RubyLsp
|
|
|
281
321
|
|
|
282
322
|
Bundler::CLI::Update.new({ conservative: true }, gems).run
|
|
283
323
|
correct_relative_remote_paths if @custom_lockfile.exist?
|
|
324
|
+
@needs_update_path.delete
|
|
284
325
|
@last_updated_path.write(Time.now.iso8601)
|
|
285
326
|
env
|
|
286
|
-
rescue Bundler::GemNotFound, Bundler::GitError
|
|
287
|
-
# If a gem is not installed, skip the upgrade and try to install it with a single retry
|
|
288
|
-
@retry ? env : run_bundle_install_directly(env, force_install: true)
|
|
289
327
|
end
|
|
290
328
|
|
|
291
|
-
|
|
329
|
+
#: (Hash[String, String] env) -> Hash[String, String]
|
|
292
330
|
def run_bundle_install_through_command(env)
|
|
293
331
|
# If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
|
|
294
332
|
# to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just created
|
|
@@ -345,7 +383,7 @@ module RubyLsp
|
|
|
345
383
|
end
|
|
346
384
|
|
|
347
385
|
# Gather all Bundler settings (global and local) and return them as a hash that can be used as the environment
|
|
348
|
-
|
|
386
|
+
#: -> Hash[String, String]
|
|
349
387
|
def bundler_settings_as_env
|
|
350
388
|
local_config_path = File.join(@project_path, ".bundle")
|
|
351
389
|
|
|
@@ -357,10 +395,17 @@ module RubyLsp
|
|
|
357
395
|
Bundler::Settings.new
|
|
358
396
|
end
|
|
359
397
|
|
|
398
|
+
# List of Bundler settings that don't make sense for the composed bundle and are better controlled manually by the
|
|
399
|
+
# user
|
|
400
|
+
ignored_settings = ["bin", "cache_all", "cache_all_platforms"]
|
|
401
|
+
|
|
360
402
|
# Map all settings to their environment variable names with `key_for` and their values. For example, the if the
|
|
361
403
|
# setting name `e` is `path` with a value of `vendor/bundle`, then it will return `"BUNDLE_PATH" =>
|
|
362
404
|
# "vendor/bundle"`
|
|
363
|
-
settings
|
|
405
|
+
settings
|
|
406
|
+
.all
|
|
407
|
+
.reject { |setting| ignored_settings.include?(setting) }
|
|
408
|
+
.to_h do |e|
|
|
364
409
|
key = settings.key_for(e)
|
|
365
410
|
value = Array(settings[e]).join(":").tr(" ", ":")
|
|
366
411
|
|
|
@@ -368,7 +413,7 @@ module RubyLsp
|
|
|
368
413
|
end
|
|
369
414
|
end
|
|
370
415
|
|
|
371
|
-
|
|
416
|
+
#: -> void
|
|
372
417
|
def install_bundler_if_needed
|
|
373
418
|
# Try to find the bundler version specified in the lockfile in installed gems. If not found, install it
|
|
374
419
|
requirement = Gem::Requirement.new(@bundler_version.to_s)
|
|
@@ -377,7 +422,7 @@ module RubyLsp
|
|
|
377
422
|
Gem.install("bundler", @bundler_version.to_s)
|
|
378
423
|
end
|
|
379
424
|
|
|
380
|
-
|
|
425
|
+
#: -> bool
|
|
381
426
|
def should_bundle_update?
|
|
382
427
|
# If `ruby-lsp`, `ruby-lsp-rails` and `debug` are in the Gemfile, then we shouldn't try to upgrade them or else it
|
|
383
428
|
# will produce version control changes
|
|
@@ -400,16 +445,19 @@ module RubyLsp
|
|
|
400
445
|
|
|
401
446
|
# When a lockfile has remote references based on relative file paths, we need to ensure that they are pointing to
|
|
402
447
|
# the correct place since after copying the relative path is no longer valid
|
|
403
|
-
|
|
448
|
+
#: -> void
|
|
404
449
|
def correct_relative_remote_paths
|
|
405
450
|
content = @custom_lockfile.read
|
|
406
451
|
content.gsub!(/remote: (.*)/) do |match|
|
|
407
|
-
|
|
452
|
+
last_match = Regexp.last_match #: as !nil
|
|
453
|
+
path = last_match[1]
|
|
408
454
|
|
|
409
455
|
# We should only apply the correction if the remote is a relative path. It might also be a URI, like
|
|
410
456
|
# `https://rubygems.org` or an absolute path, in which case we shouldn't do anything
|
|
411
457
|
if path && !URI(path).scheme
|
|
412
|
-
|
|
458
|
+
bundle_dir = @gemfile #: as !nil
|
|
459
|
+
.dirname
|
|
460
|
+
"remote: #{File.expand_path(path, bundle_dir)}"
|
|
413
461
|
else
|
|
414
462
|
match
|
|
415
463
|
end
|
|
@@ -422,7 +470,7 @@ module RubyLsp
|
|
|
422
470
|
end
|
|
423
471
|
|
|
424
472
|
# Detects if the project is a Rails app by looking if the superclass of the main class is `Rails::Application`
|
|
425
|
-
|
|
473
|
+
#: -> bool
|
|
426
474
|
def rails_app?
|
|
427
475
|
config = Pathname.new("config/application.rb").expand_path
|
|
428
476
|
application_contents = config.read(external_encoding: Encoding::UTF_8) if config.exist?
|
|
@@ -431,19 +479,11 @@ module RubyLsp
|
|
|
431
479
|
/class .* < (::)?Rails::Application/.match?(application_contents)
|
|
432
480
|
end
|
|
433
481
|
|
|
434
|
-
|
|
482
|
+
#: -> void
|
|
435
483
|
def patch_thor_to_print_progress_to_stderr!
|
|
436
484
|
return unless defined?(Bundler::Thor::Shell::Basic)
|
|
437
485
|
|
|
438
|
-
Bundler::Thor::Shell::Basic.prepend(
|
|
439
|
-
extend T::Sig
|
|
440
|
-
|
|
441
|
-
sig { returns(IO) }
|
|
442
|
-
def stdout
|
|
443
|
-
$stderr
|
|
444
|
-
end
|
|
445
|
-
end)
|
|
446
|
-
|
|
486
|
+
Bundler::Thor::Shell::Basic.prepend(ThorPatch)
|
|
447
487
|
Bundler.ui.level = :info
|
|
448
488
|
end
|
|
449
489
|
end
|
data/lib/ruby_lsp/static_docs.rb
CHANGED
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
|
|
4
4
|
module RubyLsp
|
|
5
5
|
# The path to the `static_docs` directory, where we keep long-form static documentation
|
|
6
|
-
STATIC_DOCS_PATH =
|
|
6
|
+
STATIC_DOCS_PATH = File.join(
|
|
7
|
+
File.dirname(
|
|
8
|
+
File.dirname(
|
|
9
|
+
__dir__, #: as !nil
|
|
10
|
+
),
|
|
11
|
+
),
|
|
12
|
+
"static_docs",
|
|
13
|
+
) #: String
|
|
7
14
|
|
|
8
15
|
# A map of keyword => short documentation to be displayed on hover or completion
|
|
9
|
-
KEYWORD_DOCS =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
T::Hash[String, String],
|
|
14
|
-
)
|
|
16
|
+
KEYWORD_DOCS = {
|
|
17
|
+
"break" => "Terminates the execution of a block or loop",
|
|
18
|
+
"yield" => "Invokes the passed block with the given arguments",
|
|
19
|
+
}.freeze #: Hash[String, String]
|
|
15
20
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
|
@@ -3,34 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
module RubyLsp
|
|
5
5
|
class Store
|
|
6
|
-
extend T::Sig
|
|
7
|
-
|
|
8
6
|
class NonExistingDocumentError < StandardError; end
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
attr_accessor :features_configuration
|
|
12
|
-
|
|
13
|
-
sig { returns(String) }
|
|
8
|
+
#: String
|
|
14
9
|
attr_accessor :client_name
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
#: (GlobalState global_state) -> void
|
|
17
12
|
def initialize(global_state)
|
|
18
13
|
@global_state = global_state
|
|
19
|
-
@state =
|
|
20
|
-
@
|
|
21
|
-
{
|
|
22
|
-
inlayHint: RequestConfig.new({
|
|
23
|
-
enableAll: false,
|
|
24
|
-
implicitRescue: false,
|
|
25
|
-
implicitHashValue: false,
|
|
26
|
-
}),
|
|
27
|
-
},
|
|
28
|
-
T::Hash[Symbol, RequestConfig],
|
|
29
|
-
)
|
|
30
|
-
@client_name = T.let("Unknown", String)
|
|
14
|
+
@state = {} #: Hash[String, Document[untyped]]
|
|
15
|
+
@client_name = "Unknown" #: String
|
|
31
16
|
end
|
|
32
17
|
|
|
33
|
-
|
|
18
|
+
#: (URI::Generic uri) -> Document[untyped]
|
|
34
19
|
def get(uri)
|
|
35
20
|
document = @state[uri.to_s]
|
|
36
21
|
return document unless document.nil?
|
|
@@ -43,78 +28,65 @@ module RubyLsp
|
|
|
43
28
|
ext = File.extname(path)
|
|
44
29
|
language_id = case ext
|
|
45
30
|
when ".erb", ".rhtml"
|
|
46
|
-
|
|
31
|
+
:erb
|
|
47
32
|
when ".rbs"
|
|
48
|
-
|
|
33
|
+
:rbs
|
|
49
34
|
else
|
|
50
|
-
|
|
35
|
+
:ruby
|
|
51
36
|
end
|
|
52
37
|
|
|
53
38
|
set(uri: uri, source: File.binread(path), version: 0, language_id: language_id)
|
|
54
|
-
|
|
39
|
+
@state[uri.to_s] #: as !nil
|
|
55
40
|
rescue Errno::ENOENT
|
|
56
41
|
raise NonExistingDocumentError, uri.to_s
|
|
57
42
|
end
|
|
58
43
|
|
|
59
|
-
|
|
60
|
-
params(
|
|
61
|
-
uri: URI::Generic,
|
|
62
|
-
source: String,
|
|
63
|
-
version: Integer,
|
|
64
|
-
language_id: Document::LanguageId,
|
|
65
|
-
).returns(Document[T.untyped])
|
|
66
|
-
end
|
|
44
|
+
#: (uri: URI::Generic, source: String, version: Integer, language_id: Symbol) -> Document[untyped]
|
|
67
45
|
def set(uri:, source:, version:, language_id:)
|
|
68
46
|
@state[uri.to_s] = case language_id
|
|
69
|
-
when
|
|
47
|
+
when :erb
|
|
70
48
|
ERBDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
|
71
|
-
when
|
|
49
|
+
when :rbs
|
|
72
50
|
RBSDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
|
73
51
|
else
|
|
74
52
|
RubyDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
|
75
53
|
end
|
|
76
54
|
end
|
|
77
55
|
|
|
78
|
-
|
|
56
|
+
#: (uri: URI::Generic, edits: Array[Hash[Symbol, untyped]], version: Integer) -> void
|
|
79
57
|
def push_edits(uri:, edits:, version:)
|
|
80
|
-
|
|
58
|
+
@state[uri.to_s] #: as !nil
|
|
59
|
+
.push_edits(edits, version: version)
|
|
81
60
|
end
|
|
82
61
|
|
|
83
|
-
|
|
62
|
+
#: -> void
|
|
84
63
|
def clear
|
|
85
64
|
@state.clear
|
|
86
65
|
end
|
|
87
66
|
|
|
88
|
-
|
|
67
|
+
#: -> bool
|
|
89
68
|
def empty?
|
|
90
69
|
@state.empty?
|
|
91
70
|
end
|
|
92
71
|
|
|
93
|
-
|
|
72
|
+
#: (URI::Generic uri) -> void
|
|
94
73
|
def delete(uri)
|
|
95
74
|
@state.delete(uri.to_s)
|
|
96
75
|
end
|
|
97
76
|
|
|
98
|
-
|
|
77
|
+
#: (URI::Generic uri) -> bool
|
|
99
78
|
def key?(uri)
|
|
100
79
|
@state.key?(uri.to_s)
|
|
101
80
|
end
|
|
102
81
|
|
|
103
|
-
|
|
82
|
+
#: { (String uri, Document[untyped] document) -> void } -> void
|
|
104
83
|
def each(&block)
|
|
105
84
|
@state.each do |uri, document|
|
|
106
85
|
block.call(uri, document)
|
|
107
86
|
end
|
|
108
87
|
end
|
|
109
88
|
|
|
110
|
-
|
|
111
|
-
type_parameters(:T)
|
|
112
|
-
.params(
|
|
113
|
-
uri: URI::Generic,
|
|
114
|
-
request_name: String,
|
|
115
|
-
block: T.proc.params(document: Document[T.untyped]).returns(T.type_parameter(:T)),
|
|
116
|
-
).returns(T.type_parameter(:T))
|
|
117
|
-
end
|
|
89
|
+
#: [T] (URI::Generic uri, String request_name) { (Document[untyped] document) -> T } -> T
|
|
118
90
|
def cache_fetch(uri, request_name, &block)
|
|
119
91
|
get(uri).cache_fetch(request_name, &block)
|
|
120
92
|
end
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
|
@@ -4,24 +4,11 @@
|
|
|
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
|
-
|
|
11
|
-
extend T::Helpers
|
|
12
|
-
|
|
13
|
-
requires_ancestor { Kernel }
|
|
14
|
-
|
|
15
|
-
sig do
|
|
16
|
-
type_parameters(:T)
|
|
17
|
-
.params(
|
|
18
|
-
source: T.nilable(String),
|
|
19
|
-
uri: URI::Generic,
|
|
20
|
-
stub_no_typechecker: T::Boolean,
|
|
21
|
-
load_addons: T::Boolean,
|
|
22
|
-
block: T.proc.params(server: RubyLsp::Server, uri: URI::Generic).returns(T.type_parameter(:T)),
|
|
23
|
-
).returns(T.type_parameter(:T))
|
|
24
|
-
end
|
|
11
|
+
#: [T] (?String? source, ?URI::Generic uri, ?stub_no_typechecker: bool, ?load_addons: bool) { (RubyLsp::Server server, URI::Generic uri) -> T } -> T
|
|
25
12
|
def with_server(source = nil, uri = Kernel.URI("file:///fake.rb"), stub_no_typechecker: false, load_addons: true,
|
|
26
13
|
&block)
|
|
27
14
|
server = RubyLsp::Server.new(test_mode: true)
|
|
@@ -58,7 +45,7 @@ module RubyLsp
|
|
|
58
45
|
end
|
|
59
46
|
end
|
|
60
47
|
|
|
61
|
-
|
|
48
|
+
#: (RubyLsp::Server server) -> RubyLsp::Result
|
|
62
49
|
def pop_result(server)
|
|
63
50
|
result = server.pop_response
|
|
64
51
|
result = server.pop_response until result.is_a?(RubyLsp::Result) || result.is_a?(RubyLsp::Error)
|