ruby-lsp 0.23.4 → 0.23.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8b49e72ac08f80faf14e0ed347d4af75f2d55b7465f14412e1e0900f422783f
4
- data.tar.gz: 0b496376c25068929d275d71a87c414a7ecf185b3dbf6dfba27ccdc8c1b0a61f
3
+ metadata.gz: 563db8ddc06dc53f37db5ea4a42a48b7af8ed5bb2639c42e87e3c2bdd7fe38d1
4
+ data.tar.gz: a1820031a007e14c42600fd897e43fad044f26e05f02e117c806821cdc46e9d7
5
5
  SHA512:
6
- metadata.gz: '0791955260be2f7374522ef551b0636bd87a9947b3b20818d8e0f06dc5046b0b0c7c85beb5c69af2d146c998b2ce4298d7d1bf141be4d71bc5aacf07e0f7c151'
7
- data.tar.gz: e08f19fc4dd44035cb4e569daf1eab628caec729e903353eaf1ec97aa7644e80552fa903da4d739f696e6de704d7939aebb4685781d81a88b1375a4ab1137225
6
+ metadata.gz: 2d28983a474a7b32486f20df65ec2a808ddf2be31bc0c4d7d62ae1c3fa24448b28699917d23b6f4f00045ff45e25aeff1e27c6a27d40c6a7dc6887be630f8828
7
+ data.tar.gz: d10bd35268476cd2b2d319405dd58433d32df48a621cf4d45555cafb6e293d3bc4b3844afe7bf6dc0d39e68a5f5a7be70b935874796a3852a68ac5c61c592709
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.23.4
1
+ 0.23.6
@@ -7,6 +7,7 @@
7
7
  # !!!!!!!
8
8
 
9
9
  setup_error = nil
10
+ install_error = nil
10
11
 
11
12
  # Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
12
13
  # Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
@@ -45,12 +46,6 @@ rescue Errno::ECHILD
45
46
  # In theory, the child process can finish before we even get to the wait call, but that is not an error
46
47
  end
47
48
 
48
- error_path = File.join(".ruby-lsp", "install_error")
49
-
50
- install_error = if File.exist?(error_path)
51
- Marshal.load(File.read(error_path))
52
- end
53
-
54
49
  begin
55
50
  bundle_env_path = File.join(".ruby-lsp", "bundle_env")
56
51
  # We can't require `bundler/setup` because that file prematurely exits the process if setup fails. However, we can't
@@ -67,6 +62,14 @@ begin
67
62
 
68
63
  require "bundler"
69
64
  Bundler.ui.level = :silent
65
+
66
+ # This Marshal load can only happen after requiring Bundler because it will load a custom error class from Bundler
67
+ # itself. If we try to load before requiring, the class will not be defined and loading will fail
68
+ error_path = File.join(".ruby-lsp", "install_error")
69
+ install_error = if File.exist?(error_path)
70
+ Marshal.load(File.read(error_path))
71
+ end
72
+
70
73
  Bundler.setup
71
74
  $stderr.puts("Composed Bundle set up successfully")
72
75
  end
@@ -143,7 +143,7 @@ module RubyIndexer
143
143
 
144
144
  if current_owner
145
145
  expression = node.expression
146
- name = (expression.is_a?(Prism::SelfNode) ? "<Class:#{@stack.last}>" : "<Class:#{expression.slice}>")
146
+ name = (expression.is_a?(Prism::SelfNode) ? "<Class:#{last_name_in_stack}>" : "<Class:#{expression.slice}>")
147
147
  real_nesting = actual_nesting(name)
148
148
 
149
149
  existing_entries = T.cast(@index[real_nesting.join("::")], T.nilable(T::Array[Entry::SingletonClass]))
@@ -376,7 +376,6 @@ module RubyIndexer
376
376
  ))
377
377
 
378
378
  @owner_stack << singleton
379
- @stack << "<Class:#{@stack.last}>"
380
379
  end
381
380
  end
382
381
 
