ruby-lsp 0.12.5 → 0.13.0

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: 2cb6c373890a19400025a75be13971ff5e5e33772bd74a178784a035ba94c168
4
- data.tar.gz: d52ad65673ea9f1dfde13cfa6beec0f8c860a75b7f08453238597d584227b56f
3
+ metadata.gz: d6bace664e1540c1333df9dabfb2e7bdd8bda0f910033b47b188e4a82ddacb15
4
+ data.tar.gz: be9e5da0492b26b5cb6e07074d01d7874104b70286c1578342b9639158fed147
5
5
  SHA512:
6
- metadata.gz: ba64686636c26776797fc6e7e70156f4c7f304dbe27e98c73c2a60a7255ae7260159838d68c0364e98bc58d26c404118ad9e3d1cfe13766a2221cbc4d2ac6a7c
7
- data.tar.gz: fcf587173133668739ddc639d913ce34877cc1c0d3451b2ddc49254940ef8ba49815cae57dce2c6de423d3d3fb0db1059f1b8d2d19cd9d1de3af72fb26819759
6
+ metadata.gz: 7d8e61433686271a68dafe8db79765ed509a115ab6f06060d7669d279f48218fe2eada2c12e3b45f155d27a41774771681cdab92700d27f426e75ed89287ca03
7
+ data.tar.gz: 611c050b751050934d0fd93e9e52089fe63fc4d947be75a440dfa0465933814d2682268a10135d036c2cee83fa739156d2b11e42e679ecbed7de62acfee9d621
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.12.5
1
+ 0.13.0
data/exe/ruby-lsp-check CHANGED
@@ -17,8 +17,6 @@ end
17
17
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
18
18
  require "ruby_lsp/internal"
19
19
 
20
- RubyLsp::Addon.load_addons
21
-
22
20
  T::Utils.run_all_sig_blocks
23
21
 
24
22
  files = Dir.glob("#{Dir.pwd}/**/*.rb")
@@ -28,6 +26,7 @@ puts "Verifying that all automatic LSP requests execute successfully. This may t
28
26
  errors = {}
29
27
  store = RubyLsp::Store.new
30
28
  message_queue = Thread::Queue.new
29
+ RubyLsp::Addon.load_addons(message_queue)
31
30
  executor = RubyLsp::Executor.new(store, message_queue)
32
31
 
33
32
  files.each_with_index do |file, index|
@@ -50,13 +49,30 @@ end
50
49
  puts "\n"
51
50
  message_queue.close
52
51
 
52
+ # Indexing
53
+ puts "Verifying that indexing executes successfully. This may take a while..."
54
+
55
+ index = RubyIndexer::Index.new
56
+ indexables = RubyIndexer.configuration.indexables
57
+
58
+ indexables.each_with_index do |indexable, i|
59
+ result = Prism.parse(File.read(indexable.full_path))
60
+ collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
61
+ collector.collect(result.value)
62
+ rescue => e
63
+ errors[indexable.full_path] = e
64
+ ensure
65
+ print("\033[M\033[0KIndexed #{i + 1}/#{indexables.length}") unless ENV["CI"]
66
+ end
67
+ puts "\n"
68
+
53
69
  if errors.empty?
54
- puts "All automatic LSP requests executed successfully"
70
+ puts "All operations completed successfully!"
55
71
  exit
56
72
  end
57
73
 
58
74
  puts <<~ERRORS
59
- Errors while executing requests:
75
+ Errors while executing:
60
76
 
61
77
  #{errors.map { |file, error| "#{file}: #{error.message}" }.join("\n")}
62
78
  ERRORS
@@ -141,8 +141,18 @@ module RubyIndexer
141
141
 
142
142
  sig { params(node: Prism::CallNode).void }
143
143
  def handle_call_node(node)
144
- message = node.message
145
- handle_private_constant(node) if message == "private_constant"
144
+ message = node.name
145
+
146
+ case message
147
+ when :private_constant
148
+ handle_private_constant(node)
149
+ when :attr_reader
150
+ handle_attribute(node, reader: true, writer: false)
151
+ when :attr_writer
152
+ handle_attribute(node, reader: false, writer: true)
153
+ when :attr_accessor
154
+ handle_attribute(node, reader: true, writer: true)
155
+ end
146
156
  end
147
157
 
148
158
  sig { params(node: Prism::DefNode).void }
@@ -312,5 +322,29 @@ module RubyIndexer
312
322
  "#{@stack.join("::")}::#{name}"
313
323
  end.delete_prefix("::")
314
324
  end
325
+
326
+ sig { params(node: Prism::CallNode, reader: T::Boolean, writer: T::Boolean).void }
327
+ def handle_attribute(node, reader:, writer:)
328
+ arguments = node.arguments&.arguments
329
+ return unless arguments
330
+
331
+ receiver = node.receiver
332
+ return unless receiver.nil? || receiver.is_a?(Prism::SelfNode)
333
+
334
+ comments = collect_comments(node)
335
+ arguments.each do |argument|
336
+ name, loc = case argument
337
+ when Prism::SymbolNode
338
+ [argument.value, argument.value_loc]
339
+ when Prism::StringNode
340
+ [argument.content, argument.content_loc]
341
+ end
342
+
343
+ next unless name && loc
344
+
345
+ @index << Entry::Accessor.new(name, @file_path, loc, comments, @current_owner) if reader
346
+ @index << Entry::Accessor.new("#{name}=", @file_path, loc, comments, @current_owner) if writer
347
+ end
348
+ end
315
349
  end
316
350
  end
@@ -120,7 +120,7 @@ module RubyIndexer
120
120
  IndexablePath.new(RbConfig::CONFIG["rubylibdir"], path)
121
121
  end,
122
122
  )
123
- else
123
+ elsif pathname.extname == ".rb"
124
124
  # If the default_path is a Ruby file, we index it
