ruby-lsp 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,24 +5,28 @@ require_relative "test_case"
5
5
 
6
6
  module RubyIndexer
7
7
  class EnhancementTest < TestCase
8
+ def teardown
9
+ super
10
+ Enhancement.clear
11
+ end
12
+
8
13
  def test_enhancing_indexing_included_hook
9
- enhancement_class = Class.new(Enhancement) do
10
- def on_call_node_enter(owner, node, file_path, code_units_cache)
14
+ Class.new(Enhancement) do
15
+ def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
16
+ owner = @listener.current_owner
11
17
  return unless owner
12
- return unless node.name == :extend
18
+ return unless call_node.name == :extend
13
19
 
14
- arguments = node.arguments&.arguments
20
+ arguments = call_node.arguments&.arguments
15
21
  return unless arguments
16
22
 
17
- location = Location.from_prism_location(node.location, code_units_cache)
18
-
19
23
  arguments.each do |node|
20
24
  next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
21
25
 
22
26
  module_name = node.full_name
23
27
  next unless module_name == "ActiveSupport::Concern"
24
28
 
25
- @index.register_included_hook(owner.name) do |index, base|
29
+ @listener.register_included_hook do |index, base|
26
30
  class_methods_name = "#{owner.name}::ClassMethods"
27
31
 
28
32
  if index.indexed?(class_methods_name)
@@ -31,16 +35,11 @@ module RubyIndexer
31
35
  end
32
36
  end
33
37
 
34
- @index.add(Entry::Method.new(
38
+ @listener.add_method(
35
39
  "new_method",
36
- file_path,
37
- location,
38
- location,
39
- nil,
40
+ call_node.location,
40
41
  [Entry::Signature.new([Entry::RequiredParameter.new(name: :a)])],
41
- Entry::Visibility::PUBLIC,
42
- owner,
43
- ))
42
+ )
44
43
  rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
45
44
  Prism::ConstantPathNode::MissingNodesInConstantPathError
46
45
  # Do nothing
@@ -48,7 +47,6 @@ module RubyIndexer
48
47
  end
49
48
  end
50
49
 
51
- @index.register_enhancement(enhancement_class.new(@index))
52
50
  index(<<~RUBY)
53
51
  module ActiveSupport
54
52
  module Concern
@@ -96,9 +94,9 @@ module RubyIndexer
96
94
  end
97
95
 
98
96
  def test_enhancing_indexing_configuration_dsl
99
- enhancement_class = Class.new(Enhancement) do
100
- def on_call_node_enter(owner, node, file_path, code_units_cache)
101
- return unless owner
97
+ Class.new(Enhancement) do
98
+ def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
99
+ return unless @listener.current_owner
102
100
 
103
101
  name = node.name
104
102
  return unless name == :has_many
@@ -109,22 +107,14 @@ module RubyIndexer
109
107
  association_name = arguments.first
110
108
  return unless association_name.is_a?(Prism::SymbolNode)
111
109
 
112
- location = Location.from_prism_location(association_name.location, code_units_cache)
113
-
114
- @index.add(Entry::Method.new(
110
+ @listener.add_method(
115
111
  T.must(association_name.value),
116
- file_path,
117
- location,
118
- location,
119
- nil,
112
+ association_name.location,
120
113
  [],
121
- Entry::Visibility::PUBLIC,
122
- owner,
123
- ))
114
+ )
124
115
  end
125
116
  end
126
117
 
127
- @index.register_enhancement(enhancement_class.new(@index))
128
118
  index(<<~RUBY)
129
119
  module ActiveSupport
130
120
  module Concern
@@ -157,8 +147,8 @@ module RubyIndexer
157
147
  end
158
148
 
159
149
  def test_error_handling_in_on_call_node_enter_enhancement
160
- enhancement_class = Class.new(Enhancement) do
161
- def on_call_node_enter(owner, node, file_path, code_units_cache)
150
+ Class.new(Enhancement) do
151
+ def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
162
152
  raise "Error"
163
153
  end
164
154
 
@@ -169,8 +159,6 @@ module RubyIndexer
169
159
  end
170
160
  end
171
161
 
172
- @index.register_enhancement(enhancement_class.new(@index))
173
-
174
162
  _stdout, stderr = capture_io do
175
163
  index(<<~RUBY)
