ruby-lsp 0.12.5 → 0.13.1

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp-check +20 -4
  4. data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +36 -2
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +1 -1
  6. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +132 -13
  7. data/lib/ruby_indexer/test/configuration_test.rb +10 -0
  8. data/lib/ruby_indexer/test/index_test.rb +11 -0
  9. data/lib/ruby_indexer/test/method_test.rb +202 -0
  10. data/lib/ruby_lsp/addon.rb +9 -13
  11. data/lib/ruby_lsp/document.rb +3 -0
  12. data/lib/ruby_lsp/executor.rb +33 -28
  13. data/lib/ruby_lsp/listener.rb +4 -5
  14. data/lib/ruby_lsp/requests/code_actions.rb +2 -16
  15. data/lib/ruby_lsp/requests/code_lens.rb +29 -7
  16. data/lib/ruby_lsp/requests/completion.rb +11 -8
  17. data/lib/ruby_lsp/requests/definition.rb +3 -4
  18. data/lib/ruby_lsp/requests/diagnostics.rb +0 -5
  19. data/lib/ruby_lsp/requests/document_highlight.rb +2 -3
  20. data/lib/ruby_lsp/requests/document_link.rb +2 -3
  21. data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
  22. data/lib/ruby_lsp/requests/folding_ranges.rb +12 -15
  23. data/lib/ruby_lsp/requests/formatting.rb +0 -5
  24. data/lib/ruby_lsp/requests/hover.rb +3 -4
  25. data/lib/ruby_lsp/requests/inlay_hints.rb +20 -3
  26. data/lib/ruby_lsp/requests/on_type_formatting.rb +31 -4
  27. data/lib/ruby_lsp/requests/semantic_highlighting.rb +28 -11
  28. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +92 -21
  29. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +1 -1
  30. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -8
  31. data/lib/ruby_lsp/requests/workspace_symbol.rb +2 -0
  32. data/lib/ruby_lsp/store.rb +21 -0
  33. data/lib/ruby_lsp/utils.rb +18 -4
  34. metadata +7 -7
@@ -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,13 @@ 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
+ lenses_configuration = T.must(@store.features_configuration.dig(:codeLens))
102
+ code_lens = Requests::CodeLens.new(uri, lenses_configuration, dispatcher)
102
103
 
103
- semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher, @message_queue)
104
+ semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher)
104
105
  dispatcher.dispatch(document.tree)
105
106
 
106
107
  # Store all responses retrieve in this round of visits in the cache and then return the response for the request
@@ -265,13 +266,7 @@ module RubyLsp
265
266
  target = parent if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
266
267
 
267
268
  dispatcher = Prism::Dispatcher.new
268
- base_listener = Requests::Definition.new(
269
- uri,
270
- nesting,
271
- @index,
272
- dispatcher,
273
- @message_queue,
274
- )
269
+ base_listener = Requests::Definition.new(uri, nesting, @index, dispatcher)
275
270
  dispatcher.dispatch_once(target)
276
271
  base_listener.response
277
272
  end
@@ -297,7 +292,7 @@ module RubyLsp
297
292
 
298
293
  # Instantiate all listeners
299
294
  dispatcher = Prism::Dispatcher.new
300
- hover = Requests::Hover.new(@index, nesting, dispatcher, @message_queue)
295
+ hover = Requests::Hover.new(@index, nesting, dispatcher)
301
296
 
302
297
  # Emit events for all listeners
303
298
  dispatcher.dispatch_once(target)
@@ -355,6 +350,11 @@ module RubyLsp
355
350
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
356
351
  return if @store.formatter == "none"
357
352
 
353
+ # Do not format files outside of the workspace. For example, if someone is looking at a gem's source code, we
354
+ # don't want to format it
355
+ path = uri.to_standardized_path
356
+ return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))
357
+
358
358
  Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).run
359
359
  end
360
360
 
@@ -380,7 +380,7 @@ module RubyLsp
380
380
 
381
381
  target, parent = document.locate_node(position)
382
382
  dispatcher = Prism::Dispatcher.new
