ruby-lsp 0.23.11 → 0.23.17

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-launcher +20 -11
  5. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
  6. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
  8. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +134 -183
  9. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -10
  10. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +97 -217
  11. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +139 -281
  12. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
  13. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +18 -19
  14. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +23 -55
  15. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +47 -61
  16. data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
  17. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +7 -11
  18. data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
  19. data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
  20. data/lib/ruby_indexer/test/configuration_test.rb +48 -7
  21. data/lib/ruby_indexer/test/constant_test.rb +34 -34
  22. data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
  23. data/lib/ruby_indexer/test/index_test.rb +146 -135
  24. data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
  25. data/lib/ruby_indexer/test/method_test.rb +149 -123
  26. data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
  27. data/lib/ruby_indexer/test/rbs_indexer_test.rb +68 -73
  28. data/lib/ruby_indexer/test/test_case.rb +9 -3
  29. data/lib/ruby_indexer/test/uri_test.rb +15 -2
  30. data/lib/ruby_lsp/addon.rb +44 -71
  31. data/lib/ruby_lsp/base_server.rb +29 -32
  32. data/lib/ruby_lsp/client_capabilities.rb +10 -12
  33. data/lib/ruby_lsp/document.rb +40 -54
  34. data/lib/ruby_lsp/erb_document.rb +37 -41
  35. data/lib/ruby_lsp/global_state.rb +52 -57
  36. data/lib/ruby_lsp/internal.rb +2 -0
  37. data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
  38. data/lib/ruby_lsp/listeners/completion.rb +67 -73
  39. data/lib/ruby_lsp/listeners/definition.rb +44 -58
  40. data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
  41. data/lib/ruby_lsp/listeners/document_link.rb +50 -70
  42. data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
  43. data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
  44. data/lib/ruby_lsp/listeners/hover.rb +92 -110
  45. data/lib/ruby_lsp/listeners/inlay_hints.rb +4 -11
  46. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
  47. data/lib/ruby_lsp/listeners/signature_help.rb +12 -27
  48. data/lib/ruby_lsp/listeners/spec_style.rb +155 -0
  49. data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
  50. data/lib/ruby_lsp/listeners/test_style.rb +167 -90
  51. data/lib/ruby_lsp/node_context.rb +12 -39
  52. data/lib/ruby_lsp/rbs_document.rb +9 -7
  53. data/lib/ruby_lsp/requests/code_action_resolve.rb +63 -59
  54. data/lib/ruby_lsp/requests/code_actions.rb +14 -26
  55. data/lib/ruby_lsp/requests/code_lens.rb +20 -19
  56. data/lib/ruby_lsp/requests/completion.rb +7 -20
  57. data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
  58. data/lib/ruby_lsp/requests/definition.rb +7 -17
  59. data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
  60. data/lib/ruby_lsp/requests/discover_tests.rb +18 -5
  61. data/lib/ruby_lsp/requests/document_highlight.rb +5 -15
  62. data/lib/ruby_lsp/requests/document_link.rb +6 -17
  63. data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
  64. data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
  65. data/lib/ruby_lsp/requests/formatting.rb +6 -9
  66. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +85 -0
  67. data/lib/ruby_lsp/requests/hover.rb +10 -20
  68. data/lib/ruby_lsp/requests/inlay_hints.rb +6 -17
  69. data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
  70. data/lib/ruby_lsp/requests/prepare_rename.rb +4 -9
  71. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
  72. data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
  73. data/lib/ruby_lsp/requests/references.rb +9 -53
  74. data/lib/ruby_lsp/requests/rename.rb +20 -46
  75. data/lib/ruby_lsp/requests/request.rb +8 -19
  76. data/lib/ruby_lsp/requests/selection_ranges.rb +6 -6
  77. data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
  78. data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
  79. data/lib/ruby_lsp/requests/signature_help.rb +8 -26
  80. data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
  81. data/lib/ruby_lsp/requests/support/common.rb +15 -55
  82. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
  83. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +11 -14
  84. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +22 -34
  85. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
  86. data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
  87. data/lib/ruby_lsp/requests/support/source_uri.rb +20 -32
  88. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
  89. data/lib/ruby_lsp/requests/support/test_item.rb +16 -14
  90. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
  91. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
  92. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +5 -5
  93. data/lib/ruby_lsp/response_builders/document_symbol.rb +14 -19
  94. data/lib/ruby_lsp/response_builders/hover.rb +11 -14
  95. data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
  96. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +60 -88
  97. data/lib/ruby_lsp/response_builders/signature_help.rb +5 -6
  98. data/lib/ruby_lsp/response_builders/test_collection.rb +43 -10
  99. data/lib/ruby_lsp/ruby_document.rb +24 -92
  100. data/lib/ruby_lsp/scope.rb +7 -11
  101. data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
  102. data/lib/ruby_lsp/server.rb +182 -99
  103. data/lib/ruby_lsp/setup_bundler.rb +65 -60
  104. data/lib/ruby_lsp/static_docs.rb +11 -7
  105. data/lib/ruby_lsp/store.rb +29 -47
  106. data/lib/ruby_lsp/test_helper.rb +2 -12
  107. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +191 -0
  108. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +105 -0
  109. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +94 -0
  110. data/lib/ruby_lsp/type_inferrer.rb +13 -14
  111. data/lib/ruby_lsp/utils.rb +92 -83
  112. metadata +9 -3
