ruby-lsp 0.23.5 → 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: 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