125
125
  indexables << IndexablePath.new(RbConfig::CONFIG["rubylibdir"], default_path)
126
126
  end
@@ -90,20 +90,69 @@ module RubyIndexer
90
90
  end
91
91
  end
92
92
 
93
+ # A required method parameter, e.g. `def foo(a)`
93
94
  class RequiredParameter < Parameter
94
95
  end
95
96
 
96
- class Method < Entry
97
+ # An optional method parameter, e.g. `def foo(a = 123)`
98
+ class OptionalParameter < Parameter
99
+ end
100
+
101
+ # An required keyword method parameter, e.g. `def foo(a:)`
102
+ class KeywordParameter < Parameter
103
+ end
104
+
105
+ # An optional keyword method parameter, e.g. `def foo(a: 123)`
106
+ class OptionalKeywordParameter < Parameter
107
+ end
108
+
109
+ class Member < Entry
97
110
  extend T::Sig
98
111
  extend T::Helpers
99
- abstract!
100
112
 
101
- sig { returns(T::Array[Parameter]) }
102
- attr_reader :parameters
113
+ abstract!
103
114
 
104
115
  sig { returns(T.nilable(Entry::Namespace)) }
105
116
  attr_reader :owner
106
117
 
118
+ sig do
119
+ params(
120
+ name: String,
121
+ file_path: String,
122
+ location: Prism::Location,
123
+ comments: T::Array[String],
124
+ owner: T.nilable(Entry::Namespace),
125
+ ).void
126
+ end
127
+ def initialize(name, file_path, location, comments, owner)
128
+ super(name, file_path, location, comments)
129
+ @owner = owner
130
+ end
131
+
132
+ sig { abstract.returns(T::Array[Parameter]) }
133
+ def parameters; end
134
+ end
135
+
136
+ class Accessor < Member
137
+ extend T::Sig
138
+
139
+ sig { override.returns(T::Array[Parameter]) }
140
+ def parameters
141
+ params = []
142
+ params << RequiredParameter.new(name: name.delete_suffix("=").to_sym) if name.end_with?("=")
143
+ params
144
+ end
145
+ end
146
+
147
+ class Method < Member
148
+ extend T::Sig
149
+ extend T::Helpers
150
+
151
+ abstract!
152
+
153
+ sig { override.returns(T::Array[Parameter]) }
154
+ attr_reader :parameters
155
+
107
156
  sig do
108
157
  params(
109
158
  name: String,
@@ -115,9 +164,9 @@ module RubyIndexer
115
164
  ).void
116
165
  end
117
166
  def initialize(name, file_path, location, comments, parameters_node, owner) # rubocop:disable Metrics/ParameterLists
118
- super(name, file_path, location, comments)
167
+ super(name, file_path, location, comments, owner)
168
+
119
169
  @parameters = T.let(list_params(parameters_node), T::Array[Parameter])
120
- @owner = owner
121
170
  end
122
171
 
123
172
  private
@@ -126,20 +175,42 @@ module RubyIndexer
126
175
  def list_params(parameters_node)
127
176
  return [] unless parameters_node
128
177
 
129
- parameters_node.requireds.filter_map do |required|
178
+ parameters = []
179
+
180
+ parameters_node.requireds.each do |required|
130
181
  name = parameter_name(required)
131
182
  next unless name
132
183
 
133
- RequiredParameter.new(name: name)
184
+ parameters << RequiredParameter.new(name: name)
134
185
  end
135
- end
136
186
 
137
- sig do
138
- params(node: Prism::Node).returns(T.nilable(Symbol))
187
+ parameters_node.optionals.each do |optional|
188
+ name = parameter_name(optional)
189
+ next unless name
190
+
191
+ parameters << OptionalParameter.new(name: name)
192
+ end
193
+
194
+ parameters_node.keywords.each do |keyword|
195
+ name = parameter_name(keyword)
196
+ next unless name
197
+
198
+ case keyword
199
+ when Prism::RequiredKeywordParameterNode
200
+ parameters << KeywordParameter.new(name: name)
201
+ when Prism::OptionalKeywordParameterNode
202
+ parameters << OptionalKeywordParameter.new(name: name)
203
+ end
204
+ end
205
+
206
+ parameters
139
207
  end
208
+
209
+ sig { params(node: Prism::Node).returns(T.nilable(Symbol)) }
140
210
  def parameter_name(node)
141
211
  case node
142
- when Prism::RequiredParameterNode
212
+ when Prism::RequiredParameterNode, Prism::OptionalParameterNode,
213
+ Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode
143
214
  node.name
144
215
  when Prism::MultiTargetNode
145
216
  names = [*node.lefts, *node.rest, *node.rights].map { |parameter_node| parameter_name(parameter_node) }
@@ -84,6 +84,16 @@ module RubyIndexer
84
84
  )
85
85
  end
86
86
 
87
+ def test_indexables_does_not_include_non_ruby_files_inside_rubylibdir
88
+ path = Pathname.new(RbConfig::CONFIG["rubylibdir"]).join("extra_file.txt").to_s
89
+ FileUtils.touch(path)
90
+ indexables = @config.indexables
91
+
92
+ assert(indexables.none? { |indexable| indexable.full_path == path })
93
+ ensure
94
+ FileUtils.rm(T.must(path))
95
+ end
96
+
87
97
  def test_paths_are_unique
88
98
  @config.load_config
89
99
  indexables = @config.indexables
@@ -246,5 +246,16 @@ module RubyIndexer
246
246
  entry = T.must(entries.first).first
247
247
  assert_equal("baz", entry.name)
248
248
  end
249
+
250
+ def test_indexing_prism_fixtures_succeeds
251
+ fixtures = Dir.glob("test/fixtures/prism/test/prism/fixtures/**/*.txt")
252
+
253
+ fixtures.each do |fixture|
254
+ indexable_path = IndexablePath.new("", fixture)
255
+ @index.index_single(indexable_path)
256
+ end
257
+
258
+ refute_empty(@index.instance_variable_get(:@entries))
259
+ end
249
260
  end