@@ -3,13 +3,12 @@
3
3
 
4
4
  module RubyLsp
5
5
  class Server < BaseServer
6
- extend T::Sig
7
-
8
6
  # Only for testing
9
- sig { returns(GlobalState) }
7
+ #: GlobalState
10
8
  attr_reader :global_state
11
9
 
12
- sig { override.params(message: T::Hash[Symbol, T.untyped]).void }
10
+ # @override
11
+ #: (Hash[Symbol, untyped] message) -> void
13
12
  def process_message(message)
14
13
  case message[:method]
15
14
  when "initialize"
@@ -112,6 +111,10 @@ module RubyLsp
112
111
  diagnose_state(message)
113
112
  when "rubyLsp/discoverTests"
114
113
  discover_tests(message)
114
+ when "rubyLsp/resolveTestCommands"
115
+ resolve_test_commands(message)
116
+ when "experimental/goToRelevantFile"
117
+ experimental_go_to_relevant_file(message)
115
118
  when "$/cancelRequest"
116
119
  @global_state.synchronize { @cancelled_requests << message[:params][:id] }
117
120
  when nil
@@ -161,7 +164,7 @@ module RubyLsp
161
164
  end
162
165
 
163
166
  # Process responses to requests that were sent to the client
164
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
167
+ #: (Hash[Symbol, untyped] message) -> void
165
168
  def process_response(message)
166
169
  case message.dig(:result, :method)
167
170
  when "window/showMessageRequest"
@@ -169,7 +172,7 @@ module RubyLsp
169
172
  end
170
173
  end
171
174
 
172
- sig { params(include_project_addons: T::Boolean).void }
175
+ #: (?include_project_addons: bool) -> void
173
176
  def load_addons(include_project_addons: true)
174
177
  # If invoking Bundler.setup failed, then the load path will not be configured properly and trying to load add-ons
175
178
  # with Gem.find_files will find every single version installed of an add-on, leading to requiring several
@@ -209,7 +212,7 @@ module RubyLsp
209
212
 
210
213
  private
211
214
 
212
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
215
+ #: (Hash[Symbol, untyped] message) -> void
213
216
  def run_initialize(message)
214
217
  options = message[:params]
215
218
  global_state_notifications = @global_state.apply_options(options)
@@ -220,7 +223,8 @@ module RubyLsp
220
223
  configured_features = options.dig(:initializationOptions, :enabledFeatures)
221
224
 
222
225
  configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
223
- T.must(@store.features_configuration.dig(:inlayHint)).configuration.merge!(configured_hints) if configured_hints
226
+ @store.features_configuration.dig(:inlayHint) #: as !nil
227
+ .configuration.merge!(configured_hints) if configured_hints
224
228
 
225
229
  enabled_features = case configured_features
226
230
  when Array
@@ -238,7 +242,9 @@ module RubyLsp
238
242
 
239
243
  bundle_env_path = File.join(".ruby-lsp", "bundle_env")
240
244
  bundle_env = if File.exist?(bundle_env_path)
241
- env = File.readlines(bundle_env_path).to_h { |line| T.cast(line.chomp.split("=", 2), [String, String]) }
245
+ env = File.readlines(bundle_env_path).to_h do |line|
246
+ line.chomp.split("=", 2) #: as [String, String]
247
+ end
242
248
  FileUtils.rm(bundle_env_path)
243
249
  env
244
250
  end
@@ -290,6 +296,8 @@ module RubyLsp
290
296
  experimental: {
291
297
  addon_detection: true,
292
298
  compose_bundle: true,
299
+ go_to_relevant_file: true,
300
+ full_test_discovery: true,
293
301
  },
294
302
  ),
