ruby-lsp 0.23.5 → 0.23.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 924e57270229eb4d0b709b01023d16f414591f8ea40c3b8a51ae6fb6cd09cc4f
4
- data.tar.gz: 72f4f544baf412f8580fe28287ba5324c7f646a9436797ebea59f34f3712d62e
3
+ metadata.gz: 563db8ddc06dc53f37db5ea4a42a48b7af8ed5bb2639c42e87e3c2bdd7fe38d1
4
+ data.tar.gz: a1820031a007e14c42600fd897e43fad044f26e05f02e117c806821cdc46e9d7
5
5
  SHA512:
6
- metadata.gz: 650cf172b8b1408f5498cb2010c2f94b3ef0a4bc93212ef63e24c2177ef5996455625a1bb64fe51f75322a42625e9ebcf156f95f5412c44839fe4957df4c957f
7
- data.tar.gz: ce9b866443836810fe5884c2f977d4fd91fe020023ff547a9d60e525715ce388429aa7a901b0acc84876d68cada8f30dac2836494b7403fd8c2e845bea86ce79
6
+ metadata.gz: 2d28983a474a7b32486f20df65ec2a808ddf2be31bc0c4d7d62ae1c3fa24448b28699917d23b6f4f00045ff45e25aeff1e27c6a27d40c6a7dc6887be630f8828
7
+ data.tar.gz: d10bd35268476cd2b2d319405dd58433d32df48a621cf4d45555cafb6e293d3bc4b3844afe7bf6dc0d39e68a5f5a7be70b935874796a3852a68ac5c61c592709
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.23.5
1
+ 0.23.6
@@ -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
@@ -338,7 +338,7 @@ 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)
341
+ @global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
342
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.
@@ -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.5
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