250
261
  end
@@ -70,6 +70,42 @@ module RubyIndexer
70
70
  assert_instance_of(Entry::RequiredParameter, parameter)
71
71
  end
72
72
 
73
+ def test_method_with_optional_parameters
74
+ index(<<~RUBY)
75
+ class Foo
76
+ def bar(a = 123)
77
+ end
78
+ end
79
+ RUBY
80
+
81
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
82
+ entry = T.must(@index["bar"].first)
83
+ assert_equal(1, entry.parameters.length)
84
+ parameter = entry.parameters.first
85
+ assert_equal(:a, parameter.name)
86
+ assert_instance_of(Entry::OptionalParameter, parameter)
87
+ end
88
+
89
+ def test_method_with_keyword_parameters
90
+ index(<<~RUBY)
91
+ class Foo
92
+ def bar(a:, b: 123)
93
+ end
94
+ end
95
+ RUBY
96
+
97
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
98
+ entry = T.must(@index["bar"].first)
99
+ assert_equal(2, entry.parameters.length)
100
+ a, b = entry.parameters
101
+
102
+ assert_equal(:a, a.name)
103
+ assert_instance_of(Entry::KeywordParameter, a)
104
+
105
+ assert_equal(:b, b.name)
106
+ assert_instance_of(Entry::OptionalKeywordParameter, b)
107
+ end
108
+
73
109
  def test_keeps_track_of_method_owner
74
110
  index(<<~RUBY)
75
111
  class Foo
@@ -83,5 +119,35 @@ module RubyIndexer
83
119
 
84
120
  assert_equal("Foo", owner_name)
85
121
  end
122
+
123
+ def test_keeps_track_of_attributes
124
+ index(<<~RUBY)
125
+ class Foo
126
+ # Hello there
127
+ attr_reader :bar, :other
128
+ attr_writer :baz
129
+ attr_accessor :qux
130
+ end
131
+ RUBY
132
+
133
+ assert_entry("bar", Entry::Accessor, "/fake/path/foo.rb:2-15:2-18")
134
+ assert_equal("Hello there", @index["bar"].first.comments.join("\n"))
135
+ assert_entry("other", Entry::Accessor, "/fake/path/foo.rb:2-21:2-26")
136
+ assert_equal("Hello there", @index["other"].first.comments.join("\n"))
137
+ assert_entry("baz=", Entry::Accessor, "/fake/path/foo.rb:3-15:3-18")
138
+ assert_entry("qux", Entry::Accessor, "/fake/path/foo.rb:4-17:4-20")
139
+ assert_entry("qux=", Entry::Accessor, "/fake/path/foo.rb:4-17:4-20")
140
+ end
141
+
142
+ def test_ignores_attributes_invoked_on_constant
143
+ index(<<~RUBY)
144
+ class Foo
145
+ end
146
+
147
+ Foo.attr_reader :bar
148
+ RUBY
149
+
150
+ assert_no_entry("bar")
151
+ end
86
152
  end
87
153
  end
@@ -41,8 +41,8 @@ module RubyLsp
41
41
  end
42
42
 
43
43
  # Discovers and loads all addons. Returns the list of activated addons
44
- sig { returns(T::Array[Addon]) }
45
- def load_addons
44
+ sig { params(message_queue: Thread::Queue).returns(T::Array[Addon]) }
45
+ def load_addons(message_queue)
46
46
  # Require all addons entry points, which should be placed under
47
47
  # `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
48
48
  Gem.find_files("ruby_lsp/**/addon.rb").each do |addon|
@@ -55,7 +55,7 @@ module RubyLsp
55
55
  # Activate each one of the discovered addons. If any problems occur in the addons, we don't want to
56
56
  # fail to boot the server
57
57
  addons.each do |addon|
58
- addon.activate
58
+ addon.activate(message_queue)
59
59
  nil
60
60
  rescue => e
61
61
  addon.add_error(e)
@@ -94,8 +94,8 @@ module RubyLsp
94
94
 
95
95
  # Each addon should implement `MyAddon#activate` and use to perform any sort of initialization, such as
96
96
  # reading information into memory or even spawning a separate process
97
- sig { abstract.void }
98
- def activate; end
97
+ sig { abstract.params(message_queue: Thread::Queue).void }
98
+ def activate(message_queue); end
99
99
 
100
100
  # Each addon should implement `MyAddon#deactivate` and use to perform any clean up, like shutting down a
101
101
  # child process
@@ -111,10 +111,9 @@ module RubyLsp
111
111
  overridable.params(
112
112
  uri: URI::Generic,
113
113
  dispatcher: Prism::Dispatcher,
114
- message_queue: Thread::Queue,
115
114
  ).returns(T.nilable(Listener[T::Array[Interface::CodeLens]]))
116
115
  end
117
- def create_code_lens_listener(uri, dispatcher, message_queue); end
116
+ def create_code_lens_listener(uri, dispatcher); end
118
117
 
119
118
  # Creates a new Hover listener. This method is invoked on every Hover request
120
119
  sig do
@@ -122,19 +121,17 @@ module RubyLsp
122
121
  nesting: T::Array[String],
123
122
  index: RubyIndexer::Index,
124
123
  dispatcher: Prism::Dispatcher,
125
- message_queue: Thread::Queue,
126
124
  ).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
127
125
  end
128
- def create_hover_listener(nesting, index, dispatcher, message_queue); end
126
+ def create_hover_listener(nesting, index, dispatcher); end
129
127
 
130
128
  # Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
131
129
  sig do
132
130
  overridable.params(
133
131
  dispatcher: Prism::Dispatcher,
134
- message_queue: Thread::Queue,
135
132
  ).returns(T.nilable(Listener[T::Array[Interface::DocumentSymbol]]))
136
133
  end
