ruby-lsp 0.22.1 → 0.23.10

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +12 -11
  5. data/exe/ruby-lsp-check +5 -5
  6. data/exe/ruby-lsp-launcher +41 -15
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
  8. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +191 -100
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +174 -61
  11. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +12 -0
  12. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
  13. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +82 -61
  14. data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
  15. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +36 -0
  16. data/lib/ruby_indexer/ruby_indexer.rb +2 -1
  17. data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
  18. data/lib/ruby_indexer/test/classes_and_modules_test.rb +30 -6
  19. data/lib/ruby_indexer/test/configuration_test.rb +116 -51
  20. data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
  21. data/lib/ruby_indexer/test/index_test.rb +143 -44
  22. data/lib/ruby_indexer/test/instance_variables_test.rb +20 -0
  23. data/lib/ruby_indexer/test/method_test.rb +86 -8
  24. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  25. data/lib/ruby_indexer/test/reference_finder_test.rb +90 -2
  26. data/lib/ruby_indexer/test/test_case.rb +2 -2
  27. data/lib/ruby_indexer/test/uri_test.rb +72 -0
  28. data/lib/ruby_lsp/addon.rb +9 -0
  29. data/lib/ruby_lsp/base_server.rb +17 -18
  30. data/lib/ruby_lsp/client_capabilities.rb +7 -1
  31. data/lib/ruby_lsp/document.rb +72 -10
  32. data/lib/ruby_lsp/erb_document.rb +5 -3
  33. data/lib/ruby_lsp/global_state.rb +42 -3
  34. data/lib/ruby_lsp/internal.rb +3 -1
  35. data/lib/ruby_lsp/listeners/code_lens.rb +9 -5
  36. data/lib/ruby_lsp/listeners/completion.rb +78 -6
  37. data/lib/ruby_lsp/listeners/definition.rb +80 -19
  38. data/lib/ruby_lsp/listeners/document_highlight.rb +3 -2
  39. data/lib/ruby_lsp/listeners/document_link.rb +21 -3
  40. data/lib/ruby_lsp/listeners/document_symbol.rb +12 -1
  41. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  42. data/lib/ruby_lsp/listeners/hover.rb +59 -2
  43. data/lib/ruby_lsp/load_sorbet.rb +3 -3
  44. data/lib/ruby_lsp/rbs_document.rb +2 -2
  45. data/lib/ruby_lsp/requests/code_action_resolve.rb +90 -6
  46. data/lib/ruby_lsp/requests/code_actions.rb +57 -1
  47. data/lib/ruby_lsp/requests/completion.rb +8 -1
  48. data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
  49. data/lib/ruby_lsp/requests/definition.rb +7 -1
  50. data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
  51. data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
  52. data/lib/ruby_lsp/requests/folding_ranges.rb +2 -6
  53. data/lib/ruby_lsp/requests/formatting.rb +2 -6
  54. data/lib/ruby_lsp/requests/hover.rb +1 -1
  55. data/lib/ruby_lsp/requests/on_type_formatting.rb +2 -2
  56. data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
  57. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
  58. data/lib/ruby_lsp/requests/references.rb +29 -2
  59. data/lib/ruby_lsp/requests/rename.rb +17 -7
  60. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  61. data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -4
  62. data/lib/ruby_lsp/requests/signature_help.rb +1 -1
  63. data/lib/ruby_lsp/requests/support/common.rb +2 -9
  64. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +3 -3
  65. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -13
  66. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
  67. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -3
  68. data/lib/ruby_lsp/ruby_document.rb +80 -6
  69. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  70. data/lib/ruby_lsp/server.rb +205 -61
  71. data/lib/ruby_lsp/setup_bundler.rb +50 -43
  72. data/lib/ruby_lsp/store.rb +7 -7
  73. data/lib/ruby_lsp/test_helper.rb +45 -11
  74. data/lib/ruby_lsp/type_inferrer.rb +60 -31
  75. data/lib/ruby_lsp/utils.rb +63 -3
  76. metadata +8 -8
  77. data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +0 -29
