ruby-lsp 0.23.11 → 0.26.5

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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +10 -4
  5. data/exe/ruby-lsp-check +0 -4
  6. data/exe/ruby-lsp-launcher +46 -22
  7. data/exe/ruby-lsp-test-exec +6 -0
  8. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -2
  9. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -6
  10. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
  11. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +159 -183
  12. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +10 -14
  13. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +130 -253
  14. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +189 -285
  15. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
  16. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +23 -27
  17. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +31 -59
  18. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +58 -68
  19. data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
  20. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +7 -11
  21. data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
  22. data/lib/ruby_indexer/test/classes_and_modules_test.rb +85 -40
  23. data/lib/ruby_indexer/test/configuration_test.rb +52 -14
  24. data/lib/ruby_indexer/test/constant_test.rb +34 -34
  25. data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
  26. data/lib/ruby_indexer/test/index_test.rb +226 -139
  27. data/lib/ruby_indexer/test/instance_variables_test.rb +61 -37
  28. data/lib/ruby_indexer/test/method_test.rb +166 -123
  29. data/lib/ruby_indexer/test/prefix_tree_test.rb +21 -21
  30. data/lib/ruby_indexer/test/rbs_indexer_test.rb +71 -76
  31. data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
  32. data/lib/ruby_indexer/test/test_case.rb +9 -3
  33. data/lib/ruby_indexer/test/uri_test.rb +15 -2
  34. data/lib/ruby_lsp/addon.rb +88 -86
  35. data/lib/ruby_lsp/base_server.rb +79 -65
  36. data/lib/ruby_lsp/client_capabilities.rb +16 -13
  37. data/lib/ruby_lsp/document.rb +205 -104
  38. data/lib/ruby_lsp/erb_document.rb +45 -47
  39. data/lib/ruby_lsp/global_state.rb +134 -86
  40. data/lib/ruby_lsp/internal.rb +8 -3
  41. data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
  42. data/lib/ruby_lsp/listeners/completion.rb +81 -76
  43. data/lib/ruby_lsp/listeners/definition.rb +78 -72
  44. data/lib/ruby_lsp/listeners/document_highlight.rb +149 -151
  45. data/lib/ruby_lsp/listeners/document_link.rb +93 -86
  46. data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
  47. data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
  48. data/lib/ruby_lsp/listeners/hover.rb +109 -117
  49. data/lib/ruby_lsp/listeners/inlay_hints.rb +8 -13
  50. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
  51. data/lib/ruby_lsp/listeners/signature_help.rb +12 -27
  52. data/lib/ruby_lsp/listeners/spec_style.rb +231 -0
  53. data/lib/ruby_lsp/listeners/test_discovery.rb +107 -0
  54. data/lib/ruby_lsp/listeners/test_style.rb +207 -95
  55. data/lib/ruby_lsp/node_context.rb +12 -39
  56. data/lib/ruby_lsp/rbs_document.rb +10 -11
  57. data/lib/ruby_lsp/requests/code_action_resolve.rb +92 -66
  58. data/lib/ruby_lsp/requests/code_actions.rb +34 -31
  59. data/lib/ruby_lsp/requests/code_lens.rb +31 -21
  60. data/lib/ruby_lsp/requests/completion.rb +8 -21
  61. data/lib/ruby_lsp/requests/completion_resolve.rb +14 -12
  62. data/lib/ruby_lsp/requests/definition.rb +8 -20
  63. data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
  64. data/lib/ruby_lsp/requests/discover_tests.rb +20 -7
  65. data/lib/ruby_lsp/requests/document_highlight.rb +6 -16
  66. data/lib/ruby_lsp/requests/document_link.rb +6 -17
  67. data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
  68. data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
  69. data/lib/ruby_lsp/requests/formatting.rb +6 -9
  70. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +139 -0
  71. data/lib/ruby_lsp/requests/hover.rb +12 -25
  72. data/lib/ruby_lsp/requests/inlay_hints.rb +8 -19
  73. data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
  74. data/lib/ruby_lsp/requests/prepare_rename.rb +5 -10
  75. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
  76. data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
  77. data/lib/ruby_lsp/requests/references.rb +17 -57
  78. data/lib/ruby_lsp/requests/rename.rb +27 -51
  79. data/lib/ruby_lsp/requests/request.rb +13 -25
  80. data/lib/ruby_lsp/requests/selection_ranges.rb +7 -7
  81. data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
  82. data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
  83. data/lib/ruby_lsp/requests/signature_help.rb +9 -27
  84. data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
  85. data/lib/ruby_lsp/requests/support/common.rb +23 -61
  86. data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
  87. data/lib/ruby_lsp/requests/support/package_url.rb +414 -0
  88. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
  89. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +13 -16
  90. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +34 -36
  91. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
  92. data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
  93. data/lib/ruby_lsp/requests/support/source_uri.rb +22 -33
  94. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
  95. data/lib/ruby_lsp/requests/support/test_item.rb +16 -14
  96. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
  97. data/lib/ruby_lsp/requests/workspace_symbol.rb +24 -16
  98. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +6 -9
  99. data/lib/ruby_lsp/response_builders/document_symbol.rb +15 -21
  100. data/lib/ruby_lsp/response_builders/hover.rb +12 -18
  101. data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
  102. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +62 -91
  103. data/lib/ruby_lsp/response_builders/signature_help.rb +6 -8
  104. data/lib/ruby_lsp/response_builders/test_collection.rb +35 -13
  105. data/lib/ruby_lsp/ruby_document.rb +32 -98
  106. data/lib/ruby_lsp/scope.rb +7 -11
  107. data/lib/ruby_lsp/scripts/compose_bundle.rb +7 -5
  108. data/lib/ruby_lsp/server.rb +305 -198
  109. data/lib/ruby_lsp/setup_bundler.rb +146 -86
  110. data/lib/ruby_lsp/static_docs.rb +12 -7
  111. data/lib/ruby_lsp/store.rb +21 -49
  112. data/lib/ruby_lsp/test_helper.rb +3 -16
  113. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +241 -0
  114. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +145 -0
  115. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +92 -0
  116. data/lib/ruby_lsp/type_inferrer.rb +13 -14
  117. data/lib/ruby_lsp/utils.rb +138 -93
  118. data/static_docs/break.md +103 -0
  119. metadata +15 -20
  120. 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 use
