ruby-lsp 0.23.11 → 0.23.16

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 +123 -169
  9. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -7
  10. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +92 -202
  11. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +116 -222
  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 +22 -45
  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 +5 -9
  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 +139 -135
  24. data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
  25. data/lib/ruby_indexer/test/method_test.rb +143 -117
  26. data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
  27. data/lib/ruby_indexer/test/rbs_indexer_test.rb +65 -71
  28. data/lib/ruby_indexer/test/test_case.rb +2 -2
  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 +39 -45
  34. data/lib/ruby_lsp/erb_document.rb +36 -40
  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 +60 -66
  39. data/lib/ruby_lsp/listeners/definition.rb +38 -52
  40. data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
  41. data/lib/ruby_lsp/listeners/document_link.rb +46 -63
  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 +83 -102
  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 +11 -26
  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 +160 -88
  51. data/lib/ruby_lsp/node_context.rb +12 -39
  52. data/lib/ruby_lsp/rbs_document.rb +8 -6
  53. data/lib/ruby_lsp/requests/code_action_resolve.rb +24 -25
  54. data/lib/ruby_lsp/requests/code_actions.rb +14 -26
  55. data/lib/ruby_lsp/requests/code_lens.rb +6 -17
  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 +8 -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 +87 -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 +8 -37
  74. data/lib/ruby_lsp/requests/rename.rb +19 -42
  75. data/lib/ruby_lsp/requests/request.rb +7 -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 +16 -51
  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 +10 -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 +13 -18
  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 +6 -10
  99. data/lib/ruby_lsp/ruby_document.rb +24 -62
  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 +147 -79
  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 +24 -42
  106. data/lib/ruby_lsp/test_helper.rb +2 -12
  107. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +164 -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 +49 -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
@@ -290,6 +294,8 @@ module RubyLsp
290
294
  experimental: {
291
295
  addon_detection: true,
292
296
  compose_bundle: true,
297
+ go_to_relevant_file: true,
298
+ full_test_discovery: true,
293
299
  },
294
300
  ),
