ruby-lsp 0.23.15 → 0.26.9

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +17 -14
  4. data/exe/ruby-lsp-check +0 -4
  5. data/exe/ruby-lsp-launcher +41 -14
  6. data/exe/ruby-lsp-test-exec +6 -0
  7. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +0 -1
  8. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +0 -1
  9. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +4 -3
  10. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +42 -20
  11. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +1 -7
  12. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +49 -62
  13. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +84 -74
  14. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +6 -9
  15. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +9 -14
  16. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +12 -8
  17. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +4 -4
  18. data/lib/ruby_lsp/addon.rb +44 -15
  19. data/lib/ruby_lsp/base_server.rb +56 -37
  20. data/lib/ruby_lsp/client_capabilities.rb +6 -1
  21. data/lib/ruby_lsp/document.rb +174 -62
  22. data/lib/ruby_lsp/erb_document.rb +10 -8
  23. data/lib/ruby_lsp/global_state.rb +86 -33
  24. data/lib/ruby_lsp/internal.rb +6 -3
  25. data/lib/ruby_lsp/listeners/completion.rb +22 -11
  26. data/lib/ruby_lsp/listeners/definition.rb +41 -21
  27. data/lib/ruby_lsp/listeners/document_highlight.rb +26 -1
  28. data/lib/ruby_lsp/listeners/document_link.rb +64 -28
  29. data/lib/ruby_lsp/listeners/hover.rb +27 -16
  30. data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
  31. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +2 -2
  32. data/lib/ruby_lsp/listeners/signature_help.rb +2 -2
  33. data/lib/ruby_lsp/listeners/spec_style.rb +155 -79
  34. data/lib/ruby_lsp/listeners/test_discovery.rb +39 -21
  35. data/lib/ruby_lsp/listeners/test_style.rb +75 -35
  36. data/lib/ruby_lsp/rbs_document.rb +3 -6
  37. data/lib/ruby_lsp/requests/code_action_resolve.rb +83 -58
  38. data/lib/ruby_lsp/requests/code_actions.rb +20 -5
  39. data/lib/ruby_lsp/requests/code_lens.rb +27 -6
  40. data/lib/ruby_lsp/requests/completion.rb +3 -3
  41. data/lib/ruby_lsp/requests/completion_resolve.rb +8 -6
  42. data/lib/ruby_lsp/requests/definition.rb +4 -7
  43. data/lib/ruby_lsp/requests/discover_tests.rb +2 -2
  44. data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
  45. data/lib/ruby_lsp/requests/document_link.rb +1 -1
  46. data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
  47. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +64 -12
  48. data/lib/ruby_lsp/requests/hover.rb +3 -6
  49. data/lib/ruby_lsp/requests/inlay_hints.rb +4 -4
  50. data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
  51. data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
  52. data/lib/ruby_lsp/requests/references.rb +10 -21
  53. data/lib/ruby_lsp/requests/rename.rb +9 -10
  54. data/lib/ruby_lsp/requests/request.rb +8 -8
  55. data/lib/ruby_lsp/requests/selection_ranges.rb +2 -2
  56. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  57. data/lib/ruby_lsp/requests/show_syntax_tree.rb +2 -2
  58. data/lib/ruby_lsp/requests/signature_help.rb +2 -2
  59. data/lib/ruby_lsp/requests/support/annotation.rb +1 -1
  60. data/lib/ruby_lsp/requests/support/common.rb +9 -12
  61. data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
  62. data/lib/ruby_lsp/requests/support/package_url.rb +414 -0
  63. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +7 -1
  64. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
  65. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -3
  66. data/lib/ruby_lsp/requests/support/source_uri.rb +7 -4
  67. data/lib/ruby_lsp/requests/support/test_item.rb +7 -1
  68. data/lib/ruby_lsp/requests/workspace_symbol.rb +20 -12
  69. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +1 -4
  70. data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -3
  71. data/lib/ruby_lsp/response_builders/hover.rb +1 -4
  72. data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
  73. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +4 -5
  74. data/lib/ruby_lsp/response_builders/signature_help.rb +1 -2
  75. data/lib/ruby_lsp/response_builders/test_collection.rb +29 -3
  76. data/lib/ruby_lsp/ruby_document.rb +14 -42
  77. data/lib/ruby_lsp/scripts/compose_bundle.rb +3 -3
  78. data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +3 -1
  79. data/lib/ruby_lsp/server.rb +173 -130
  80. data/lib/ruby_lsp/setup_bundler.rb +114 -47
  81. data/lib/ruby_lsp/static_docs.rb +1 -0
  82. data/lib/ruby_lsp/store.rb +6 -16
  83. data/lib/ruby_lsp/test_helper.rb +1 -4
  84. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +121 -17
  85. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +65 -25
  86. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +16 -18
  87. data/lib/ruby_lsp/utils.rb +102 -13
  88. data/static_docs/break.md +103 -0
  89. metadata +8 -33
  90. data/lib/ruby_indexer/test/class_variables_test.rb +0 -140
  91. data/lib/ruby_indexer/test/classes_and_modules_test.rb +0 -770
  92. data/lib/ruby_indexer/test/configuration_test.rb +0 -280
  93. data/lib/ruby_indexer/test/constant_test.rb +0 -402
  94. data/lib/ruby_indexer/test/enhancements_test.rb +0 -325
  95. data/lib/ruby_indexer/test/global_variable_test.rb +0 -49
  96. data/lib/ruby_indexer/test/index_test.rb +0 -2190
  97. data/lib/ruby_indexer/test/instance_variables_test.rb +0 -240
  98. data/lib/ruby_indexer/test/method_test.rb +0 -973
  99. data/lib/ruby_indexer/test/prefix_tree_test.rb +0 -150
  100. data/lib/ruby_indexer/test/rbs_indexer_test.rb +0 -380
  101. data/lib/ruby_indexer/test/reference_finder_test.rb +0 -330
  102. data/lib/ruby_indexer/test/test_case.rb +0 -51
  103. data/lib/ruby_indexer/test/uri_test.rb +0 -85
  104. data/lib/ruby_lsp/load_sorbet.rb +0 -62
