ruby-lsp 0.13.0 → 0.13.2

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: d6bace664e1540c1333df9dabfb2e7bdd8bda0f910033b47b188e4a82ddacb15
4
- data.tar.gz: be9e5da0492b26b5cb6e07074d01d7874104b70286c1578342b9639158fed147
3
+ metadata.gz: 2329129b74c75a0bb4113d1f1489463f666b74ef495d0ac65bad675fbd83d1d3
4
+ data.tar.gz: 2f31f495b9ca0b1a9f909758dabce816411d0b64a006577c93c7a80a253791e5
5
5
  SHA512:
6
- metadata.gz: 7d8e61433686271a68dafe8db79765ed509a115ab6f06060d7669d279f48218fe2eada2c12e3b45f155d27a41774771681cdab92700d27f426e75ed89287ca03
7
- data.tar.gz: 611c050b751050934d0fd93e9e52089fe63fc4d947be75a440dfa0465933814d2682268a10135d036c2cee83fa739156d2b11e42e679ecbed7de62acfee9d621
6
+ metadata.gz: 9c3ab34cb3e7877105ca21df3f521589d9838bd15e77a3d17cc4df91ca1f93221ef845e514a902a03a63a1f82ad6b5cf90fa4f9a93037fbfb57af5f7a48b0ebe
7
+ data.tar.gz: d4867c86eb238abf039f40592d7ff48acd85749fcf9349ab72fdea218ff5201156fc0d511987ed768b1fb2dcc9fd55a76bfac611f3fc565b7b2ba8c8d1054a72
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.13.0
1
+ 0.13.2
@@ -193,7 +193,7 @@ module RubyIndexer
193
193
  # When working on a gem, we need to make sure that its gemspec dependencies can't be excluded. This is necessary
194
194
  # because Bundler doesn't assign groups to gemspec dependencies
195
195
  this_gem = Bundler.definition.dependencies.find do |d|
196
- d.to_spec.full_gem_path == Dir.pwd
196
+ d.to_spec&.full_gem_path == Dir.pwd
197
197
  rescue Gem::MissingSpecError
198
198
  false
199
199
  end
@@ -81,9 +81,13 @@ module RubyIndexer
81
81
 
82
82
  abstract!
83
83
 
84
+ # Name includes just the name of the parameter, excluding symbols like splats
84
85
  sig { returns(Symbol) }
85
86
  attr_reader :name
86
87
 
88
+ # Decorated name is the parameter name including the splat or block prefix, e.g.: `*foo`, `**foo` or `&block`
89
+ alias_method :decorated_name, :name
90
+
87
91
  sig { params(name: Symbol).void }
88
92
  def initialize(name:)
89
93
  @name = name
@@ -100,10 +104,48 @@ module RubyIndexer
100
104
 
101
105
  # An required keyword method parameter, e.g. `def foo(a:)`
102
106
  class KeywordParameter < Parameter
107
+ sig { override.returns(Symbol) }
108
+ def decorated_name
109
+ :"#{@name}:"
110
+ end
103
111
  end
104
112
 
105
113
  # An optional keyword method parameter, e.g. `def foo(a: 123)`
106
114
  class OptionalKeywordParameter < Parameter
115
+ sig { override.returns(Symbol) }
116
+ def decorated_name
117
+ :"#{@name}:"
118
+ end
119
+ end
120
+
121
+ # A rest method parameter, e.g. `def foo(*a)`
122
+ class RestParameter < Parameter
123
+ DEFAULT_NAME = T.let(:"<anonymous splat>", Symbol)
124
+
125
+ sig { override.returns(Symbol) }
126
+ def decorated_name
127
+ :"*#{@name}"
128
+ end
129
+ end
130
+
131
+ # A keyword rest method parameter, e.g. `def foo(**a)`
132
+ class KeywordRestParameter < Parameter
133
+ DEFAULT_NAME = T.let(:"<anonymous keyword splat>", Symbol)
134
+
135
+ sig { override.returns(Symbol) }
136
+ def decorated_name
137
+ :"**#{@name}"
138
+ end
139
+ end
140
+
141
+ # A block method parameter, e.g. `def foo(&block)`
142
+ class BlockParameter < Parameter
143
+ DEFAULT_NAME = T.let(:"<anonymous block>", Symbol)
144
+
145
+ sig { override.returns(Symbol) }
146
+ def decorated_name
147
+ :"&#{@name}"
148
+ end
107
149
  end
108
150
 
109
151
  class Member < Entry
@@ -203,17 +245,52 @@ module RubyIndexer
203
245
  end
204
246
  end
205
247
 
248
+ rest = parameters_node.rest
249
+
250
+ if rest.is_a?(Prism::RestParameterNode)
251
+ rest_name = rest.name || RestParameter::DEFAULT_NAME
252
+ parameters << RestParameter.new(name: rest_name)
253
+ end
254
+
255
+ keyword_rest = parameters_node.keyword_rest
256
+
257
+ if keyword_rest.is_a?(Prism::KeywordRestParameterNode)
258
+ keyword_rest_name = parameter_name(keyword_rest) || KeywordRestParameter::DEFAULT_NAME
259
+ parameters << KeywordRestParameter.new(name: keyword_rest_name)
260
+ end
261
+
262
+ parameters_node.posts.each do |post|
263
+ name = parameter_name(post)
264
+ next unless name
265
+
266
+ parameters << RequiredParameter.new(name: name)
267
+ end
268
+
269
+ block = parameters_node.block
270
+ parameters << BlockParameter.new(name: block.name || BlockParameter::DEFAULT_NAME) if block
271
+
206
272
  parameters
207
273
  end
208
274
 
209
- sig { params(node: Prism::Node).returns(T.nilable(Symbol)) }
275
+ sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(Symbol)) }
210
276
  def parameter_name(node)
211
277
  case node
212
278
  when Prism::RequiredParameterNode, Prism::OptionalParameterNode,
213
- Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode
279
+ Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode,
280
+ Prism::RestParameterNode, Prism::KeywordRestParameterNode
214
281
  node.name
215
282
  when Prism::MultiTargetNode