@@ -0,0 +1,72 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "test_helper"
5
+
6
+ module RubyIndexer
7
+ class URITest < Minitest::Test
8
+ def test_from_path_on_unix
9
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
10
+ assert_equal("/some/unix/path/to/file.rb", uri.path)
11
+ end
12
+
13
+ def test_from_path_on_windows
14
+ uri = URI::Generic.from_path(path: "C:/some/windows/path/to/file.rb")
15
+ assert_equal("/C:/some/windows/path/to/file.rb", uri.path)
16
+ end
17
+
18
+ def test_from_path_on_windows_with_lowercase_drive
19
+ uri = URI::Generic.from_path(path: "c:/some/windows/path/to/file.rb")
20
+ assert_equal("/c:/some/windows/path/to/file.rb", uri.path)
21
+ end
22
+
23
+ def test_to_standardized_path_on_unix
24
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
25
+ assert_equal(uri.path, uri.to_standardized_path)
26
+ end
27
+
28
+ def test_to_standardized_path_on_windows
29
+ uri = URI::Generic.from_path(path: "C:/some/windows/path/to/file.rb")
30
+ assert_equal("C:/some/windows/path/to/file.rb", uri.to_standardized_path)
31
+ end
32
+
33
+ def test_to_standardized_path_on_windows_with_lowercase_drive
34
+ uri = URI::Generic.from_path(path: "c:/some/windows/path/to/file.rb")
35
+ assert_equal("c:/some/windows/path/to/file.rb", uri.to_standardized_path)
36
+ end
37
+
38
+ def test_to_standardized_path_on_windows_with_received_uri
39
+ uri = URI("file:///c%3A/some/windows/path/to/file.rb")
40
+ assert_equal("c:/some/windows/path/to/file.rb", uri.to_standardized_path)
41
+ end
42
+
43
+ def test_plus_signs_are_properly_unescaped
44
+ path = "/opt/rubies/3.3.0/lib/ruby/3.3.0+0/pathname.rb"
45
+ uri = URI::Generic.from_path(path: path)
46
+ assert_equal(path, uri.to_standardized_path)
47
+ end
48
+
49
+ def test_from_path_with_fragment
50
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb", fragment: "L1,3-2,9")
51
+ assert_equal("file:///some/unix/path/to/file.rb#L1,3-2,9", uri.to_s)
52
+ end
53
+
54
+ def test_from_path_windows_long_file_paths
55
+ uri = URI::Generic.from_path(path: "//?/C:/hostedtoolcache/windows/Ruby/3.3.1/x64/lib/ruby/3.3.0/open-uri.rb")
56
+ assert_equal("C:/hostedtoolcache/windows/Ruby/3.3.1/x64/lib/ruby/3.3.0/open-uri.rb", uri.to_standardized_path)
57
+ end
58
+
59
+ def test_from_path_computes_require_path_when_load_path_entry_is_given
60
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb", load_path_entry: "/some/unix/path")
61
+ assert_equal("to/file", uri.require_path)
62
+ end
63
+
64
+ def test_allows_adding_require_path_with_load_path_entry
65
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
66
+ assert_nil(uri.require_path)
67
+
68
+ uri.add_require_path_from_load_entry("/some/unix/path")
69
+ assert_equal("to/file", uri.require_path)
70
+ end
71
+ end
72
+ end
@@ -97,6 +97,15 @@ module RubyLsp
97
97
  errors
98
98
  end
99
99
 
100
+ # Unloads all add-ons. Only intended to be invoked once when shutting down the Ruby LSP server
101
+ sig { void }
102
+ def unload_addons
103
+ @addons.each(&:deactivate)
104
+ @addons.clear
105
+ @addon_classes.clear
106
+ @file_watcher_addons.clear
107
+ end
108
+
100
109
  # Get a reference to another add-on object by name and version. If an add-on exports an API that can be used by
101
110
  # other add-ons, this is the way to get access to that API.
102
111
  #
@@ -18,22 +18,21 @@ module RubyLsp
18
18
  @incoming_queue = T.let(Thread::Queue.new, Thread::Queue)