@@ -3,6 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  class Server < BaseServer
6
+ NON_REPORTABLE_SETUP_ERRORS = [Bundler::GemNotFound, Bundler::GitError].freeze #: Array[singleton(StandardError)]
7
+
6
8
  # Only for testing
7
9
  #: GlobalState
8
10
  attr_reader :global_state
@@ -32,6 +34,8 @@ module RubyLsp
32
34
  text_document_document_link(message)
33
35
  when "textDocument/codeLens"
34
36
  text_document_code_lens(message)
37
+ when "codeLens/resolve"
38
+ code_lens_resolve(message)
35
39
  when "textDocument/semanticTokens/full"
36
40
  text_document_semantic_tokens_full(message)
37
41
  when "textDocument/semanticTokens/full/delta"
@@ -92,13 +96,10 @@ module RubyLsp
92
96
  id: message[:id],
93
97
  response:
94
98
  Addon.addons.map do |addon|
95
- version_method = addon.method(:version)
96
-
97
- # If the add-on doesn't define a `version` method, we'd be calling the abstract method defined by
98
- # Sorbet, which would raise an error.
99
- # Therefore, we only call the method if it's defined by the add-on itself
100
- if version_method.owner != Addon
101
- version = addon.version
99
+ version = begin
100
+ addon.version
101
+ rescue AbstractMethodInvokedError
102
+ nil
102
103
  end
103
104
 
104
105
  { name: addon.name, version: version, errored: addon.error? }
@@ -122,30 +123,22 @@ module RubyLsp
122
123
  end
123
124
  rescue DelegateRequestError
124
125
  send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