216
- names = [*node.lefts, *node.rest, *node.rights].map { |parameter_node| parameter_name(parameter_node) }
283
+ names = node.lefts.map { |parameter_node| parameter_name(parameter_node) }
284
+
285
+ rest = node.rest
286
+ if rest.is_a?(Prism::SplatNode)
287
+ name = rest.expression&.slice
288
+ names << (rest.operator == "*" ? "*#{name}".to_sym : name&.to_sym)
289
+ end
290
+
291
+ names << nil if rest.is_a?(Prism::ImplicitRestNode)
292
+
293
+ names.concat(node.rights.map { |parameter_node| parameter_name(parameter_node) })
217
294
 
218
295
  names_with_commas = names.join(", ")
219
296
  :"(#{names_with_commas})"
@@ -106,6 +106,142 @@ module RubyIndexer
106
106
  assert_instance_of(Entry::OptionalKeywordParameter, b)
107
107
  end
108
108
 
109
+ def test_method_with_rest_and_keyword_rest_parameters
110
+ index(<<~RUBY)
111
+ class Foo
112
+ def bar(*a, **b)
113
+ end
114
+ end
115
+ RUBY
116
+
117
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
118
+ entry = T.must(@index["bar"].first)
119
+ assert_equal(2, entry.parameters.length)
120
+ a, b = entry.parameters
121
+
122
+ assert_equal(:a, a.name)
123
+ assert_instance_of(Entry::RestParameter, a)
124
+
125
+ assert_equal(:b, b.name)
126
+ assert_instance_of(Entry::KeywordRestParameter, b)
127
+ end
128
+
129
+ def test_method_with_post_parameters
130
+ index(<<~RUBY)
131
+ class Foo
132
+ def bar(*a, b)
133
+ end
134
+
135
+ def baz(**a, b)
136
+ end
137
+
138
+ def qux(*a, (b, c))
139
+ end
140
+ RUBY
141
+
142
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
143
+ entry = T.must(@index["bar"].first)
144
+ assert_equal(2, entry.parameters.length)
145
+ a, b = entry.parameters
146
+
147
+ assert_equal(:a, a.name)
148
+ assert_instance_of(Entry::RestParameter, a)
149
+
150
+ assert_equal(:b, b.name)
151
+ assert_instance_of(Entry::RequiredParameter, b)
152
+
153
+ entry = T.must(@index["baz"].first)
154
+ assert_equal(2, entry.parameters.length)
155
+ a, b = entry.parameters
156
+
157
+ assert_equal(:a, a.name)
158
+ assert_instance_of(Entry::KeywordRestParameter, a)
159
+
160
+ assert_equal(:b, b.name)
161
+ assert_instance_of(Entry::RequiredParameter, b)
162
+
163
+ entry = T.must(@index["qux"].first)
164
+ assert_equal(2, entry.parameters.length)
165
+ _a, second = entry.parameters
166
+
167
+ assert_equal(:"(b, c)", second.name)
168
+ assert_instance_of(Entry::RequiredParameter, second)
169
+ end
170
+
171
+ def test_method_with_destructured_rest_parameters
172
+ index(<<~RUBY)
173
+ class Foo
174
+ def bar((a, *b))
175
+ end
176
+ end
177
+ RUBY
178
+
179
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
180
+ entry = T.must(@index["bar"].first)
181
+ assert_equal(1, entry.parameters.length)
182
+ param = entry.parameters.first
183
+
184
+ assert_equal(:"(a, *b)", param.name)
185
+ assert_instance_of(Entry::RequiredParameter, param)
186
+ end
187
+
188
+ def test_method_with_block_parameters
189
+ index(<<~RUBY)
190
+ class Foo
191
+ def bar(&block)
192
+ end
193
+
194
+ def baz(&)
195
+ end
196
+ end
197
+ RUBY
198
+
199
+ entry = T.must(@index["bar"].first)
200
+ param = entry.parameters.first
201
+ assert_equal(:block, param.name)
202
+ assert_instance_of(Entry::BlockParameter, param)
203
+
204
+ entry = T.must(@index["baz"].first)
205
+ assert_equal(1, entry.parameters.length)
206
+
207
+ param = entry.parameters.first
208
+ assert_equal(Entry::BlockParameter::DEFAULT_NAME, param.name)
209
+ assert_instance_of(Entry::BlockParameter, param)
210
+ end
211
+
212
+ def test_method_with_anonymous_rest_parameters
213
+ index(<<~RUBY)
214
+ class Foo
215
+ def bar(*, **)
216
+ end
217
+ end
218
+ RUBY
219
+
220
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
221
+ entry = T.must(@index["bar"].first)
222
+ assert_equal(2, entry.parameters.length)
223
+ first, second = entry.parameters
224
+
225
+ assert_equal(Entry::RestParameter::DEFAULT_NAME, first.name)
226
+ assert_instance_of(Entry::RestParameter, first)
227
+
228
+ assert_equal(Entry::KeywordRestParameter::DEFAULT_NAME, second.name)
229
+ assert_instance_of(Entry::KeywordRestParameter, second)
230
+ end
231
+
232
+ def test_method_with_forbidden_keyword_splat_parameter
233
+ index(<<~RUBY)
234
+ class Foo
235
+ def bar(**nil)
236
+ end
237
+ end
238
+ RUBY
239
+
240
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
241
+ entry = T.must(@index["bar"].first)
242
+ assert_empty(entry.parameters)
243
+ end
244
+
109
245
  def test_keeps_track_of_method_owner
110
246
  index(<<~RUBY)
111
247
  class Foo
@@ -8,10 +8,6 @@ module RubyLsp
8
8
 
9
9
  abstract!
10
10
 
11
- PositionShape = T.type_alias { { line: Integer, character: Integer } }
12
- RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
13
- EditShape = T.type_alias { { range: RangeShape, text: String } }
14
-
15
11
  sig { returns(Prism::ParseResult) }
16
12
  attr_reader :parse_result
17
13
 
@@ -24,6 +20,9 @@ module RubyLsp
24
20
  sig { returns(URI::Generic) }
25
21
  attr_reader :uri
26
22
 
