ruby-lsp 0.23.10 → 0.23.12

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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-launcher +12 -11
  5. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
  6. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +52 -77
  7. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +61 -144
  8. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +8 -6
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +74 -183
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +55 -181
  11. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
  12. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +12 -14
  13. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +21 -44
  14. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +40 -58
  15. data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +9 -16
  16. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
  17. data/lib/ruby_indexer/test/classes_and_modules_test.rb +75 -0
  18. data/lib/ruby_indexer/test/configuration_test.rb +32 -2
  19. data/lib/ruby_indexer/test/index_test.rb +21 -0
  20. data/lib/ruby_indexer/test/method_test.rb +2 -2
  21. data/lib/ruby_lsp/addon.rb +32 -67
  22. data/lib/ruby_lsp/base_server.rb +12 -11
  23. data/lib/ruby_lsp/client_capabilities.rb +4 -6
  24. data/lib/ruby_lsp/document.rb +21 -32
  25. data/lib/ruby_lsp/erb_document.rb +17 -27
  26. data/lib/ruby_lsp/global_state.rb +30 -32
  27. data/lib/ruby_lsp/internal.rb +6 -0
  28. data/lib/ruby_lsp/listeners/code_lens.rb +21 -39
  29. data/lib/ruby_lsp/listeners/completion.rb +34 -53
  30. data/lib/ruby_lsp/listeners/definition.rb +35 -49
  31. data/lib/ruby_lsp/listeners/document_highlight.rb +60 -69
  32. data/lib/ruby_lsp/listeners/document_link.rb +9 -19
  33. data/lib/ruby_lsp/listeners/document_symbol.rb +34 -48
  34. data/lib/ruby_lsp/listeners/folding_ranges.rb +31 -38
  35. data/lib/ruby_lsp/listeners/hover.rb +37 -47
  36. data/lib/ruby_lsp/listeners/inlay_hints.rb +3 -10
  37. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +29 -35
  38. data/lib/ruby_lsp/listeners/signature_help.rb +4 -23
  39. data/lib/ruby_lsp/listeners/spec_style.rb +199 -0
  40. data/lib/ruby_lsp/listeners/test_style.rb +270 -0
  41. data/lib/ruby_lsp/node_context.rb +8 -35
  42. data/lib/ruby_lsp/rbs_document.rb +7 -5
  43. data/lib/ruby_lsp/requests/code_action_resolve.rb +10 -10
  44. data/lib/ruby_lsp/requests/code_actions.rb +5 -14
  45. data/lib/ruby_lsp/requests/code_lens.rb +4 -13
  46. data/lib/ruby_lsp/requests/completion.rb +4 -15
  47. data/lib/ruby_lsp/requests/completion_resolve.rb +4 -4
  48. data/lib/ruby_lsp/requests/definition.rb +4 -12
  49. data/lib/ruby_lsp/requests/diagnostics.rb +6 -9
  50. data/lib/ruby_lsp/requests/discover_tests.rb +74 -0
  51. data/lib/ruby_lsp/requests/document_highlight.rb +3 -11
  52. data/lib/ruby_lsp/requests/document_link.rb +4 -13
  53. data/lib/ruby_lsp/requests/document_symbol.rb +4 -7
  54. data/lib/ruby_lsp/requests/folding_ranges.rb +4 -7
  55. data/lib/ruby_lsp/requests/formatting.rb +4 -7
  56. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
  57. data/lib/ruby_lsp/requests/hover.rb +6 -16
  58. data/lib/ruby_lsp/requests/inlay_hints.rb +4 -13
  59. data/lib/ruby_lsp/requests/on_type_formatting.rb +17 -24
  60. data/lib/ruby_lsp/requests/prepare_rename.rb +3 -8
  61. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +4 -13
  62. data/lib/ruby_lsp/requests/range_formatting.rb +3 -4
  63. data/lib/ruby_lsp/requests/references.rb +5 -35
  64. data/lib/ruby_lsp/requests/rename.rb +9 -35
  65. data/lib/ruby_lsp/requests/request.rb +5 -17
  66. data/lib/ruby_lsp/requests/selection_ranges.rb +3 -3
  67. data/lib/ruby_lsp/requests/semantic_highlighting.rb +6 -23
  68. data/lib/ruby_lsp/requests/show_syntax_tree.rb +4 -5
  69. data/lib/ruby_lsp/requests/signature_help.rb +6 -24
  70. data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
  71. data/lib/ruby_lsp/requests/support/common.rb +12 -47
  72. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -14
  73. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +7 -10
  74. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -15
  75. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
  76. data/lib/ruby_lsp/requests/support/sorbet.rb +1 -7
  77. data/lib/ruby_lsp/requests/support/source_uri.rb +5 -16
  78. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +7 -10
  79. data/lib/ruby_lsp/requests/support/test_item.rb +60 -0
  80. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +4 -5
  81. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -3
  82. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +4 -4
  83. data/lib/ruby_lsp/response_builders/document_symbol.rb +8 -11
  84. data/lib/ruby_lsp/response_builders/hover.rb +5 -5
  85. data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
  86. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +18 -40
  87. data/lib/ruby_lsp/response_builders/signature_help.rb +4 -5
  88. data/lib/ruby_lsp/response_builders/test_collection.rb +34 -0
  89. data/lib/ruby_lsp/ruby_document.rb +15 -40
  90. data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +106 -0
  91. data/lib/ruby_lsp/scope.rb +6 -10
  92. data/lib/ruby_lsp/server.rb +169 -72
  93. data/lib/ruby_lsp/setup_bundler.rb +25 -17
  94. data/lib/ruby_lsp/store.rb +12 -28
  95. data/lib/ruby_lsp/test_helper.rb +3 -12
  96. data/lib/ruby_lsp/test_reporter.rb +71 -0
  97. data/lib/ruby_lsp/test_unit_test_runner.rb +96 -0
  98. data/lib/ruby_lsp/type_inferrer.rb +9 -13
  99. data/lib/ruby_lsp/utils.rb +27 -65
  100. metadata +12 -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"