295
303
  serverInfo: {
@@ -346,7 +354,7 @@ module RubyLsp
346
354
  end
347
355
  end
348
356
 
349
- sig { void }
357
+ #: -> void
350
358
  def run_initialized
351
359
  load_addons
352
360
  RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
@@ -373,17 +381,17 @@ module RubyLsp
373
381
  check_formatter_is_available
374
382
  end
375
383
 
376
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
384
+ #: (Hash[Symbol, untyped] message) -> void
377
385
  def text_document_did_open(message)
378
386
  @global_state.synchronize do
379
387
  text_document = message.dig(:params, :textDocument)
380
388
  language_id = case text_document[:languageId]
381
389
  when "erb", "eruby"
382
- Document::LanguageId::ERB
390
+ :erb
383
391
  when "rbs"
384
- Document::LanguageId::RBS
392
+ :rbs
385
393
  else
386
- Document::LanguageId::Ruby
394
+ :ruby
387
395
  end
388
396
 
389
397
  document = @store.set(
@@ -412,7 +420,7 @@ module RubyLsp
412
420
  end
413
421
  end
414
422
 
415
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
423
+ #: (Hash[Symbol, untyped] message) -> void
416
424
  def text_document_did_close(message)
417
425
  @global_state.synchronize do
418
426
  uri = message.dig(:params, :textDocument, :uri)
@@ -423,7 +431,7 @@ module RubyLsp
423
431
  end
424
432
  end
425
433
 
426
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
434
+ #: (Hash[Symbol, untyped] message) -> void
427
435
  def text_document_did_change(message)
428
436
  params = message[:params]
429
437
  text_document = params[:textDocument]
@@ -433,7 +441,7 @@ module RubyLsp
433
441
  end
434
442
  end
435
443
 
436
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
444
+ #: (Hash[Symbol, untyped] message) -> void
437
445
  def text_document_selection_range(message)
438
446
  uri = message.dig(:params, :textDocument, :uri)
439
447
  ranges = @store.cache_fetch(uri, "textDocument/selectionRange") do |document|
@@ -459,7 +467,7 @@ module RubyLsp
459
467
  send_message(Result.new(id: message[:id], response: response))
460
468
  end
461
469
 
462
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
470
+ #: (Hash[Symbol, untyped] message) -> void
463
471
  def run_combined_requests(message)
464
472
  uri = URI(message.dig(:params, :textDocument, :uri))
465
473
  document = @store.get(uri)
@@ -483,8 +491,15 @@ module RubyLsp
483
491
  folding_range = Requests::FoldingRanges.new(parse_result.comments, dispatcher)
484
492
  document_symbol = Requests::DocumentSymbol.new(uri, dispatcher)
485
493
  document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
486
- code_lens = Requests::CodeLens.new(@global_state, uri, dispatcher)
487
- inlay_hint = Requests::InlayHints.new(document, T.must(@store.features_configuration.dig(:inlayHint)), dispatcher)
494
+ inlay_hint = Requests::InlayHints.new(
495
+ document,
496
+ @store.features_configuration.dig(:inlayHint), #: as !nil
497
+ dispatcher,
498
+ )
499
+
500
+ # The code lens listener requires the index to be populated, so the DeclarationListener must be inserted first in
501
+ # the dispatcher's state
502
+ code_lens = nil #: Requests::CodeLens?
488
503
 
489
504
  if document.is_a?(RubyDocument) && document.should_index?
490
505
  # Re-index the file as it is modified. This mode of indexing updates entries only. Require path trees are only
@@ -495,10 +510,12 @@ module RubyLsp
495
510
  @global_state.index.handle_change(uri) do |index|
496
511
  index.delete(uri, skip_require_paths_tree: true)
497
512
  RubyIndexer::DeclarationListener.new(index, dispatcher, parse_result, uri, collect_comments: true)
513
+ code_lens = Requests::CodeLens.new(@global_state, uri, dispatcher)
498
514
  dispatcher.dispatch(parse_result.value)
499
515
  end
500
516
  end
501
517
  else
518
+ code_lens = Requests::CodeLens.new(@global_state, uri, dispatcher)
502
519
  dispatcher.dispatch(parse_result.value)
503
520
  end
504
521
 
@@ -507,7 +524,11 @@ module RubyLsp
507
524
  document.cache_set("textDocument/foldingRange", folding_range.perform)
508
525
  document.cache_set("textDocument/documentSymbol", document_symbol.perform)
509
526
  document.cache_set("textDocument/documentLink", document_link.perform)
510
- document.cache_set("textDocument/codeLens", code_lens.perform)
527
+ document.cache_set(
528
+ "textDocument/codeLens",
529
+ code_lens #: as !nil
530
+ .perform,
531
+ )
511
532
  document.cache_set("textDocument/inlayHint", inlay_hint.perform)
512
533
 
513
534
  send_message(Result.new(id: message[:id], response: document.cache_get(message[:method])))
@@ -518,7 +539,7 @@ module RubyLsp
518
539
  alias_method :text_document_code_lens, :run_combined_requests
519
540
  alias_method :text_document_folding_range, :run_combined_requests
520
541
 
521
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
542
+ #: (Hash[Symbol, untyped] message) -> void
522
543
  def text_document_semantic_tokens_full(message)
523
544
  document = @store.get(message.dig(:params, :textDocument, :uri))
524
545
 
@@ -539,7 +560,7 @@ module RubyLsp
539
560
  send_message(Result.new(id: message[:id], response: semantic_highlighting.perform))
540
561
  end
541
562
 
542
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
563
+ #: (Hash[Symbol, untyped] message) -> void
543
564
  def text_document_semantic_tokens_delta(message)
544
565
  document = @store.get(message.dig(:params, :textDocument, :uri))
545
566
 
@@ -564,7 +585,7 @@ module RubyLsp
564
585
  send_message(Result.new(id: message[:id], response: request.perform))
565
586
  end
566
587
 
567
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
588
+ #: (Hash[Symbol, untyped] message) -> void
568
589
  def text_document_semantic_tokens_range(message)
569
590
  params = message[:params]
570
591
  range = params[:range]
@@ -593,7 +614,7 @@ module RubyLsp
593
614
  send_message(Result.new(id: message[:id], response: request.perform))
594
615
  end
595
616
 
596
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
617
+ #: (Hash[Symbol, untyped] message) -> void
597
618
  def text_document_range_formatting(message)
598
619
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
599
620
  if @global_state.formatter == "none"
@@ -621,7 +642,7 @@ module RubyLsp
621
642
  send_message(Result.new(id: message[:id], response: response))
622
643
  end
623
644
 
624
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
645
+ #: (Hash[Symbol, untyped] message) -> void
625
646
  def text_document_formatting(message)
626
647
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
627
648
  if @global_state.formatter == "none"
@@ -662,10 +683,14 @@ module RubyLsp
662
683
  "Formatting error: #{error.message}",
663
684
  type: Constant::MessageType::ERROR,
664
685
  ))