176
164
  module ActiveSupport
@@ -192,8 +180,8 @@ module RubyIndexer
192
180
  end
193
181
 
194
182
  def test_error_handling_in_on_call_node_leave_enhancement
195
- enhancement_class = Class.new(Enhancement) do
196
- def on_call_node_leave(owner, node, file_path, code_units_cache)
183
+ Class.new(Enhancement) do
184
+ def on_call_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
197
185
  raise "Error"
198
186
  end
199
187
 
@@ -204,8 +192,6 @@ module RubyIndexer
204
192
  end
205
193
  end
206
194
 
207
- @index.register_enhancement(enhancement_class.new(@index))
208
-
209
195
  _stdout, stderr = capture_io do
210
196
  index(<<~RUBY)
211
197
  module ActiveSupport
@@ -225,5 +211,115 @@ module RubyIndexer
225
211
  # The module should still be indexed
226
212
  assert_entry("ActiveSupport::Concern", Entry::Module, "/fake/path/foo.rb:1-2:5-5")
227
213
  end
214
+
215
+ def test_advancing_namespace_stack_from_enhancement
216
+ Class.new(Enhancement) do
217
+ def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
218
+ owner = @listener.current_owner
219
+ return unless owner
220
+
221
+ case call_node.name
222
+ when :class_methods
223
+ @listener.add_module("ClassMethods", call_node.location, call_node.location)
224
+ when :extend
225
+ arguments = call_node.arguments&.arguments
226
+ return unless arguments
227
+
228
+ arguments.each do |node|
229
+ next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
230
+
231
+ module_name = node.full_name
232
+ next unless module_name == "ActiveSupport::Concern"
233
+
234
+ @listener.register_included_hook do |index, base|
235
+ class_methods_name = "#{owner.name}::ClassMethods"
236
+
237
+ if index.indexed?(class_methods_name)
238
+ singleton = index.existing_or_new_singleton_class(base.name)
239
+ singleton.mixin_operations << Entry::Include.new(class_methods_name)
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ def on_call_node_leave(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
247
+ return unless call_node.name == :class_methods
248
+
249
+ @listener.pop_namespace_stack
250
+ end
251
+ end
252
+
253
+ index(<<~RUBY)
254
+ module ActiveSupport
255
+ module Concern
256
+ end
257
+ end
258
+
259
+ module MyConcern
260
+ extend ActiveSupport::Concern
261
+
262
+ class_methods do
263
+ def foo; end
264
+ end
265
+ end
266
+
267
+ class User
268
+ include MyConcern
269
+ end
270
+ RUBY
271
+
272
+ assert_equal(
273
+ [
274
+ "User::<Class:User>",
275
+ "MyConcern::ClassMethods",
276
+ "Object::<Class:Object>",
277
+ "BasicObject::<Class:BasicObject>",
278
+ "Class",
279
+ "Module",
280
+ "Object",
281
+ "Kernel",
282
+ "BasicObject",
283
+ ],
284
+ @index.linearized_ancestors_of("User::<Class:User>"),
285
+ )
286
+
287
+ refute_nil(@index.resolve_method("foo", "User::<Class:User>"))
288
+ end
289
+
290
+ def test_creating_anonymous_classes_from_enhancement
291
+ Class.new(Enhancement) do
292
+ def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
293
+ case call_node.name
294
+ when :context
295
+ arguments = call_node.arguments&.arguments
296
+ first_argument = arguments&.first
297
+ return unless first_argument.is_a?(Prism::StringNode)
298
+
299
+ @listener.add_class(
300
+ "<RSpec:#{first_argument.content}>",
301
+ call_node.location,
302
+ first_argument.location,
303
+ )
304
+ when :subject
305
+ @listener.add_method("subject", call_node.location, [])
306
+ end
307
+ end
308
+
309
+ def on_call_node_leave(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
310
+ return unless call_node.name == :context
311
+
312
+ @listener.pop_namespace_stack
313
+ end
314
+ end
315
+
316
+ index(<<~RUBY)
317
+ context "does something" do
318
+ subject { call_whatever }
319
+ end
320
+ RUBY
321
+
322
+ refute_nil(@index.resolve_method("subject", "<RSpec:does something>"))
323
+ end
228
324
  end
229
325
  end
@@ -1672,6 +1672,38 @@ module RubyIndexer
1672
1672
  )
1673
1673
  end
1674
1674
 
1675
+ def test_extend_self
1676
+ @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1677
+ module Foo
1678
+ def bar
1679
+ end
1680
+
1681
+ extend self
1682
+
1683
+ def baz
1684
+ end
1685
+ end
1686
+ RUBY
1687
+
1688
+ ["bar", "baz"].product(["Foo", "Foo::<Class:Foo>"]).each do |method, receiver|
1689
+ entry = @index.resolve_method(method, receiver)&.first
1690
+ refute_nil(entry)
1691
+ assert_equal(method, T.must(entry).name)
1692
+ end
1693
+
1694
+ assert_equal(
1695
+ [
1696
+ "Foo::<Class:Foo>",
1697
+ "Foo",
1698
+ "Module",
1699
+ "Object",
1700
+ "Kernel",
1701
+ "BasicObject",
1702
+ ],
1703
+ @index.linearized_ancestors_of("Foo::<Class:Foo>"),
1704
+ )
1705
+ end
1706
+
1675
1707
  def test_linearizing_singleton_ancestors
1676
1708
  @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1677
1709
  module First
@@ -2023,5 +2055,12 @@ module RubyIndexer
2023
2055
  ),