@@ -108,6 +107,14 @@ module RubyLsp
108
107
  )
109
108
  when "rubyLsp/composeBundle"
110
109
  compose_bundle(message)
110
+ when "rubyLsp/diagnoseState"
111
+ diagnose_state(message)
112
+ when "rubyLsp/discoverTests"
113
+ discover_tests(message)
114
+ when "rubyLsp/resolveTestCommands"
115
+ resolve_test_commands(message)
116
+ when "experimental/goToRelevantFile"
117
+ experimental_go_to_relevant_file(message)
111
118
  when "$/cancelRequest"
112
119
  @global_state.synchronize { @cancelled_requests << message[:params][:id] }
113
120
  when nil
@@ -157,7 +164,7 @@ module RubyLsp
157
164
  end
158
165
 
159
166
  # Process responses to requests that were sent to the client
160
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
167
+ #: (Hash[Symbol, untyped] message) -> void
161
168
  def process_response(message)
162
169
  case message.dig(:result, :method)
163
170
  when "window/showMessageRequest"
@@ -165,7 +172,7 @@ module RubyLsp
165
172
  end
166
173
  end
167
174
 
168
- sig { params(include_project_addons: T::Boolean).void }
175
+ #: (?include_project_addons: bool) -> void
169
176
  def load_addons(include_project_addons: true)
170
177
  # If invoking Bundler.setup failed, then the load path will not be configured properly and trying to load add-ons
171
178
  # with Gem.find_files will find every single version installed of an add-on, leading to requiring several
@@ -205,7 +212,7 @@ module RubyLsp
205
212
 
206
213
  private
207
214
 
208
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
215
+ #: (Hash[Symbol, untyped] message) -> void
209
216
  def run_initialize(message)