19
19
  @outgoing_queue = T.let(Thread::Queue.new, Thread::Queue)
20
20
  @cancelled_requests = T.let([], T::Array[Integer])
21
- @mutex = T.let(Mutex.new, Mutex)
22
21
  @worker = T.let(new_worker, Thread)
23
22
  @current_request_id = T.let(1, Integer)
24
- @store = T.let(Store.new, Store)
23
+ @global_state = T.let(GlobalState.new, GlobalState)
24
+ @store = T.let(Store.new(@global_state), Store)
25
25
  @outgoing_dispatcher = T.let(
26
26
  Thread.new do
27
27
  unless @test_mode
28
28
  while (message = @outgoing_queue.pop)
29
- @mutex.synchronize { @writer.write(message.to_hash) }
29
+ @global_state.synchronize { @writer.write(message.to_hash) }
30
30
  end
31
31
  end
32
32
  end,
33
33
  Thread,
34
34
  )
35
35
 
36
- @global_state = T.let(GlobalState.new, GlobalState)
37
36
  Thread.main.priority = 1
38
37
 
39
38
  # We read the initialize request in `exe/ruby-lsp` to be able to determine the workspace URI where Bundler should
@@ -51,7 +50,7 @@ module RubyLsp
51
50
  # source. Altering the source reference during parsing will put the parser in an invalid internal state, since
52
51
  # it started parsing with one source but then it changed in the middle. We don't want to do this for text
53
52
  # synchronization notifications
54
- @mutex.synchronize do
53
+ @global_state.synchronize do
55
54
  uri = message.dig(:params, :textDocument, :uri)
56
55
 
57
56
  if uri
@@ -94,20 +93,14 @@ module RubyLsp
94
93
  when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
95
94
  process_message(message)
96
95
  when "shutdown"
97
- send_log_message("Shutting down Ruby LSP...")
98
-
99
- shutdown
100
-
101
- @mutex.synchronize do
96
+ @global_state.synchronize do
97
+ send_log_message("Shutting down Ruby LSP...")
98
+ shutdown
102
99
  run_shutdown
103
100
  @writer.write(Result.new(id: message[:id], response: nil).to_hash)
104
101
  end
105
102
  when "exit"
106
- @mutex.synchronize do
107
- status = @incoming_queue.closed? ? 0 : 1
108
- send_log_message("Shutdown complete with status #{status}")
109
- exit(status)
110
- end
103
+ @global_state.synchronize { exit(@incoming_queue.closed? ? 0 : 1) }
111
104
  else
112
105
  @incoming_queue << message
113
106
  end
@@ -122,8 +115,8 @@ module RubyLsp
122
115
  @outgoing_queue.close
123
116
  @cancelled_requests.clear
124
117
 
125
- @worker.join
126
- @outgoing_dispatcher.join
118
+ @worker.terminate
119
+ @outgoing_dispatcher.terminate
127
120
  @store.clear
128
121
  end
129
122
 
@@ -133,6 +126,12 @@ module RubyLsp
133
126
  @outgoing_queue.pop
134
127
  end
135
128
 
129
+ # This method is only intended to be used in tests! Pushes a message to the incoming queue directly
130
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
131
+ def push_message(message)
132
+ @incoming_queue << message
133
+ end
134
+
136
135
  sig { abstract.params(message: T::Hash[Symbol, T.untyped]).void }
137
136
  def process_message(message); end
138
137
 
@@ -152,7 +151,7 @@ module RubyLsp
152
151
  id = message[:id]
153
152
 
154
153
  # Check if the request was cancelled before trying to process it
155
- @mutex.synchronize do
154
+ @global_state.synchronize do
156
155
  if id && @cancelled_requests.include?(id)
157
156
  send_message(Result.new(id: id, response: nil))
158
157
  @cancelled_requests.delete(id)
@@ -11,7 +11,8 @@ module RubyLsp
11
11
  attr_reader :supports_watching_files,
12
12
  :supports_request_delegation,
13
13
  :window_show_message_supports_extra_properties,