2024
2056
  )
2025
2057
  end
2058
+
2059
+ def test_prevents_multiple_calls_to_index_all
2060
+ # For this test class, `index_all` is already called once in `setup`.
2061
+ assert_raises(Index::IndexNotEmptyError) do
2062
+ @index.index_all
2063
+ end
2064
+ end
2026
2065
  end
2027
2066
  end
@@ -141,7 +141,7 @@ module RubyIndexer
141
141
  # The first entry points to the location of the module_function call
142
142
  assert_equal("Test", first_entry.owner.name)
143
143
  assert_instance_of(Entry::Module, first_entry.owner)
144
- assert_equal(Entry::Visibility::PRIVATE, first_entry.visibility)
144
+ assert_predicate(first_entry, :private?)
145
145
  # The second entry points to the public singleton method
146
146
  assert_equal("Test::<Class:Test>", second_entry.owner.name)
147
147
  assert_instance_of(Entry::SingletonClass, second_entry.owner)
@@ -149,6 +149,39 @@ module RubyIndexer
149
149
  end
150
150
  end
151
151
 
152
+ def test_comments_documentation
153
+ index(<<~RUBY)
154
+ # Documentation for Foo
155
+
156
+ class Foo
157
+ # ####################
158
+ # Documentation for bar
159
+ # ####################
160
+ #
161
+ def bar
162
+ end
163
+
164
+ # test
165
+
166
+ # Documentation for baz
167
+ def baz; end
168
+ def ban; end
169
+ end
170
+ RUBY
171
+
172
+ foo_comment = @index["Foo"].first.comments
173
+ assert_equal("Documentation for Foo", foo_comment)
174
+
175
+ bar_comment = @index["bar"].first.comments
176
+ assert_equal("####################\nDocumentation for bar\n####################\n", bar_comment)
177
+
178
+ baz_comment = @index["baz"].first.comments
179
+ assert_equal("Documentation for baz", baz_comment)
180
+
181
+ ban_comment = @index["ban"].first.comments
182
+ assert_empty(ban_comment)
183
+ end
184
+
152
185
  def test_method_with_parameters
153
186
  index(<<~RUBY)
154
187
  class Foo
@@ -10,7 +10,8 @@ module RubyLsp
10
10
  sig { returns(T::Boolean) }
11
11
  attr_reader :supports_watching_files,
12
12
  :supports_request_delegation,
13
- :window_show_message_supports_extra_properties
13
+ :window_show_message_supports_extra_properties,
14
+ :supports_progress
14
15
 
15
16
  sig { void }
16
17
  def initialize
@@ -28,6 +29,9 @@ module RubyLsp
28
29
 
29
30
  # Which resource operations the editor supports, like renaming files
30
31
  @supported_resource_operations = T.let([], T::Array[String])
32
+
33
+ # The editor supports displaying progress requests
34
+ @supports_progress = T.let(false, T::Boolean)
31
35
  end
32
36
 
33
37
  sig { params(capabilities: T::Hash[Symbol, T.untyped]).void }