137
- def create_document_symbol_listener(dispatcher, message_queue); end
134
+ def create_document_symbol_listener(dispatcher); end
138
135
 
139
136
  # Creates a new Definition listener. This method is invoked on every Definition request
140
137
  sig do
@@ -143,9 +140,8 @@ module RubyLsp
143
140
  nesting: T::Array[String],
144
141
  index: RubyIndexer::Index,
145
142
  dispatcher: Prism::Dispatcher,
146
- message_queue: Thread::Queue,
147
143
  ).returns(T.nilable(Listener[T.nilable(T.any(T::Array[Interface::Location], Interface::Location))]))
148
144
  end
149
- def create_definition_listener(uri, nesting, index, dispatcher, message_queue); end
145
+ def create_definition_listener(uri, nesting, index, dispatcher); end
150
146
  end
151
147
  end
@@ -41,7 +41,7 @@ module RubyLsp
41
41
  when "initialize"
42
42
  initialize_request(request.dig(:params))
43
43
  when "initialized"
44
- Addon.load_addons
44
+ Addon.load_addons(@message_queue)
45
45
 
46
46
  errored_addons = Addon.addons.select(&:error?)
47
47
 
@@ -95,12 +95,12 @@ module RubyLsp
95
95
 
96
96
  # Run listeners for the document
97
97
  dispatcher = Prism::Dispatcher.new
98
- folding_range = Requests::FoldingRanges.new(document.parse_result.comments, dispatcher, @message_queue)
99
- document_symbol = Requests::DocumentSymbol.new(dispatcher, @message_queue)
100
- document_link = Requests::DocumentLink.new(uri, document.comments, dispatcher, @message_queue)
101
- code_lens = Requests::CodeLens.new(uri, dispatcher, @message_queue)
98
+ folding_range = Requests::FoldingRanges.new(document.parse_result.comments, dispatcher)
99
+ document_symbol = Requests::DocumentSymbol.new(dispatcher)
100
+ document_link = Requests::DocumentLink.new(uri, document.comments, dispatcher)
101
+ code_lens = Requests::CodeLens.new(uri, dispatcher)
102
102
 
103
- semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher, @message_queue)
103
+ semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher)
104
104
  dispatcher.dispatch(document.tree)
105
105
 
106
106
  # Store all responses retrieve in this round of visits in the cache and then return the response for the request
@@ -265,13 +265,7 @@ module RubyLsp
265
265
  target = parent if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
266
266
 
267
267
  dispatcher = Prism::Dispatcher.new
268
- base_listener = Requests::Definition.new(
269
- uri,
270
- nesting,
271
- @index,
272
- dispatcher,
273
- @message_queue,
274
- )
268
+ base_listener = Requests::Definition.new(uri, nesting, @index, dispatcher)
275
269
  dispatcher.dispatch_once(target)
276
270
  base_listener.response
277
271
  end
@@ -297,7 +291,7 @@ module RubyLsp
297
291
 
298
292
  # Instantiate all listeners
299
293
  dispatcher = Prism::Dispatcher.new
300
- hover = Requests::Hover.new(@index, nesting, dispatcher, @message_queue)
294
+ hover = Requests::Hover.new(@index, nesting, dispatcher)
301
295
 
302
296
  # Emit events for all listeners
303
297
  dispatcher.dispatch_once(target)
@@ -355,6 +349,11 @@ module RubyLsp
355
349
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
356
350
  return if @store.formatter == "none"
357
351
 
352
+ # Do not format files outside of the workspace. For example, if someone is looking at a gem's source code, we
353
+ # don't want to format it
354
+ path = uri.to_standardized_path
355
+ return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))
356
+
358
357
  Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).run
359
358
  end
360
359
 
@@ -380,7 +379,7 @@ module RubyLsp
380
379
 
381
380
  target, parent = document.locate_node(position)
382
381
  dispatcher = Prism::Dispatcher.new
383
- listener = Requests::DocumentHighlight.new(target, parent, dispatcher, @message_queue)
382
+ listener = Requests::DocumentHighlight.new(target, parent, dispatcher)
384
383
  dispatcher.visit(document.tree)
385
384
  listener.response
386
385
  end
@@ -393,7 +392,7 @@ module RubyLsp
393
392
  end_line = range.dig(:end, :line)
394
393
 
395
394
  dispatcher = Prism::Dispatcher.new
396
- listener = Requests::InlayHints.new(start_line..end_line, dispatcher, @message_queue)
395
+ listener = Requests::InlayHints.new(start_line..end_line, dispatcher)
397
396
  dispatcher.visit(document.tree)
398
397
  listener.response
399
398
  end
@@ -443,6 +442,11 @@ module RubyLsp
443
442
 
444
443
  sig { params(uri: URI::Generic).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
445
444
  def diagnostic(uri)
445
+ # Do not compute diagnostics for files outside of the workspace. For example, if someone is looking at a gem's
446
+ # source code, we don't want to show diagnostics for it
447
+ path = uri.to_standardized_path
448
+ return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))
449
+
446
450
  response = @store.cache_fetch(uri, "textDocument/diagnostic") do |document|
447
451
  Requests::Diagnostics.new(document).run
448
452
  end
@@ -457,11 +461,7 @@ module RubyLsp
457
461
  end_line = range.dig(:end, :line)
458
462
 
459
463
  dispatcher = Prism::Dispatcher.new
460
- listener = Requests::SemanticHighlighting.new(
461
- dispatcher,
462
- @message_queue,
463
- range: start_line..end_line,
464
- )
464
+ listener = Requests::SemanticHighlighting.new(dispatcher, range: start_line..end_line)
465
465
  dispatcher.visit(document.tree)
466
466
 
467
467
  Requests::Support::SemanticTokenEncoder.new.encode(listener.response)
@@ -513,12 +513,7 @@ module RubyLsp
513
513
  return unless target
514
514
 
515
515
  dispatcher = Prism::Dispatcher.new