@@ -386,7 +385,6 @@ module RubyIndexer
386
385
 
387
386
  if node.receiver.is_a?(Prism::SelfNode)
388
387
  @owner_stack.pop
389
- @stack.pop
390
388
  end
391
389
  end
392
390
 
@@ -1127,5 +1125,15 @@ module RubyIndexer
1127
1125
  @index.add(entry)
1128
1126
  @stack << short_name
1129
1127
  end
1128
+
1129
+ # Returns the last name in the stack not as we found it, but in terms of declared constants. For example, if the
1130
+ # last entry in the stack is a compact namespace like `Foo::Bar`, then the last name is `Bar`
1131
+ sig { returns(T.nilable(String)) }
1132
+ def last_name_in_stack
1133
+ name = @stack.last
1134
+ return unless name
1135
+
1136
+ name.split("::").last
1137
+ end
1130
1138
  end
1131
1139
  end
@@ -37,6 +37,19 @@ module RubyIndexer
37
37
  end
38
38
  end
39
39
 
40
+ class InstanceVariableTarget < Target
41
+ extend T::Sig
42
+
43
+ sig { returns(String) }
44
+ attr_reader :name
45
+
46
+ sig { params(name: String).void }
47
+ def initialize(name)
48
+ super()
49
+ @name = name
50
+ end
51
+ end
52
+
40
53
  class Reference
41
54
  extend T::Sig
42
55
 
@@ -94,6 +107,12 @@ module RubyIndexer
94
107
  :on_constant_or_write_node_enter,
95
108
  :on_constant_and_write_node_enter,
96
109
  :on_constant_operator_write_node_enter,
110
+ :on_instance_variable_read_node_enter,
111
+ :on_instance_variable_write_node_enter,
112
+ :on_instance_variable_and_write_node_enter,
113
+ :on_instance_variable_operator_write_node_enter,
114
+ :on_instance_variable_or_write_node_enter,
115
+ :on_instance_variable_target_node_enter,
97
116
  :on_call_node_enter,
98
117
  )
99
118
  end
@@ -262,6 +281,36 @@ module RubyIndexer
262
281
  end
263
282
  end
264
283
 
284
+ sig { params(node: Prism::InstanceVariableReadNode).void }
285
+ def on_instance_variable_read_node_enter(node)
286
+ collect_instance_variable_references(node.name.to_s, node.location, false)
287
+ end
288
+
289
+ sig { params(node: Prism::InstanceVariableWriteNode).void }
290
+ def on_instance_variable_write_node_enter(node)
291
+ collect_instance_variable_references(node.name.to_s, node.name_loc, true)
292
+ end
293
+
294
+ sig { params(node: Prism::InstanceVariableAndWriteNode).void }
295
+ def on_instance_variable_and_write_node_enter(node)
296
+ collect_instance_variable_references(node.name.to_s, node.name_loc, true)
297
+ end
298
+
299
+ sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
300
+ def on_instance_variable_operator_write_node_enter(node)
301
+ collect_instance_variable_references(node.name.to_s, node.name_loc, true)
302
+ end
303
+
304
+ sig { params(node: Prism::InstanceVariableOrWriteNode).void }
305
+ def on_instance_variable_or_write_node_enter(node)
306
+ collect_instance_variable_references(node.name.to_s, node.name_loc, true)
307
+ end
308
+
309
+ sig { params(node: Prism::InstanceVariableTargetNode).void }
310
+ def on_instance_variable_target_node_enter(node)
311
+ collect_instance_variable_references(node.name.to_s, node.location, true)
312
+ end
313
+
265
314
  sig { params(node: Prism::CallNode).void }
266
315
  def on_call_node_enter(node)
267
316
  if @target.is_a?(MethodTarget) && (name = node.name.to_s) == @target.method_name
@@ -305,6 +354,13 @@ module RubyIndexer
305
354
  end
306
355
  end
307
356
 