686
+ send_message(Notification.window_log_message(
687
+ "Formatting failed with\r\n: #{error.full_message}",
688
+ type: Constant::MessageType::ERROR,
689
+ ))
665
690
  send_empty_response(message[:id])
666
691
  end
667
692
 
668
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
693
+ #: (Hash[Symbol, untyped] message) -> void
669
694
  def text_document_document_highlight(message)
670
695
  params = message[:params]
671
696
  dispatcher = Prism::Dispatcher.new
@@ -681,7 +706,7 @@ module RubyLsp
681
706
  send_message(Result.new(id: message[:id], response: request.perform))
682
707
  end
683
708
 
684
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
709
+ #: (Hash[Symbol, untyped] message) -> void
685
710
  def text_document_on_type_formatting(message)
686
711
  params = message[:params]
687
712
  document = @store.get(params.dig(:textDocument, :uri))
@@ -704,7 +729,7 @@ module RubyLsp
704
729
  )
705
730
  end
706
731
 
707
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
732
+ #: (Hash[Symbol, untyped] message) -> void
708
733
  def text_document_hover(message)
709
734
  params = message[:params]
710
735
  dispatcher = Prism::Dispatcher.new
@@ -729,7 +754,7 @@ module RubyLsp
729
754
  )
730
755
  end
731
756
 
732
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
757
+ #: (Hash[Symbol, untyped] message) -> void
733
758
  def text_document_rename(message)
734
759
  params = message[:params]
735
760
  document = @store.get(params.dig(:textDocument, :uri))
@@ -749,7 +774,7 @@ module RubyLsp
749
774
  send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
750
775
  end
751
776
 
752
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
777
+ #: (Hash[Symbol, untyped] message) -> void
753
778
  def text_document_prepare_rename(message)
754
779
  params = message[:params]
755
780
  document = @store.get(params.dig(:textDocument, :uri))
@@ -767,7 +792,7 @@ module RubyLsp
767
792
  )
768
793
  end
769
794
 
770
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
795
+ #: (Hash[Symbol, untyped] message) -> void
771
796
  def text_document_references(message)
772
797
  params = message[:params]
773
798
  document = @store.get(params.dig(:textDocument, :uri))
@@ -785,15 +810,19 @@ module RubyLsp
785
810
  )
786
811
  end
787
812
 
788
- sig { params(document: Document[T.untyped]).returns(RubyDocument::SorbetLevel) }
813
+ #: (Document[untyped] document) -> SorbetLevel
789
814
  def sorbet_level(document)
790
- return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
791
- return RubyDocument::SorbetLevel::Ignore unless document.is_a?(RubyDocument)
815
+ return SorbetLevel.ignore unless document.is_a?(RubyDocument)
816
+ return SorbetLevel.ignore unless @global_state.has_type_checker
792
817
 