295
301
  serverInfo: {
@@ -346,7 +352,7 @@ module RubyLsp
346
352
  end
347
353
  end
348
354
 
349
- sig { void }
355
+ #: -> void
350
356
  def run_initialized
351
357
  load_addons
352
358
  RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
@@ -373,7 +379,7 @@ module RubyLsp
373
379
  check_formatter_is_available
374
380
  end
375
381
 
376
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
382
+ #: (Hash[Symbol, untyped] message) -> void
377
383
  def text_document_did_open(message)
378
384
  @global_state.synchronize do
379
385
  text_document = message.dig(:params, :textDocument)
@@ -412,7 +418,7 @@ module RubyLsp
412
418
  end
413
419
  end
414
420
 
415
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
421
+ #: (Hash[Symbol, untyped] message) -> void
416
422
  def text_document_did_close(message)
417
423
  @global_state.synchronize do
418
424
  uri = message.dig(:params, :textDocument, :uri)
@@ -423,7 +429,7 @@ module RubyLsp
423
429
  end
424
430
  end
425
431
 
426
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
432
+ #: (Hash[Symbol, untyped] message) -> void
427
433
  def text_document_did_change(message)
428
434
  params = message[:params]
429
435
  text_document = params[:textDocument]
@@ -433,7 +439,7 @@ module RubyLsp
433
439
  end
434
440
  end
435
441
 
436
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
442
+ #: (Hash[Symbol, untyped] message) -> void
437
443
  def text_document_selection_range(message)
438
444
  uri = message.dig(:params, :textDocument, :uri)
439
445
  ranges = @store.cache_fetch(uri, "textDocument/selectionRange") do |document|
@@ -459,7 +465,7 @@ module RubyLsp
459
465
  send_message(Result.new(id: message[:id], response: response))
460
466
  end
461
467
 
462
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
468
+ #: (Hash[Symbol, untyped] message) -> void
463
469
  def run_combined_requests(message)
464
470
  uri = URI(message.dig(:params, :textDocument, :uri))
465
471
  document = @store.get(uri)
@@ -484,7 +490,11 @@ module RubyLsp
484
490
  document_symbol = Requests::DocumentSymbol.new(uri, dispatcher)
485
491
  document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
486
492
  code_lens = Requests::CodeLens.new(@global_state, uri, dispatcher)
487
- inlay_hint = Requests::InlayHints.new(document, T.must(@store.features_configuration.dig(:inlayHint)), dispatcher)
493
+ inlay_hint = Requests::InlayHints.new(
494
+ document,
495
+ @store.features_configuration.dig(:inlayHint), #: as !nil
496
+ dispatcher,
497
+ )
488
498
 
489
499
  if document.is_a?(RubyDocument) && document.should_index?
490
500
  # Re-index the file as it is modified. This mode of indexing updates entries only. Require path trees are only
@@ -518,7 +528,7 @@ module RubyLsp
518
528
  alias_method :text_document_code_lens, :run_combined_requests
519
529
  alias_method :text_document_folding_range, :run_combined_requests
520
530
 
521
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
531
+ #: (Hash[Symbol, untyped] message) -> void
522
532
  def text_document_semantic_tokens_full(message)
523
533
  document = @store.get(message.dig(:params, :textDocument, :uri))
524
534
 
@@ -539,7 +549,7 @@ module RubyLsp
539
549
  send_message(Result.new(id: message[:id], response: semantic_highlighting.perform))
540
550
  end
541
551
 
542
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
552
+ #: (Hash[Symbol, untyped] message) -> void
543
553
  def text_document_semantic_tokens_delta(message)
544
554
  document = @store.get(message.dig(:params, :textDocument, :uri))
545
555
 
@@ -564,7 +574,7 @@ module RubyLsp
564
574
  send_message(Result.new(id: message[:id], response: request.perform))
565
575
  end
566
576
 
567
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
577
+ #: (Hash[Symbol, untyped] message) -> void
568
578
  def text_document_semantic_tokens_range(message)
569
579
  params = message[:params]
570
580
  range = params[:range]
@@ -593,7 +603,7 @@ module RubyLsp
593
603
  send_message(Result.new(id: message[:id], response: request.perform))
594
604
  end
595
605
 
596
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
606
+ #: (Hash[Symbol, untyped] message) -> void
597
607
  def text_document_range_formatting(message)
598
608
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
599
609
  if @global_state.formatter == "none"
@@ -621,7 +631,7 @@ module RubyLsp
621
631
  send_message(Result.new(id: message[:id], response: response))
622
632
  end
623
633
 
624
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
634
+ #: (Hash[Symbol, untyped] message) -> void
625
635
  def text_document_formatting(message)
626
636
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
627
637
  if @global_state.formatter == "none"
@@ -665,7 +675,7 @@ module RubyLsp
665
675
  send_empty_response(message[:id])
666
676
  end
667
677
 
668
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
678
+ #: (Hash[Symbol, untyped] message) -> void
669
679
  def text_document_document_highlight(message)
670
680
  params = message[:params]
671
681
  dispatcher = Prism::Dispatcher.new
@@ -681,7 +691,7 @@ module RubyLsp
681
691
  send_message(Result.new(id: message[:id], response: request.perform))
682
692
  end
683
693
 
684
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
694
+ #: (Hash[Symbol, untyped] message) -> void
685
695
  def text_document_on_type_formatting(message)
686
696
  params = message[:params]
687
697
  document = @store.get(params.dig(:textDocument, :uri))
@@ -704,7 +714,7 @@ module RubyLsp
704
714
  )
705
715
  end
706
716
 
707
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
717
+ #: (Hash[Symbol, untyped] message) -> void
708
718
  def text_document_hover(message)
709
719
  params = message[:params]
710
720
  dispatcher = Prism::Dispatcher.new
@@ -729,7 +739,7 @@ module RubyLsp
729
739
  )
730
740
  end
731
741
 
732
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
742
+ #: (Hash[Symbol, untyped] message) -> void
733
743
  def text_document_rename(message)
734
744
  params = message[:params]
735
745
  document = @store.get(params.dig(:textDocument, :uri))
@@ -749,7 +759,7 @@ module RubyLsp
749
759
  send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
750
760
  end
751
761
 
752
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
762
+ #: (Hash[Symbol, untyped] message) -> void
753
763
  def text_document_prepare_rename(message)
754
764
  params = message[:params]
755
765
  document = @store.get(params.dig(:textDocument, :uri))