357
+ sig { params(name: String, location: Prism::Location, declaration: T::Boolean).void }
358
+ def collect_instance_variable_references(name, location, declaration)
359
+ return unless @target.is_a?(InstanceVariableTarget) && name == @target.name
360
+
361
+ @references << Reference.new(name, location, declaration: declaration)
362
+ end
363
+
308
364
  sig do
309
365
  params(
310
366
  node: T.any(
@@ -647,5 +647,24 @@ module RubyIndexer
647
647
  entry = @index["Foo"].first
648
648
  assert_empty(entry.comments)
649
649
  end
650
+
651
+ def test_singleton_inside_compact_namespace
652
+ index(<<~RUBY)
653
+ module Foo::Bar
654
+ class << self
655
+ def baz; end
656
+ end
657
+ end
658
+ RUBY
659
+
660
+ # Verify we didn't index the incorrect name
661
+ assert_nil(@index["Foo::Bar::<Class:Foo::Bar>"])
662
+
663
+ # Verify we indexed the correct name
664
+ assert_entry("Foo::Bar::<Class:Bar>", Entry::SingletonClass, "/fake/path/foo.rb:1-2:3-5")
665
+
666
+ method = @index["baz"]&.first
667
+ assert_equal("Foo::Bar::<Class:Bar>", method.owner.name)
668
+ end
650
669
  end
651
670
  end
@@ -216,6 +216,64 @@ module RubyIndexer
216
216
  assert_equal(11, refs[2].location.start_line)
217
217
  end
218
218
 
219
+ def test_finds_instance_variable_read_references
220
+ refs = find_instance_variable_references("@foo", <<~RUBY)
221
+ class Foo
222
+ def foo
223
+ @foo
224
+ end
225
+ end
226
+ RUBY
227
+ assert_equal(1, refs.size)
228
+
229
+ assert_equal("@foo", refs[0].name)
230
+ assert_equal(3, refs[0].location.start_line)
231
+ end
232
+
233
+ def test_finds_instance_variable_write_references
234
+ refs = find_instance_variable_references("@foo", <<~RUBY)
235
+ class Foo
236
+ def write
237
+ @foo = 1
238
+ @foo &&= 2
239
+ @foo ||= 3
240
+ @foo += 4
241
+ @foo, @bar = []
242
+ end
243
+ end
244
+ RUBY
245
+ assert_equal(5, refs.size)
246
+
247
+ assert_equal(["@foo"], refs.map(&:name).uniq)
248
+ assert_equal(3, refs[0].location.start_line)
249
+ assert_equal(4, refs[1].location.start_line)
250
+ assert_equal(5, refs[2].location.start_line)
251
+ assert_equal(6, refs[3].location.start_line)
252
+ assert_equal(7, refs[4].location.start_line)
253
+ end
254
+
255
+ def test_finds_instance_variable_references_ignore_context
256
+ refs = find_instance_variable_references("@name", <<~RUBY)
257
+ class Foo
258
+ def name
259
+ @name = "foo"
260
+ end
261
+ end
262
+ class Bar
263
+ def name
264
+ @name = "bar"
265
+ end
266
+ end
267
+ RUBY
268
+ assert_equal(2, refs.size)
269
+
270
+ assert_equal("@name", refs[0].name)
271
+ assert_equal(3, refs[0].location.start_line)
272
+
273
+ assert_equal("@name", refs[1].name)
274
+ assert_equal(8, refs[1].location.start_line)
275
+ end
276
+
219
277
  private
220
278
 
221
279
  def find_const_references(const_name, source)
@@ -228,6 +286,11 @@ module RubyIndexer
228
286
  find_references(target, source)
229
287
  end
230
288
 
289
+ def find_instance_variable_references(instance_variable_name, source)
290
+ target = ReferenceFinder::InstanceVariableTarget.new(instance_variable_name)
291
+ find_references(target, source)
292
+ end
293
+
231
294
  def find_references(target, source)
232
295
  file_path = "/fake.rb"
233
296
  index = Index.new
@@ -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) }
@@ -94,6 +94,8 @@ module RubyLsp
94
94
  @workspace_uri = URI(workspace_uri) if workspace_uri