16
- # the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to the
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
- FOUR_HOURS = T.let(4 * 60 * 60, Integer)
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
- sig { params(project_path: String, options: T.untyped).void }
34
+ #: (String project_path, **untyped options) -> void
31
35
  def initialize(project_path, **options)
32
36
  @project_path = project_path
33
- @branch = T.let(options[:branch], T.nilable(String))
34
- @launcher = T.let(options[:launcher], T.nilable(T::Boolean))
35
- patch_thor_to_print_progress_to_stderr! if @launcher
37
+ @branch = options[:branch] #: String?
38
+ @launcher = options[:launcher] #: bool?
39
+ force_output_to_stderr! if @launcher
36
40
 
37
41
  # Regular bundle paths
38
- @gemfile = T.let(
39
- begin
40
- Bundler.default_gemfile
41
- rescue Bundler::GemfileNotFound
42
- nil
43
- end,
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 = T.let(@gemfile ? Digest::SHA256.hexdigest(@gemfile.read) : nil, T.nilable(String))
49
- @lockfile_hash = T.let(@lockfile&.exist? ? Digest::SHA256.hexdigest(@lockfile.read) : nil, T.nilable(String))
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 = T.let(@gemfile&.basename&.to_s || "Gemfile", String)
52
+ @gemfile_name = @gemfile&.basename&.to_s || "Gemfile" #: String
52
53
 
53
54
  # Custom bundle paths
54
- @custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(@project_path), Pathname)
55
- @custom_gemfile = T.let(@custom_dir + @gemfile_name, Pathname)
56
- @custom_lockfile = T.let(@custom_dir + (@lockfile&.basename || "Gemfile.lock"), Pathname)
57
- @lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
58
- @last_updated_path = T.let(@custom_dir + "last_updated", Pathname)
59
- @error_path = T.let(@custom_dir + "install_error", Pathname)
60
- @already_composed_path = T.let(@custom_dir + "bundle_is_composed", Pathname)
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 = T.let(dependencies, T::Hash[String, T.untyped])
64
- @bundler_version = T.let(bundler_version, T.nilable(Gem::Version))
65
- @rails_app = T.let(rails_app?, T::Boolean)
66
- @retry = T.let(false, T::Boolean)
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
- sig { returns(T::Hash[String, String]) }
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
- sig { returns(T::Hash[String, T.untyped]) }
133
+ #: -> Hash[String, untyped]
132
134
  def composed_bundle_dependencies