14
- :supports_progress
14
+ :supports_progress,
15
+ :supports_diagnostic_refresh
15
16
 
16
17
  sig { void }
17
18
  def initialize
@@ -32,6 +33,9 @@ module RubyLsp
32
33
 
33
34
  # The editor supports displaying progress requests
34
35
  @supports_progress = T.let(false, T::Boolean)
36
+
37
+ # The editor supports server initiated refresh for diagnostics
38
+ @supports_diagnostic_refresh = T.let(false, T::Boolean)
35
39
  end
36
40
 
37
41
  sig { params(capabilities: T::Hash[Symbol, T.untyped]).void }
@@ -57,6 +61,8 @@ module RubyLsp
57
61
 
58
62
  progress = capabilities.dig(:window, :workDoneProgress)
59
63
  @supports_progress = progress if progress
64
+
65
+ @supports_diagnostic_refresh = workspace_capabilities.dig(:diagnostics, :refreshSupport) || false
60
66
  end
61
67
 
62
68
  sig { returns(T::Boolean) }
@@ -15,6 +15,7 @@ module RubyLsp
15
15
  extend T::Helpers
16
16
  extend T::Generic
17
17
 
18
+ class LocationNotFoundError < StandardError; end
18
19
  ParseResultType = type_member
19
20
 
20
21
  # This maximum number of characters for providing expensive features, like semantic highlighting and diagnostics.
@@ -39,19 +40,24 @@ module RubyLsp
39
40
  sig { returns(Encoding) }
40
41
  attr_reader :encoding
41
42
 
43
+ sig { returns(T.nilable(Edit)) }
44
+ attr_reader :last_edit
45
+
42
46
  sig { returns(T.any(Interface::SemanticTokens, Object)) }
43
47
  attr_accessor :semantic_tokens
44
48
 
45
- sig { params(source: String, version: Integer, uri: URI::Generic, encoding: Encoding).void }
46
- def initialize(source:, version:, uri:, encoding: Encoding::UTF_8)
49
+ sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
50
+ def initialize(source:, version:, uri:, global_state:)
51
+ @source = source
52
+ @version = version
53
+ @global_state = global_state
47
54
  @cache = T.let(Hash.new(EMPTY_CACHE), T::Hash[String, T.untyped])
48
55
  @semantic_tokens = T.let(EMPTY_CACHE, T.any(Interface::SemanticTokens, Object))
49
- @encoding = T.let(encoding, Encoding)
50
- @source = T.let(source, String)
51
- @version = T.let(version, Integer)
56
+ @encoding = T.let(global_state.encoding, Encoding)
52
57
  @uri = T.let(uri, URI::Generic)
53
58
  @needs_parsing = T.let(true, T::Boolean)
54
59
  @parse_result = T.let(T.unsafe(nil), ParseResultType)
60
+ @last_edit = T.let(nil, T.nilable(Edit))
55
61
  parse!
56
62
  end
57
63
 
@@ -63,7 +69,6 @@ module RubyLsp
63
69
  sig { abstract.returns(LanguageId) }
64
70
  def language_id; end
65
71
 
66
- # TODO: remove this method once all non-positional requests have been migrated to the listener pattern
67
72
  sig do
68
73
  type_parameters(:T)