23
+ sig { returns(String) }
24
+ attr_reader :encoding
25
+
27
26
  sig { params(source: String, version: Integer, uri: URI::Generic, encoding: String).void }
28
27
  def initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind::UTF8)
29
28
  @cache = T.let({}, T::Hash[String, T.untyped])
@@ -77,7 +76,7 @@ module RubyLsp
77
76
  @cache[request_name]
78
77
  end
79
78
 
80
- sig { params(edits: T::Array[EditShape], version: Integer).void }
79
+ sig { params(edits: T::Array[T::Hash[Symbol, T.untyped]], version: Integer).void }
81
80
  def push_edits(edits, version:)
82
81
  edits.each do |edit|
83
82
  range = edit[:range]
@@ -109,7 +108,7 @@ module RubyLsp
109
108
 
110
109
  sig do
111
110
  params(
112
- position: PositionShape,
111
+ position: T::Hash[Symbol, T.untyped],
113
112
  node_types: T::Array[T.class_of(Prism::Node)],
114
113
  ).returns([T.nilable(Prism::Node), T.nilable(Prism::Node), T::Array[String]])
115
114
  end
@@ -190,7 +189,7 @@ module RubyLsp
190
189
  end
191
190
 
192
191
  # Finds the character index inside the source string for a given line and column
193
- sig { params(position: PositionShape).returns(Integer) }
192
+ sig { params(position: T::Hash[Symbol, T.untyped]).returns(Integer) }
194
193
  def find_char_position(position)
195
194
  # Find the character index for the beginning of the requested line
196
195
  until @current_line == position[:line]
@@ -25,6 +25,7 @@ module RubyLsp
25
25
  begin
26
26
  response = run(request)
27
27
  rescue StandardError, LoadError => e
28
+ warn(e.message)
28
29
  error = e
29
30
  end
30
31
 
@@ -98,7 +99,8 @@ module RubyLsp
98
99
  folding_range = Requests::FoldingRanges.new(document.parse_result.comments, dispatcher)
99
100
  document_symbol = Requests::DocumentSymbol.new(dispatcher)
100
101
  document_link = Requests::DocumentLink.new(uri, document.comments, dispatcher)
101
- code_lens = Requests::CodeLens.new(uri, dispatcher)
102
+ lenses_configuration = T.must(@store.features_configuration.dig(:codeLens))
103
+ code_lens = Requests::CodeLens.new(uri, lenses_configuration, dispatcher)
102
104
 
103
105
  semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher)
104
106
  dispatcher.dispatch(document.tree)
@@ -170,6 +172,8 @@ module RubyLsp
170
172
  completion(uri, request.dig(:params, :position))
171
173
  when "textDocument/definition"
172
174
  definition(uri, request.dig(:params, :position))
175
+ when "textDocument/signatureHelp"
176
+ signature_help(uri, request.dig(:params, :position), request.dig(:params, :context))
173
177
  when "workspace/didChangeWatchedFiles"
174
178
  did_change_watched_files(request.dig(:params, :changes))
175
179
  when "workspace/symbol"
@@ -239,12 +243,44 @@ module RubyLsp
239
243
  end
240
244
  end
241
245
 
246
+ sig do
247
+ params(
248
+ uri: URI::Generic,
249
+ position: T::Hash[Symbol, T.untyped],
250
+ context: T::Hash[Symbol, T.untyped],
251
+ ).returns(T.any(T.nilable(Interface::SignatureHelp), T::Hash[Symbol, T.untyped]))
252
+ end
253
+ def signature_help(uri, position, context)
254
+ current_signature = context[:activeSignatureHelp]
255
+ document = @store.get(uri)
256
+ target, parent, nesting = document.locate_node(
257
+ { line: position[:line], character: position[:character] - 2 },
258
+ node_types: [Prism::CallNode],
259
+ )
260
+
261
+ # If we're typing a nested method call (e.g.: `foo(bar)`), then we may end up locating `bar` as the target method
262
+ # call incorrectly. To correct that, we check if there's an active signature with the same name as the parent node
263
+ # and then replace the target
264
+ if current_signature && parent.is_a?(Prism::CallNode)
265
+ active_signature = current_signature[:activeSignature] || 0
266
+
267
+ if current_signature.dig(:signatures, active_signature, :label)&.start_with?(parent.message)
268
+ target = parent
269
+ end
270
+ end
271
+
272
+ dispatcher = Prism::Dispatcher.new
273
+ listener = Requests::SignatureHelp.new(context, nesting, @index, dispatcher)
274
+ dispatcher.dispatch_once(target)
275
+ listener.response
276
+ end
277
+
242
278
  sig { params(query: T.nilable(String)).returns(T::Array[Interface::WorkspaceSymbol]) }
243
279
  def workspace_symbol(query)
244
280
  Requests::WorkspaceSymbol.new(query, @index).run
245
281
  end
246
282
 
247
- sig { params(uri: URI::Generic, range: T.nilable(Document::RangeShape)).returns({ ast: String }) }
283
+ sig { params(uri: URI::Generic, range: T.nilable(T::Hash[Symbol, T.untyped])).returns({ ast: String }) }
248
284
  def show_syntax_tree(uri, range)
249
285
  { ast: Requests::ShowSyntaxTree.new(@store.get(uri), range).run }
250
286
  end
@@ -252,7 +288,7 @@ module RubyLsp
252
288
  sig do
253
289
  params(
254
290
  uri: URI::Generic,
255
- position: Document::PositionShape,
291
+ position: T::Hash[Symbol, T.untyped],
256
292
  ).returns(T.nilable(T.any(T::Array[Interface::Location], Interface::Location)))
257
293
  end
258
294
  def definition(uri, position)
@@ -273,7 +309,7 @@ module RubyLsp
273
309
  sig do
274
310
  params(
275
311
  uri: URI::Generic,
276
- position: Document::PositionShape,
312
+ position: T::Hash[Symbol, T.untyped],
277
313
  ).returns(T.nilable(Interface::Hover))
278
314
  end
279
315
  def hover(uri, position)
@@ -300,7 +336,7 @@ module RubyLsp
300
336
  end
301
337
 
302
338
  sig do