793
- document.sorbet_level
818
+ sigil = document.parse_result.magic_comments.find do |comment|
819
+ comment.key == "typed"
820
+ end&.value
821
+
822
+ SorbetLevel.new(sigil)
794
823
  end
795
824
 
796
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
825
+ #: (Hash[Symbol, untyped] message) -> void
797
826
  def text_document_inlay_hint(message)
798
827
  params = message[:params]
799
828
  document = @store.get(params.dig(:textDocument, :uri))
@@ -811,7 +840,7 @@ module RubyLsp
811
840
  return
812
841
  end
813
842
 
814
- hints_configurations = T.must(@store.features_configuration.dig(:inlayHint))
843
+ hints_configurations = @store.features_configuration.dig(:inlayHint) #: as !nil
815
844
  dispatcher = Prism::Dispatcher.new
816
845
 
817
846
  unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
@@ -827,7 +856,7 @@ module RubyLsp
827
856
  send_message(Result.new(id: message[:id], response: result.select { |hint| range.cover?(hint.position[:line]) }))
828
857
  end
829
858
 
830
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
859
+ #: (Hash[Symbol, untyped] message) -> void
831
860
  def text_document_code_action(message)
832
861
  params = message[:params]
833
862
  document = @store.get(params.dig(:textDocument, :uri))
@@ -849,7 +878,7 @@ module RubyLsp
849
878
  )
850
879
  end
851
880
 
852
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
881
+ #: (Hash[Symbol, untyped] message) -> void
853
882
  def code_action_resolve(message)
854
883
  params = message[:params]
855
884
  uri = URI(params.dig(:data, :uri))
@@ -861,20 +890,12 @@ module RubyLsp
861
890
  end
862
891
 
863
892
  result = Requests::CodeActionResolve.new(document, @global_state, params).perform
864
-
865
- case result
866
- when Requests::CodeActionResolve::Error::EmptySelection
867
- fail_request_and_notify(message[:id], "Invalid selection for extract variable refactor")
868
- when Requests::CodeActionResolve::Error::InvalidTargetRange
869
- fail_request_and_notify(message[:id], "Couldn't find an appropriate location to place extracted refactor")
870
- when Requests::CodeActionResolve::Error::UnknownCodeAction
871
- fail_request_and_notify(message[:id], "Unknown code action")
872
- else
873
- send_message(Result.new(id: message[:id], response: result))
874
- end
893
+ send_message(Result.new(id: message[:id], response: result))
894
+ rescue Requests::CodeActionResolve::CodeActionError => e
895
+ fail_request_and_notify(message[:id], e.message)
875
896
  end
876
897
 
877
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
898
+ #: (Hash[Symbol, untyped] message) -> void
878
899
  def text_document_diagnostic(message)
879
900
  # Do not compute diagnostics for files outside of the workspace. For example, if someone is looking at a gem's
880
901
  # source code, we don't want to show diagnostics for it
@@ -911,10 +932,14 @@ module RubyLsp
911
932
  "Error running diagnostics: #{error.message}",
912
933
  type: Constant::MessageType::ERROR,
913
934
  ))
935
+ send_message(Notification.window_log_message(
936
+ "Diagnostics failed with\r\n: #{error.full_message}",
937
+ type: Constant::MessageType::ERROR,
938
+ ))
914
939
  send_empty_response(message[:id])
915
940
  end
916
941
 
917
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
942
+ #: (Hash[Symbol, untyped] message) -> void
918
943
  def text_document_completion(message)
919
944
  params = message[:params]
920
945
  dispatcher = Prism::Dispatcher.new
@@ -939,7 +964,7 @@ module RubyLsp
939
964
  )
940
965
  end
941
966
 
942
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
967
+ #: (Hash[Symbol, untyped] message) -> void
943
968
  def text_document_completion_item_resolve(message)
944
969
  # When responding to a delegated completion request, it means we're handling a completion item that isn't related
945
970
  # to Ruby (probably related to an ERB host language like HTML). We need to return the original completion item
@@ -958,7 +983,7 @@ module RubyLsp
958
983
  ))
959
984
  end
960
985
 
961
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
986
+ #: (Hash[Symbol, untyped] message) -> void
962
987
  def text_document_signature_help(message)
963
988
  params = message[:params]
964
989
  dispatcher = Prism::Dispatcher.new
@@ -984,7 +1009,7 @@ module RubyLsp
984
1009
  )
985
1010
  end
986
1011
 
987
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1012
+ #: (Hash[Symbol, untyped] message) -> void
988
1013
  def text_document_definition(message)
989
1014
  params = message[:params]