95
95
 
96
96
  specified_formatter = options.dig(:initializationOptions, :formatter)
97
+ rubocop_has_addon = defined?(::RuboCop::Version::STRING) &&
98
+ Gem::Requirement.new(">= 1.70.0").satisfied_by?(Gem::Version.new(::RuboCop::Version::STRING))
97
99
 
98
100
  if specified_formatter
99
101
  @formatter = specified_formatter
@@ -101,6 +103,12 @@ module RubyLsp
101
103
  if specified_formatter != "auto"
102
104
  notifications << Notification.window_log_message("Using formatter specified by user: #{@formatter}")
103
105
  end
106
+
107
+ # If the user had originally configured to use `rubocop`, but their version doesn't provide the add-on yet,
108
+ # fallback to the internal integration
109
+ if specified_formatter == "rubocop" && !rubocop_has_addon
110
+ @formatter = "rubocop_internal"
111
+ end
104
112
  end
105
113
 
106
114
  if @formatter == "auto"
@@ -109,6 +117,23 @@ module RubyLsp
109
117
  end
110
118
 
111
119
  specified_linters = options.dig(:initializationOptions, :linters)
120
+
121
+ if specified_formatter == "rubocop" || specified_linters&.include?("rubocop")
122
+ notifications << Notification.window_log_message(<<~MESSAGE, type: Constant::MessageType::WARNING)
123
+ Formatter is configured to be `rubocop`. As of RuboCop v1.70.0, this identifier activates the add-on
124
+ implemented in the rubocop gem itself instead of the internal integration provided by the Ruby LSP.
125
+
126
+ If you wish to use the internal integration, please configure the formatter as `rubocop_internal`.
127
+ MESSAGE
128
+ end
129
+
130
+ # If the user had originally configured to use `rubocop`, but their version doesn't provide the add-on yet,
131
+ # fall back to the internal integration
132
+ if specified_linters&.include?("rubocop") && !rubocop_has_addon
133
+ specified_linters.delete("rubocop")
134
+ specified_linters << "rubocop_internal"
135
+ end
136
+
112
137
  @linters = specified_linters || detect_linters(direct_dependencies, all_dependencies)
113
138
 
114
139
  notifications << if specified_linters
@@ -185,13 +210,13 @@ module RubyLsp
185
210
  sig { params(direct_dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(String) }
186
211
  def detect_formatter(direct_dependencies, all_dependencies)
187
212
  # NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
188
- return "rubocop" if direct_dependencies.any?(/^rubocop/)
213
+ return "rubocop_internal" if direct_dependencies.any?(/^rubocop/)
189
214
 
190
215
  syntax_tree_is_direct_dependency = direct_dependencies.include?("syntax_tree")
191
216
  return "syntax_tree" if syntax_tree_is_direct_dependency
192
217
 
193
218
  rubocop_is_transitive_dependency = all_dependencies.include?("rubocop")
194
- return "rubocop" if dot_rubocop_yml_present && rubocop_is_transitive_dependency
219
+ return "rubocop_internal" if dot_rubocop_yml_present && rubocop_is_transitive_dependency
195
220
 
196
221
  "none"
197
222
  end
@@ -203,7 +228,7 @@ module RubyLsp
203
228
  linters = []
204
229
 
205
230
  if dependencies.any?(/^rubocop/) || (all_dependencies.include?("rubocop") && dot_rubocop_yml_present)
206
- linters << "rubocop"
231
+ linters << "rubocop_internal"
207
232
  end
208
233
 
209
234
  linters
@@ -120,7 +120,7 @@ module RubyLsp
120
120
  [target, node_value(target)]
121
121
  when Prism::ModuleNode, Prism::ClassNode, Prism::SingletonClassNode, Prism::DefNode, Prism::CaseNode,
122
122
  Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::IfNode, Prism::UnlessNode
123
- target
123
+ [target, nil]
124
124
  end
125
125
 
126
126
  @target = T.let(highlight_target, T.nilable(Prism::Node))
@@ -620,7 +620,8 @@ module RubyLsp
620
620
 
621
621
  sig { params(keyword_loc: T.nilable(Prism::Location), end_loc: T.nilable(Prism::Location)).void }
622
622
  def add_matching_end_highlights(keyword_loc, end_loc)
623
- return unless keyword_loc && end_loc && end_loc.length.positive?
623
+ return unless keyword_loc && end_loc
624
+ return unless end_loc.length.positive?
624
625
  return unless covers_target_position?(keyword_loc) || covers_target_position?(end_loc)
625
626
 
626
627
  add_highlight(Constant::DocumentHighlightKind::TEXT, keyword_loc)
@@ -128,7 +128,13 @@ module RubyLsp
128
128
  gem_version = resolve_version(uri)
129
129
  return if gem_version.nil?
130
130
 
131
- file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(uri.path))
131
+ path = uri.path
132
+ return unless path
133
+
134
+ gem_name = uri.gem_name
135
+ return unless gem_name
136
+
137
+ file_path = self.class.gem_paths.dig(gem_name, gem_version, CGI.unescape(path))
132
138
  return if file_path.nil?