125
- rescue StandardError, LoadError => e
126
+ rescue StandardError, LoadError, SystemExit => e
126
127
  # If an error occurred in a request, we have to return an error response or else the editor will hang
127
128
  if message[:id]
128
129
  # If a document is deleted before we are able to process all of its enqueued requests, we will try to read it
129
130
  # from disk and it raise this error. This is expected, so we don't include the `data` attribute to avoid
130
- # reporting these to our telemetry
131
+ # reporting these to our telemetry.
132
+ #
133
+ # Similarly, if we receive a location for an invalid position in the
134
+ # document, we don't report it to telemetry
131
135
  case e
132
- when Store::NonExistingDocumentError
136
+ when Store::NonExistingDocumentError, Document::InvalidLocationError
133
137
  send_message(Error.new(
134
138
  id: message[:id],
135
139
  code: Constant::ErrorCodes::INVALID_PARAMS,
136
140
  message: e.full_message,
137
141
  ))
138
- when Document::LocationNotFoundError
139
- send_message(Error.new(
140
- id: message[:id],
141
- code: Constant::ErrorCodes::REQUEST_FAILED,
142
- message: <<~MESSAGE,
143
- Request #{message[:method]} failed to find the target position.
144
- The file might have been modified while the server was in the middle of searching for the target.
145
- If you experience this regularly, please report any findings and extra information on
146
- https://github.com/Shopify/ruby-lsp/issues/2446
147
- MESSAGE
148
- ))
149
142
  else