@@ -50,6 +54,9 @@ module RubyLsp
50
54
  :additionalPropertiesSupport,
51
55
  )
52
56
  @window_show_message_supports_extra_properties = supports_additional_properties || false
57
+
58
+ progress = capabilities.dig(:window, :workDoneProgress)
59
+ @supports_progress = progress if progress
53
60
  end
54
61
 
55
62
  sig { returns(T::Boolean) }
@@ -21,7 +21,7 @@ module RubyLsp
21
21
  attr_reader :encoding
22
22
 
23
23
  sig { returns(T::Boolean) }
24
- attr_reader :experimental_features, :top_level_bundle
24
+ attr_reader :top_level_bundle
25
25
 
26
26
  sig { returns(TypeInferrer) }
27
27
  attr_reader :type_inferrer
@@ -40,7 +40,6 @@ module RubyLsp
40
40
  @has_type_checker = T.let(true, T::Boolean)
41
41
  @index = T.let(RubyIndexer::Index.new, RubyIndexer::Index)
42
42
  @supported_formatters = T.let({}, T::Hash[String, Requests::Support::Formatter])
43
- @experimental_features = T.let(false, T::Boolean)
44
43
  @type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
45
44
  @addon_settings = T.let({}, T::Hash[String, T.untyped])
46
45
  @top_level_bundle = T.let(
@@ -53,6 +52,7 @@ module RubyLsp
53
52
  T::Boolean,
54
53
  )
55
54
  @client_capabilities = T.let(ClientCapabilities.new, ClientCapabilities)
55
+ @enabled_feature_flags = T.let({}, T::Hash[Symbol, T::Boolean])
56
56
  end
57
57
 
58
58
  sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
@@ -130,7 +130,6 @@ module RubyLsp
130
130
  end
131
131
  @index.configuration.encoding = @encoding
132
132
 
133
- @experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
134
133
  @client_capabilities.apply_client_capabilities(options[:capabilities]) if options[:capabilities]
135
134
 
136
135
  addon_settings = options.dig(:initializationOptions, :addonSettings)
@@ -139,9 +138,17 @@ module RubyLsp
139
138
  @addon_settings.merge!(addon_settings)
140
139
  end
141
140
 
141
+ enabled_flags = options.dig(:initializationOptions, :enabledFeatureFlags)
142
+ @enabled_feature_flags = enabled_flags if enabled_flags
143
+
142
144
  notifications
143
145
  end
144
146
 
147
+ sig { params(flag: Symbol).returns(T.nilable(T::Boolean)) }
148
+ def enabled_feature?(flag)
149
+ @enabled_feature_flags[flag]
150
+ end
151
+
145
152
  sig { returns(String) }
146
153
  def workspace_path
147
154
  T.must(@workspace_uri.to_standardized_path)
@@ -159,6 +166,11 @@ module RubyLsp
159
166
  end
160
167
  end
161
168
 
169
+ sig { returns(T::Boolean) }
170
+ def supports_watching_files
171
+ @client_capabilities.supports_watching_files
172
+ end
173
+
162
174
  private
163
175
 
164
176
  sig { params(direct_dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(String) }
@@ -21,6 +21,7 @@ require "prism"
21
21
  require "prism/visitor"
22
22
  require "language_server-protocol"
23
23
  require "rbs"
24
+ require "fileutils"
24
25
 
25
26
  require "ruby-lsp"
26
27
  require "ruby_lsp/base_server"
@@ -92,14 +92,15 @@ module RubyLsp
92
92
  target: T.nilable(Prism::Node),
93
93
  parent: T.nilable(Prism::Node),
94
94
  dispatcher: Prism::Dispatcher,
95
+ position: T::Hash[Symbol, T.untyped],
95
96
  ).void
96
97
  end
97
- def initialize(response_builder, target, parent, dispatcher)
98
+ def initialize(response_builder, target, parent, dispatcher, position)
98
99
  @response_builder = response_builder
99
100
 
100
101
  return unless target && parent
101
102
 
102
- highlight_target =
103
+ highlight_target, highlight_target_value =
103
104
  case target
104
105
  when Prism::GlobalVariableReadNode, Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode,
105
106
  Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableTargetNode, Prism::GlobalVariableWriteNode,