69
74
  .params(
@@ -105,6 +110,19 @@ module RubyLsp
105
110
  @version = version
106
111
  @needs_parsing = true
107
112
  @cache.clear
113
+
114
+ last_edit = edits.last
115
+ return unless last_edit
116
+
117
+ last_edit_range = last_edit[:range]
118
+
119
+ @last_edit = if last_edit_range[:start] == last_edit_range[:end]
120
+ Insert.new(last_edit_range)
121
+ elsif last_edit[:text].empty?
122
+ Delete.new(last_edit_range)
123
+ else
124
+ Replace.new(last_edit_range)
125
+ end
108
126
  end
109
127
 
110
128
  # Returns `true` if the document was parsed and `false` if nothing needed parsing
@@ -114,16 +132,52 @@ module RubyLsp
114
132
  sig { abstract.returns(T::Boolean) }
115
133
  def syntax_error?; end
116
134
 
135
+ sig { returns(T::Boolean) }
136
+ def past_expensive_limit?
137
+ @source.length > MAXIMUM_CHARACTERS_FOR_EXPENSIVE_FEATURES
138
+ end
139
+
140
+ sig do
141
+ params(
142
+ start_pos: T::Hash[Symbol, T.untyped],
143
+ end_pos: T.nilable(T::Hash[Symbol, T.untyped]),
144
+ ).returns([Integer, T.nilable(Integer)])
145
+ end
146
+ def find_index_by_position(start_pos, end_pos = nil)
147
+ @global_state.synchronize do
148
+ scanner = create_scanner
149
+ start_index = scanner.find_char_position(start_pos)
150
+ end_index = scanner.find_char_position(end_pos) if end_pos
151
+ [start_index, end_index]
152
+ end
153
+ end
154
+
155
+ private
156
+
117
157
  sig { returns(Scanner) }
118
158
  def create_scanner
119
159
  Scanner.new(@source, @encoding)
120
160
  end
121
161
 
122
- sig { returns(T::Boolean) }
123
- def past_expensive_limit?
124
- @source.length > MAXIMUM_CHARACTERS_FOR_EXPENSIVE_FEATURES
162
+ class Edit
163
+ extend T::Sig
164
+ extend T::Helpers
165
+
166
+ abstract!
167
+
168
+ sig { returns(T::Hash[Symbol, T.untyped]) }
169
+ attr_reader :range
170
+
171
+ sig { params(range: T::Hash[Symbol, T.untyped]).void }
172
+ def initialize(range)
173
+ @range = range
174
+ end
125
175
  end
126
176
 
177
+ class Insert < Edit; end
178
+ class Replace < Edit; end
179
+ class Delete < Edit; end
180
+
127
181
  class Scanner
128
182
  extend T::Sig
129
183
 
@@ -144,7 +198,15 @@ module RubyLsp
144
198
  def find_char_position(position)
145
199
  # Find the character index for the beginning of the requested line
146
200
  until @current_line == position[:line]
147
- @pos += 1 until LINE_BREAK == @source[@pos]
201
+ until LINE_BREAK == @source[@pos]
202
+ @pos += 1
203
+
204
+ if @pos >= @source.length
205
+ # Pack the code points back into the original string to provide context in the error message
206
+ raise LocationNotFoundError, "Requested position: #{position}\nSource:\n\n#{@source.pack("U*")}"
207
+ end
208
+ end
209
+
148
210
  @pos += 1
149
211
  @current_line += 1
150
212
  end
@@ -19,8 +19,8 @@ module RubyLsp
19
19
  end
20
20
  attr_reader :code_units_cache
21
21
 
22
- sig { params(source: String, version: Integer, uri: URI::Generic, encoding: Encoding).void }
23
- def initialize(source:, version:, uri:, encoding: Encoding::UTF_8)
22
+ sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
23
+ def initialize(source:, version:, uri:, global_state:)
24
24
  # This has to be initialized before calling super because we call `parse` in the parent constructor, which
25
25
  # overrides this with the proper virtual host language source
26
26
  @host_language_source = T.let("", String)
@@ -63,9 +63,11 @@ module RubyLsp
63
63
  ).returns(NodeContext)
64
64
  end
65
65
  def locate_node(position, node_types: [])
66
+ char_position, _ = find_index_by_position(position)
67
+
66
68
  RubyDocument.locate(
67
69
  @parse_result.value,
68
- create_scanner.find_char_position(position),
70
+ char_position,
69
71
  code_units_cache: @code_units_cache,
70
72
  node_types: node_types,
71
73
  )
@@ -29,6 +29,12 @@ module RubyLsp
29
29
  sig { returns(ClientCapabilities) }
30
30
  attr_reader :client_capabilities
31
31
 
32
+ sig { returns(URI::Generic) }
33
+ attr_reader :workspace_uri
34
+
35
+ sig { returns(T.nilable(String)) }
36
+ attr_reader :telemetry_machine_id
37
+
32
38
  sig { void }