303
- params(uri: URI::Generic, content_changes: T::Array[Document::EditShape], version: Integer).returns(Object)
339
+ params(uri: URI::Generic, content_changes: T::Array[T::Hash[Symbol, T.untyped]], version: Integer).returns(Object)
304
340
  end
305
341
  def text_document_did_change(uri, content_changes, version)
306
342
  @store.push_edits(uri: uri, edits: content_changes, version: version)
@@ -322,7 +358,7 @@ module RubyLsp
322
358
  sig do
323
359
  params(
324
360
  uri: URI::Generic,
325
- positions: T::Array[Document::PositionShape],
361
+ positions: T::Array[T::Hash[Symbol, T.untyped]],
326
362
  ).returns(T.nilable(T::Array[T.nilable(Requests::Support::SelectionRange)]))
327
363
  end
328
364
  def selection_range(uri, positions)
@@ -360,7 +396,7 @@ module RubyLsp
360
396
  sig do
361
397
  params(
362
398
  uri: URI::Generic,
363
- position: Document::PositionShape,
399
+ position: T::Hash[Symbol, T.untyped],
364
400
  character: String,
365
401
  ).returns(T::Array[Interface::TextEdit])
366
402
  end
@@ -371,7 +407,7 @@ module RubyLsp
371
407
  sig do
372
408
  params(
373
409
  uri: URI::Generic,
374
- position: Document::PositionShape,
410
+ position: T::Hash[Symbol, T.untyped],
375
411
  ).returns(T.nilable(T::Array[Interface::DocumentHighlight]))
376
412
  end
377
413
  def document_highlight(uri, position)
@@ -384,7 +420,12 @@ module RubyLsp
384
420
  listener.response
385
421
  end
386
422
 
387
- sig { params(uri: URI::Generic, range: Document::RangeShape).returns(T.nilable(T::Array[Interface::InlayHint])) }
423
+ sig do
424
+ params(
425
+ uri: URI::Generic,
426
+ range: T::Hash[Symbol, T.untyped],
427
+ ).returns(T.nilable(T::Array[Interface::InlayHint]))
428
+ end
388
429
  def inlay_hint(uri, range)
389
430
  document = @store.get(uri)
390
431
 
@@ -392,7 +433,8 @@ module RubyLsp
392
433
  end_line = range.dig(:end, :line)
393
434
 
394
435
  dispatcher = Prism::Dispatcher.new
395
- listener = Requests::InlayHints.new(start_line..end_line, dispatcher)
436
+ hints_configurations = T.must(@store.features_configuration.dig(:inlayHint))
437
+ listener = Requests::InlayHints.new(start_line..end_line, hints_configurations, dispatcher)
396
438
  dispatcher.visit(document.tree)
397
439
  listener.response
398
440
  end
@@ -400,7 +442,7 @@ module RubyLsp
400
442
  sig do
401
443
  params(
402
444
  uri: URI::Generic,
403
- range: Document::RangeShape,
445
+ range: T::Hash[Symbol, T.untyped],
404
446
  context: T::Hash[Symbol, T.untyped],
405
447
  ).returns(T.nilable(T::Array[Interface::CodeAction]))
406
448
  end
@@ -454,7 +496,7 @@ module RubyLsp
454
496
  Interface::FullDocumentDiagnosticReport.new(kind: "full", items: response) if response
455
497
  end
456
498
 
457
- sig { params(uri: URI::Generic, range: Document::RangeShape).returns(Interface::SemanticTokens) }
499
+ sig { params(uri: URI::Generic, range: T::Hash[Symbol, T.untyped]).returns(Interface::SemanticTokens) }
458
500
  def semantic_tokens_range(uri, range)
459
501
  document = @store.get(uri)
460
502
  start_line = range.dig(:start, :line)
@@ -470,7 +512,7 @@ module RubyLsp
470
512
  sig do
471
513
  params(
472
514
  uri: URI::Generic,
473
- position: Document::PositionShape,
515
+ position: T::Hash[Symbol, T.untyped],
474
516
  ).returns(T.nilable(T::Array[Interface::CompletionItem]))
475
517
  end
476
518
  def completion(uri, position)
@@ -602,6 +644,11 @@ module RubyLsp
602
644
  configured_features = options.dig(:initializationOptions, :enabledFeatures)
603
645
  @store.experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
604
646
 
647
+ configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
648
+ configured_lenses = options.dig(:initializationOptions, :featuresConfiguration, :codeLens)
649
+ T.must(@store.features_configuration.dig(:inlayHint)).configuration.merge!(configured_hints) if configured_hints
650
+ T.must(@store.features_configuration.dig(:codeLens)).configuration.merge!(configured_lenses) if configured_lenses
651
+
605
652
  enabled_features = case configured_features
606
653
  when Array
607
654
  # If the configuration is using an array, then absent features are disabled and present ones are enabled. That's
@@ -663,7 +710,7 @@ module RubyLsp
663
710
  on_type_formatting_provider = if enabled_features["onTypeFormatting"]
664
711
  Interface::DocumentOnTypeFormattingOptions.new(
665
712
  first_trigger_character: "{",
666
- more_trigger_character: ["\n", "|"],
713
+ more_trigger_character: ["\n", "|", "d"],
667
714
  )
668
715
  end
669
716
 
@@ -685,6 +732,13 @@ module RubyLsp
685
732
  )
686
733
  end
687
734
 
735
+ signature_help_provider = if enabled_features["signatureHelp"]
736
+ # Identifier characters are automatically included, such as A-Z, a-z, 0-9, _, * or :
737
+ Interface::SignatureHelpOptions.new(
738
+ trigger_characters: ["(", " ", ","],
739
+ )
740
+ end
741
+
688
742
  # Dynamically registered capabilities
689
743
  file_watching_caps = options.dig(:capabilities, :workspace, :didChangeWatchedFiles)
690
744
 
@@ -737,6 +791,7 @@ module RubyLsp
737
791
  code_lens_provider: code_lens_provider,
738
792
  definition_provider: enabled_features["definition"],
739
793
  workspace_symbol_provider: enabled_features["workspaceSymbol"],