150
143
  send_message(Error.new(
151
144
  id: message[:id],
@@ -180,6 +173,7 @@ module RubyLsp
180
173
  return if @setup_error
181
174
 
182
175
  errors = Addon.load_addons(@global_state, @outgoing_queue, include_project_addons: include_project_addons)
176
+ return if test_mode?
183
177
 
184
178
  if errors.any?
185
179
  send_log_message(
@@ -192,21 +186,13 @@ module RubyLsp
192
186
 
193
187
  if errored_addons.any?
194
188
  send_message(
195
- Notification.new(
196
- method: "window/showMessage",
197
- params: Interface::ShowMessageParams.new(
198
- type: Constant::MessageType::WARNING,
199
- message: "Error loading add-ons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
200
- ),
189
+ Notification.window_show_message(
190
+ "Error loading add-ons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
191
+ type: Constant::MessageType::WARNING,
201
192
  ),
202
193
  )
203
194
 
204
- unless @test_mode
205
- send_log_message(
206
- errored_addons.map(&:errors_details).join("\n\n"),
207
- type: Constant::MessageType::WARNING,
208
- )
209
- end
195
+ send_log_message(errored_addons.map(&:errors_details).join("\n\n"), type: Constant::MessageType::WARNING)
210
196
  end
211
197
  end
212
198
 
@@ -222,10 +208,6 @@ module RubyLsp
222
208
 
223
209
  configured_features = options.dig(:initializationOptions, :enabledFeatures)
224
210
 
225
- configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
226
- @store.features_configuration.dig(:inlayHint) #: as !nil
227
- .configuration.merge!(configured_hints) if configured_hints
228
-
229
211
  enabled_features = case configured_features
230
212
  when Array
231
213
  # If the configuration is using an array, then absent features are disabled and present ones are enabled. That's
@@ -242,7 +224,9 @@ module RubyLsp
242
224
 
243
225
  bundle_env_path = File.join(".ruby-lsp", "bundle_env")
244
226
  bundle_env = if File.exist?(bundle_env_path)
245
- env = File.readlines(bundle_env_path).to_h { |line| T.cast(line.chomp.split("=", 2), [String, String]) }
227
+ env = File.readlines(bundle_env_path).to_h do |line|
228
+ line.chomp.split("=", 2) #: as [String, String]
229
+ end
246
230
  FileUtils.rm(bundle_env_path)
247
231
  env
248
232
  end
@@ -321,7 +305,7 @@ module RubyLsp
321
305
  @current_request_id,
322
306
  Interface::RelativePattern.new(
323
307
  base_uri: @global_state.workspace_uri.to_s,
324
- pattern: "{.rubocop.yml,.rubocop}",
308
+ pattern: "{.rubocop.yml,.rubocop,.rubocop_todo.yml}",
325
309
  ),
326
310
  registration_id: "rubocop-watcher",
327
311
  ))
@@ -333,7 +317,7 @@ module RubyLsp
333
317
 
334
318
  global_state_notifications.each { |notification| send_message(notification) }
335
319
 
336
- if @setup_error
320
+ if @setup_error && NON_REPORTABLE_SETUP_ERRORS.none? { |error_class| @setup_error.is_a?(error_class) }
337
321
  send_message(Notification.telemetry(
338
322
  type: "error",
339
323
  errorMessage: @setup_error.message,
@@ -377,56 +361,53 @@ module RubyLsp
377
361
 
378
362
  perform_initial_indexing
379
363
  check_formatter_is_available
364
+ update_server if @global_state.enabled_feature?(:launcher)
380
365
  end
381
366
 
382
367
  #: (Hash[Symbol, untyped] message) -> void
383
368
  def text_document_did_open(message)
384
- @global_state.synchronize do
385
- text_document = message.dig(:params, :textDocument)
386
- language_id = case text_document[:languageId]
387
- when "erb", "eruby"
388
- Document::LanguageId::ERB
389
- when "rbs"
390
- Document::LanguageId::RBS
391
- else
392
- Document::LanguageId::Ruby
393
- end
369
+ text_document = message.dig(:params, :textDocument)
370
+ language_id = case text_document[:languageId]
371
+ when "erb", "eruby"
372
+ :erb
373
+ when "rbs"
374
+ :rbs
375
+ else
376
+ :ruby
377
+ end
394
378
 
395
- document = @store.set(
396
- uri: text_document[:uri],
397
- source: text_document[:text],
398
- version: text_document[:version],
399
- language_id: language_id,
400
- )
379
+ document = @store.set(
380
+ uri: text_document[:uri],
381
+ source: text_document[:text],
382
+ version: text_document[:version],
383
+ language_id: language_id,
384
+ )
401
385
 
402
- if document.past_expensive_limit? && text_document[:uri].scheme == "file"
403
- log_message = <<~MESSAGE
404
- The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
405
- diagnostics will be disabled.
406
- MESSAGE
386
+ if document.past_expensive_limit? && text_document[:uri].scheme == "file"
387
+ log_message = <<~MESSAGE
388
+ The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
389
+ diagnostics will be disabled.
390
+ MESSAGE
407
391
 
408
- send_message(
409
- Notification.new(
410
- method: "window/logMessage",
411
- params: Interface::LogMessageParams.new(
412
- type: Constant::MessageType::WARNING,
413
- message: log_message,
414
- ),
392
+ send_message(
393
+ Notification.new(
394
+ method: "window/logMessage",
395
+ params: Interface::LogMessageParams.new(
396
+ type: Constant::MessageType::WARNING,
397
+ message: log_message,
415
398
  ),
416
- )
417
- end
399
+ ),
400
+ )
418
401
  end
419
402
  end
420
403
 
421
404
  #: (Hash[Symbol, untyped] message) -> void
422
405
  def text_document_did_close(message)
423
- @global_state.synchronize do
424
- uri = message.dig(:params, :textDocument, :uri)
425
- @store.delete(uri)
406
+ uri = message.dig(:params, :textDocument, :uri)
407
+ @store.delete(uri)
426
408
 
427
- # Clear diagnostics for the closed file, so that they no longer appear in the problems tab
428
- send_message(Notification.publish_diagnostics(uri.to_s, []))
429
- end
409
+ # Clear diagnostics for the closed file, so that they no longer appear in the problems tab
410
+ send_message(Notification.publish_diagnostics(uri.to_s, []))
430
411
  end
431
412
 
432
413
  #: (Hash[Symbol, untyped] message) -> void
@@ -434,9 +415,7 @@ module RubyLsp
434
415
  params = message[:params]
435
416
  text_document = params[:textDocument]
436
417
 
437
- @global_state.synchronize do
438
- @store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
439
- end
418
+ @store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
440
419
  end
441
420
 
442
421
  #: (Hash[Symbol, untyped] message) -> void
@@ -489,13 +468,16 @@ module RubyLsp
489
468
  folding_range = Requests::FoldingRanges.new(parse_result.comments, dispatcher)
490
469
  document_symbol = Requests::DocumentSymbol.new(uri, dispatcher)
491
470
  document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
492
- code_lens = Requests::CodeLens.new(@global_state, uri, dispatcher)
493
471
  inlay_hint = Requests::InlayHints.new(
472
+ @global_state,
494
473
  document,
495
- @store.features_configuration.dig(:inlayHint), #: as !nil
496
474
  dispatcher,
497
475
  )
498
476
 
477
+ # The code lens listener requires the index to be populated, so the DeclarationListener must be inserted first in
478
+ # the dispatcher's state
479
+ code_lens = nil #: Requests::CodeLens?
480
+
499
481
  if document.is_a?(RubyDocument) && document.should_index?
500
482
  # Re-index the file as it is modified. This mode of indexing updates entries only. Require path trees are only
501
483
  # updated on save
@@ -505,11 +487,13 @@ module RubyLsp
505
487
  @global_state.index.handle_change(uri) do |index|
506
488
  index.delete(uri, skip_require_paths_tree: true)
507
489
  RubyIndexer::DeclarationListener.new(index, dispatcher, parse_result, uri, collect_comments: true)
508
- dispatcher.dispatch(parse_result.value)
490
+ code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
491
+ dispatcher.dispatch(document.ast)
509
492
  end
510
493
  end
511
494
  else
512
- dispatcher.dispatch(parse_result.value)
495
+ code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
496
+ dispatcher.dispatch(document.ast)
513
497
  end
514
498
 
515
499
  # Store all responses retrieve in this round of visits in the cache and then return the response for the request
@@ -517,7 +501,11 @@ module RubyLsp
517
501
  document.cache_set("textDocument/foldingRange", folding_range.perform)
518
502
  document.cache_set("textDocument/documentSymbol", document_symbol.perform)
519
503
  document.cache_set("textDocument/documentLink", document_link.perform)
520
- document.cache_set("textDocument/codeLens", code_lens.perform)
504
+ document.cache_set(
505
+ "textDocument/codeLens",
506
+ code_lens #: as !nil
507
+ .perform,
508
+ )
521
509
  document.cache_set("textDocument/inlayHint", inlay_hint.perform)
522
510
 
523
511
  send_message(Result.new(id: message[:id], response: document.cache_get(message[:method])))
@@ -544,7 +532,7 @@ module RubyLsp
544
532
 
545
533
  dispatcher = Prism::Dispatcher.new
546
534
  semantic_highlighting = Requests::SemanticHighlighting.new(@global_state, dispatcher, document, nil)
547
- dispatcher.visit(document.parse_result.value)
535
+ dispatcher.visit(document.ast)
548
536
 
549
537
  send_message(Result.new(id: message[:id], response: semantic_highlighting.perform))
550
538
  end
@@ -570,7 +558,7 @@ module RubyLsp
570
558
  document,
571
559
  message.dig(:params, :previousResultId),
572
560
  )
573
- dispatcher.visit(document.parse_result.value)
561
+ dispatcher.visit(document.ast)
574
562
  send_message(Result.new(id: message[:id], response: request.perform))
575
563
  end
576
564
 
@@ -599,7 +587,7 @@ module RubyLsp
599
587
  nil,
600
588
  range: range.dig(:start, :line)..range.dig(:end, :line),
601
589
  )
602
- dispatcher.visit(document.parse_result.value)
590
+ dispatcher.visit(document.ast)
603
591
  send_message(Result.new(id: message[:id], response: request.perform))
604
592
  end
605
593
 
@@ -672,6 +660,10 @@ module RubyLsp
672
660
  "Formatting error: #{error.message}",
673
661
  type: Constant::MessageType::ERROR,
674
662
  ))
663
+ send_message(Notification.window_log_message(
664
+ "Formatting failed with\r\n: #{error.full_message}",
665
+ type: Constant::MessageType::ERROR,
666
+ ))
675
667
  send_empty_response(message[:id])
676
668
  end
677
669
 
@@ -687,7 +679,7 @@ module RubyLsp
687
679
  end
688
680
 
689
681
  request = Requests::DocumentHighlight.new(@global_state, document, params[:position], dispatcher)
690
- dispatcher.dispatch(document.parse_result.value)
682
+ dispatcher.dispatch(document.ast)
691
683
  send_message(Result.new(id: message[:id], response: request.perform))
692
684
  end
693
685
 
@@ -795,12 +787,16 @@ module RubyLsp
795
787
  )