516
- listener = Requests::Completion.new(
517
- @index,
518
- nesting,
519
- dispatcher,
520
- @message_queue,
521
- )
516
+ listener = Requests::Completion.new(@index, nesting, dispatcher)
522
517
  dispatcher.dispatch_once(target)
523
518
  listener.response
524
519
  end
@@ -583,6 +578,9 @@ module RubyLsp
583
578
  def initialize_request(options)
584
579
  @store.clear
585
580
 
581
+ workspace_uri = options.dig(:workspaceFolders, 0, :uri)
582
+ @store.workspace_uri = URI(workspace_uri) if workspace_uri
583
+
586
584
  encodings = options.dig(:capabilities, :general, :positionEncodings)
587
585
  @store.encoding = if encodings.nil? || encodings.empty?
588
586
  Constant::PositionEncodingKind::UTF16
@@ -14,10 +14,9 @@ module RubyLsp
14
14
 
15
15
  abstract!
16
16
 
17
- sig { params(dispatcher: Prism::Dispatcher, message_queue: Thread::Queue).void }
18
- def initialize(dispatcher, message_queue)
17
+ sig { params(dispatcher: Prism::Dispatcher).void }
18
+ def initialize(dispatcher)
19
19
  @dispatcher = dispatcher
20
- @message_queue = message_queue
21
20
  end
22
21
 
23
22
  sig { returns(ResponseType) }
@@ -43,8 +42,8 @@ module RubyLsp
43
42
  # When inheriting from ExtensibleListener, the `super` of constructor must be called **after** the subclass's own
44
43
  # ivars have been initialized. This is because the constructor of ExtensibleListener calls
45
44
  # `initialize_external_listener` which may depend on the subclass's ivars.
46
- sig { params(dispatcher: Prism::Dispatcher, message_queue: Thread::Queue).void }
47
- def initialize(dispatcher, message_queue)
45
+ sig { params(dispatcher: Prism::Dispatcher).void }
46
+ def initialize(dispatcher)
48
47
  super
49
48
  @response_merged = T.let(false, T::Boolean)
50
49
  @external_listeners = T.let(
@@ -47,16 +47,18 @@ module RubyLsp
47
47
  sig { override.returns(ResponseType) }
48
48
  attr_reader :_response
49
49
 
50
- sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher, message_queue: Thread::Queue).void }
51
- def initialize(uri, dispatcher, message_queue)
50
+ sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher).void }
51
+ def initialize(uri, dispatcher)
52
52
  @uri = T.let(uri, URI::Generic)
53
53
  @_response = T.let([], ResponseType)
54
54
  @path = T.let(uri.to_standardized_path, T.nilable(String))
55
55
  # visibility_stack is a stack of [current_visibility, previous_visibility]
56
56
  @visibility_stack = T.let([[:public, :public]], T::Array[T::Array[T.nilable(Symbol)]])
57
57
  @class_stack = T.let([], T::Array[String])
58
+ @group_id = T.let(1, Integer)
59
+ @group_id_stack = T.let([], T::Array[Integer])
58
60
 
59
- super(dispatcher, message_queue)
61
+ super(dispatcher)
60
62
 
61
63
  dispatcher.register(
62
64
  self,
@@ -82,12 +84,16 @@ module RubyLsp
82
84
  kind: :group,
83
85
  )
84
86
  end
87
+
88
+ @group_id_stack.push(@group_id)
89
+ @group_id += 1
85
90
  end
86
91
 
87
92
  sig { params(node: Prism::ClassNode).void }
88
93
  def on_class_node_leave(node)
89
94
  @visibility_stack.pop
90
95
  @class_stack.pop
96
+ @group_id_stack.pop
91
97
  end
92
98
 
93
99
  sig { params(node: Prism::DefNode).void }
@@ -146,7 +152,7 @@ module RubyLsp
146
152
 
147
153
  sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
148
154
  def initialize_external_listener(addon)
149
- addon.create_code_lens_listener(@uri, @dispatcher, @message_queue)
155
+ addon.create_code_lens_listener(@uri, @dispatcher)
150
156
  end
151
157
 
152
158
  sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
@@ -174,12 +180,15 @@ module RubyLsp
174
180
  },
175
181
  ]
176
182
 
183
+ grouping_data = { group_id: @group_id_stack.last, kind: kind }
184
+ grouping_data[:id] = @group_id if kind == :group
185
+
177
186
  @_response << create_code_lens(
178
187
  node,
179
188
  title: "Run",
180
189
  command_name: "rubyLsp.runTest",
181
190
  arguments: arguments,
182
- data: { type: "test", kind: kind },
191
+ data: { type: "test", **grouping_data },
183
192
  )
184
193
 
185
194
  @_response << create_code_lens(
@@ -187,7 +196,7 @@ module RubyLsp
187
196
  title: "Run In Terminal",
188
197
  command_name: "rubyLsp.runTestInTerminal",
189
198
  arguments: arguments,
190
- data: { type: "test_in_terminal", kind: kind },
199
+ data: { type: "test_in_terminal", **grouping_data },
191
200
  )
192
201
 
193
202
  @_response << create_code_lens(
@@ -195,7 +204,7 @@ module RubyLsp
195
204
  title: "Debug",
196
205
  command_name: "rubyLsp.debugTest",
197
206
  arguments: arguments,
198
- data: { type: "debug", kind: kind },
207
+ data: { type: "debug", **grouping_data },
199
208
  )
200
209
  end
201
210
 
@@ -36,11 +36,10 @@ module RubyLsp
36
36
  index: RubyIndexer::Index,
37
37
  nesting: T::Array[String],
38
38
  dispatcher: Prism::Dispatcher,
39
- message_queue: Thread::Queue,
40
39
  ).void
41
40
  end
42
- def initialize(index, nesting, dispatcher, message_queue)
43
- super(dispatcher, message_queue)
41
+ def initialize(index, nesting, dispatcher)
42
+ super(dispatcher)
44
43
  @_response = T.let([], ResponseType)