33
39
  def initialize
34
40
  @workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
@@ -53,6 +59,13 @@ module RubyLsp
53
59
  )
54
60
  @client_capabilities = T.let(ClientCapabilities.new, ClientCapabilities)
55
61
  @enabled_feature_flags = T.let({}, T::Hash[Symbol, T::Boolean])
62
+ @mutex = T.let(Mutex.new, Mutex)
63
+ @telemetry_machine_id = T.let(nil, T.nilable(String))
64
+ end
65
+
66
+ sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
67
+ def synchronize(&block)
68
+ @mutex.synchronize(&block)
56
69
  end
57
70
 
58
71
  sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
@@ -85,6 +98,8 @@ module RubyLsp
85
98
  @workspace_uri = URI(workspace_uri) if workspace_uri
86
99
 
87
100
  specified_formatter = options.dig(:initializationOptions, :formatter)
101
+ rubocop_has_addon = defined?(::RuboCop::Version::STRING) &&
102
+ Gem::Requirement.new(">= 1.70.0").satisfied_by?(Gem::Version.new(::RuboCop::Version::STRING))
88
103
 
89
104
  if specified_formatter
90
105
  @formatter = specified_formatter
@@ -92,6 +107,12 @@ module RubyLsp
92
107
  if specified_formatter != "auto"
93
108
  notifications << Notification.window_log_message("Using formatter specified by user: #{@formatter}")
94
109
  end
110
+
111
+ # If the user had originally configured to use `rubocop`, but their version doesn't provide the add-on yet,
112
+ # fallback to the internal integration
113
+ if specified_formatter == "rubocop" && !rubocop_has_addon
114
+ @formatter = "rubocop_internal"
115
+ end
95
116
  end
96
117
 
97
118
  if @formatter == "auto"
@@ -100,6 +121,23 @@ module RubyLsp
100
121
  end
101
122
 
102
123
  specified_linters = options.dig(:initializationOptions, :linters)
124
+
125
+ if specified_formatter == "rubocop" || specified_linters&.include?("rubocop")
126
+ notifications << Notification.window_log_message(<<~MESSAGE, type: Constant::MessageType::WARNING)
127
+ Formatter is configured to be `rubocop`. As of RuboCop v1.70.0, this identifier activates the add-on
128
+ implemented in the rubocop gem itself instead of the internal integration provided by the Ruby LSP.
129
+
130
+ If you wish to use the internal integration, please configure the formatter as `rubocop_internal`.
131
+ MESSAGE
132
+ end
133
+
134
+ # If the user had originally configured to use `rubocop`, but their version doesn't provide the add-on yet,
135
+ # fall back to the internal integration
136
+ if specified_linters&.include?("rubocop") && !rubocop_has_addon
137
+ specified_linters.delete("rubocop")
138
+ specified_linters << "rubocop_internal"
139
+ end
140
+
103
141
  @linters = specified_linters || detect_linters(direct_dependencies, all_dependencies)
104
142
 
105
143
  notifications << if specified_linters
@@ -141,6 +179,7 @@ module RubyLsp
141
179
  enabled_flags = options.dig(:initializationOptions, :enabledFeatureFlags)
142
180
  @enabled_feature_flags = enabled_flags if enabled_flags
143
181
 
182
+ @telemetry_machine_id = options.dig(:initializationOptions, :telemetryMachineId)
144
183
  notifications
145
184
  end
146
185
 