383
- listener = Requests::DocumentHighlight.new(target, parent, dispatcher, @message_queue)
383
+ listener = Requests::DocumentHighlight.new(target, parent, dispatcher)
384
384
  dispatcher.visit(document.tree)
385
385
  listener.response
386
386
  end
@@ -393,7 +393,8 @@ module RubyLsp
393
393
  end_line = range.dig(:end, :line)
394
394
 
395
395
  dispatcher = Prism::Dispatcher.new
396
- listener = Requests::InlayHints.new(start_line..end_line, dispatcher, @message_queue)
396
+ hints_configurations = T.must(@store.features_configuration.dig(:inlayHint))
397
+ listener = Requests::InlayHints.new(start_line..end_line, hints_configurations, dispatcher)
397
398
  dispatcher.visit(document.tree)
398
399
  listener.response
399
400
  end
@@ -443,6 +444,11 @@ module RubyLsp
443
444
 
444
445
  sig { params(uri: URI::Generic).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
445
446
  def diagnostic(uri)
447
+ # Do not compute diagnostics for files outside of the workspace. For example, if someone is looking at a gem's
448
+ # source code, we don't want to show diagnostics for it
449
+ path = uri.to_standardized_path
450
+ return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))
451
+
446
452
  response = @store.cache_fetch(uri, "textDocument/diagnostic") do |document|
447
453
  Requests::Diagnostics.new(document).run
448
454
  end
@@ -457,11 +463,7 @@ module RubyLsp
457
463
  end_line = range.dig(:end, :line)
458
464
 
459
465
  dispatcher = Prism::Dispatcher.new
460
- listener = Requests::SemanticHighlighting.new(
461
- dispatcher,
462
- @message_queue,
463
- range: start_line..end_line,
464
- )
466
+ listener = Requests::SemanticHighlighting.new(dispatcher, range: start_line..end_line)
465
467
  dispatcher.visit(document.tree)
466
468
 
467
469
  Requests::Support::SemanticTokenEncoder.new.encode(listener.response)
@@ -513,12 +515,7 @@ module RubyLsp
513
515
  return unless target
514
516
 
515
517
  dispatcher = Prism::Dispatcher.new
516
- listener = Requests::Completion.new(
517
- @index,
518
- nesting,
519
- dispatcher,
520
- @message_queue,
521
- )
518
+ listener = Requests::Completion.new(@index, nesting, dispatcher)
522
519
  dispatcher.dispatch_once(target)
523
520
  listener.response
524
521
  end
@@ -583,6 +580,9 @@ module RubyLsp
583
580
  def initialize_request(options)
584
581
  @store.clear
585
582
 
583
+ workspace_uri = options.dig(:workspaceFolders, 0, :uri)
584
+ @store.workspace_uri = URI(workspace_uri) if workspace_uri
585
+
586
586
  encodings = options.dig(:capabilities, :general, :positionEncodings)
587
587
  @store.encoding = if encodings.nil? || encodings.empty?
588
588
  Constant::PositionEncodingKind::UTF16
@@ -604,6 +604,11 @@ module RubyLsp
604
604
  configured_features = options.dig(:initializationOptions, :enabledFeatures)
605
605
  @store.experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
606
606
 
607
+ configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
608
+ configured_lenses = options.dig(:initializationOptions, :featuresConfiguration, :codeLens)
609
+ T.must(@store.features_configuration.dig(:inlayHint)).configuration.merge!(configured_hints) if configured_hints
610
+ T.must(@store.features_configuration.dig(:codeLens)).configuration.merge!(configured_lenses) if configured_lenses
611
+
607
612
  enabled_features = case configured_features
608
613
  when Array
609
614
  # If the configuration is using an array, then absent features are disabled and present ones are enabled. That's
@@ -665,7 +670,7 @@ module RubyLsp
665
670
  on_type_formatting_provider = if enabled_features["onTypeFormatting"]
666
671
  Interface::DocumentOnTypeFormattingOptions.new(
667
672
  first_trigger_character: "{",
668
- more_trigger_character: ["\n", "|"],
673
+ more_trigger_character: ["\n", "|", "d"],
669
674
  )