796
788
  end
797
789
 
798
- #: (Document[untyped] document) -> RubyDocument::SorbetLevel
790
+ #: (Document[untyped] document) -> SorbetLevel
799
791
  def sorbet_level(document)
800
- return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
801
- return RubyDocument::SorbetLevel::Ignore unless document.is_a?(RubyDocument)
792
+ return SorbetLevel.ignore unless document.is_a?(RubyDocument)
793
+ return SorbetLevel.ignore unless @global_state.has_type_checker
794
+
795
+ sigil = document.parse_result.magic_comments.find do |comment|
796
+ comment.key == "typed"
797
+ end&.value
802
798
 
803
- document.sorbet_level
799
+ SorbetLevel.new(sigil)
804
800
  end
805
801
 
806
802
  #: (Hash[Symbol, untyped] message) -> void
@@ -821,7 +817,6 @@ module RubyLsp
821
817
  return
822
818
  end
823
819
 
824
- hints_configurations = @store.features_configuration.dig(:inlayHint) #: as !nil
825
820
  dispatcher = Prism::Dispatcher.new
826
821
 
827
822
  unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
@@ -829,8 +824,8 @@ module RubyLsp
829
824
  return
830
825
  end
831
826
 
832
- request = Requests::InlayHints.new(document, hints_configurations, dispatcher)
833
- dispatcher.visit(document.parse_result.value)
827
+ request = Requests::InlayHints.new(@global_state, document, dispatcher)
828
+ dispatcher.visit(document.ast)
834
829
  result = request.perform