@@ -176,13 +215,13 @@ module RubyLsp
176
215
  sig { params(direct_dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(String) }
177
216
  def detect_formatter(direct_dependencies, all_dependencies)
178
217
  # NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
179
- return "rubocop" if direct_dependencies.any?(/^rubocop/)
218
+ return "rubocop_internal" if direct_dependencies.any?(/^rubocop/)
180
219
 
181
220
  syntax_tree_is_direct_dependency = direct_dependencies.include?("syntax_tree")
182
221
  return "syntax_tree" if syntax_tree_is_direct_dependency
183
222
 
184
223
  rubocop_is_transitive_dependency = all_dependencies.include?("rubocop")
185
- return "rubocop" if dot_rubocop_yml_present && rubocop_is_transitive_dependency
224
+ return "rubocop_internal" if dot_rubocop_yml_present && rubocop_is_transitive_dependency
186
225
 
187
226
  "none"
188
227
  end
@@ -194,7 +233,7 @@ module RubyLsp
194
233
  linters = []
195
234
 
196
235
  if dependencies.any?(/^rubocop/) || (all_dependencies.include?("rubocop") && dot_rubocop_yml_present)
197
- linters << "rubocop"
236
+ linters << "rubocop_internal"
198
237
  end
199
238
 
200
239
  linters
@@ -22,11 +22,12 @@ require "prism/visitor"
22
22
  require "language_server-protocol"
23
23
  require "rbs"
24
24
  require "fileutils"
25
+ require "open3"
26
+ require "securerandom"
25
27
 
26
28
  require "ruby-lsp"
27
29
  require "ruby_lsp/base_server"
28
30
  require "ruby_indexer/ruby_indexer"
29
- require "core_ext/uri"
30
31
  require "ruby_lsp/utils"
31
32
  require "ruby_lsp/static_docs"
32
33
  require "ruby_lsp/scope"
@@ -78,6 +79,7 @@ require "ruby_lsp/requests/hover"
78
79
  require "ruby_lsp/requests/inlay_hints"
79
80
  require "ruby_lsp/requests/on_type_formatting"
80
81
  require "ruby_lsp/requests/prepare_type_hierarchy"
82
+ require "ruby_lsp/requests/prepare_rename"
81
83
  require "ruby_lsp/requests/range_formatting"
82
84
  require "ruby_lsp/requests/references"
83
85
  require "ruby_lsp/requests/rename"
@@ -15,7 +15,7 @@ module RubyLsp
15
15
  "bundle exec ruby"
16
16
  rescue Bundler::GemfileNotFound
17
17
  "ruby"
18
- end + " -Itest ",
18
+ end,
19
19
  String,
20
20
  )
21
21
  ACCESS_MODIFIERS = T.let([:public, :private, :protected], T::Array[Symbol])
@@ -198,7 +198,7 @@ module RubyLsp
198
198
 
199
199
  @response_builder << create_code_lens(
200
200
  node,
201
- title: "Run",
201
+ title: "Run",
202
202
  command_name: "rubyLsp.runTest",
203
203
  arguments: arguments,
204
204
  data: { type: "test", **grouping_data },
@@ -206,7 +206,7 @@ module RubyLsp
206
206
 
207
207
  @response_builder << create_code_lens(
208
208
  node,
209
- title: "Run In Terminal",
209
+ title: "Run In Terminal",
210
210
  command_name: "rubyLsp.runTestInTerminal",
211
211
  arguments: arguments,
212
212
  data: { type: "test_in_terminal", **grouping_data },
@@ -229,7 +229,11 @@ module RubyLsp
229
229
  ).returns(String)
230
230
  end
231
231
  def generate_test_command(group_stack: [], spec_name: nil, method_name: nil)
232
- command = BASE_COMMAND + T.must(@path)
232
+ path = T.must(@path)
233
+ command = BASE_COMMAND
234
+ command += " -Itest" if File.fnmatch?("**/test/**/*", path, File::FNM_PATHNAME)
235
+ command += " -Ispec" if File.fnmatch?("**/spec/**/*", path, File::FNM_PATHNAME)
236
+ command += " #{path}"
233
237
 
234
238
  case @global_state.test_library
235
239
  when "minitest"
@@ -282,7 +286,7 @@ module RubyLsp
282
286
  when Prism::StringNode
283
287
  first_argument.content
284
288
  when Prism::ConstantReadNode, Prism::ConstantPathNode
285
- constant_name(first_argument)
289
+ RubyIndexer::Index.constant_name(first_argument)
286
290
  end
287
291
 
288
292
  return unless name