990
1015
  dispatcher = Prism::Dispatcher.new
@@ -1009,8 +1034,23 @@ module RubyLsp
1009
1034
  )
1010
1035
  end
1011
1036
 
1012
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1037
+ #: (Hash[Symbol, untyped] message) -> void
1013
1038
  def workspace_did_change_watched_files(message)
1039
+ # If indexing is not complete yet, delay processing did change watched file notifications. We need initial
1040
+ # indexing to be in place so that we can handle file changes appropriately without risking duplicates. We also
1041
+ # have to sleep before re-inserting the notification in the queue otherwise the worker can get stuck in its own
1042
+ # loop of pushing and popping the same notification
1043
+ unless @global_state.index.initial_indexing_completed
1044
+ Thread.new do
1045
+ sleep(2)
1046
+ # We have to ensure that the queue is not closed yet, since nothing stops the user from saving a file and then
1047
+ # immediately telling the LSP to shutdown
1048
+ @incoming_queue << message unless @incoming_queue.closed?
1049
+ end
1050
+
1051
+ return
1052
+ end
1053
+
1014
1054
  changes = message.dig(:params, :changes)
1015
1055
  # We allow add-ons to register for watching files and we have no restrictions for what they register for. If the
1016
1056
  # same pattern is registered more than once, the LSP will receive duplicate change notifications. Receiving them
@@ -1037,7 +1077,8 @@ module RubyLsp
1037
1077
  end
1038
1078
 
1039
1079
  Addon.file_watcher_addons.each do |addon|
1040
- T.unsafe(addon).workspace_did_change_watched_files(changes)
1080
+ addon #: as untyped
1081
+ .workspace_did_change_watched_files(changes)
1041
1082
  rescue => e