@@ -116,13 +117,17 @@ module RubyLsp
116
117
  Prism::CallNode, Prism::BlockParameterNode, Prism::RequiredKeywordParameterNode,
117
118
  Prism::RequiredKeywordParameterNode, Prism::KeywordRestParameterNode, Prism::OptionalParameterNode,
118
119
  Prism::RequiredParameterNode, Prism::RestParameterNode
120
+ [target, node_value(target)]
121
+ when Prism::ModuleNode, Prism::ClassNode, Prism::SingletonClassNode, Prism::DefNode, Prism::CaseNode,
122
+ Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::IfNode, Prism::UnlessNode
119
123
  target
120
124
  end
121
125
 
122
126
  @target = T.let(highlight_target, T.nilable(Prism::Node))
123
- @target_value = T.let(node_value(highlight_target), T.nilable(String))
127
+ @target_value = T.let(highlight_target_value, T.nilable(String))
128
+ @target_position = position
124
129
 
125
- if @target && @target_value
130
+ if @target
126
131
  dispatcher.register(
127
132
  self,
128
133
  :on_call_node_enter,
@@ -172,6 +177,13 @@ module RubyLsp
172
177
  :on_global_variable_or_write_node_enter,
173
178
  :on_global_variable_and_write_node_enter,
174
179
  :on_global_variable_operator_write_node_enter,
180
+ :on_singleton_class_node_enter,
181
+ :on_case_node_enter,
182
+ :on_while_node_enter,
183
+ :on_until_node_enter,
184
+ :on_for_node_enter,
185
+ :on_if_node_enter,
186
+ :on_unless_node_enter,
175
187
  )
176
188
  end
177
189
  end
@@ -189,6 +201,8 @@ module RubyLsp
189
201
 
190
202
  sig { params(node: Prism::DefNode).void }
191
203
  def on_def_node_enter(node)
204
+ add_matching_end_highlights(node.def_keyword_loc, node.end_keyword_loc) if @target.is_a?(Prism::DefNode)
205
+
192
206
  return unless matches?(node, [Prism::CallNode, Prism::DefNode])
193
207
 
194
208
  add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc)
@@ -252,6 +266,8 @@ module RubyLsp
252
266
 
253
267
  sig { params(node: Prism::ClassNode).void }
254
268
  def on_class_node_enter(node)
269
+ add_matching_end_highlights(node.class_keyword_loc, node.end_keyword_loc) if @target.is_a?(Prism::ClassNode)
270
+
255
271
  return unless matches?(node, CONSTANT_NODES + CONSTANT_PATH_NODES + [Prism::ClassNode])
256
272
 
257
273
  add_highlight(Constant::DocumentHighlightKind::WRITE, node.constant_path.location)
@@ -259,6 +275,8 @@ module RubyLsp
259
275
 
260
276
  sig { params(node: Prism::ModuleNode).void }
261
277
  def on_module_node_enter(node)
278
+ add_matching_end_highlights(node.module_keyword_loc, node.end_keyword_loc) if @target.is_a?(Prism::ModuleNode)
279
+
262
280
  return unless matches?(node, CONSTANT_NODES + CONSTANT_PATH_NODES + [Prism::ModuleNode])
263
281
 
264
282
  add_highlight(Constant::DocumentHighlightKind::WRITE, node.constant_path.location)
@@ -511,6 +529,55 @@ module RubyLsp
511
529
  add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc)
512
530
  end
513
531
 