210
217
  options = message[:params]
211
218
  global_state_notifications = @global_state.apply_options(options)
@@ -286,6 +293,7 @@ module RubyLsp
286
293
  experimental: {
287
294
  addon_detection: true,
288
295
  compose_bundle: true,
296
+ go_to_relevant_file: true,
289
297
  },
290
298
  ),
291
299
  serverInfo: {
@@ -342,7 +350,7 @@ module RubyLsp
342
350
  end
343
351
  end
344
352
 
345
- sig { void }
353
+ #: -> void
346
354
  def run_initialized
347
355
  load_addons
348
356
  RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
@@ -369,7 +377,7 @@ module RubyLsp
369
377
  check_formatter_is_available
370
378
  end
371
379
 
372
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
380
+ #: (Hash[Symbol, untyped] message) -> void
373
381
  def text_document_did_open(message)
374
382
  @global_state.synchronize do
375
383
  text_document = message.dig(:params, :textDocument)
@@ -408,7 +416,7 @@ module RubyLsp
408
416
  end
409
417
  end
410
418
 
411
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
419
+ #: (Hash[Symbol, untyped] message) -> void
412
420
  def text_document_did_close(message)
413
421
  @global_state.synchronize do
414
422
  uri = message.dig(:params, :textDocument, :uri)
@@ -419,7 +427,7 @@ module RubyLsp
419
427
  end
420
428
  end
421
429
 
422
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
430
+ #: (Hash[Symbol, untyped] message) -> void
423
431
  def text_document_did_change(message)
424
432
  params = message[:params]
425
433
  text_document = params[:textDocument]
@@ -429,7 +437,7 @@ module RubyLsp
429
437
  end
430
438
  end
431
439
 
432
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
440
+ #: (Hash[Symbol, untyped] message) -> void
433
441
  def text_document_selection_range(message)
434
442
  uri = message.dig(:params, :textDocument, :uri)
435
443
  ranges = @store.cache_fetch(uri, "textDocument/selectionRange") do |document|
@@ -455,7 +463,7 @@ module RubyLsp
455
463
  send_message(Result.new(id: message[:id], response: response))
456
464
  end
457
465
 
458
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
466
+ #: (Hash[Symbol, untyped] message) -> void
459
467
  def run_combined_requests(message)
460
468
  uri = URI(message.dig(:params, :textDocument, :uri))
461
469
  document = @store.get(uri)
@@ -514,7 +522,7 @@ module RubyLsp
514
522
  alias_method :text_document_code_lens, :run_combined_requests
515
523
  alias_method :text_document_folding_range, :run_combined_requests
516
524
 
517
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
525
+ #: (Hash[Symbol, untyped] message) -> void
518
526
  def text_document_semantic_tokens_full(message)
519
527
  document = @store.get(message.dig(:params, :textDocument, :uri))
520
528
 
@@ -535,7 +543,7 @@ module RubyLsp
535
543
  send_message(Result.new(id: message[:id], response: semantic_highlighting.perform))
536
544
  end
537
545
 
538
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
546
+ #: (Hash[Symbol, untyped] message) -> void
539
547
  def text_document_semantic_tokens_delta(message)
540
548
  document = @store.get(message.dig(:params, :textDocument, :uri))
541
549
 
@@ -560,7 +568,7 @@ module RubyLsp
560
568
  send_message(Result.new(id: message[:id], response: request.perform))
561
569
  end
562
570
 
563
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
571
+ #: (Hash[Symbol, untyped] message) -> void
564
572
  def text_document_semantic_tokens_range(message)
565
573
  params = message[:params]
566
574
  range = params[:range]
@@ -589,7 +597,7 @@ module RubyLsp
589
597
  send_message(Result.new(id: message[:id], response: request.perform))
590
598
  end
591
599
 
592
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
600
+ #: (Hash[Symbol, untyped] message) -> void
593
601
  def text_document_range_formatting(message)
594
602
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
595
603
  if @global_state.formatter == "none"
@@ -617,7 +625,7 @@ module RubyLsp
617
625
  send_message(Result.new(id: message[:id], response: response))
618
626
  end
619
627
 
620
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
628
+ #: (Hash[Symbol, untyped] message) -> void
621
629
  def text_document_formatting(message)
622
630
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
623
631
  if @global_state.formatter == "none"
@@ -661,7 +669,7 @@ module RubyLsp
661
669
  send_empty_response(message[:id])
662
670
  end
663
671
 
664
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
672
+ #: (Hash[Symbol, untyped] message) -> void
665
673
  def text_document_document_highlight(message)
666
674
  params = message[:params]
667
675
  dispatcher = Prism::Dispatcher.new
@@ -677,7 +685,7 @@ module RubyLsp
677
685
  send_message(Result.new(id: message[:id], response: request.perform))
678
686
  end
679
687
 
680
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
688
+ #: (Hash[Symbol, untyped] message) -> void
681
689
  def text_document_on_type_formatting(message)
682
690
  params = message[:params]
683
691
  document = @store.get(params.dig(:textDocument, :uri))
@@ -700,7 +708,7 @@ module RubyLsp
700
708
  )