45
44
  @index = index
46
45
  @nesting = nesting
@@ -137,18 +136,22 @@ module RubyLsp
137
136
 
138
137
  receiver = T.must(receiver_entries.first)
139
138
 
140
- candidates = T.cast(@index.prefix_search(name), T::Array[T::Array[RubyIndexer::Entry::Method]])
141
- candidates.each do |entries|
142
- entry = entries.find { |e| e.owner&.name == receiver.name }
139
+ @index.prefix_search(name).each do |entries|
140
+ entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name }
143
141
  next unless entry
144
142
 
145
- @_response << build_method_completion(entry, node)
143
+ @_response << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
146
144
  end
147
145
  end
148
146
 
149
147
  private
150
148
 
151
- sig { params(entry: RubyIndexer::Entry::Method, node: Prism::CallNode).returns(Interface::CompletionItem) }
149
+ sig do
150
+ params(
151
+ entry: RubyIndexer::Entry::Member,
152
+ node: Prism::CallNode,
153
+ ).returns(Interface::CompletionItem)
154
+ end
152
155
  def build_method_completion(entry, node)
153
156
  name = entry.name
154
157
  parameters = entry.parameters
@@ -37,16 +37,15 @@ module RubyLsp
37
37
  nesting: T::Array[String],
38
38
  index: RubyIndexer::Index,
39
39
  dispatcher: Prism::Dispatcher,
40
- message_queue: Thread::Queue,
41
40
  ).void
42
41
  end
43
- def initialize(uri, nesting, index, dispatcher, message_queue)
42
+ def initialize(uri, nesting, index, dispatcher)
44
43
  @uri = uri
45
44
  @nesting = nesting
46
45
  @index = index
47
46
  @_response = T.let(nil, ResponseType)
48
47
 
49
- super(dispatcher, message_queue)
48
+ super(dispatcher)
50
49
 
51
50
  dispatcher.register(
52
51
  self,
@@ -58,7 +57,7 @@ module RubyLsp
58
57
 
59
58
  sig { override.params(addon: Addon).returns(T.nilable(RubyLsp::Listener[ResponseType])) }
60
59
  def initialize_external_listener(addon)
61
- addon.create_definition_listener(@uri, @nesting, @index, @dispatcher, @message_queue)
60
+ addon.create_definition_listener(@uri, @nesting, @index, @dispatcher)
62
61
  end
63
62
 
64
63
  sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
@@ -32,13 +32,8 @@ module RubyLsp
32
32
  def run
33
33
  # Running RuboCop is slow, so to avoid excessive runs we only do so if the file is syntactically valid
34
34
  return syntax_error_diagnostics if @document.syntax_error?
35
-
36
35
  return unless defined?(Support::RuboCopDiagnosticsRunner)
37
36
 
38
- # Don't try to run RuboCop diagnostics for files outside the current working directory
39
- path = @uri.to_standardized_path
40
- return unless path.nil? || path.start_with?(T.must(WORKSPACE_URI.to_standardized_path))
41
-
42
37
  Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document).map!(&:to_lsp_diagnostic)
43
38
  end
44
39
 
@@ -114,11 +114,10 @@ module RubyLsp
114
114
  target: T.nilable(Prism::Node),
115
115
  parent: T.nilable(Prism::Node),
116
116
  dispatcher: Prism::Dispatcher,
117
- message_queue: Thread::Queue,
118
117
  ).void
119
118
  end
120
- def initialize(target, parent, dispatcher, message_queue)
121
- super(dispatcher, message_queue)
119
+ def initialize(target, parent, dispatcher)
120
+ super(dispatcher)
122
121
 
123
122
  @_response = T.let([], T::Array[Interface::DocumentHighlight])
124
123
 
@@ -80,11 +80,10 @@ module RubyLsp
80
80
  uri: URI::Generic,
81
81
  comments: T::Array[Prism::Comment],
82
82
  dispatcher: Prism::Dispatcher,
83
- message_queue: Thread::Queue,
84
83
  ).void
85
84
  end
86
- def initialize(uri, comments, dispatcher, message_queue)
87
- super(dispatcher, message_queue)
85
+ def initialize(uri, comments, dispatcher)
86
+ super(dispatcher)
88
87
 
89
88
  # Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
90
89
  # in the URI
@@ -49,8 +49,8 @@ module RubyLsp
49
49
  sig { override.returns(T::Array[Interface::DocumentSymbol]) }
50
50
  attr_reader :_response
51
51
 
52
- sig { params(dispatcher: Prism::Dispatcher, message_queue: Thread::Queue).void }
53
- def initialize(dispatcher, message_queue)
52
+ sig { params(dispatcher: Prism::Dispatcher).void }
53
+ def initialize(dispatcher)
54
54
  @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
55
55
  @_response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
56
56
  @stack = T.let(
@@ -80,7 +80,7 @@ module RubyLsp
80
80
 
81
81
  sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
82
82
  def initialize_external_listener(addon)
83
- addon.create_document_symbol_listener(@dispatcher, @message_queue)
83
+ addon.create_document_symbol_listener(@dispatcher)
84
84
  end
85
85
 
86
86
  # Merges responses from other listeners
@@ -21,9 +21,9 @@ module RubyLsp
21
21
 
22
22
  ResponseType = type_member { { fixed: T::Array[Interface::FoldingRange] } }
23
23
 
24
- sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher, queue: Thread::Queue).void }
25
- def initialize(comments, dispatcher, queue)
26
- super(dispatcher, queue)
24
+ sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher).void }
25
+ def initialize(comments, dispatcher)
26
+ super(dispatcher)
27
27
 
28
28
  @_response = T.let([], ResponseType)
29
29
  @requires = T.let([], T::Array[Prism::CallNode])