532
+ sig { params(node: Prism::SingletonClassNode).void }
533
+ def on_singleton_class_node_enter(node)
534
+ return unless @target.is_a?(Prism::SingletonClassNode)
535
+
536
+ add_matching_end_highlights(node.class_keyword_loc, node.end_keyword_loc)
537
+ end
538
+
539
+ sig { params(node: Prism::CaseNode).void }
540
+ def on_case_node_enter(node)
541
+ return unless @target.is_a?(Prism::CaseNode)
542
+
543
+ add_matching_end_highlights(node.case_keyword_loc, node.end_keyword_loc)
544
+ end
545
+
546
+ sig { params(node: Prism::WhileNode).void }
547
+ def on_while_node_enter(node)
548
+ return unless @target.is_a?(Prism::WhileNode)
549
+
550
+ add_matching_end_highlights(node.keyword_loc, node.closing_loc)
551
+ end
552
+
553
+ sig { params(node: Prism::UntilNode).void }
554
+ def on_until_node_enter(node)
555
+ return unless @target.is_a?(Prism::UntilNode)
556
+
557
+ add_matching_end_highlights(node.keyword_loc, node.closing_loc)
558
+ end
559
+
560
+ sig { params(node: Prism::ForNode).void }
561
+ def on_for_node_enter(node)
562
+ return unless @target.is_a?(Prism::ForNode)
563
+
564
+ add_matching_end_highlights(node.for_keyword_loc, node.end_keyword_loc)
565
+ end
566
+
567
+ sig { params(node: Prism::IfNode).void }
568
+ def on_if_node_enter(node)
569
+ return unless @target.is_a?(Prism::IfNode)
570
+
571
+ add_matching_end_highlights(node.if_keyword_loc, node.end_keyword_loc)
572
+ end
573
+
574
+ sig { params(node: Prism::UnlessNode).void }
575
+ def on_unless_node_enter(node)
576
+ return unless @target.is_a?(Prism::UnlessNode)
577
+
578
+ add_matching_end_highlights(node.keyword_loc, node.end_keyword_loc)
579
+ end
580
+
514
581
  private
515
582
 
516
583
  sig { params(node: Prism::Node, classes: T::Array[T.class_of(Prism::Node)]).returns(T.nilable(T::Boolean)) }
@@ -550,6 +617,26 @@ module RubyLsp
550
617
  node.constant_path.slice
551
618
  end
552
619
  end
620
+
621
+ sig { params(keyword_loc: T.nilable(Prism::Location), end_loc: T.nilable(Prism::Location)).void }
622
+ def add_matching_end_highlights(keyword_loc, end_loc)
623
+ return unless keyword_loc && end_loc && end_loc.length.positive?
624
+ return unless covers_target_position?(keyword_loc) || covers_target_position?(end_loc)
625
+
626
+ add_highlight(Constant::DocumentHighlightKind::TEXT, keyword_loc)
627
+ add_highlight(Constant::DocumentHighlightKind::TEXT, end_loc)
628
+ end
629
+
630
+ sig { params(location: Prism::Location).returns(T::Boolean) }
631
+ def covers_target_position?(location)
632
+ start_line = location.start_line - 1
633
+ end_line = location.end_line - 1
634
+ start_covered = start_line < @target_position[:line] ||
635
+ (start_line == @target_position[:line] && location.start_column <= @target_position[:character])
636
+ end_covered = end_line > @target_position[:line] ||
637
+ (end_line == @target_position[:line] && location.end_column >= @target_position[:character])
638
+ start_covered && end_covered
639
+ end
553
640
  end
554
641
  end
555
642
  end
@@ -118,6 +118,8 @@ module RubyLsp
118
118
  Prism::InstanceVariableWriteNode
119
119
 
120
120
  !covers_position?(target.name_loc, position)
121
+ when Prism::CallNode
122
+ !covers_position?(target.message_loc, position)
121
123
  else
122
124
  false
123
125
  end
@@ -38,7 +38,13 @@ module RubyLsp
38
38
  ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight].new,
39
39
  ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight],
40
40
  )
41
- Listeners::DocumentHighlight.new(@response_builder, node_context.node, node_context.parent, dispatcher)
41
+ Listeners::DocumentHighlight.new(
42
+ @response_builder,
43
+ node_context.node,
44
+ node_context.parent,
45
+ dispatcher,
46
+ position,
47
+ )
42
48
  end
43
49
 
44
50
  sig { override.returns(T::Array[Interface::DocumentHighlight]) }
@@ -103,6 +103,8 @@ module RubyLsp
103
103
  Prism::GlobalVariableOrWriteNode,
104
104
  Prism::GlobalVariableWriteNode
105
105
  !covers_position?(target.name_loc, position)
106
+ when Prism::CallNode
107
+ !covers_position?(target.message_loc, position)
106
108
  else
107
109
  false
108
110
  end
@@ -15,6 +15,7 @@ rescue LoadError
15
15
  return
16
16
  end
17
17
 
18
+ # Remember to update the version in the documentation (usage/dependency-compatibility section) if you change this
18
19
  # Ensure that RuboCop is at least version 1.4.0
19
20
  begin
20
21
  gem("rubocop", ">= 1.4.0")