@@ -767,7 +777,7 @@ module RubyLsp
767
777
  )
768
778
  end
769
779
 
770
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
780
+ #: (Hash[Symbol, untyped] message) -> void
771
781
  def text_document_references(message)
772
782
  params = message[:params]
773
783
  document = @store.get(params.dig(:textDocument, :uri))
@@ -785,7 +795,7 @@ module RubyLsp
785
795
  )
786
796
  end
787
797
 
788
- sig { params(document: Document[T.untyped]).returns(RubyDocument::SorbetLevel) }
798
+ #: (Document[untyped] document) -> RubyDocument::SorbetLevel
789
799
  def sorbet_level(document)
790
800
  return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
791
801
  return RubyDocument::SorbetLevel::Ignore unless document.is_a?(RubyDocument)
@@ -793,7 +803,7 @@ module RubyLsp
793
803
  document.sorbet_level
794
804
  end
795
805
 
796
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
806
+ #: (Hash[Symbol, untyped] message) -> void
797
807
  def text_document_inlay_hint(message)
798
808
  params = message[:params]
799
809
  document = @store.get(params.dig(:textDocument, :uri))
@@ -811,7 +821,7 @@ module RubyLsp
811
821
  return
812
822
  end
813
823
 
814
- hints_configurations = T.must(@store.features_configuration.dig(:inlayHint))
824
+ hints_configurations = @store.features_configuration.dig(:inlayHint) #: as !nil
815
825
  dispatcher = Prism::Dispatcher.new
816
826
 
817
827
  unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
@@ -827,7 +837,7 @@ module RubyLsp
827
837
  send_message(Result.new(id: message[:id], response: result.select { |hint| range.cover?(hint.position[:line]) }))
828
838
  end
829
839
 
830
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
840
+ #: (Hash[Symbol, untyped] message) -> void
831
841
  def text_document_code_action(message)
832
842
  params = message[:params]
833
843
  document = @store.get(params.dig(:textDocument, :uri))
@@ -849,7 +859,7 @@ module RubyLsp
849
859
  )
850
860
  end
851
861
 
852
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
862
+ #: (Hash[Symbol, untyped] message) -> void
853
863
  def code_action_resolve(message)
854
864
  params = message[:params]
855
865
  uri = URI(params.dig(:data, :uri))
@@ -874,7 +884,7 @@ module RubyLsp
874
884
  end
875
885
  end
876
886
 
877
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
887
+ #: (Hash[Symbol, untyped] message) -> void
878
888
  def text_document_diagnostic(message)
879
889
  # Do not compute diagnostics for files outside of the workspace. For example, if someone is looking at a gem's
880
890
  # source code, we don't want to show diagnostics for it
@@ -914,7 +924,7 @@ module RubyLsp
914
924
  send_empty_response(message[:id])
915
925
  end
916
926
 
917
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
927
+ #: (Hash[Symbol, untyped] message) -> void
918
928
  def text_document_completion(message)
919
929
  params = message[:params]
920
930
  dispatcher = Prism::Dispatcher.new
@@ -939,7 +949,7 @@ module RubyLsp
939
949
  )
940
950
  end
941
951
 
942
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
952
+ #: (Hash[Symbol, untyped] message) -> void
943
953
  def text_document_completion_item_resolve(message)
944
954
  # When responding to a delegated completion request, it means we're handling a completion item that isn't related
945
955
  # to Ruby (probably related to an ERB host language like HTML). We need to return the original completion item
@@ -958,7 +968,7 @@ module RubyLsp
958
968
  ))
959
969
  end
960
970
 
961
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
971
+ #: (Hash[Symbol, untyped] message) -> void
962
972
  def text_document_signature_help(message)
963
973
  params = message[:params]
964
974
  dispatcher = Prism::Dispatcher.new
@@ -984,7 +994,7 @@ module RubyLsp
984
994
  )
985
995
  end
986
996
 
987
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
997
+ #: (Hash[Symbol, untyped] message) -> void
988
998
  def text_document_definition(message)
989
999
  params = message[:params]
990
1000
  dispatcher = Prism::Dispatcher.new
@@ -1009,8 +1019,23 @@ module RubyLsp
1009
1019
  )
1010
1020
  end
1011
1021
 