835
830
  document.cache_set("textDocument/inlayHint", result)
836
831
 
@@ -871,17 +866,9 @@ module RubyLsp
871
866
  end
872
867
 
873
868
  result = Requests::CodeActionResolve.new(document, @global_state, params).perform
874
-
875
- case result
876
- when Requests::CodeActionResolve::Error::EmptySelection
877
- fail_request_and_notify(message[:id], "Invalid selection for extract variable refactor")
878
- when Requests::CodeActionResolve::Error::InvalidTargetRange
879
- fail_request_and_notify(message[:id], "Couldn't find an appropriate location to place extracted refactor")
880
- when Requests::CodeActionResolve::Error::UnknownCodeAction
881
- fail_request_and_notify(message[:id], "Unknown code action")
882
- else
883
- send_message(Result.new(id: message[:id], response: result))
884
- end
869
+ send_message(Result.new(id: message[:id], response: result))
870
+ rescue Requests::CodeActionResolve::CodeActionError => e
871
+ fail_request_and_notify(message[:id], e.message)
885
872
  end
886
873
 
887
874
  #: (Hash[Symbol, untyped] message) -> void
@@ -921,6 +908,10 @@ module RubyLsp
921
908
  "Error running diagnostics: #{error.message}",
922
909
  type: Constant::MessageType::ERROR,