133
- @composed_bundle_dependencies ||= T.let(
134
- begin
135
- original_bundle_gemfile = ENV["BUNDLE_GEMFILE"]
136
-
137
- if @custom_lockfile.exist?
138
- ENV["BUNDLE_GEMFILE"] = @custom_gemfile.to_s
139
- Bundler::LockfileParser.new(@custom_lockfile.read).dependencies
140
- else
141
- {}
142
- end
143
- ensure
144
- ENV["BUNDLE_GEMFILE"] = original_bundle_gemfile
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
- sig { void }
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
- sig { returns([T::Hash[String, T.untyped], T.nilable(Gem::Version)]) }
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
- sig { params(bundle_gemfile: T.nilable(Pathname)).returns(T::Hash[String, String]) }
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,68 @@ module RubyLsp
259
267
  env
260
268
  end
261
269
 
262
- sig { params(env: T::Hash[String, String], force_install: T::Boolean).returns(T::Hash[String, String]) }
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)
265
273
 
266
- # The ENV can only be merged after checking if an update is required because we depend on the original value of
267
- # ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
268
- should_update = should_bundle_update?
269
- T.unsafe(ENV).merge!(env)
274
+ # The should_bundle_update? check needs to run on the original Bundler environment, but everything else (like
275
+ # updating or running install) requires the modified environment. Here we compute the check ahead of time and
276
+ # merge the environment to ensure correct results.
277
+ #
278
+ # The symptoms of having these operations in the wrong order is seeing unwanted modifications in the application's
279
+ # main lockfile because we accidentally run update on the main bundle instead of the composed one.
280
+ needs_update = should_bundle_update?
281
+ ENV.merge!(env)
282
+
283
+ return update(env) if @needs_update_path.exist?
284
+
285
+ FileUtils.touch(@needs_update_path) if needs_update
286
+
287
+ $stderr.puts("Ruby LSP> Checking if the composed bundle is satisfied...")
270
288
 
271
- unless should_update && !force_install
272
- Bundler::CLI::Install.new({}).run
273
- correct_relative_remote_paths if @custom_lockfile.exist?
289
+ begin
290
+ missing_gems = bundle_check
291
+ rescue Errno::EPIPE, Bundler::HTTPError
292
+ # These are errors cases where we cannot recover
293
+ raise
294
+ rescue => e
295
+ # If anything fails with bundle check, try to bundle install
296
+ $stderr.puts("Ruby LSP> Running bundle install because #{e.message}")
297
+ bundle_install
274
298
  return env
275
299
  end
276
300
 
301
+ if missing_gems.empty?
302
+ $stderr.puts("Ruby LSP> Bundle already satisfied")
303
+ else
304
+ $stderr.puts(<<~MESSAGE)
305
+ Ruby LSP> Running bundle install because the following gems are not installed:
306
+ #{missing_gems.map { |g| "#{g.name}: #{g.version}" }.join("\n")}
307
+ MESSAGE
308
+
309
+ bundle_install
310
+ end
311
+
312
+ env
313
+ end
314
+
315
+ # Essentially the same as bundle check, but simplified
316
+ #: -> Array[Gem::Specification]
317
+ def bundle_check
318
+ definition = Bundler.definition
319
+ definition.validate_runtime!
320
+ definition.check!
321
+ definition.missing_specs
322
+ end
323
+
324
+ #: -> void
325
+ def bundle_install
326
+ Bundler::CLI::Install.new({ "no-cache" => true }).run
327
+ correct_relative_remote_paths if @custom_lockfile.exist?
328
+ end
329
+
330
+ #: (Hash[String, String]) -> Hash[String, String]
331
+ def update(env)
277
332
  # Try to auto upgrade the gems we depend on, unless they are in the Gemfile as that would result in undesired
278
333
  # source control changes
279
334
  gems = ["ruby-lsp", "debug", "prism"].reject { |dep| @dependencies[dep] }
@@ -281,14 +336,12 @@ module RubyLsp
281
336
 