1012
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1022
+ #: (Hash[Symbol, untyped] message) -> void
1013
1023
  def workspace_did_change_watched_files(message)
1024
+ # If indexing is not complete yet, delay processing did change watched file notifications. We need initial
1025
+ # indexing to be in place so that we can handle file changes appropriately without risking duplicates. We also
1026
+ # have to sleep before re-inserting the notification in the queue otherwise the worker can get stuck in its own
1027
+ # loop of pushing and popping the same notification
1028
+ unless @global_state.index.initial_indexing_completed
1029
+ Thread.new do
1030
+ sleep(2)
1031
+ # We have to ensure that the queue is not closed yet, since nothing stops the user from saving a file and then
1032
+ # immediately telling the LSP to shutdown
1033
+ @incoming_queue << message unless @incoming_queue.closed?
1034
+ end
1035
+
1036
+ return
1037
+ end
1038
+
1014
1039
  changes = message.dig(:params, :changes)
1015
1040
  # We allow add-ons to register for watching files and we have no restrictions for what they register for. If the
1016
1041
  # same pattern is registered more than once, the LSP will receive duplicate change notifications. Receiving them
@@ -1037,7 +1062,8 @@ module RubyLsp
1037
1062
  end
1038
1063
 
1039
1064
  Addon.file_watcher_addons.each do |addon|
1040
- T.unsafe(addon).workspace_did_change_watched_files(changes)
1065
+ addon #: as untyped
1066
+ .workspace_did_change_watched_files(changes)
1041
1067
  rescue => e