@@ -40,6 +40,7 @@ module RubyLsp
40
40
  :on_array_node_enter,
41
41
  :on_block_node_enter,
42
42
  :on_case_node_enter,
43
+ :on_case_match_node_enter,
43
44
  :on_class_node_enter,
44
45
  :on_module_node_enter,
45
46
  :on_for_node_enter,
@@ -51,7 +52,6 @@ module RubyLsp
51
52
  :on_else_node_enter,
52
53
  :on_ensure_node_enter,
53
54
  :on_begin_node_enter,
54
- :on_string_concat_node_enter,
55
55
  :on_def_node_enter,
56
56
  :on_call_node_enter,
57
57
  :on_lambda_node_enter,
@@ -91,10 +91,10 @@ module RubyLsp
91
91
 
92
92
  sig { params(node: Prism::InterpolatedStringNode).void }
93
93
  def on_interpolated_string_node_enter(node)
94
- opening_loc = node.opening_loc
95
- closing_loc = node.closing_loc
94
+ opening_loc = node.opening_loc || node.location
95
+ closing_loc = node.closing_loc || node.parts.last&.location || node.location
96
96
 
97
- add_lines_range(opening_loc.start_line, closing_loc.start_line - 1) if opening_loc && closing_loc
97
+ add_lines_range(opening_loc.start_line, closing_loc.start_line - 1)
98
98
  end
99
99
 
100
100
  sig { params(node: Prism::ArrayNode).void }
@@ -112,6 +112,11 @@ module RubyLsp
112
112
  add_simple_range(node)
113
113
  end
114
114
 
115
+ sig { params(node: Prism::CaseMatchNode).void }
116
+ def on_case_match_node_enter(node)
117
+ add_simple_range(node)
118
+ end
119
+
115
120
  sig { params(node: Prism::ClassNode).void }
116
121
  def on_class_node_enter(node)
117
122
  add_simple_range(node)
@@ -167,14 +172,6 @@ module RubyLsp
167
172
  add_simple_range(node)
168
173
  end
169
174
 
170
- sig { params(node: Prism::StringConcatNode).void }
171
- def on_string_concat_node_enter(node)
172
- left = T.let(node.left, Prism::Node)
173
- left = left.left while left.is_a?(Prism::StringConcatNode)
174
-
175
- add_lines_range(left.location.start_line, node.right.location.end_line - 1)
176
- end
177
-
178
175
  sig { params(node: Prism::DefNode).void }
179
176
  def on_def_node_enter(node)
180
177
  params = node.parameters
@@ -64,11 +64,6 @@ module RubyLsp
64
64
  sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
65
65
  def run
66
66
  return if @formatter == "none"
67
-
68
- # Don't try to format files outside the current working directory
69
- path = @uri.to_standardized_path
70
- return unless path.nil? || path.start_with?(T.must(WORKSPACE_URI.to_standardized_path))
71
-
72
67
  return if @document.syntax_error?
73
68
 
74
69
  formatted_text = formatted_file
@@ -37,15 +37,14 @@ module RubyLsp
37
37
  index: RubyIndexer::Index,
38
38
  nesting: T::Array[String],
39
39
  dispatcher: Prism::Dispatcher,
40
- message_queue: Thread::Queue,
41
40
  ).void
42
41
  end
43
- def initialize(index, nesting, dispatcher, message_queue)
42
+ def initialize(index, nesting, dispatcher)
44
43
  @index = index
45
44
  @nesting = nesting
46
45
  @_response = T.let(nil, ResponseType)
47
46
 