282
337
  Bundler::CLI::Update.new({ conservative: true }, gems).run
283
338
  correct_relative_remote_paths if @custom_lockfile.exist?
339
+ @needs_update_path.delete
284
340
  @last_updated_path.write(Time.now.iso8601)
285
341
  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
342
  end
290
343
 
291
- sig { params(env: T::Hash[String, String]).returns(T::Hash[String, String]) }
344
+ #: (Hash[String, String] env) -> Hash[String, String]
292
345
  def run_bundle_install_through_command(env)
293
346
  # If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
294
347
  # to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just created
@@ -345,7 +398,7 @@ module RubyLsp
345
398
  end
346
399
 
347
400
  # Gather all Bundler settings (global and local) and return them as a hash that can be used as the environment
348
- sig { returns(T::Hash[String, String]) }
401
+ #: -> Hash[String, String]
349
402
  def bundler_settings_as_env
350
403
  local_config_path = File.join(@project_path, ".bundle")
351
404
 
@@ -357,10 +410,17 @@ module RubyLsp
357
410
  Bundler::Settings.new
358
411
  end
359
412
 
413
+ # List of Bundler settings that don't make sense for the composed bundle and are better controlled manually by the
414
+ # user
415
+ ignored_settings = ["bin", "cache_all", "cache_all_platforms"]
416
+
360
417
  # Map all settings to their environment variable names with `key_for` and their values. For example, the if the
361
418
  # setting name `e` is `path` with a value of `vendor/bundle`, then it will return `"BUNDLE_PATH" =>
362
419
  # "vendor/bundle"`
363
- settings.all.to_h do |e|
420
+ settings
421
+ .all
422
+ .reject { |setting| ignored_settings.include?(setting) }
423
+ .to_h do |e|
364
424
  key = settings.key_for(e)
365
425
  value = Array(settings[e]).join(":").tr(" ", ":")
366
426
 
@@ -368,7 +428,7 @@ module RubyLsp
368
428
  end
369
429
  end
370
430
 
371
- sig { void }
431
+ #: -> void
372
432
  def install_bundler_if_needed
373
433
  # Try to find the bundler version specified in the lockfile in installed gems. If not found, install it
374
434
  requirement = Gem::Requirement.new(@bundler_version.to_s)
@@ -377,7 +437,7 @@ module RubyLsp
377
437
  Gem.install("bundler", @bundler_version.to_s)
378
438
  end
379
439
 
380
- sig { returns(T::Boolean) }
440
+ #: -> bool
381
441
  def should_bundle_update?
382
442
  # If `ruby-lsp`, `ruby-lsp-rails` and `debug` are in the Gemfile, then we shouldn't try to upgrade them or else it
383
443
  # will produce version control changes
@@ -400,16 +460,19 @@ module RubyLsp
400
460
 
401
461
  # When a lockfile has remote references based on relative file paths, we need to ensure that they are pointing to
402
462
  # the correct place since after copying the relative path is no longer valid
403
- sig { void }
463
+ #: -> void
404
464
  def correct_relative_remote_paths
405
465
  content = @custom_lockfile.read
406
466
  content.gsub!(/remote: (.*)/) do |match|
407
- path = T.must(Regexp.last_match)[1]
467
+ last_match = Regexp.last_match #: as !nil
468
+ path = last_match[1]
408
469
 
409
470
  # We should only apply the correction if the remote is a relative path. It might also be a URI, like
410
471
  # `https://rubygems.org` or an absolute path, in which case we shouldn't do anything
411
472
  if path && !URI(path).scheme
412
- "remote: #{File.expand_path(path, T.must(@gemfile).dirname)}"
473
+ bundle_dir = @gemfile #: as !nil
474
+ .dirname
475
+ "remote: #{File.expand_path(path, bundle_dir)}"
413
476
  else
414
477
  match
415
478
  end
@@ -422,7 +485,7 @@ module RubyLsp
422
485
  end
423
486
 
424
487
  # Detects if the project is a Rails app by looking if the superclass of the main class is `Rails::Application`
425
- sig { returns(T::Boolean) }
488
+ #: -> bool
426
489
  def rails_app?
427
490
  config = Pathname.new("config/application.rb").expand_path
428
491
  application_contents = config.read(external_encoding: Encoding::UTF_8) if config.exist?