701
709
  end
702
710
 
703
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
711
+ #: (Hash[Symbol, untyped] message) -> void
704
712
  def text_document_hover(message)
705
713
  params = message[:params]
706
714
  dispatcher = Prism::Dispatcher.new
@@ -725,7 +733,7 @@ module RubyLsp
725
733
  )
726
734
  end
727
735
 
728
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
736
+ #: (Hash[Symbol, untyped] message) -> void
729
737
  def text_document_rename(message)
730
738
  params = message[:params]
731
739
  document = @store.get(params.dig(:textDocument, :uri))
@@ -745,7 +753,7 @@ module RubyLsp
745
753
  send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
746
754
  end
747
755
 
748
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
756
+ #: (Hash[Symbol, untyped] message) -> void
749
757
  def text_document_prepare_rename(message)
750
758
  params = message[:params]
751
759
  document = @store.get(params.dig(:textDocument, :uri))
@@ -763,7 +771,7 @@ module RubyLsp
763
771
  )
764
772
  end
765
773
 
766
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
774
+ #: (Hash[Symbol, untyped] message) -> void
767
775
  def text_document_references(message)
768
776
  params = message[:params]
769
777
  document = @store.get(params.dig(:textDocument, :uri))
@@ -781,7 +789,7 @@ module RubyLsp
781
789
  )
782
790
  end
783
791
 
784
- sig { params(document: Document[T.untyped]).returns(RubyDocument::SorbetLevel) }
792
+ #: (Document[untyped] document) -> RubyDocument::SorbetLevel
785
793
  def sorbet_level(document)
786
794
  return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
787
795
  return RubyDocument::SorbetLevel::Ignore unless document.is_a?(RubyDocument)
@@ -789,7 +797,7 @@ module RubyLsp
789
797
  document.sorbet_level
790
798
  end
791
799
 
792
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
800
+ #: (Hash[Symbol, untyped] message) -> void
793
801
  def text_document_inlay_hint(message)
794
802
  params = message[:params]
795
803
  document = @store.get(params.dig(:textDocument, :uri))
@@ -823,7 +831,7 @@ module RubyLsp
823
831
  send_message(Result.new(id: message[:id], response: result.select { |hint| range.cover?(hint.position[:line]) }))
824
832
  end
825
833
 
826
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
834
+ #: (Hash[Symbol, untyped] message) -> void
827
835
  def text_document_code_action(message)
828
836
  params = message[:params]
829
837
  document = @store.get(params.dig(:textDocument, :uri))
@@ -845,7 +853,7 @@ module RubyLsp
845
853
  )
846
854
  end
847
855
 