48
- super(dispatcher, message_queue)
47
+ super(dispatcher)
49
48
  dispatcher.register(
50
49
  self,
51
50
  :on_constant_read_node_enter,
@@ -57,7 +56,7 @@ module RubyLsp
57
56
 
58
57
  sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
59
58
  def initialize_external_listener(addon)
60
- addon.create_hover_listener(@nesting, @index, @dispatcher, @message_queue)
59
+ addon.create_hover_listener(@nesting, @index, @dispatcher)
61
60
  end
62
61
 
63
62
  # Merges responses from other hover listeners
@@ -39,9 +39,9 @@ module RubyLsp
39
39
  sig { override.returns(ResponseType) }
40
40
  attr_reader :_response
41
41
 
42
- sig { params(range: T::Range[Integer], dispatcher: Prism::Dispatcher, message_queue: Thread::Queue).void }
43
- def initialize(range, dispatcher, message_queue)
44
- super(dispatcher, message_queue)
42
+ sig { params(range: T::Range[Integer], dispatcher: Prism::Dispatcher).void }
43
+ def initialize(range, dispatcher)
44
+ super(dispatcher)
45
45
 
46
46
  @_response = T.let([], ResponseType)
47
47
  @range = range
@@ -20,8 +20,8 @@ module RubyLsp
20
20
 
21
21
  END_REGEXES = T.let(
22
22
  [
23
- /(if|unless|for|while|class|module|until|def|case).*/,
24
- /.*\sdo/,
23
+ /\b(if|unless|for|while|class|module|until|def|case)\b.*/,
24
+ /.*\s\bdo\b/,
25
25
  ],
26
26
  T::Array[Regexp],
27
27
  )
@@ -139,7 +139,6 @@ module RubyLsp
139
139
  sig { params(spaces: String).void }
140
140
  def handle_comment_line(spaces)
141
141
  add_edit_with_text("##{spaces}")
142
- move_cursor_to(@position[:line], @indentation + spaces.size + 1)
143
142
  end
144
143
 
145
144
  sig { params(text: String, position: Document::PositionShape).void }
@@ -107,20 +107,15 @@ module RubyLsp
107
107
  sig { override.returns(ResponseType) }
108
108
  attr_reader :_response
109
109
 
110
- sig do
111
- params(
112
- dispatcher: Prism::Dispatcher,
113
- message_queue: Thread::Queue,
114
- range: T.nilable(T::Range[Integer]),
115
- ).void
116
- end
117
- def initialize(dispatcher, message_queue, range: nil)
118
- super(dispatcher, message_queue)
110
+ sig { params(dispatcher: Prism::Dispatcher, range: T.nilable(T::Range[Integer])).void }
111
+ def initialize(dispatcher, range: nil)
112
+ super(dispatcher)
119
113
 
120
114
  @_response = T.let([], ResponseType)
121
115
  @range = range
122
116
  @special_methods = T.let(nil, T.nilable(T::Array[String]))
123
117
  @current_scope = T.let(ParameterScope.new, ParameterScope)
118
+ @inside_regex_capture = T.let(false, T::Boolean)
124
119
 
125
120
  dispatcher.register(
126
121
  self,
@@ -152,6 +147,8 @@ module RubyLsp
152
147
  :on_local_variable_or_write_node_enter,
153
148
  :on_local_variable_target_node_enter,
154
149
  :on_block_local_variable_node_enter,
150
+ :on_match_write_node_enter,
151
+ :on_match_write_node_leave,
155
152
  )
156
153
  end
157
154
 
@@ -165,14 +162,28 @@ module RubyLsp
165
162
  # We can't push a semantic token for [] and []= because the argument inside the brackets is a part of
166
163
  # the message_loc
167
164
  return if message.start_with?("[") && (message.end_with?("]") || message.end_with?("]="))
168
-
169
- return process_regexp_locals(node) if message == "=~"
165
+ return if message == "=~"
170
166
  return if special_method?(message)
171
167
 
172
168
  type = Support::Sorbet.annotation?(node) ? :type : :method
173
169
  add_token(T.must(node.message_loc), type)
174
170
  end
175
171
 
172
+ sig { params(node: Prism::MatchWriteNode).void }
173
+ def on_match_write_node_enter(node)
174
+ call = node.call
175
+
176
+ if call.message == "=~"
177
+ @inside_regex_capture = true
178
+ process_regexp_locals(call)
179
+ end
180
+ end
181
+
182
+ sig { params(node: Prism::MatchWriteNode).void }
183
+ def on_match_write_node_leave(node)
184
+ @inside_regex_capture = true if node.call.message == "=~"
185
+ end
186
+
176
187
  sig { params(node: Prism::ConstantReadNode).void }
177
188
  def on_constant_read_node_enter(node)
178
189
  return unless visible?(node, @range)
@@ -359,6 +370,12 @@ module RubyLsp
359
370
 
360
371
  sig { params(node: Prism::LocalVariableTargetNode).void }
361
372
  def on_local_variable_target_node_enter(node)
373
+ # If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
374
+ # Unfortunately, if the regex contains a backslash, the location will be incorrect and we'll end up highlighting
375
+ # the entire regex as a local variable. We process these captures in process_regexp_locals instead and then
376
+ # prevent pushing local variable target tokens. See https://github.com/ruby/prism/issues/1912
377
+ return if @inside_regex_capture
378
+
362
379
  return unless visible?(node, @range)
363
380
 
364
381
  add_token(node.location, @current_scope.type_for(node.name))
@@ -34,15 +34,10 @@ module RubyLsp
34
34
 
35
35
  sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
36
36
  def run(uri, document)
37
- relative_path = Pathname.new(T.must(uri.to_standardized_path || uri.opaque))
38
- .relative_path_from(T.must(WORKSPACE_URI.to_standardized_path))
39
- return if @options.ignore_files.any? { |pattern| File.fnmatch(pattern, relative_path) }
37
+ path = uri.to_standardized_path
38
+ return if path && @options.ignore_files.any? { |pattern| File.fnmatch?("*/#{pattern}", path) }
40
39
 
41
- SyntaxTree.format(
42
- document.source,
43
- @options.print_width,
44
- options: @options.formatter_options,
45
- )
40
+ SyntaxTree.format(document.source, @options.print_width, options: @options.formatter_options)
46
41
  end
47
42
  end
48
43
  end
@@ -75,6 +75,8 @@ module RubyLsp
75
75
  Constant::SymbolKind::CONSTANT
76
76
  when RubyIndexer::Entry::Method
77
77
  entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
78
+ when RubyIndexer::Entry::Accessor
79
+ Constant::SymbolKind::PROPERTY
78
80
  end
79
81
  end
80
82
  end
@@ -17,6 +17,9 @@ module RubyLsp
17
17
  sig { returns(T::Boolean) }
18
18
  attr_accessor :experimental_features
19
19
 
20
+ sig { returns(URI::Generic) }
21
+ attr_accessor :workspace_uri
22
+
20
23
  sig { void }
21
24
  def initialize
22
25
  @state = T.let({}, T::Hash[String, Document])
@@ -24,6 +27,7 @@ module RubyLsp
24
27
  @formatter = T.let("auto", String)
25
28
  @supports_progress = T.let(true, T::Boolean)
26
29
  @experimental_features = T.let(false, T::Boolean)
30
+ @workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
27
31
  end
28
32
 
29
33
  sig { params(uri: URI::Generic).returns(Document) }
@@ -4,10 +4,6 @@
4
4
  module RubyLsp
5
5
  # Used to indicate that a request shouldn't return a response
6
6
  VOID = T.let(Object.new.freeze, Object)
7
-
8
- # This freeze is not redundant since the interpolated string is mutable
9
- WORKSPACE_URI = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
10
-
11
7
  BUNDLE_PATH = T.let(
12
8
  begin
13
9
  Bundler.bundle_path.to_s
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.12.5
4
+ version: 0.13.0
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-24 00:00:00.000000000 Z
11
+ date: 2023-11-30 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.17.1
33
+ version: 0.18.0
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
- version: '0.18'
36
+ version: '0.19'
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.17.1
43
+ version: 0.18.0
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.18'
46
+ version: '0.19'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sorbet-runtime
49
49
  requirement: !ruby/object:Gem::Requirement