133
139
 
134
140
  @response_builder << Interface::DocumentLink.new(
@@ -149,7 +155,10 @@ module RubyLsp
149
155
 
150
156
  return @gem_version unless @gem_version.nil? || @gem_version.empty?
151
157
 
152
- GEM_TO_VERSION_MAP[uri.gem_name]
158
+ gem_name = uri.gem_name
159
+ return unless gem_name
160
+
161
+ GEM_TO_VERSION_MAP[gem_name]
153
162
  end
154
163
  end
155
164
  end
@@ -6,11 +6,11 @@ require "sorbet-runtime"
6
6
  begin
7
7
  T::Configuration.default_checked_level = :never
8
8
  # Suppresses call validation errors
9
- T::Configuration.call_validation_error_handler = ->(*) {}
9
+ T::Configuration.call_validation_error_handler = ->(*arg) {}
10
10
  # Suppresses errors caused by T.cast, T.let, T.must, etc.
11
- T::Configuration.inline_type_error_handler = ->(*) {}
11
+ T::Configuration.inline_type_error_handler = ->(*arg) {}
12
12
  # Suppresses errors caused by incorrect parameter ordering
13
- T::Configuration.sig_validation_error_handler = ->(*) {}
13
+ T::Configuration.sig_validation_error_handler = ->(*arg) {}
14
14
  rescue
15
15
  # Need this rescue so that if another gem has
16
16
  # already set the checked level by the time we
@@ -39,6 +39,12 @@ module RubyLsp
39
39
  Prism::ConstantReadNode,
40
40
  Prism::ConstantPathNode,
41
41
  Prism::ConstantPathTargetNode,
42
+ Prism::InstanceVariableAndWriteNode,
43
+ Prism::InstanceVariableOperatorWriteNode,
44
+ Prism::InstanceVariableOrWriteNode,
45
+ Prism::InstanceVariableReadNode,
46
+ Prism::InstanceVariableTargetNode,
47
+ Prism::InstanceVariableWriteNode,
42
48
  Prism::CallNode,
43
49
  Prism::DefNode,
44
50
  ],
@@ -62,6 +68,12 @@ module RubyLsp
62
68
  Prism::ConstantReadNode,
63
69
  Prism::ConstantPathNode,
64
70
  Prism::ConstantPathTargetNode,
71
+ Prism::InstanceVariableAndWriteNode,
72
+ Prism::InstanceVariableOperatorWriteNode,
73
+ Prism::InstanceVariableOrWriteNode,
74
+ Prism::InstanceVariableReadNode,
75
+ Prism::InstanceVariableTargetNode,
76
+ Prism::InstanceVariableWriteNode,
65
77
  Prism::CallNode,
66
78
  Prism::DefNode,
67
79
  ),
@@ -97,6 +109,12 @@ module RubyLsp
97
109
  Prism::ConstantReadNode,