848
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
856
+ #: (Hash[Symbol, untyped] message) -> void
849
857
  def code_action_resolve(message)
850
858
  params = message[:params]
851
859
  uri = URI(params.dig(:data, :uri))
@@ -870,7 +878,7 @@ module RubyLsp
870
878
  end
871
879
  end
872
880
 
873
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
881
+ #: (Hash[Symbol, untyped] message) -> void
874
882
  def text_document_diagnostic(message)
875
883
  # Do not compute diagnostics for files outside of the workspace. For example, if someone is looking at a gem's
876
884
  # source code, we don't want to show diagnostics for it
@@ -910,7 +918,7 @@ module RubyLsp
910
918
  send_empty_response(message[:id])
911
919
  end
912
920
 
913
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
921
+ #: (Hash[Symbol, untyped] message) -> void
914
922
  def text_document_completion(message)
915
923
  params = message[:params]
916
924
  dispatcher = Prism::Dispatcher.new
@@ -935,7 +943,7 @@ module RubyLsp
935
943
  )
936
944
  end
937
945
 
938
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
946
+ #: (Hash[Symbol, untyped] message) -> void
939
947
  def text_document_completion_item_resolve(message)
940
948
  # When responding to a delegated completion request, it means we're handling a completion item that isn't related
941
949
  # to Ruby (probably related to an ERB host language like HTML). We need to return the original completion item
@@ -954,7 +962,7 @@ module RubyLsp
954
962
  ))
955
963
  end
956
964
 
957
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
965
+ #: (Hash[Symbol, untyped] message) -> void
958
966
  def text_document_signature_help(message)
959
967
  params = message[:params]
960
968
  dispatcher = Prism::Dispatcher.new
@@ -980,7 +988,7 @@ module RubyLsp
980
988
  )
981
989
  end
982
990
 
983
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
991
+ #: (Hash[Symbol, untyped] message) -> void
984
992
  def text_document_definition(message)
985
993
  params = message[:params]
986
994
  dispatcher = Prism::Dispatcher.new
@@ -1005,8 +1013,23 @@ module RubyLsp
1005
1013
  )
1006
1014
  end
1007
1015
 
1008
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1016
+ #: (Hash[Symbol, untyped] message) -> void
1009
1017
  def workspace_did_change_watched_files(message)
1018
+ # If indexing is not complete yet, delay processing did change watched file notifications. We need initial
1019
+ # indexing to be in place so that we can handle file changes appropriately without risking duplicates. We also
1020
+ # have to sleep before re-inserting the notification in the queue otherwise the worker can get stuck in its own
1021
+ # loop of pushing and popping the same notification
1022
+ unless @global_state.index.initial_indexing_completed
1023
+ Thread.new do
1024
+ sleep(2)
1025
+ # We have to ensure that the queue is not closed yet, since nothing stops the user from saving a file and then
1026
+ # immediately telling the LSP to shutdown
1027
+ @incoming_queue << message unless @incoming_queue.closed?
1028
+ end
1029
+
1030
+ return
1031
+ end
1032
+
1010
1033
  changes = message.dig(:params, :changes)
1011
1034
  # We allow add-ons to register for watching files and we have no restrictions for what they register for. If the
1012
1035
  # same pattern is registered more than once, the LSP will receive duplicate change notifications. Receiving them
@@ -1042,32 +1065,34 @@ module RubyLsp
1042
1065
  end
1043
1066
  end
1044
1067
 
1045
- sig { params(index: RubyIndexer::Index, file_path: String, change_type: Integer).void }
1068
+ #: (RubyIndexer::Index index, String file_path, Integer change_type) -> void
1046
1069
  def handle_ruby_file_change(index, file_path, change_type)