923
910
  ))
911
+ send_message(Notification.window_log_message(
912
+ "Diagnostics failed with\r\n: #{error.full_message}",
913
+ type: Constant::MessageType::ERROR,
914
+ ))
924
915
  send_empty_response(message[:id])
925
916
  end
926
917
 
@@ -1056,13 +1047,14 @@ module RubyLsp
1056
1047
 
1057
1048
  file_name = File.basename(file_path)
1058
1049
 
1059
- if file_name == ".rubocop.yml" || file_name == ".rubocop"
1050
+ if file_name == ".rubocop.yml" || file_name == ".rubocop" || file_name == ".rubocop_todo.yml"
1060
1051
  handle_rubocop_config_change(uri)
1061
1052
  end
1062
1053
  end
1063
1054
 
1064
1055
  Addon.file_watcher_addons.each do |addon|
1065
- T.unsafe(addon).workspace_did_change_watched_files(changes)
1056
+ addon #: as untyped
1057
+ .workspace_did_change_watched_files(changes)
1066
1058
  rescue => e
1067
1059
  send_log_message(
1068
1060
  "Error in #{addon.name} add-on while processing watched file notifications: #{e.full_message}",
@@ -1106,11 +1098,7 @@ module RubyLsp
1106
1098
  @global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
1107
1099
 
1108
1100
  # Clear all document caches for pull diagnostics
1109
- @global_state.synchronize do
1110
- @store.each do |_uri, document|
1111
- document.cache_set("textDocument/diagnostic", Document::EMPTY_CACHE)
1112
- end
1113
- end
1101
+ @store.each { |_uri, document| document.clear_cache("textDocument/diagnostic") }
1114
1102
 
1115
1103
  # Request a pull diagnostic refresh from the editor
1116
1104
  if @global_state.client_capabilities.supports_diagnostic_refresh
@@ -1225,7 +1213,7 @@ module RubyLsp
1225
1213
  }
1226
1214
  end
1227
1215
  end
1228
- rescue Bundler::GemNotFound
1216
+ rescue Bundler::GemNotFound, Bundler::GemfileNotFound, Errno::ENOENT
1229
1217
  []
1230
1218
  end
1231
1219
 
@@ -1264,9 +1252,25 @@ module RubyLsp
1264
1252
  # allocations and garbage collections are faster
1265
1253
  GC.compact unless @test_mode
1266
1254
 
1255
+ @global_state.synchronize do
1256
+ # If we linearize ancestors while the index is not fully populated, we may end up caching incorrect results
1257
+ # that were missing namespaces. After indexing is complete, we need to clear the ancestors cache and start
1258
+ # again
1259
+ @global_state.index.clear_ancestors
1260
+
1261
+ # The results for code lens depend on ancestor linearization, so we need to clear any previously computed
1262
+ # responses
1263
+ @store.each { |_uri, document| document.clear_cache("textDocument/codeLens") }
1264
+ end
1265
+
1267
1266
  # Always end the progress notification even if indexing failed or else it never goes away and the user has no
1268
1267
  # way of dismissing it
1269
1268
  end_progress("indexing-progress")
1269
+
1270
+ # Request a code lens refresh if we populated them before all test parent classes were indexed
1271
+ if @global_state.client_capabilities.supports_code_lens_refresh
1272
+ send_message(Request.new(id: @current_request_id, method: "workspace/codeLens/refresh", params: nil))
1273
+ end
1270
1274
  end
1271
1275
  end
1272
1276
 
@@ -1392,8 +1396,38 @@ module RubyLsp
1392
1396
 
1393
1397
  # We compose the bundle in a thread so that the LSP continues to work while we're checking for its validity. Once
1394
1398
  # we return the response back to the editor, then the restart is triggered