98
110
  Prism::ConstantPathNode,
99
111
  Prism::ConstantPathTargetNode,
112
+ Prism::InstanceVariableAndWriteNode,
113
+ Prism::InstanceVariableOperatorWriteNode,
114
+ Prism::InstanceVariableOrWriteNode,
115
+ Prism::InstanceVariableReadNode,
116
+ Prism::InstanceVariableTargetNode,
117
+ Prism::InstanceVariableWriteNode,
100
118
  Prism::CallNode,
101
119
  Prism::DefNode,
102
120
  ),
@@ -114,6 +132,14 @@ module RubyLsp
114
132
 
115
133
  fully_qualified_name = T.must(entries.first).name
116
134
  RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
135
+ when
136
+ Prism::InstanceVariableAndWriteNode,
137
+ Prism::InstanceVariableOperatorWriteNode,
138
+ Prism::InstanceVariableOrWriteNode,
139
+ Prism::InstanceVariableReadNode,
140
+ Prism::InstanceVariableTargetNode,
141
+ Prism::InstanceVariableWriteNode
142
+ RubyIndexer::ReferenceFinder::InstanceVariableTarget.new(target_node.name.to_s)
117
143
  when Prism::CallNode, Prism::DefNode
118
144
  RubyIndexer::ReferenceFinder::MethodTarget.new(target_node.name.to_s)
119
145
  end
@@ -31,7 +31,7 @@ module RubyLsp
31
31
 
32
32
  # TODO: avoid passing document once we have alternative ways to get at
33
33
  # encoding and file source