794
+ signature_help_provider: signature_help_provider,
740
795
  ),
741
796
  serverInfo: {
742
797
  name: "Ruby LSP",
@@ -137,7 +137,7 @@ module RubyLsp
137
137
 
138
138
  private
139
139
 
140
- sig { params(range: Document::RangeShape, new_text: String).returns(Interface::TextEdit) }
140
+ sig { params(range: T::Hash[Symbol, T.untyped], new_text: String).returns(Interface::TextEdit) }
141
141
  def create_text_edit(range, new_text)
142
142
  Interface::TextEdit.new(
143
143
  range: Interface::Range.new(
@@ -22,7 +22,7 @@ module RubyLsp
22
22
  sig do
23
23
  params(
24
24
  document: Document,
25
- range: Document::RangeShape,
25
+ range: T::Hash[Symbol, T.untyped],
26
26
  context: T::Hash[Symbol, T.untyped],
27
27
  ).void
28
28
  end
@@ -38,14 +38,8 @@ module RubyLsp
38
38
  def run
39
39
  diagnostics = @context[:diagnostics]
40
40
 
41
- code_actions = diagnostics.filter_map do |diagnostic|
42
- code_action = diagnostic.dig(:data, :code_action)
43
- next if code_action.nil?
44
-
45
- # We want to return only code actions that are within range or that do not have any edits, such as refactor
46
- # code actions
47
- range = code_action.dig(:edit, :documentChanges, 0, :edits, 0, :range)
48
- code_action if diagnostic.dig(:data, :correctable) && cover?(range)
41
+ code_actions = diagnostics.flat_map do |diagnostic|
42
+ diagnostic.dig(:data, :code_actions) || []
49
43
  end
50
44
 
51
45
  # Only add refactor actions if there's a non empty selection in the editor
@@ -55,15 +49,7 @@ module RubyLsp
55
49
 
56
50
  private
57
51
 
58
- sig { params(range: T.nilable(Document::RangeShape)).returns(T::Boolean) }
59
- def cover?(range)
60
- range.nil? ||
61
- ((@range.dig(:start, :line))..(@range.dig(:end, :line))).cover?(
62
- (range.dig(:start, :line))..(range.dig(:end, :line)),
63
- )
64
- end
65
-
66
- sig { params(range: Document::RangeShape, uri: URI::Generic).returns(Interface::CodeAction) }
52
+ sig { params(range: T::Hash[Symbol, T.untyped], uri: URI::Generic).returns(Interface::CodeAction) }
67
53
  def refactor_code_action(range, uri)
68
54
  Interface::CodeAction.new(
69
55
  title: "Refactor: Extract Variable",
@@ -11,6 +11,10 @@ module RubyLsp
11
11
  # [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
12
12
  # request informs the editor of runnable commands such as tests
13
13
  #
14
+ # # Configuration
15
+ #
16
+ # To disable gem code lenses, set `rubyLsp.featuresConfiguration.codeLens.gemfileLinks` to `false`.
17
+ #
14
18
  # # Example
15
19
  #
16
20
  # ```ruby
@@ -47,8 +51,14 @@ module RubyLsp
47
51
  sig { override.returns(ResponseType) }
48
52
  attr_reader :_response
49
53
 
50
- sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher).void }
51
- def initialize(uri, dispatcher)
54
+ sig do
55
+ params(
56
+ uri: URI::Generic,
57
+ lenses_configuration: RequestConfig,
58
+ dispatcher: Prism::Dispatcher,
59
+ ).void
60
+ end
61
+ def initialize(uri, lenses_configuration, dispatcher)
52
62
  @uri = T.let(uri, URI::Generic)
53
63
  @_response = T.let([], ResponseType)
54
64
  @path = T.let(uri.to_standardized_path, T.nilable(String))
@@ -57,6 +67,7 @@ module RubyLsp
57
67
  @class_stack = T.let([], T::Array[String])
58
68
  @group_id = T.let(1, Integer)
59
69
  @group_id_stack = T.let([], T::Array[Integer])
70
+ @lenses_configuration = lenses_configuration
60
71
 
61
72
  super(dispatcher)
62
73
 
@@ -134,6 +145,8 @@ module RubyLsp
134
145
  end
135
146
 
136
147
  if @path&.include?(GEMFILE_NAME) && name == :gem && arguments
148
+ return unless @lenses_configuration.enabled?(:gemfileLinks)
149
+
137
150
  first_argument = arguments.arguments.first
138
151
  return unless first_argument.is_a?(Prism::StringNode)
139
152
 
@@ -154,13 +154,11 @@ module RubyLsp
154
154
  end
155
155
  def build_method_completion(entry, node)
156
156
  name = entry.name
157
- parameters = entry.parameters
158
- new_text = parameters.empty? ? name : "#{name}(#{parameters.map(&:name).join(", ")})"
159
157
 
160
158
  Interface::CompletionItem.new(
161
159
  label: name,
162
160
  filter_text: name,
163
- text_edit: Interface::TextEdit.new(range: range_from_node(node), new_text: new_text),
161
+ text_edit: Interface::TextEdit.new(range: range_from_node(node), new_text: name),
164
162
  kind: Constant::CompletionItemKind::METHOD,
165
163
  label_details: Interface::CompletionItemLabelDetails.new(
166
164
  description: entry.file_name,
@@ -9,6 +9,14 @@ module RubyLsp
9
9
  # are labels added directly in the code that explicitly show the user something that might
10
10
  # otherwise just be implied.
11
11
  #
12
+ # # Configuration
13
+ #
14
+ # To enable rescue hints, set `rubyLsp.featuresConfiguration.inlayHint.implicitRescue` to `true`.
15
+ #
16
+ # To enable hash value hints, set `rubyLsp.featuresConfiguration.inlayHint.implicitHashValue` to `true`.
17
+ #
18
+ # To enable all hints, set `rubyLsp.featuresConfiguration.inlayHint.enableAll` to `true`.
19
+ #
12
20
  # # Example
13
21
  #
14
22
  # ```ruby
@@ -39,18 +47,26 @@ module RubyLsp
39
47
  sig { override.returns(ResponseType) }
40
48
  attr_reader :_response
41
49
 
42
- sig { params(range: T::Range[Integer], dispatcher: Prism::Dispatcher).void }
43
- def initialize(range, dispatcher)
50
+ sig do
51
+ params(
52
+ range: T::Range[Integer],
53
+ hints_configuration: RequestConfig,
54
+ dispatcher: Prism::Dispatcher,
55
+ ).void
56
+ end
57
+ def initialize(range, hints_configuration, dispatcher)
44
58
  super(dispatcher)
45
59
 
46
60
  @_response = T.let([], ResponseType)
47
61
  @range = range
62
+ @hints_configuration = hints_configuration
48
63
 
49
64
  dispatcher.register(self, :on_rescue_node_enter, :on_implicit_node_enter)
50
65
  end
51
66
 
52
67
  sig { params(node: Prism::RescueNode).void }
53
68
  def on_rescue_node_enter(node)
69
+ return unless @hints_configuration.enabled?(:implicitRescue)
54
70
  return unless node.exceptions.empty?
55
71
 
56
72
  loc = node.location
@@ -66,6 +82,7 @@ module RubyLsp
66
82
 
67
83
  sig { params(node: Prism::ImplicitNode).void }
68
84
  def on_implicit_node_enter(node)
85
+ return unless @hints_configuration.enabled?(:implicitHashValue)
69
86
  return unless visible?(node, @range)
70
87
 
71
88
  node_value = node.value
@@ -26,7 +26,7 @@ module RubyLsp
26
26
  T::Array[Regexp],
27
27
  )
28
28
 
29
- sig { params(document: Document, position: Document::PositionShape, trigger_character: String).void }
29
+ sig { params(document: Document, position: T::Hash[Symbol, T.untyped], trigger_character: String).void }
30
30
  def initialize(document, position, trigger_character)
31
31
  super(document)
32
32
 
@@ -60,6 +60,8 @@ module RubyLsp
60
60
  handle_statement_end
61
61
  end
62
62
  end
63
+ when "d"
64
+ auto_indent_after_end_keyword
63
65
  end
64
66
 
65
67
  @edits
@@ -118,7 +120,7 @@ module RubyLsp
118
120
  current_line = @lines[@position[:line]]
119
121
  next_line = @lines[@position[:line] + 1]
120
122
 
121
- if current_line.nil? || current_line.strip.empty?
123
+ if current_line.nil? || current_line.strip.empty? || current_line.include?(")") || current_line.include?("]")
122
124
  add_edit_with_text("\n")
123
125
  add_edit_with_text("#{indents}end")
124
126
  move_cursor_to(@position[:line], @indentation + 2)
@@ -141,7 +143,7 @@ module RubyLsp
141
143
  add_edit_with_text("##{spaces}")
142
144
  end
143
145
 
144
- sig { params(text: String, position: Document::PositionShape).void }
146
+ sig { params(text: String, position: T::Hash[Symbol, T.untyped]).void }
145
147
  def add_edit_with_text(text, position = @position)
146
148
  pos = Interface::Position.new(
147
149
  line: position[:line],
@@ -185,6 +187,32 @@ module RubyLsp
185
187
 
186
188
  count
187
189
  end
190
+
191
+ sig { void }
192
+ def auto_indent_after_end_keyword
193
+ current_line = @lines[@position[:line]]
194
+ return unless current_line&.strip == "end"
195
+
196
+ target, _parent, _nesting = @document.locate_node({
197
+ line: @position[:line],
198
+ character: @position[:character] - 1,
199
+ })
200
+
201
+ statements = case target
202
+ when Prism::IfNode, Prism::UnlessNode, Prism::ForNode, Prism::WhileNode, Prism::UntilNode
203
+ target.statements
204
+ end
205
+ return unless statements
206
+
207
+ statements.body.each do |node|
208
+ loc = node.location
209
+ next unless loc.start_column == @indentation
210
+
211
+ add_edit_with_text(" ", { line: loc.start_line - 1, character: 0 })
212
+ end
213
+
214
+ move_cursor_to(@position[:line], @position[:character])
215
+ end
188
216
  end
189
217
  end
190
218
  end
@@ -20,7 +20,7 @@ module RubyLsp
20
20
  class ShowSyntaxTree < BaseRequest
21
21
  extend T::Sig
22
22
 
23
- sig { params(document: Document, range: T.nilable(Document::RangeShape)).void }
23
+ sig { params(document: Document, range: T.nilable(T::Hash[Symbol, T.untyped])).void }
24
24
  def initialize(document, range)
25
25
  super(document)
26
26
 
@@ -0,0 +1,95 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # ![Signature help demo](../../signature_help.gif)
7
+ #
8
+ # The [signature help
9
+ # request](https://microsoft.github.io/language-server-protocol/specification#textDocument_signatureHelp) displays
10
+ # information about the parameters of a method as you type an invocation.
11
+ #
12
+ # Currently only supports methods invoked directly on `self` without taking inheritance into account.
13
+ #
14
+ # # Example
15
+ #
16
+ # ```ruby
17
+ # class Foo
18
+ # def bar(a, b, c)
19
+ # end
20
+ #
21
+ # def baz
22
+ # bar( # -> Signature help will show the parameters of `bar`
23
+ # end
24
+ # ```
25
+ class SignatureHelp < Listener
26
+ extend T::Sig
27
+ extend T::Generic
28
+
29
+ ResponseType = type_member { { fixed: T.nilable(T.any(Interface::SignatureHelp, T::Hash[Symbol, T.untyped])) } }
30
+
31
+ sig { override.returns(ResponseType) }
32
+ attr_reader :_response
33
+
34
+ sig do
35
+ params(
36
+ context: T::Hash[Symbol, T.untyped],
37
+ nesting: T::Array[String],
38
+ index: RubyIndexer::Index,
39
+ dispatcher: Prism::Dispatcher,
40
+ ).void
41
+ end
42
+ def initialize(context, nesting, index, dispatcher)
43
+ @context = context
44
+ @nesting = nesting
45
+ @index = index
46
+ @_response = T.let(nil, ResponseType)
47
+
48
+ super(dispatcher)
49
+ dispatcher.register(self, :on_call_node_enter)
50
+ end
51
+
52
+ sig { params(node: Prism::CallNode).void }
53
+ def on_call_node_enter(node)
54
+ return if DependencyDetector.instance.typechecker
55
+ return unless self_receiver?(node)
56
+
57
+ message = node.message
58
+ return unless message
59
+
60
+ target_method = @index.resolve_method(message, @nesting.join("::"))
61
+ return unless target_method
62
+
63
+ parameters = target_method.parameters
64
+ name = target_method.name
65
+
66
+ # If the method doesn't have any parameters, there's no need to show signature help
67
+ return if parameters.empty?
68
+
69
+ label = "#{name}(#{parameters.map(&:decorated_name).join(", ")})"
70
+
71
+ arguments_node = node.arguments
72
+ arguments = arguments_node&.arguments || []
73
+ active_parameter = (arguments.length - 1).clamp(0, parameters.length - 1)
74
+
75
+ # If there are arguments, then we need to check if there's a trailing comma after the end of the last argument
76
+ # to advance the active parameter to the next one
77
+ if arguments_node &&
78
+ node.slice.byteslice(arguments_node.location.end_offset - node.location.start_offset) == ","
79
+ active_parameter += 1
80
+ end
81
+
82
+ @_response = Interface::SignatureHelp.new(
83
+ signatures: [
84
+ Interface::SignatureInformation.new(
85
+ label: label,
86
+ parameters: parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
87
+ documentation: markdown_from_index_entries("", target_method),
88
+ ),
89
+ ],
90
+ active_parameter: active_parameter,
91
+ )
92
+ end
93
+ end
94
+ end
95
+ end
@@ -19,30 +19,23 @@ module RubyLsp
19
19
  T::Hash[Symbol, Integer],
20
20
  )
21
21
 
22
- sig { params(offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
23
- def initialize(offense, uri)
22
+ # TODO: avoid passing document once we have alternative ways to get at
23
+ # encoding and file source
24
+ sig { params(document: Document, offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
25
+ def initialize(document, offense, uri)
26
+ @document = document
24
27
  @offense = offense
25
28
  @uri = uri
26
29
  end
27
30
 
28
- sig { returns(Interface::CodeAction) }
29
- def to_lsp_code_action
30
- Interface::CodeAction.new(
31
- title: "Autocorrect #{@offense.cop_name}",
32
- kind: Constant::CodeActionKind::QUICK_FIX,
33
- edit: Interface::WorkspaceEdit.new(
34
- document_changes: [
35
- Interface::TextDocumentEdit.new(
36
- text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
37
- uri: @uri.to_s,
38
- version: nil,
39
- ),
40
- edits: @offense.correctable? ? offense_replacements : [],
41
- ),
42
- ],
43
- ),
44
- is_preferred: true,
45
- )
31
+ sig { returns(T::Array[Interface::CodeAction]) }
32
+ def to_lsp_code_actions
33
+ code_actions = []
34
+
35
+ code_actions << autocorrect_action if @offense.correctable?
36
+ code_actions << disable_line_action
37
+
38
+ code_actions
46
39
  end
47
40
 
48
41
  sig { returns(Interface::Diagnostic) }
@@ -65,7 +58,7 @@ module RubyLsp
65
58
  ),
66
59
  data: {
67
60
  correctable: @offense.correctable?,
68
- code_action: to_lsp_code_action,
61
+ code_actions: to_lsp_code_actions,
69
62
  },
70
63
  )
71
64
  end
@@ -90,6 +83,26 @@ module RubyLsp
90
83
  Interface::CodeDescription.new(href: doc_url) if doc_url
91
84
  end
92
85
 
86
+ sig { returns(Interface::CodeAction) }
87
+ def autocorrect_action
88
+ Interface::CodeAction.new(
89
+ title: "Autocorrect #{@offense.cop_name}",
90
+ kind: Constant::CodeActionKind::QUICK_FIX,
91
+ edit: Interface::WorkspaceEdit.new(
92
+ document_changes: [
93
+ Interface::TextDocumentEdit.new(
94
+ text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
95
+ uri: @uri.to_s,
96
+ version: nil,
97
+ ),
98
+ edits: @offense.correctable? ? offense_replacements : [],
99
+ ),
100
+ ],
101
+ ),
102
+ is_preferred: true,
103
+ )
104
+ end
105
+
93
106
  sig { returns(T::Array[Interface::TextEdit]) }
94
107
  def offense_replacements
95
108
  @offense.corrector.as_replacements.map do |range, replacement|
@@ -102,6 +115,64 @@ module RubyLsp
102
115
  )
103
116
  end
104
117
  end
118
+
119
+ sig { returns(Interface::CodeAction) }
120
+ def disable_line_action
121
+ Interface::CodeAction.new(
122
+ title: "Disable #{@offense.cop_name} for this line",
123
+ kind: Constant::CodeActionKind::QUICK_FIX,
124
+ edit: Interface::WorkspaceEdit.new(
125
+ document_changes: [
126
+ Interface::TextDocumentEdit.new(
127
+ text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
128
+ uri: @uri.to_s,
129
+ version: nil,
130
+ ),
131
+ edits: line_disable_comment,
132
+ ),
133
+ ],
134
+ ),
135
+ )
136
+ end
137
+
138
+ sig { returns(T::Array[Interface::TextEdit]) }
139
+ def line_disable_comment
140
+ new_text = if @offense.source_line.include?(" # rubocop:disable ")
141
+ ",#{@offense.cop_name}"
142
+ else
143
+ " # rubocop:disable #{@offense.cop_name}"
144
+ end
145
+
146
+ eol = Interface::Position.new(
147
+ line: @offense.line - 1,
148
+ character: length_of_line(@offense.source_line),
149
+ )
150
+
151
+ # TODO: fails for multiline strings - may be preferable to use block
152
+ # comments to disable some offenses
153
+ inline_comment = Interface::TextEdit.new(
154
+ range: Interface::Range.new(start: eol, end: eol),
155
+ new_text: new_text,
156
+ )
157
+
158
+ [inline_comment]
159
+ end
160
+
161
+ sig { params(line: String).returns(Integer) }
162
+ def length_of_line(line)
163
+ if @document.encoding == Constant::PositionEncodingKind::UTF16
164
+ line_length = 0
165
+ line.codepoints.each do |codepoint|
166
+ line_length += 1
167
+ if codepoint > RubyLsp::Document::Scanner::SURROGATE_PAIR_START
168
+ line_length += 1
169
+ end
170
+ end
171
+ line_length
172
+ else
173
+ line.length
174
+ end
175
+ end
105
176
  end
106
177
  end
107
178
  end
@@ -25,7 +25,7 @@ module RubyLsp
25
25
  @runner.run(filename, document.source)
26
26
 
27
27
  @runner.offenses.map do |offense|
28
- Support::RuboCopDiagnostic.new(offense, uri)
28
+ Support::RuboCopDiagnostic.new(document, offense, uri)
29
29
  end
30
30
  end
31
31
  end
@@ -7,7 +7,7 @@ module RubyLsp
7
7
  class SelectionRange < Interface::SelectionRange
8
8
  extend T::Sig
9
9
 
10
- sig { params(position: Document::PositionShape).returns(T::Boolean) }
10
+ sig { params(position: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
11
11
  def cover?(position)
12
12
  start_covered = range.start.line < position[:line] ||
13
13
  (range.start.line == position[:line] && range.start.character <= position[:character])
@@ -22,6 +22,7 @@ module RubyLsp
22
22
  # - [Definition](rdoc-ref:RubyLsp::Requests::Definition)
23
23
  # - [ShowSyntaxTree](rdoc-ref:RubyLsp::Requests::ShowSyntaxTree)
24
24
  # - [WorkspaceSymbol](rdoc-ref:RubyLsp::Requests::WorkspaceSymbol)
25
+ # - [SignatureHelp](rdoc-ref:RubyLsp::Requests::SignatureHelp)
25
26
 
26
27
  module Requests
27
28
  autoload :BaseRequest, "ruby_lsp/requests/base_request"
@@ -43,6 +44,7 @@ module RubyLsp
43
44
  autoload :Definition, "ruby_lsp/requests/definition"
44
45
  autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
45
46
  autoload :WorkspaceSymbol, "ruby_lsp/requests/workspace_symbol"
47
+ autoload :SignatureHelp, "ruby_lsp/requests/signature_help"
46
48
 
47
49
  # :nodoc:
48
50
  module Support
@@ -20,6 +20,9 @@ module RubyLsp
20
20
  sig { returns(URI::Generic) }
21
21
  attr_accessor :workspace_uri
22
22
 
23
+ sig { returns(T::Hash[Symbol, RequestConfig]) }
24
+ attr_accessor :features_configuration
25
+
23
26
  sig { void }
24
27
  def initialize
25
28
  @state = T.let({}, T::Hash[String, Document])
@@ -28,6 +31,20 @@ module RubyLsp
28
31
  @supports_progress = T.let(true, T::Boolean)
29
32
  @experimental_features = T.let(false, T::Boolean)
30
33
  @workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
34
+ @features_configuration = T.let(
35
+ {
36
+ codeLens: RequestConfig.new({
37
+ enableAll: false,
38
+ gemfileLinks: true,
39
+ }),
40
+ inlayHint: RequestConfig.new({
41
+ enableAll: false,
42
+ implicitRescue: false,
43
+ implicitHashValue: false,
44
+ }),
45
+ },
46
+ T::Hash[Symbol, RequestConfig],
47
+ )
31
48
  end
32
49
 
33
50
  sig { params(uri: URI::Generic).returns(Document) }
@@ -46,7 +63,7 @@ module RubyLsp
46
63
  @state[uri.to_s] = document
47
64
  end
48
65
 
49
- sig { params(uri: URI::Generic, edits: T::Array[Document::EditShape], version: Integer).void }
66
+ sig { params(uri: URI::Generic, edits: T::Array[T::Hash[Symbol, T.untyped]], version: Integer).void }
50
67
  def push_edits(uri:, edits:, version:)
51
68
  T.must(@state[uri.to_s]).push_edits(edits, version: version)
52
69
  end
@@ -74,4 +74,22 @@ module RubyLsp
74
74
  @cancelled = true
75
75
  end
76
76
  end
77
+
78
+ # A request configuration, to turn on/off features
79
+ class RequestConfig
80
+ extend T::Sig
81
+
82
+ sig { returns(T::Hash[Symbol, T::Boolean]) }
83
+ attr_accessor :configuration
84
+
85
+ sig { params(configuration: T::Hash[Symbol, T::Boolean]).void }
86
+ def initialize(configuration)
87
+ @configuration = configuration
88
+ end
89
+
90
+ sig { params(feature: Symbol).returns(T.nilable(T::Boolean)) }
91
+ def enabled?(feature)
92
+ @configuration[:enableAll] || @configuration[feature]
93
+ end
94
+ end
77
95
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.13.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-30 00:00:00.000000000 Z
11
+ date: 2023-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -30,20 +30,20 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.18.0
33
+ version: 0.19.0
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
- version: '0.19'
36
+ version: '0.20'
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 0.18.0
43
+ version: 0.19.0
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.19'
46
+ version: '0.20'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sorbet-runtime
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -118,6 +118,7 @@ files:
118
118
  - lib/ruby_lsp/requests/selection_ranges.rb
119
119
  - lib/ruby_lsp/requests/semantic_highlighting.rb
120
120
  - lib/ruby_lsp/requests/show_syntax_tree.rb
121
+ - lib/ruby_lsp/requests/signature_help.rb
121
122
  - lib/ruby_lsp/requests/support/annotation.rb
122
123
  - lib/ruby_lsp/requests/support/common.rb
123
124
  - lib/ruby_lsp/requests/support/dependency_detector.rb
@@ -157,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
158
  - !ruby/object:Gem::Version
158
159
  version: '0'
159
160
  requirements: []
160
- rubygems_version: 3.4.21
161
+ rubygems_version: 3.4.22
161
162
  signing_key:
162
163
  specification_version: 4
163
164
  summary: An opinionated language server for Ruby