1042
1068
  send_log_message(
1043
1069
  "Error in #{addon.name} add-on while processing watched file notifications: #{e.full_message}",
@@ -1046,32 +1072,34 @@ module RubyLsp
1046
1072
  end
1047
1073
  end
1048
1074
 
1049
- sig { params(index: RubyIndexer::Index, file_path: String, change_type: Integer).void }
1075
+ #: (RubyIndexer::Index index, String file_path, Integer change_type) -> void
1050
1076
  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.
1077
+ @global_state.synchronize do
1078
+ load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
1079
+ uri = URI::Generic.from_path(load_path_entry: load_path_entry, path: file_path)
1080
+
1081
+ case change_type
1082
+ when Constant::FileChangeType::CREATED
1083
+ content = File.read(file_path)
1084
+ # If we receive a late created notification for a file that has already been claimed by the client, we want to
1085
+ # handle change for that URI so that the require path tree is updated
1086
+ @store.key?(uri) ? index.handle_change(uri, content) : index.index_single(uri, content)
1087
+ when Constant::FileChangeType::CHANGED
1088
+ content = File.read(file_path)
1089
+ # We only handle changes on file watched notifications if the client is not the one managing this URI.
1090
+ # Otherwise, these changes are handled when running the combined requests
1091
+ index.handle_change(uri, content) unless @store.key?(uri)
1092
+ when Constant::FileChangeType::DELETED
1093
+ index.delete(uri)
1094
+ end
1095
+ rescue Errno::ENOENT
1096
+ # If a file is created and then delete immediately afterwards, we will process the created notification before
1097
+ # we receive the deleted one, but the file no longer exists. This may happen when running a test suite that
1098
+ # creates and deletes files automatically.
1099
+ end
1072
1100
  end
1073
1101
 
1074
- sig { params(uri: URI::Generic).void }
1102
+ #: (URI::Generic uri) -> void
1075
1103
  def handle_rubocop_config_change(uri)
1076
1104
  return unless defined?(Requests::Support::RuboCopFormatter)
1077
1105
 
@@ -1091,7 +1119,7 @@ module RubyLsp
1091
1119
  end
1092
1120
  end
1093
1121
 
1094
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1122
+ #: (Hash[Symbol, untyped] message) -> void
1095
1123
  def workspace_symbol(message)
1096
1124
  send_message(
1097
1125
  Result.new(
@@ -1104,7 +1132,7 @@ module RubyLsp
1104
1132
  )
1105
1133
  end
1106
1134
 
1107
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1135
+ #: (Hash[Symbol, untyped] message) -> void
1108
1136
  def text_document_show_syntax_tree(message)
1109
1137
  params = message[:params]
1110
1138
  document = @store.get(params.dig(:textDocument, :uri))
@@ -1123,7 +1151,26 @@ module RubyLsp
1123
1151
  send_message(Result.new(id: message[:id], response: response))
1124
1152
  end
1125
1153
 
1126
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1154
+ #: (Hash[Symbol, untyped] message) -> void
1155
+ def experimental_go_to_relevant_file(message)
1156
+ path = message.dig(:params, :textDocument, :uri).to_standardized_path
1157
+ unless path.nil? || path.start_with?(@global_state.workspace_path)
1158
+ send_empty_response(message[:id])
1159
+ return
1160
+ end
1161
+
1162
+ unless path
1163
+ send_empty_response(message[:id])
1164
+ return
1165
+ end
1166
+
1167
+ response = {
1168
+ locations: Requests::GoToRelevantFile.new(path, @global_state.workspace_path).perform,
1169
+ }
1170
+ send_message(Result.new(id: message[:id], response: response))
1171
+ end
1172
+
1173
+ #: (Hash[Symbol, untyped] message) -> void
1127
1174
  def text_document_prepare_type_hierarchy(message)
1128
1175
  params = message[:params]
1129
1176
  document = @store.get(params.dig(:textDocument, :uri))
@@ -1142,7 +1189,7 @@ module RubyLsp
1142
1189
  send_message(Result.new(id: message[:id], response: response))
1143
1190
  end
1144
1191
 
1145
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1192
+ #: (Hash[Symbol, untyped] message) -> void
1146
1193
  def type_hierarchy_supertypes(message)
1147
1194
  response = Requests::TypeHierarchySupertypes.new(
1148
1195
  @global_state.index,
@@ -1151,14 +1198,14 @@ module RubyLsp
1151
1198
  send_message(Result.new(id: message[:id], response: response))
1152
1199
  end
1153
1200
 
1154
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1201
+ #: (Hash[Symbol, untyped] message) -> void
1155
1202
  def type_hierarchy_subtypes(message)
1156
1203
  # TODO: implement subtypes
1157
1204
  # The current index representation doesn't allow us to find the children of an entry.
1158
1205
  send_message(Result.new(id: message[:id], response: nil))
1159
1206
  end
1160
1207
 
1161
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1208
+ #: (Hash[Symbol, untyped] message) -> void
1162
1209
  def workspace_dependencies(message)
1163
1210
  unless @global_state.top_level_bundle
1164
1211
  send_message(Result.new(id: message[:id], response: []))
@@ -1186,12 +1233,13 @@ module RubyLsp
1186
1233
  send_message(Result.new(id: message[:id], response: response))
1187
1234
  end
1188
1235
 
1189
- sig { override.void }
1236
+ # @override
1237
+ #: -> void
1190
1238
  def shutdown
1191
1239
  Addon.unload_addons
1192
1240
  end
1193
1241
 
1194
- sig { void }
1242
+ #: -> void
1195
1243
  def perform_initial_indexing
1196
1244
  # The begin progress invocation happens during `initialize`, so that the notification is sent before we are
1197
1245
  # stuck indexing files
@@ -1223,7 +1271,7 @@ module RubyLsp
1223
1271
  end
1224
1272
  end
1225
1273
 
1226
- sig { params(id: String, title: String, percentage: Integer).void }
1274
+ #: (String id, String title, ?percentage: Integer) -> void
1227
1275
  def begin_progress(id, title, percentage: 0)
1228
1276
  return unless @global_state.client_capabilities.supports_progress
1229
1277
 
@@ -1236,14 +1284,14 @@ module RubyLsp
1236
1284
  send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
1237
1285
  end
1238
1286
 
1239
- sig { params(id: String, percentage: Integer).void }
1287
+ #: (String id, Integer percentage) -> void
1240
1288
  def progress(id, percentage)
1241
1289
  return unless @global_state.client_capabilities.supports_progress
1242
1290
 
1243
1291
  send_message(Notification.progress_report(id, percentage: percentage, message: "#{percentage}% completed"))
1244
1292
  end
1245
1293
 
1246
- sig { params(id: String).void }
1294
+ #: (String id) -> void
1247
1295
  def end_progress(id)
1248
1296
  return unless @global_state.client_capabilities.supports_progress
1249
1297
 
@@ -1253,7 +1301,7 @@ module RubyLsp
1253
1301
  # notification
1254
1302
  end
1255
1303
 
1256
- sig { void }
1304
+ #: -> void
1257
1305
  def check_formatter_is_available
1258
1306
  return if @setup_error
1259
1307
  # Warn of an unavailable `formatter` setting, e.g. `rubocop_internal` on a project which doesn't have RuboCop.
@@ -1271,7 +1319,7 @@ module RubyLsp
1271
1319
  end
1272
1320
  end
1273
1321
 
1274
- sig { params(indexing_options: T.nilable(T::Hash[Symbol, T.untyped])).void }
1322
+ #: (Hash[Symbol, untyped]? indexing_options) -> void
1275
1323
  def process_indexing_configuration(indexing_options)
1276
1324
  # Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
1277
1325
  index_path = File.join(@global_state.workspace_path, ".index.yml")
@@ -1312,7 +1360,7 @@ module RubyLsp
1312
1360
  configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
1313
1361
  end
1314
1362
 
1315
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1363
+ #: (Hash[Symbol, untyped] message) -> void
1316
1364
  def window_show_message_request(message)
1317
1365
  result = message[:result]
1318
1366
  return unless result
@@ -1327,7 +1375,7 @@ module RubyLsp
1327
1375
  # NOTE: all servers methods are void because they can produce several messages for the client. The only reason this
1328
1376
  # method returns the created thread is to that we can join it in tests and avoid flakiness. The implementation is
1329
1377
  # not supposed to rely on the return of this method
1330
- sig { params(message: T::Hash[Symbol, T.untyped]).returns(T.nilable(Thread)) }
1378
+ #: (Hash[Symbol, untyped] message) -> Thread?
1331
1379
  def compose_bundle(message)
1332
1380
  already_composed_path = File.join(@global_state.workspace_path, ".ruby-lsp", "bundle_is_composed")
1333
1381
  id = message[:id]
@@ -1352,7 +1400,9 @@ module RubyLsp
1352
1400
  Open3.capture3(
1353
1401
  Gem.ruby,
1354
1402
  "-I",
1355
- File.dirname(T.must(__dir__)),
1403
+ File.dirname(
1404
+ __dir__, #: as !nil
1405
+ ),
1356
1406
  File.expand_path("../../exe/ruby-lsp-launcher", __dir__),
1357
1407
  @global_state.workspace_uri.to_s,
1358
1408
  chdir: @global_state.workspace_path,
@@ -1374,7 +1424,7 @@ module RubyLsp
1374
1424
  end
1375
1425
 
1376
1426
  # Returns internal state information for debugging purposes
1377
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1427
+ #: (Hash[Symbol, untyped] message) -> void
1378
1428
  def diagnose_state(message)
1379
1429
  documents = {}
1380
1430
  @store.each { |uri, document| documents[uri] = document.source }
@@ -1394,7 +1444,7 @@ module RubyLsp
1394
1444
 
1395
1445
  # Discovers all available test groups and examples in a given file taking into consideration the merged response of
1396
1446
  # all add-ons
1397
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1447
+ #: (Hash[Symbol, untyped] message) -> void
1398
1448
  def discover_tests(message)
1399
1449
  document = @store.get(message.dig(:params, :textDocument, :uri))
1400
1450
 
@@ -1414,5 +1464,23 @@ module RubyLsp
1414
1464
 
1415
1465
  send_message(Result.new(id: message[:id], response: items.map(&:to_hash)))
1416
1466
  end
1467
+
1468
+ #: (Hash[Symbol, untyped] message) -> void
1469
+ def resolve_test_commands(message)
1470
+ items = message.dig(:params, :items)
1471
+ commands = Listeners::TestStyle.resolve_test_commands(items)
1472
+
1473
+ Addon.addons.each do |addon|
1474
+ commands.concat(addon.resolve_test_commands(items))
1475
+ end
1476
+
1477
+ send_message(Result.new(
1478
+ id: message[:id],
1479
+ response: {
1480
+ commands: commands,
1481
+ reporterPaths: [Listeners::TestStyle::MINITEST_REPORTER_PATH, Listeners::TestStyle::TEST_UNIT_REPORTER_PATH],
1482
+ },
1483
+ ))
1484
+ end
1417
1485
  end
1418
1486
  end