1047
- load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
1048
- uri = URI::Generic.from_path(load_path_entry: load_path_entry, path: file_path)
1049
-
1050
- content = File.read(file_path)
1051
-
1052
- case change_type
1053
- when Constant::FileChangeType::CREATED
1054
- # If we receive a late created notification for a file that has already been claimed by the client, we want to
1055
- # handle change for that URI so that the require path tree is updated
1056
- @store.key?(uri) ? index.handle_change(uri, content) : index.index_single(uri, content)
1057
- when Constant::FileChangeType::CHANGED
1058
- # We only handle changes on file watched notifications if the client is not the one managing this URI.
1059
- # Otherwise, these changes are handled when running the combined requests
1060
- index.handle_change(uri, content) unless @store.key?(uri)
1061
- when Constant::FileChangeType::DELETED
1062
- index.delete(uri)
1063
- end
1064
- rescue Errno::ENOENT
1065
- # If a file is created and then delete immediately afterwards, we will process the created notification before we
1066
- # receive the deleted one, but the file no longer exists. This may happen when running a test suite that creates
1067
- # and deletes files automatically.
1070
+ @global_state.synchronize do
1071
+ load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
1072
+ uri = URI::Generic.from_path(load_path_entry: load_path_entry, path: file_path)
1073
+
1074
+ case change_type
1075
+ when Constant::FileChangeType::CREATED
1076
+ content = File.read(file_path)
1077
+ # If we receive a late created notification for a file that has already been claimed by the client, we want to
1078
+ # handle change for that URI so that the require path tree is updated
1079
+ @store.key?(uri) ? index.handle_change(uri, content) : index.index_single(uri, content)
1080
+ when Constant::FileChangeType::CHANGED
1081
+ content = File.read(file_path)
1082
+ # We only handle changes on file watched notifications if the client is not the one managing this URI.
1083
+ # Otherwise, these changes are handled when running the combined requests
1084
+ index.handle_change(uri, content) unless @store.key?(uri)
1085
+ when Constant::FileChangeType::DELETED
1086
+ index.delete(uri)
1087
+ end
1088
+ rescue Errno::ENOENT
1089
+ # If a file is created and then delete immediately afterwards, we will process the created notification before
1090
+ # we receive the deleted one, but the file no longer exists. This may happen when running a test suite that
1091
+ # creates and deletes files automatically.
1092
+ end
1068
1093
  end
1069
1094
 
1070
- sig { params(uri: URI::Generic).void }
1095
+ #: (URI::Generic uri) -> void
1071
1096
  def handle_rubocop_config_change(uri)
1072
1097
  return unless defined?(Requests::Support::RuboCopFormatter)
1073
1098
 
@@ -1087,7 +1112,7 @@ module RubyLsp
1087
1112
  end
1088
1113
  end
1089
1114
 
1090
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1115
+ #: (Hash[Symbol, untyped] message) -> void
1091
1116
  def workspace_symbol(message)