34
- sig { params(document: RubyDocument, offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
34
+ sig { params(document: RubyDocument, offense: ::RuboCop::Cop::Offense, uri: URI::Generic).void }
35
35
  def initialize(document, offense, uri)
36
36
  @document = document
37
37
  @offense = offense
@@ -48,7 +48,7 @@ module RubyLsp
48
48
  code_actions
49
49
  end
50
50
 
51
- sig { params(config: RuboCop::Config).returns(Interface::Diagnostic) }
51
+ sig { params(config: ::RuboCop::Config).returns(Interface::Diagnostic) }
52
52
  def to_lsp_diagnostic(config)
53
53
  # highlighted_area contains the begin and end position of the first line
54
54
  # This ensures that multiline offenses don't clutter the editor
@@ -90,7 +90,7 @@ module RubyLsp
90
90
  RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
91
91
  end
92
92
 
93
- sig { params(config: RuboCop::Config).returns(T.nilable(Interface::CodeDescription)) }
93
+ sig { params(config: ::RuboCop::Config).returns(T.nilable(Interface::CodeDescription)) }
94
94
  def code_description(config)
95
95
  cop = RuboCopRunner.find_cop_by_name(@offense.cop_name)
96
96
  return unless cop
@@ -40,10 +40,10 @@ module RubyLsp
40
40
  For more details, run RuboCop on the command line.
41
41
  EOS
42
42
 
43
- sig { params(rubocop_error: T.any(RuboCop::ErrorWithAnalyzedFileLocation, StandardError)).void }
43
+ sig { params(rubocop_error: T.any(::RuboCop::ErrorWithAnalyzedFileLocation, StandardError)).void }
44
44
  def initialize(rubocop_error)
45
45
  message = case rubocop_error
46
- when RuboCop::ErrorWithAnalyzedFileLocation
46
+ when ::RuboCop::ErrorWithAnalyzedFileLocation
47
47
  format(MESSAGE, "for the #{rubocop_error.cop.name} cop")
48
48
  when StandardError
49
49
  format(MESSAGE, rubocop_error.message)
@@ -53,7 +53,7 @@ module RubyLsp
53
53
  end
54
54
 
55
55
  # :nodoc:
56
- class RuboCopRunner < RuboCop::Runner
56
+ class RuboCopRunner < ::RuboCop::Runner
57
57
  extend T::Sig
58
58
 
59
59
  class ConfigurationError < StandardError; end
@@ -68,14 +68,14 @@ module RubyLsp
68
68
  T::Array[String],
69
69
  )
70
70
 
71
- sig { returns(T::Array[RuboCop::Cop::Offense]) }
71
+ sig { returns(T::Array[::RuboCop::Cop::Offense]) }
72
72
  attr_reader :offenses
73
73
 
74
74
  sig { returns(::RuboCop::Config) }
75
75
  attr_reader :config_for_working_directory
76
76
 
77
77
  begin
78
- RuboCop::Options.new.parse(["--raise-cop-error"])
78
+ ::RuboCop::Options.new.parse(["--raise-cop-error"])
79
79
  DEFAULT_ARGS << "--raise-cop-error"
80
80
  rescue OptionParser::InvalidOption
81
81
  # older versions of RuboCop don't support this flag
@@ -85,7 +85,7 @@ module RubyLsp
85
85
  sig { params(args: String).void }
86
86
  def initialize(*args)
87
87
  @options = T.let({}, T::Hash[Symbol, T.untyped])
88
- @offenses = T.let([], T::Array[RuboCop::Cop::Offense])
88
+ @offenses = T.let([], T::Array[::RuboCop::Cop::Offense])
89
89
  @errors = T.let([], T::Array[String])
90
90
  @warnings = T.let([], T::Array[String])
91
91
 
@@ -113,9 +113,9 @@ module RubyLsp
113
113
  # RuboCop rescues interrupts and then sets the `@aborting` variable to true. We don't want them to be rescued,
114
114
  # so here we re-raise in case RuboCop received an interrupt.
115
115
  raise Interrupt if aborting?
116
- rescue RuboCop::Runner::InfiniteCorrectionLoop => error
116
+ rescue ::RuboCop::Runner::InfiniteCorrectionLoop => error
117
117
  raise Formatting::Error, error.message
118
- rescue RuboCop::ValidationError => error
118
+ rescue ::RuboCop::ValidationError => error
119
119
  raise ConfigurationError, error.message
120
120
  rescue StandardError => error
121
121
  raise InternalRuboCopError, error
@@ -129,25 +129,25 @@ module RubyLsp
129
129
  class << self
130
130
  extend T::Sig
131
131
 
132
- sig { params(cop_name: String).returns(T.nilable(T.class_of(RuboCop::Cop::Base))) }
132
+ sig { params(cop_name: String).returns(T.nilable(T.class_of(::RuboCop::Cop::Base))) }
133
133
  def find_cop_by_name(cop_name)
134
134
  cop_registry[cop_name]&.first
135
135
  end
136
136
 
137
137
  private
138
138
 
139
- sig { returns(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]) }
139
+ sig { returns(T::Hash[String, [T.class_of(::RuboCop::Cop::Base)]]) }
140
140
  def cop_registry
141
141
  @cop_registry ||= T.let(
142
- RuboCop::Cop::Registry.global.to_h,
143
- T.nilable(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]),
142
+ ::RuboCop::Cop::Registry.global.to_h,
143
+ T.nilable(T::Hash[String, [T.class_of(::RuboCop::Cop::Base)]]),
144
144
  )
145
145
  end
146
146
  end
147
147
 
148
148
  private
149
149
 
150
- sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
150
+ sig { params(_file: String, offenses: T::Array[::RuboCop::Cop::Offense]).void }
151
151
  def file_finished(_file, offenses)
152
152
  @offenses = offenses
153
153
  end
@@ -338,8 +338,8 @@ module RubyLsp
338
338
  unless @setup_error
339
339
  if defined?(Requests::Support::RuboCopFormatter)
340
340
  begin
341
- @global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
342
- rescue RuboCop::Error => e
341
+ @global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
342
+ rescue ::RuboCop::Error => e
343
343
  # The user may have provided unknown config switches in .rubocop or
344
344
  # is trying to load a non-existent config file.