1399
+ launch_bundle_compose("Recomposing the bundle ahead of restart") do |stderr, status|
1400
+ if status&.exitstatus == 0
1401
+ # Create a signal for the restart that it can skip composing the bundle and launch directly
1402
+ FileUtils.touch(already_composed_path)
1403
+ send_message(Result.new(id: id, response: { success: true }))
1404
+ else
1405
+ # This special error code makes the extension avoid restarting in case we already know that the composed
1406
+ # bundle is not valid
1407
+ send_message(
1408
+ Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
1409
+ )
1410
+ end
1411
+ end
1412
+ end
1413
+
1414
+ #: -> void
1415
+ def update_server
1416
+ return unless File.exist?(File.join(@global_state.workspace_path, ".ruby-lsp", "needs_update"))
1417
+
1418
+ launch_bundle_compose("Trying to update server") do |stderr, status|
1419
+ if status&.exitstatus == 0
1420
+ send_log_message("Successfully updated the server")
1421
+ else
1422
+ send_log_message("Failed to update server\n#{stderr}", type: Constant::MessageType::ERROR)
1423
+ end
1424
+ end
1425
+ end
1426
+
1427
+ #: (String) { (IO, Process::Status?) -> void } -> Thread
1428
+ def launch_bundle_compose(log, &block)
1395
1429
  Thread.new do
1396
- send_log_message("Recomposing the bundle ahead of restart")
1430
+ send_log_message(log)
1397
1431
 
1398
1432
  _stdout, stderr, status = Bundler.with_unbundled_env do
1399
1433
  Open3.capture3(
@@ -1404,21 +1438,12 @@ module RubyLsp
1404
1438
  ),
1405
1439
  File.expand_path("../../exe/ruby-lsp-launcher", __dir__),
1406
1440
  @global_state.workspace_uri.to_s,
1441
+ *ARGV,
1407
1442
  chdir: @global_state.workspace_path,
1408
1443
  )
1409
1444
  end
1410
1445
 
1411
- if status&.exitstatus == 0
1412
- # Create a signal for the restart that it can skip composing the bundle and launch directly
1413
- FileUtils.touch(already_composed_path)
1414
- send_message(Result.new(id: id, response: { success: true }))
1415
- else
1416
- # This special error code makes the extension avoid restarting in case we already know that the composed
1417
- # bundle is not valid
1418
- send_message(
1419
- Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
1420
- )
1421
- end
1446
+ block.call(stderr, status)
1422
1447
  end
1423
1448
  end
1424
1449
 
@@ -1475,10 +1500,28 @@ module RubyLsp
1475
1500
 
1476
1501
  send_message(Result.new(
1477
1502
  id: message[:id],
1478
- response: {
1479
- commands: commands,
1480
- reporterPaths: [Listeners::TestStyle::MINITEST_REPORTER_PATH, Listeners::TestStyle::TEST_UNIT_REPORTER_PATH],
1481
- },
1503
+ response: { commands: commands },
1504
+ ))
1505
+ end
1506
+
1507
+ #: (Hash[Symbol, untyped] message) -> void
1508
+ def code_lens_resolve(message)
1509
+ code_lens = message[:params]
1510
+ args = code_lens.dig(:data, :arguments)
1511
+
1512
+ case code_lens.dig(:data, :kind)
1513
+ when "run_test"
1514
+ code_lens[:command] = Interface::Command.new(title: "▶ Run", command: "rubyLsp.runTest", arguments: args)
1515
+ when "run_test_in_terminal"
1516
+ code_lens[:command] =
1517
+ Interface::Command.new(title: "▶ Run in terminal", command: "rubyLsp.runTestInTerminal", arguments: args)
1518
+ when "debug_test"
1519
+ code_lens[:command] = Interface::Command.new(title: "⚙ Debug", command: "rubyLsp.debugTest", arguments: args)
1520
+ end
1521
+
1522
+ send_message(Result.new(
1523
+ id: message[:id],
1524
+ response: code_lens,
1482
1525
  ))
1483
1526
  end
1484
1527
  end