1092
1117
  send_message(
1093
1118
  Result.new(
@@ -1100,7 +1125,7 @@ module RubyLsp
1100
1125
  )
1101
1126
  end
1102
1127
 
1103
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1128
+ #: (Hash[Symbol, untyped] message) -> void
1104
1129
  def text_document_show_syntax_tree(message)
1105
1130
  params = message[:params]
1106
1131
  document = @store.get(params.dig(:textDocument, :uri))
@@ -1119,7 +1144,26 @@ module RubyLsp
1119
1144
  send_message(Result.new(id: message[:id], response: response))
1120
1145
  end
1121
1146
 
1122
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1147
+ #: (Hash[Symbol, untyped] message) -> void
1148
+ def experimental_go_to_relevant_file(message)
1149
+ path = message.dig(:params, :textDocument, :uri).to_standardized_path
1150
+ unless path.nil? || path.start_with?(@global_state.workspace_path)
1151
+ send_empty_response(message[:id])
1152
+ return
1153
+ end
1154
+
1155
+ unless path
1156
+ send_empty_response(message[:id])
1157
+ return
1158
+ end
1159
+
1160
+ response = {
1161
+ locations: Requests::GoToRelevantFile.new(path, @global_state.workspace_path).perform,
1162
+ }
1163
+ send_message(Result.new(id: message[:id], response: response))
1164
+ end
1165
+
1166
+ #: (Hash[Symbol, untyped] message) -> void
1123
1167
  def text_document_prepare_type_hierarchy(message)
1124
1168
  params = message[:params]
1125
1169
  document = @store.get(params.dig(:textDocument, :uri))
@@ -1138,7 +1182,7 @@ module RubyLsp
1138
1182
  send_message(Result.new(id: message[:id], response: response))
1139
1183
  end
1140
1184
 
1141
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1185
+ #: (Hash[Symbol, untyped] message) -> void
1142
1186
  def type_hierarchy_supertypes(message)
1143
1187
  response = Requests::TypeHierarchySupertypes.new(
1144
1188
  @global_state.index,
@@ -1147,14 +1191,14 @@ module RubyLsp
1147
1191
  send_message(Result.new(id: message[:id], response: response))
1148
1192
  end
1149
1193
 
1150
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1194
+ #: (Hash[Symbol, untyped] message) -> void
1151
1195
  def type_hierarchy_subtypes(message)
1152
1196
  # TODO: implement subtypes
1153
1197
  # The current index representation doesn't allow us to find the children of an entry.
1154
1198
  send_message(Result.new(id: message[:id], response: nil))
1155
1199
  end
1156
1200
 
1157
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1201
+ #: (Hash[Symbol, untyped] message) -> void
1158
1202
  def workspace_dependencies(message)
1159
1203
  unless @global_state.top_level_bundle
1160
1204
  send_message(Result.new(id: message[:id], response: []))
@@ -1182,12 +1226,13 @@ module RubyLsp
1182
1226
  send_message(Result.new(id: message[:id], response: response))
1183
1227
  end
1184
1228
 
1185
- sig { override.void }
1229
+ # @override
1230
+ #: -> void
1186
1231
  def shutdown
1187
1232
  Addon.unload_addons
1188
1233
  end
1189
1234
 
1190
- sig { void }
1235
+ #: -> void
1191
1236
  def perform_initial_indexing
1192
1237
  # The begin progress invocation happens during `initialize`, so that the notification is sent before we are
1193
1238
  # stuck indexing files
@@ -1219,7 +1264,7 @@ module RubyLsp
1219
1264
  end
1220
1265
  end
1221
1266
 
1222
- sig { params(id: String, title: String, percentage: Integer).void }
1267
+ #: (String id, String title, ?percentage: Integer) -> void
1223
1268
  def begin_progress(id, title, percentage: 0)
1224
1269
  return unless @global_state.client_capabilities.supports_progress
1225
1270
 
@@ -1232,14 +1277,14 @@ module RubyLsp
1232
1277
  send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
1233
1278
  end
1234
1279
 
1235
- sig { params(id: String, percentage: Integer).void }
1280
+ #: (String id, Integer percentage) -> void
1236
1281
  def progress(id, percentage)
1237
1282
  return unless @global_state.client_capabilities.supports_progress
1238
1283
 
1239
1284
  send_message(Notification.progress_report(id, percentage: percentage, message: "#{percentage}% completed"))
1240
1285
  end
1241
1286
 
1242
- sig { params(id: String).void }
1287
+ #: (String id) -> void
1243
1288
  def end_progress(id)
1244
1289
  return unless @global_state.client_capabilities.supports_progress
1245
1290
 
@@ -1249,7 +1294,7 @@ module RubyLsp
1249
1294
  # notification
1250
1295
  end
1251
1296
 
1252
- sig { void }
1297
+ #: -> void
1253
1298
  def check_formatter_is_available
1254
1299
  return if @setup_error
1255
1300
  # Warn of an unavailable `formatter` setting, e.g. `rubocop_internal` on a project which doesn't have RuboCop.
@@ -1267,7 +1312,7 @@ module RubyLsp
1267
1312
  end
1268
1313
  end
1269
1314
 
1270
- sig { params(indexing_options: T.nilable(T::Hash[Symbol, T.untyped])).void }
1315
+ #: (Hash[Symbol, untyped]? indexing_options) -> void
1271
1316
  def process_indexing_configuration(indexing_options)
1272
1317
  # Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
1273
1318
  index_path = File.join(@global_state.workspace_path, ".index.yml")
@@ -1308,7 +1353,7 @@ module RubyLsp
1308
1353
  configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
1309
1354
  end
1310
1355
 
1311
- sig { params(message: T::Hash[Symbol, T.untyped]).void }
1356
+ #: (Hash[Symbol, untyped] message) -> void
1312
1357
  def window_show_message_request(message)
1313
1358
  result = message[:result]
1314
1359
  return unless result
@@ -1323,7 +1368,7 @@ module RubyLsp
1323
1368
  # NOTE: all servers methods are void because they can produce several messages for the client. The only reason this
1324
1369
  # method returns the created thread is to that we can join it in tests and avoid flakiness. The implementation is
1325
1370
  # not supposed to rely on the return of this method
1326
- sig { params(message: T::Hash[Symbol, T.untyped]).returns(T.nilable(Thread)) }
1371
+ #: (Hash[Symbol, untyped] message) -> Thread?
1327
1372
  def compose_bundle(message)
1328
1373
  already_composed_path = File.join(@global_state.workspace_path, ".ruby-lsp", "bundle_is_composed")
1329
1374
  id = message[:id]
@@ -1368,5 +1413,57 @@ module RubyLsp
1368
1413
  end
1369
1414
  end
1370
1415
  end
1416
+
1417
+ # Returns internal state information for debugging purposes
1418
+ #: (Hash[Symbol, untyped] message) -> void
1419
+ def diagnose_state(message)
1420
+ documents = {}
1421
+ @store.each { |uri, document| documents[uri] = document.source }
1422
+
1423
+ send_message(
1424
+ Result.new(
1425
+ id: message[:id],
1426
+ response: {
1427
+ workerAlive: @worker.alive?,
1428
+ backtrace: @worker.backtrace,
1429
+ documents: documents,
1430
+ incomingQueueSize: @incoming_queue.length,
1431
+ },
1432
+ ),
1433
+ )
1434
+ end
1435
+
1436
+ # Discovers all available test groups and examples in a given file taking into consideration the merged response of
1437
+ # all add-ons
1438
+ #: (Hash[Symbol, untyped] message) -> void
1439
+ def discover_tests(message)
1440
+ document = @store.get(message.dig(:params, :textDocument, :uri))
1441
+
1442
+ unless document.is_a?(RubyDocument)
1443
+ send_empty_response(message[:id])
1444
+ return
1445
+ end
1446
+
1447
+ cached_response = document.cache_get("rubyLsp/discoverTests")
1448
+ if cached_response != Document::EMPTY_CACHE
1449
+ send_message(Result.new(id: message[:id], response: cached_response.map(&:to_hash)))
1450
+ return
1451
+ end
1452
+
1453
+ items = Requests::DiscoverTests.new(@global_state, document, Prism::Dispatcher.new).perform
1454
+ document.cache_set("rubyLsp/discoverTests", items)
1455
+
1456
+ send_message(Result.new(id: message[:id], response: items.map(&:to_hash)))
1457
+ end
1458
+
1459
+ #: (Hash[Symbol, untyped] message) -> void
1460
+ def resolve_test_commands(message)
1461
+ items = message.dig(:params, :items)
1462
+
1463
+ send_message(Result.new(
1464
+ id: message[:id],
1465
+ response: { commands: Listeners::TestStyle.resolve_test_commands(items) },
1466
+ ))
1467
+ end
1371
1468
  end
1372
1469
  end