1042
1083
  send_log_message(
1043
1084
  "Error in #{addon.name} add-on while processing watched file notifications: #{e.full_message}",
@@ -1046,32 +1087,34 @@ module RubyLsp
1046
1087
  end
1047
1088
  end
1048
1089
 
1049
- sig { params(index: RubyIndexer::Index, file_path: String, change_type: Integer).void }
1090
+ #: (RubyIndexer::Index index, String file_path, Integer change_type) -> void
1050
1091
  def handle_ruby_file_change(index, file_path, change_type)
1051
- load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
1052
- uri = URI::Generic.from_path(load_path_entry: load_path_entry, path: file_path)
1053
-
1054
- content = File.read(file_path)
1055
-
1056
- case change_type
1057
- when Constant::FileChangeType::CREATED
1058
- # If we receive a late created notification for a file that has already been claimed by the client, we want to
1059
- # handle change for that URI so that the require path tree is updated
1060
- @store.key?(uri) ? index.handle_change(uri, content) : index.index_single(uri, content)
1061
- when Constant::FileChangeType::CHANGED
1062
- # We only handle changes on file watched notifications if the client is not the one managing this URI.
1063
- # Otherwise, these changes are handled when running the combined requests
1064
- index.handle_change(uri, content) unless @store.key?(uri)
1065
- when Constant::FileChangeType::DELETED
1066
- index.delete(uri)
1067
- end
1068
- rescue Errno::ENOENT
1069
- # If a file is created and then delete immediately afterwards, we will process the created notification before we
1070
- # receive the deleted one, but the file no longer exists. This may happen when running a test suite that creates
1071
- # and deletes files automatically.
1092
+ @global_state.synchronize do
1093
+ load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
1094
+ uri = URI::Generic.from_path(load_path_entry: load_path_entry, path: file_path)
1095
+
1096
+ case change_type
1097
+ when Constant::FileChangeType::CREATED
1098
+ content = File.read(file_path)
1099
+ # If we receive a late created notification for a file that has already been claimed by the client, we want to
1100
+ # handle change for that URI so that the require path tree is updated
1101
+ @store.key?(uri) ? index.handle_change(uri, content) : index.index_single(uri, content)
1102
+ when Constant::FileChangeType::CHANGED
1103
+ content = File.read(file_path)
1104
+ # We only handle changes on file watched notifications if the client is not the one managing this URI.
1105
+ # Otherwise, these changes are handled when running the combined requests
1106
+ index.handle_change(uri, content) unless @store.key?(uri)
1107
+ when Constant::FileChangeType::DELETED
1108
+ index.delete(uri)
1109
+ end
1110
+ rescue Errno::ENOENT
1111
+ # If a file is created and then delete immediately afterwards, we will process the created notification before
1112
+ # we receive the deleted one, but the file no longer exists. This may happen when running a test suite that
1113
+ # creates and deletes files automatically.
1114
+ end
1072
1115
  end
1073
1116
 
1074
- sig { params(uri: URI::Generic).void }
1117
+ #: (URI::Generic uri) -> void
1075
1118
  def handle_rubocop_config_change(uri)
1076
1119
  return unless defined?(Requests::Support::RuboCopFormatter)
1077
1120
 
@@ -1091,7 +1134,7 @@ module RubyLsp
1091
1134
  end
1092
1135
  end
1093
1136
 
1094
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1137
+ #: (Hash[Symbol, untyped] message) -> void
1095
1138
  def workspace_symbol(message)
1096
1139
  send_message(
1097
1140
  Result.new(
@@ -1104,7 +1147,7 @@ module RubyLsp
1104
1147
  )
1105
1148
  end
1106
1149
 
1107
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1150
+ #: (Hash[Symbol, untyped] message) -> void
1108
1151
  def text_document_show_syntax_tree(message)
1109
1152
  params = message[:params]
1110
1153
  document = @store.get(params.dig(:textDocument, :uri))
@@ -1123,7 +1166,26 @@ module RubyLsp
1123
1166
  send_message(Result.new(id: message[:id], response: response))
1124
1167
  end
1125
1168
 
1126
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1169
+ #: (Hash[Symbol, untyped] message) -> void
1170
+ def experimental_go_to_relevant_file(message)
1171
+ path = message.dig(:params, :textDocument, :uri).to_standardized_path
1172
+ unless path.nil? || path.start_with?(@global_state.workspace_path)
1173
+ send_empty_response(message[:id])
1174
+ return
1175
+ end
1176
+
1177
+ unless path
1178
+ send_empty_response(message[:id])
1179
+ return
1180
+ end
1181
+
1182
+ response = {
1183
+ locations: Requests::GoToRelevantFile.new(path, @global_state.workspace_path).perform,
1184
+ }
1185
+ send_message(Result.new(id: message[:id], response: response))
1186
+ end
1187
+
1188
+ #: (Hash[Symbol, untyped] message) -> void
1127
1189
  def text_document_prepare_type_hierarchy(message)
1128
1190
  params = message[:params]
1129
1191
  document = @store.get(params.dig(:textDocument, :uri))
@@ -1142,7 +1204,7 @@ module RubyLsp
1142
1204
  send_message(Result.new(id: message[:id], response: response))
1143
1205
  end
1144
1206
 
1145
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1207
+ #: (Hash[Symbol, untyped] message) -> void
1146
1208
  def type_hierarchy_supertypes(message)
1147
1209
  response = Requests::TypeHierarchySupertypes.new(
1148
1210
  @global_state.index,
@@ -1151,14 +1213,14 @@ module RubyLsp
1151
1213
  send_message(Result.new(id: message[:id], response: response))
1152
1214
  end
1153
1215
 
1154
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1216
+ #: (Hash[Symbol, untyped] message) -> void
1155
1217
  def type_hierarchy_subtypes(message)
1156
1218
  # TODO: implement subtypes
1157
1219
  # The current index representation doesn't allow us to find the children of an entry.
1158
1220
  send_message(Result.new(id: message[:id], response: nil))
1159
1221
  end
1160
1222
 
1161
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1223
+ #: (Hash[Symbol, untyped] message) -> void
1162
1224
  def workspace_dependencies(message)
1163
1225
  unless @global_state.top_level_bundle
1164
1226
  send_message(Result.new(id: message[:id], response: []))
@@ -1186,12 +1248,13 @@ module RubyLsp
1186
1248
  send_message(Result.new(id: message[:id], response: response))
1187
1249
  end
1188
1250
 
1189
- sig { override.void }
1251
+ # @override
1252
+ #: -> void
1190
1253
  def shutdown
1191
1254
  Addon.unload_addons
1192
1255
  end
1193
1256
 
1194
- sig { void }
1257
+ #: -> void
1195
1258
  def perform_initial_indexing
1196
1259
  # The begin progress invocation happens during `initialize`, so that the notification is sent before we are
1197
1260
  # stuck indexing files
@@ -1223,7 +1286,7 @@ module RubyLsp
1223
1286
  end
1224
1287
  end
1225
1288
 
1226
- sig { params(id: String, title: String, percentage: Integer).void }
1289
+ #: (String id, String title, ?percentage: Integer) -> void
1227
1290
  def begin_progress(id, title, percentage: 0)
1228
1291
  return unless @global_state.client_capabilities.supports_progress
1229
1292
 
@@ -1236,14 +1299,14 @@ module RubyLsp
1236
1299
  send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
1237
1300
  end
1238
1301
 
1239
- sig { params(id: String, percentage: Integer).void }
1302
+ #: (String id, Integer percentage) -> void
1240
1303
  def progress(id, percentage)
1241
1304
  return unless @global_state.client_capabilities.supports_progress
1242
1305
 
1243
1306
  send_message(Notification.progress_report(id, percentage: percentage, message: "#{percentage}% completed"))
1244
1307
  end
1245
1308
 
1246
- sig { params(id: String).void }
1309
+ #: (String id) -> void
1247
1310
  def end_progress(id)
1248
1311
  return unless @global_state.client_capabilities.supports_progress
1249
1312
 
@@ -1253,7 +1316,7 @@ module RubyLsp
1253
1316
  # notification
1254
1317
  end
1255
1318
 
1256
- sig { void }
1319
+ #: -> void
1257
1320
  def check_formatter_is_available
1258
1321
  return if @setup_error
1259
1322
  # Warn of an unavailable `formatter` setting, e.g. `rubocop_internal` on a project which doesn't have RuboCop.
@@ -1271,7 +1334,7 @@ module RubyLsp
1271
1334
  end
1272
1335
  end
1273
1336
 
1274
- sig { params(indexing_options: T.nilable(T::Hash[Symbol, T.untyped])).void }
1337
+ #: (Hash[Symbol, untyped]? indexing_options) -> void
1275
1338
  def process_indexing_configuration(indexing_options)
1276
1339
  # Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
1277
1340
  index_path = File.join(@global_state.workspace_path, ".index.yml")
@@ -1312,7 +1375,7 @@ module RubyLsp
1312
1375
  configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
1313
1376
  end
1314
1377
 
1315
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1378
+ #: (Hash[Symbol, untyped] message) -> void
1316
1379
  def window_show_message_request(message)
1317
1380
  result = message[:result]
1318
1381
  return unless result
@@ -1327,7 +1390,7 @@ module RubyLsp
1327
1390
  # NOTE: all servers methods are void because they can produce several messages for the client. The only reason this
1328
1391
  # method returns the created thread is to that we can join it in tests and avoid flakiness. The implementation is
1329
1392
  # not supposed to rely on the return of this method
1330
- sig { params(message: T::Hash[Symbol, T.untyped]).returns(T.nilable(Thread)) }
1393
+ #: (Hash[Symbol, untyped] message) -> Thread?
1331
1394
  def compose_bundle(message)
1332
1395
  already_composed_path = File.join(@global_state.workspace_path, ".ruby-lsp", "bundle_is_composed")
1333
1396
  id = message[:id]
@@ -1352,7 +1415,9 @@ module RubyLsp
1352
1415
  Open3.capture3(
1353
1416
  Gem.ruby,
1354
1417
  "-I",
1355
- File.dirname(T.must(__dir__)),
1418
+ File.dirname(
1419
+ __dir__, #: as !nil
1420
+ ),
1356
1421
  File.expand_path("../../exe/ruby-lsp-launcher", __dir__),
1357
1422
  @global_state.workspace_uri.to_s,
1358
1423
  chdir: @global_state.workspace_path,
@@ -1374,7 +1439,7 @@ module RubyLsp
1374
1439
  end
1375
1440
 
1376
1441
  # Returns internal state information for debugging purposes
1377
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1442
+ #: (Hash[Symbol, untyped] message) -> void
1378
1443
  def diagnose_state(message)
1379
1444
  documents = {}
1380
1445
  @store.each { |uri, document| documents[uri] = document.source }
@@ -1394,7 +1459,7 @@ module RubyLsp
1394
1459
 
1395
1460
  # Discovers all available test groups and examples in a given file taking into consideration the merged response of
1396
1461
  # all add-ons
1397
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1462
+ #: (Hash[Symbol, untyped] message) -> void
1398
1463
  def discover_tests(message)
1399
1464
  document = @store.get(message.dig(:params, :textDocument, :uri))
1400
1465
 
@@ -1414,5 +1479,23 @@ module RubyLsp
1414
1479
 
1415
1480
  send_message(Result.new(id: message[:id], response: items.map(&:to_hash)))
1416
1481
  end
1482
+
1483
+ #: (Hash[Symbol, untyped] message) -> void
1484
+ def resolve_test_commands(message)
1485
+ items = message.dig(:params, :items)
1486
+ commands = Listeners::TestStyle.resolve_test_commands(items)
1487
+
1488
+ Addon.addons.each do |addon|
1489
+ commands.concat(addon.resolve_test_commands(items))
1490
+ end
1491
+
1492
+ send_message(Result.new(
1493
+ id: message[:id],
1494
+ response: {
1495
+ commands: commands,
1496
+ reporterPaths: [Listeners::TestStyle::MINITEST_REPORTER_PATH, Listeners::TestStyle::TEST_UNIT_REPORTER_PATH],
1497
+ },
1498
+ ))
1499
+ end
1417
1500
  end
1418
1501
  end