345
345
  send_message(Notification.window_show_message(
@@ -1039,16 +1039,20 @@ module RubyLsp
1039
1039
  def handle_rubocop_config_change(uri)
1040
1040
  return unless defined?(Requests::Support::RuboCopFormatter)
1041
1041
 
1042
- send_log_message("Reloading RuboCop since #{uri} changed")
1043
- @global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
1042
+ # Register a new runner to reload configurations
1043
+ @global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
1044
1044
 
1045
- # Clear all existing diagnostics since the config changed. This has to happen under a mutex because the `state`
1046
- # hash cannot be mutated during iteration or that will throw an error
1045
+ # Clear all document caches for pull diagnostics
1047
1046
  @global_state.synchronize do
1048
- @store.each do |uri, _document|
1049
- send_message(Notification.publish_diagnostics(uri.to_s, []))
1047
+ @store.each do |_uri, document|
1048
+ document.cache_set("textDocument/diagnostic", Document::EMPTY_CACHE)
1050
1049
  end
1051
1050
  end
1051
+
1052
+ # Request a pull diagnostic refresh from the editor
1053
+ if @global_state.client_capabilities.supports_diagnostic_refresh
1054
+ send_message(Request.new(id: @current_request_id, method: "workspace/diagnostic/refresh", params: nil))
1055
+ end
1052
1056
  end
1053
1057
 
1054
1058
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
@@ -1211,16 +1215,15 @@ module RubyLsp
1211
1215
  sig { void }
1212
1216
  def check_formatter_is_available
1213
1217
  return if @setup_error
1214
- # Warn of an unavailable `formatter` setting, e.g. `rubocop` on a project which doesn't have RuboCop.
1215
- # Syntax Tree will always be available via Ruby LSP so we don't need to check for it.
1216
- return unless @global_state.formatter == "rubocop"
1218
+ # Warn of an unavailable `formatter` setting, e.g. `rubocop_internal` on a project which doesn't have RuboCop.
1219
+ return unless @global_state.formatter == "rubocop_internal"
1217
1220
 
1218
1221
  unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
1219
1222
  @global_state.formatter = "none"
1220
1223
 
1221
1224
  send_message(
1222
1225
  Notification.window_show_message(
1223
- "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the Gemfile or gemspec.",
1226
+ "Ruby LSP formatter is set to `rubocop_internal` but RuboCop was not found in the Gemfile or gemspec.",
1224
1227
  type: Constant::MessageType::ERROR,
1225
1228
  ),
1226
1229
  )
@@ -25,7 +25,7 @@ module RubyLsp
25
25
  end,
26
26
  String,
27
27
  )
28
- GUESSED_TYPES_URL = "https://shopify.github.io/ruby-lsp/design-and-roadmap.html#guessed-types"
28
+ GUESSED_TYPES_URL = "https://shopify.github.io/ruby-lsp/#guessed-types"
29
29
 
30
30
  # Request delegation for embedded languages is not yet standardized into the language server specification. Here we
31
31
  # use this custom error class as a way to return a signal to the client that the request should be delegated to the
@@ -162,7 +162,9 @@ module RubyLsp
162
162
 
163
163
  sig { override.returns(T::Hash[Symbol, T.untyped]) }
164
164
  def to_hash
165
- { method: @method, params: T.unsafe(@params).to_hash }
165
+ hash = { method: @method }
166
+ hash[:params] = T.unsafe(@params).to_hash if @params
167
+ hash
166
168
  end
167
169
  end
168
170
 
@@ -206,7 +208,9 @@ module RubyLsp
206
208
 
207
209
  sig { override.returns(T::Hash[Symbol, T.untyped]) }
208
210
  def to_hash
209
- { id: @id, method: @method, params: T.unsafe(@params).to_hash }
211
+ hash = { id: @id, method: @method }
212
+ hash[:params] = T.unsafe(@params).to_hash if @params
213
+ hash
210
214
  end
211
215
  end
212
216
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.4
4
+ version: 0.23.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-01-10 00:00:00.000000000 Z
10
+ date: 2025-01-16 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: language_server-protocol