ruby-lsp 0.12.5 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
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