670
675
  end
671
676
 
@@ -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(
@@ -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,14 +49,6 @@ 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
52
  sig { params(range: Document::RangeShape, uri: URI::Generic).returns(Interface::CodeAction) }
67
53
  def refactor_code_action(range, uri)
68
54
  Interface::CodeAction.new(
@@ -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,16 +51,25 @@ module RubyLsp
47
51
  sig { override.returns(ResponseType) }
48
52
  attr_reader :_response
49
53
 
50
- sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher, message_queue: Thread::Queue).void }
51
- def initialize(uri, dispatcher, message_queue)
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))
55
65
  # visibility_stack is a stack of [current_visibility, previous_visibility]
56
66
  @visibility_stack = T.let([[:public, :public]], T::Array[T::Array[T.nilable(Symbol)]])
57
67
  @class_stack = T.let([], T::Array[String])
68
+ @group_id = T.let(1, Integer)
69
+ @group_id_stack = T.let([], T::Array[Integer])
70
+ @lenses_configuration = lenses_configuration
58
71
 
59
- super(dispatcher, message_queue)
72
+ super(dispatcher)
60
73
 
61
74
  dispatcher.register(
62
75
  self,
@@ -82,12 +95,16 @@ module RubyLsp
82
95
  kind: :group,
83
96
  )
84
97
  end
98
+
99
+ @group_id_stack.push(@group_id)
100
+ @group_id += 1
85
101
  end
86
102
 
87
103
  sig { params(node: Prism::ClassNode).void }
88
104
  def on_class_node_leave(node)
89
105
  @visibility_stack.pop
90
106
  @class_stack.pop
107
+ @group_id_stack.pop
91
108
  end
92
109
 
93
110
  sig { params(node: Prism::DefNode).void }
@@ -128,6 +145,8 @@ module RubyLsp
128
145
  end
129
146
 
130
147
  if @path&.include?(GEMFILE_NAME) && name == :gem && arguments
148
+ return unless @lenses_configuration.enabled?(:gemfileLinks)
149
+
131
150
  first_argument = arguments.arguments.first
132
151
  return unless first_argument.is_a?(Prism::StringNode)
133
152
 
@@ -146,7 +165,7 @@ module RubyLsp
146
165
 
147
166
  sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
148
167
  def initialize_external_listener(addon)
149
- addon.create_code_lens_listener(@uri, @dispatcher, @message_queue)
168
+ addon.create_code_lens_listener(@uri, @dispatcher)
150
169
  end
151
170
 
152
171
  sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
@@ -174,12 +193,15 @@ module RubyLsp
174
193
  },
175
194
  ]
176
195
 
196
+ grouping_data = { group_id: @group_id_stack.last, kind: kind }
197
+ grouping_data[:id] = @group_id if kind == :group
198
+
177
199
  @_response << create_code_lens(
178
200
  node,
179
201
  title: "Run",
180
202
  command_name: "rubyLsp.runTest",
181
203
  arguments: arguments,
182
- data: { type: "test", kind: kind },
204
+ data: { type: "test", **grouping_data },
183
205
  )
184
206
 
185
207
  @_response << create_code_lens(
@@ -187,7 +209,7 @@ module RubyLsp
187
209
  title: "Run In Terminal",
188
210
  command_name: "rubyLsp.runTestInTerminal",
189
211
  arguments: arguments,
190
- data: { type: "test_in_terminal", kind: kind },
212
+ data: { type: "test_in_terminal", **grouping_data },
191
213
  )
192
214
 
193
215
  @_response << create_code_lens(
@@ -195,7 +217,7 @@ module RubyLsp
195
217
  title: "Debug",
196
218
  command_name: "rubyLsp.debugTest",
197
219
  arguments: arguments,
198
- data: { type: "debug", kind: kind },
220
+ data: { type: "debug", **grouping_data },
199
221
  )
200
222
  end
201
223
 
@@ -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
@@ -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, message_queue: Thread::Queue).void }
43
- def initialize(range, dispatcher, message_queue)
44
- super(dispatcher, message_queue)
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)
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