@@ -431,20 +494,17 @@ module RubyLsp
431
494
  /class .* < (::)?Rails::Application/.match?(application_contents)
432
495
  end
433
496
 
434
- sig { void }
435
- def patch_thor_to_print_progress_to_stderr!
436
- return unless defined?(Bundler::Thor::Shell::Basic)
437
-
438
- Bundler::Thor::Shell::Basic.prepend(Module.new do
439
- extend T::Sig
497
+ #: -> void
498
+ def force_output_to_stderr!
499
+ # Bundler and RubyGems have different UI objects used for printing. We need to ensure that both are configured to
500
+ # print only to stderr or else they'll break the connection with the editor
501
+ Gem::DefaultUserInteraction.ui = Gem::StreamUI.new($stdin, $stderr, $stderr, false)
440
502
 
441
- sig { returns(IO) }
442
- def stdout
443
- $stderr
444
- end
445
- end)
503
+ ui = Bundler.ui
504
+ ui.output_stream = :stderr if ui.respond_to?(:output_stream=)
505
+ ui.level = :info
446
506
 
447
- Bundler.ui.level = :info
507
+ Bundler::Thor::Shell::Basic.prepend(ThorPatch) if defined?(Bundler::Thor::Shell::Basic)
448
508
  end
449
509
  end
450
510
  end
@@ -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 = T.let(File.join(File.dirname(File.dirname(T.must(__dir__))), "static_docs"), String)
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 = T.let(
10
- {
11
- "yield" => "Invokes the passed block with the given arguments",
12
- }.freeze,
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
@@ -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
- sig { returns(T::Hash[Symbol, RequestConfig]) }
11
- attr_accessor :features_configuration
12
-
13
- sig { returns(String) }
8
+ #: String
14
9
  attr_accessor :client_name
15
10
 
16
- sig { params(global_state: GlobalState).void }
11
+ #: (GlobalState global_state) -> void
17
12
  def initialize(global_state)
18
13
  @global_state = global_state
19
- @state = T.let({}, T::Hash[String, Document[T.untyped]])
20
- @features_configuration = T.let(
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
- sig { params(uri: URI::Generic).returns(Document[T.untyped]) }
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
- Document::LanguageId::ERB
31
+ :erb
47
32
  when ".rbs"
48
- Document::LanguageId::RBS
33
+ :rbs
49
34
  else
50
- Document::LanguageId::Ruby
35
+ :ruby
51
36
  end
52
37
 
53
38
  set(uri: uri, source: File.binread(path), version: 0, language_id: language_id)
54
- T.must(@state[uri.to_s])
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
- sig do
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 Document::LanguageId::ERB
47
+ when :erb
70
48
  ERBDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
71
- when Document::LanguageId::RBS
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
- sig { params(uri: URI::Generic, edits: T::Array[T::Hash[Symbol, T.untyped]], version: Integer).void }
56
+ #: (uri: URI::Generic, edits: Array[Hash[Symbol, untyped]], version: Integer) -> void
79
57
  def push_edits(uri:, edits:, version:)
80
- T.must(@state[uri.to_s]).push_edits(edits, version: version)
58
+ @state[uri.to_s] #: as !nil
59
+ .push_edits(edits, version: version)
81
60
  end
82
61
 
83
- sig { void }
62
+ #: -> void
84
63
  def clear
85
64
  @state.clear
86
65
  end
87
66
 
88
- sig { returns(T::Boolean) }
67
+ #: -> bool
89
68
  def empty?
90
69
  @state.empty?
91
70
  end
92
71
 
93
- sig { params(uri: URI::Generic).void }
72
+ #: (URI::Generic uri) -> void
94
73
  def delete(uri)
95
74
  @state.delete(uri.to_s)
96
75
  end
97
76
 
98
- sig { params(uri: URI::Generic).returns(T::Boolean) }
77
+ #: (URI::Generic uri) -> bool
99
78
  def key?(uri)
100
79
  @state.key?(uri.to_s)
101
80
  end
102
81
 
103
- sig { params(block: T.proc.params(uri: String, document: Document[T.untyped]).void).void }
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
- sig do
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
@@ -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
- extend T::Sig
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
- sig { params(server: RubyLsp::Server).returns(RubyLsp::Result) }
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)