ruby-lsp 0.16.6 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +21 -4
  5. data/exe/ruby-lsp-check +1 -3
  6. data/exe/ruby-lsp-doctor +1 -4
  7. data/lib/core_ext/uri.rb +3 -0
  8. data/lib/ruby_indexer/lib/ruby_indexer/{collector.rb → declaration_listener.rb} +258 -140
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +101 -12
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +187 -12
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -1
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +106 -10
  13. data/lib/ruby_indexer/test/configuration_test.rb +4 -5
  14. data/lib/ruby_indexer/test/constant_test.rb +11 -8
  15. data/lib/ruby_indexer/test/index_test.rb +528 -0
  16. data/lib/ruby_indexer/test/instance_variables_test.rb +131 -0
  17. data/lib/ruby_indexer/test/method_test.rb +93 -0
  18. data/lib/ruby_indexer/test/test_case.rb +3 -1
  19. data/lib/ruby_lsp/addon.rb +8 -8
  20. data/lib/ruby_lsp/document.rb +3 -3
  21. data/lib/ruby_lsp/internal.rb +1 -0
  22. data/lib/ruby_lsp/listeners/code_lens.rb +11 -0
  23. data/lib/ruby_lsp/listeners/completion.rb +144 -51
  24. data/lib/ruby_lsp/listeners/definition.rb +77 -12
  25. data/lib/ruby_lsp/listeners/document_highlight.rb +1 -1
  26. data/lib/ruby_lsp/listeners/document_link.rb +1 -1
  27. data/lib/ruby_lsp/listeners/hover.rb +60 -6
  28. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +59 -3
  29. data/lib/ruby_lsp/listeners/signature_help.rb +4 -4
  30. data/lib/ruby_lsp/node_context.rb +28 -0
  31. data/lib/ruby_lsp/requests/code_action_resolve.rb +73 -2
  32. data/lib/ruby_lsp/requests/code_actions.rb +16 -15
  33. data/lib/ruby_lsp/requests/completion.rb +22 -13
  34. data/lib/ruby_lsp/requests/completion_resolve.rb +26 -10
  35. data/lib/ruby_lsp/requests/definition.rb +21 -5
  36. data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
  37. data/lib/ruby_lsp/requests/hover.rb +5 -6
  38. data/lib/ruby_lsp/requests/on_type_formatting.rb +8 -4
  39. data/lib/ruby_lsp/requests/signature_help.rb +3 -3
  40. data/lib/ruby_lsp/requests/support/common.rb +20 -1
  41. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -1
  42. data/lib/ruby_lsp/server.rb +10 -4
  43. metadata +10 -8
@@ -16,6 +16,17 @@ module RubyIndexer
16
16
  assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
17
17
  end
18
18
 
19
+ def test_conditional_method
20
+ index(<<~RUBY)
21
+ class Foo
22
+ def bar
23
+ end if condition
24
+ end
25
+ RUBY
26
+
27
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
28
+ end
29
+
19
30
  def test_singleton_method_using_self_receiver
20
31
  index(<<~RUBY)
21
32
  class Foo
@@ -38,6 +49,65 @@ module RubyIndexer
38
49
  assert_no_entry("bar")
39
50
  end
40
51
 
52
+ def test_method_under_dynamic_class_or_module
53
+ index(<<~RUBY)
54
+ module Foo
55
+ class self::Bar
56
+ def bar
57
+ end
58
+ end
59
+ end
60
+
61
+ module Bar
62
+ def bar
63
+ end
64
+ end
65
+ RUBY
66
+
67
+ assert_equal(2, @index["bar"].length)
68
+ first_entry = T.must(@index["bar"].first)
69
+ assert_equal("Foo::self::Bar", first_entry.owner.name)
70
+ second_entry = T.must(@index["bar"].last)
71
+ assert_equal("Bar", second_entry.owner.name)
72
+ end
73
+
74
+ def test_visibility_tracking
75
+ index(<<~RUBY)
76
+ private def foo
77
+ end
78
+
79
+ def bar; end
80
+
81
+ protected
82
+
83
+ def baz; end
84
+ RUBY
85
+
86
+ assert_entry("foo", Entry::InstanceMethod, "/fake/path/foo.rb:0-8:1-3", visibility: Entry::Visibility::PRIVATE)
87
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:3-0:3-12", visibility: Entry::Visibility::PUBLIC)
88
+ assert_entry("baz", Entry::InstanceMethod, "/fake/path/foo.rb:7-0:7-12", visibility: Entry::Visibility::PROTECTED)
89
+ end
90
+
91
+ def test_visibility_tracking_with_nested_class_or_modules
92
+ index(<<~RUBY)
93
+ class Foo
94
+ private
95
+
96
+ def foo; end
97
+
98
+ class Bar
99
+ def bar; end
100
+ end
101
+
102
+ def baz; end
103
+ end
104
+ RUBY
105
+
106
+ assert_entry("foo", Entry::InstanceMethod, "/fake/path/foo.rb:3-2:3-14", visibility: Entry::Visibility::PRIVATE)
107
+ assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:6-4:6-16", visibility: Entry::Visibility::PUBLIC)
108
+ assert_entry("baz", Entry::InstanceMethod, "/fake/path/foo.rb:9-2:9-14", visibility: Entry::Visibility::PRIVATE)
109
+ end
110
+
41
111
  def test_method_with_parameters
42
112
  index(<<~RUBY)
43
113
  class Foo
@@ -285,5 +355,28 @@ module RubyIndexer
285
355
 
286
356
  assert_no_entry("bar")
287
357
  end
358
+
359
+ def test_properly_tracks_multiple_levels_of_nesting
360
+ index(<<~RUBY)
361
+ module Foo
362
+ def first; end
363
+
364
+ module Bar
365
+ def second; end
366
+ end
367
+
368
+ def third; end
369
+ end
370
+ RUBY
371
+
372
+ entry = T.must(@index["first"]&.first)
373
+ assert_equal("Foo", T.must(entry.owner).name)
374
+
375
+ entry = T.must(@index["second"]&.first)
376
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
377
+
378
+ entry = T.must(@index["third"]&.first)
379
+ assert_equal("Foo", T.must(entry.owner).name)
380
+ end
288
381
  end
289
382
  end
@@ -15,7 +15,7 @@ module RubyIndexer
15
15
  @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), source)
16
16
  end
17
17
 
18
- def assert_entry(expected_name, type, expected_location)
18
+ def assert_entry(expected_name, type, expected_location, visibility: nil)
19
19
  entries = @index[expected_name]
20
20
  refute_empty(entries, "Expected #{expected_name} to be indexed")
21
21
 
@@ -28,6 +28,8 @@ module RubyIndexer
28
28
  ":#{location.end_line - 1}-#{location.end_column}"
29
29
 
30
30
  assert_equal(expected_location, location_string)
31
+
32
+ assert_equal(visibility, entry.visibility) if visibility
31
33
  end
32
34
 
33
35
  def refute_entry(expected_name)
@@ -99,8 +99,8 @@ module RubyLsp
99
99
  end
100
100
 
101
101
  sig { returns(String) }
102
- def backtraces
103
- @errors.filter_map(&:backtrace).join("\n\n")
102
+ def errors_details
103
+ @errors.map(&:full_message).join("\n\n")
104
104
  end
105
105
 
106
106
  # Each addon should implement `MyAddon#activate` and use to perform any sort of initialization, such as
@@ -131,11 +131,11 @@ module RubyLsp
131
131
  sig do
132
132
  overridable.params(
133
133
  response_builder: ResponseBuilders::Hover,
134
- nesting: T::Array[String],
134
+ node_context: NodeContext,
135
135
  dispatcher: Prism::Dispatcher,
136
136
  ).void
137
137
  end
138
- def create_hover_listener(response_builder, nesting, dispatcher); end
138
+ def create_hover_listener(response_builder, node_context, dispatcher); end
139
139
 
140
140
  # Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
141
141
  sig do
@@ -159,21 +159,21 @@ module RubyLsp
159
159
  overridable.params(
160
160
  response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
161
161
  uri: URI::Generic,
162
- nesting: T::Array[String],
162
+ node_context: NodeContext,
163
163
  dispatcher: Prism::Dispatcher,
164
164
  ).void
165
165
  end
166
- def create_definition_listener(response_builder, uri, nesting, dispatcher); end
166
+ def create_definition_listener(response_builder, uri, node_context, dispatcher); end
167
167
 
168
168
  # Creates a new Completion listener. This method is invoked on every Completion request
169
169
  sig do
170
170
  overridable.params(
171
171
  response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
172
- nesting: T::Array[String],
172
+ node_context: NodeContext,
173
173
  dispatcher: Prism::Dispatcher,
174
174
  uri: URI::Generic,
175
175
  ).void
176
176
  end
177
- def create_completion_listener(response_builder, nesting, dispatcher, uri); end
177
+ def create_completion_listener(response_builder, node_context, dispatcher, uri); end
178
178
  end
179
179
  end
@@ -110,7 +110,7 @@ module RubyLsp
110
110
  params(
111
111
  position: T::Hash[Symbol, T.untyped],
112
112
  node_types: T::Array[T.class_of(Prism::Node)],
113
- ).returns([T.nilable(Prism::Node), T.nilable(Prism::Node), T::Array[String]])
113
+ ).returns(NodeContext)
114
114
  end
115
115
  def locate_node(position, node_types: [])
116
116
  locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
@@ -121,7 +121,7 @@ module RubyLsp
121
121
  node: Prism::Node,
122
122
  char_position: Integer,
123
123
  node_types: T::Array[T.class_of(Prism::Node)],
124
- ).returns([T.nilable(Prism::Node), T.nilable(Prism::Node), T::Array[String]])
124
+ ).returns(NodeContext)
125
125
  end
126
126
  def locate(node, char_position, node_types: [])
127
127
  queue = T.let(node.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
@@ -170,7 +170,7 @@ module RubyLsp
170
170
  end
171
171
  end
172
172
 
173
- [closest, parent, nesting.map { |n| n.constant_path.location.slice }]
173
+ NodeContext.new(closest, parent, nesting.map { |n| n.constant_path.location.slice })
174
174
  end
175
175
 
176
176
  sig { returns(T::Boolean) }
@@ -29,6 +29,7 @@ require "ruby_lsp/global_state"
29
29
  require "ruby_lsp/server"
30
30
  require "ruby_lsp/requests"
31
31
  require "ruby_lsp/response_builders"
32
+ require "ruby_lsp/node_context"
32
33
  require "ruby_lsp/document"
33
34
  require "ruby_lsp/ruby_document"
34
35
  require "ruby_lsp/store"
@@ -42,6 +42,8 @@ module RubyLsp
42
42
  @group_stack = T.let([], T::Array[String])
43
43
  @group_id = T.let(1, Integer)
44
44
  @group_id_stack = T.let([], T::Array[Integer])
45
+ # We want to avoid adding code lenses for nested definitions
46
+ @def_depth = T.let(0, Integer)
45
47
 
46
48
  dispatcher.register(
47
49
  self,
@@ -50,6 +52,7 @@ module RubyLsp
50
52
  :on_module_node_enter,
51
53
  :on_module_node_leave,
52
54
  :on_def_node_enter,
55
+ :on_def_node_leave,
53
56
  :on_call_node_enter,
54
57
  :on_call_node_leave,
55
58
  )
@@ -88,6 +91,9 @@ module RubyLsp
88
91
 
89
92
  sig { params(node: Prism::DefNode).void }
90
93
  def on_def_node_enter(node)
94
+ @def_depth += 1
95
+ return if @def_depth > 1
96
+
91
97
  class_name = @group_stack.last
92
98
  return unless class_name&.end_with?("Test")
93
99
 
@@ -105,6 +111,11 @@ module RubyLsp
105
111
  end
106
112
  end
107
113
 
114
+ sig { params(node: Prism::DefNode).void }
115
+ def on_def_node_leave(node)
116
+ @def_depth -= 1
117
+ end
118
+
108
119
  sig { params(node: Prism::ModuleNode).void }
109
120
  def on_module_node_enter(node)
110
121
  if (path = namespace_constant_name(node))
@@ -11,17 +11,17 @@ module RubyLsp
11
11
  params(
12
12
  response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
13
13
  global_state: GlobalState,
14
- nesting: T::Array[String],
14
+ node_context: NodeContext,
15
15
  typechecker_enabled: T::Boolean,
16
16
  dispatcher: Prism::Dispatcher,
17
17
  uri: URI::Generic,
18
18
  ).void
19
19
  end
20
- def initialize(response_builder, global_state, nesting, typechecker_enabled, dispatcher, uri) # rubocop:disable Metrics/ParameterLists
20
+ def initialize(response_builder, global_state, node_context, typechecker_enabled, dispatcher, uri) # rubocop:disable Metrics/ParameterLists
21
21
  @response_builder = response_builder
22
22
  @global_state = global_state
23
23
  @index = T.let(global_state.index, RubyIndexer::Index)
24
- @nesting = nesting
24
+ @node_context = node_context
25
25
  @typechecker_enabled = typechecker_enabled
26
26
  @uri = uri
27
27
 
@@ -30,6 +30,12 @@ module RubyLsp
30
30
  :on_constant_path_node_enter,
31
31
  :on_constant_read_node_enter,
32
32
  :on_call_node_enter,
33
+ :on_instance_variable_read_node_enter,
34
+ :on_instance_variable_write_node_enter,
35
+ :on_instance_variable_and_write_node_enter,
36
+ :on_instance_variable_operator_write_node_enter,
37
+ :on_instance_variable_or_write_node_enter,
38
+ :on_instance_variable_target_node_enter,
33
39
  )
34
40
  end
35
41
 
@@ -41,13 +47,13 @@ module RubyLsp
41
47
  name = constant_name(node)
42
48
  return if name.nil?
43
49
 
44
- candidates = @index.prefix_search(name, @nesting)
50
+ candidates = @index.prefix_search(name, @node_context.nesting)
45
51
  candidates.each do |entries|
46
52
  complete_name = T.must(entries.first).name
47
53
  @response_builder << build_entry_completion(
48
54
  complete_name,
49
55
  name,
50
- node,
56
+ range_from_location(node.location),
51
57
  entries,
52
58
  top_level?(complete_name),
53
59
  )
@@ -62,6 +68,83 @@ module RubyLsp
62
68
  name = constant_name(node)
63
69
  return if name.nil?
64
70
 
71
+ constant_path_completion(name, range_from_location(node.location))
72
+ end
73
+
74
+ sig { params(node: Prism::CallNode).void }
75
+ def on_call_node_enter(node)
76
+ receiver = node.receiver
77
+
78
+ # When writing `Foo::`, the AST assigns a method call node (because you can use that syntax to invoke singleton
79
+ # methods). However, in addition to providing method completion, we also need to show possible constant
80
+ # completions
81
+ if (receiver.is_a?(Prism::ConstantReadNode) || receiver.is_a?(Prism::ConstantPathNode)) &&
82
+ node.call_operator == "::"
83
+
84
+ name = constant_name(receiver)
85
+
86
+ if name
87
+ start_loc = node.location
88
+ end_loc = T.must(node.call_operator_loc)
89
+
90
+ constant_path_completion(
91
+ "#{name}::",
92
+ Interface::Range.new(
93
+ start: Interface::Position.new(line: start_loc.start_line - 1, character: start_loc.start_column),
94
+ end: Interface::Position.new(line: end_loc.end_line - 1, character: end_loc.end_column),
95
+ ),
96
+ )
97
+ return
98
+ end
99
+ end
100
+
101
+ name = node.message
102
+ return unless name
103
+
104
+ case name
105
+ when "require"
106
+ complete_require(node)
107
+ when "require_relative"
108
+ complete_require_relative(node)
109
+ else
110
+ complete_self_receiver_method(node, name) if !@typechecker_enabled && self_receiver?(node)
111
+ end
112
+ end
113
+
114
+ sig { params(node: Prism::InstanceVariableReadNode).void }
115
+ def on_instance_variable_read_node_enter(node)
116
+ handle_instance_variable_completion(node.name.to_s, node.location)
117
+ end
118
+
119
+ sig { params(node: Prism::InstanceVariableWriteNode).void }
120
+ def on_instance_variable_write_node_enter(node)
121
+ handle_instance_variable_completion(node.name.to_s, node.name_loc)
122
+ end
123
+
124
+ sig { params(node: Prism::InstanceVariableAndWriteNode).void }
125
+ def on_instance_variable_and_write_node_enter(node)
126
+ handle_instance_variable_completion(node.name.to_s, node.name_loc)
127
+ end
128
+
129
+ sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
130
+ def on_instance_variable_operator_write_node_enter(node)
131
+ handle_instance_variable_completion(node.name.to_s, node.name_loc)
132
+ end
133
+
134
+ sig { params(node: Prism::InstanceVariableOrWriteNode).void }
135
+ def on_instance_variable_or_write_node_enter(node)
136
+ handle_instance_variable_completion(node.name.to_s, node.name_loc)
137
+ end
138
+
139
+ sig { params(node: Prism::InstanceVariableTargetNode).void }
140
+ def on_instance_variable_target_node_enter(node)
141
+ handle_instance_variable_completion(node.name.to_s, node.location)
142
+ end
143
+
144
+ private
145
+
146
+ sig { params(name: String, range: Interface::Range).void }
147
+ def constant_path_completion(name, range)
65
148
  top_level_reference = if name.start_with?("::")
66
149
  name = name.delete_prefix("::")
67
150
  true
@@ -71,54 +154,63 @@ module RubyLsp
71
154
 
72
155
  # If we're trying to provide completion for an aliased namespace, we need to first discover it's real name in
73
156
  # order to find which possible constants match the desired search
74
- *namespace, incomplete_name = name.split("::")
75
- aliased_namespace = T.must(namespace).join("::")
76
- namespace_entries = @index.resolve(aliased_namespace, @nesting)
157
+ aliased_namespace = if name.end_with?("::")
158
+ name.delete_suffix("::")
159
+ else
160
+ *namespace, incomplete_name = name.split("::")
161
+ T.must(namespace).join("::")
162
+ end
163
+
164
+ nesting = @node_context.nesting
165
+ namespace_entries = @index.resolve(aliased_namespace, nesting)
77
166
  return unless namespace_entries
78
167
 
79
168
  real_namespace = @index.follow_aliased_namespace(T.must(namespace_entries.first).name)
80
169
 
81
170
  candidates = @index.prefix_search(
82
171
  "#{real_namespace}::#{incomplete_name}",
83
- top_level_reference ? [] : @nesting,
172
+ top_level_reference ? [] : nesting,
84
173
  )
85
174
  candidates.each do |entries|
86
175
  # The only time we may have a private constant reference from outside of the namespace is if we're dealing
87
176
  # with ConstantPath and the entry name doesn't start with the current nesting
88
177
  first_entry = T.must(entries.first)
89
- next if first_entry.visibility == :private && !first_entry.name.start_with?("#{@nesting}::")
90
-
91
- constant_name = T.must(first_entry.name.split("::").last)
178
+ next if first_entry.private? && !first_entry.name.start_with?("#{nesting}::")
92
179
 
180
+ constant_name = first_entry.name.delete_prefix("#{real_namespace}::")
93
181
  full_name = aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}"
94
182
 
95
183
  @response_builder << build_entry_completion(
96
184
  full_name,
97
185
  name,
98
- node,
186
+ range,
99
187
  entries,
100
188
  top_level_reference || top_level?(T.must(entries.first).name),
101
189
  )
102
190
  end
103
191
  end
104
192
 
105
- sig { params(node: Prism::CallNode).void }
106
- def on_call_node_enter(node)
107
- name = node.message
108
- return unless name
109
-
110
- case name
111
- when "require"
112
- complete_require(node)
113
- when "require_relative"
114
- complete_require_relative(node)
115
- else
116
- complete_self_receiver_method(node, name) if !@typechecker_enabled && self_receiver?(node)
193
+ sig { params(name: String, location: Prism::Location).void }
194
+ def handle_instance_variable_completion(name, location)
195
+ @index.instance_variable_completion_candidates(name, @node_context.fully_qualified_name).each do |entry|
196
+ variable_name = entry.name
197
+
198
+ @response_builder << Interface::CompletionItem.new(
199
+ label: variable_name,
200
+ text_edit: Interface::TextEdit.new(
201
+ range: range_from_location(location),
202
+ new_text: variable_name,
203
+ ),
204
+ kind: Constant::CompletionItemKind::FIELD,
205
+ data: {
206
+ owner_name: entry.owner&.name,
207
+ },
208
+ )
117
209
  end
210
+ rescue RubyIndexer::Index::NonExistingNamespaceError
211
+ # If by any chance we haven't indexed the owner, then there's no way to find the right declaration
118
212
  end
119
213
 
120
- private
121
-
122
214
  sig { params(node: Prism::CallNode).void }
123
215
  def complete_require(node)
124
216
  arguments_node = node.arguments
@@ -166,15 +258,12 @@ module RubyLsp
166
258
 
167
259
  sig { params(node: Prism::CallNode, name: String).void }
168
260
  def complete_self_receiver_method(node, name)
169
- receiver_entries = @index[@nesting.join("::")]
261
+ receiver_entries = @index[@node_context.fully_qualified_name]
170
262
  return unless receiver_entries
171
263
 
172
264
  receiver = T.must(receiver_entries.first)
173
265
 
174
- @index.prefix_search(name).each do |entries|
175
- entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name }
176
- next unless entry
177
-
266
+ @index.method_completion_candidates(name, receiver.name).each do |entry|
178
267
  @response_builder << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
179
268
  end
180
269
  end
@@ -223,12 +312,12 @@ module RubyLsp
223
312
  params(
224
313
  real_name: String,
225
314
  incomplete_name: String,
226
- node: Prism::Node,
315
+ range: Interface::Range,
227
316
  entries: T::Array[RubyIndexer::Entry],
228
317
  top_level: T::Boolean,
229
318
  ).returns(Interface::CompletionItem)
230
319
  end
231
- def build_entry_completion(real_name, incomplete_name, node, entries, top_level)
320
+ def build_entry_completion(real_name, incomplete_name, range, entries, top_level)
232
321
  first_entry = T.must(entries.first)
233
322
  kind = case first_entry
234
323
  when RubyIndexer::Entry::Class
@@ -263,20 +352,23 @@ module RubyLsp
263
352
  #
264
353
  # Foo::B # --> completion inserts `Bar` instead of `Foo::Bar`
265
354
  # end
266
- @nesting.each do |namespace|
267
- prefix = "#{namespace}::"
268
- shortened_name = insertion_text.delete_prefix(prefix)
269
-
270
- # If a different entry exists for the shortened name, then there's a conflict and we should not shorten it
271
- conflict_name = "#{@nesting.join("::")}::#{shortened_name}"
272
- break if real_name != conflict_name && @index[conflict_name]
273
-
274
- insertion_text = shortened_name
275
-
276
- # If the user is typing a fully qualified name `Foo::Bar::Baz`, then we should not use the short name (e.g.:
277
- # `Baz`) as filtering. So we only shorten the filter text if the user is not including the namespaces in their
278
- # typing
279
- filter_text.delete_prefix!(prefix) unless incomplete_name.start_with?(prefix)
355
+ nesting = @node_context.nesting
356
+ unless @node_context.fully_qualified_name.start_with?(incomplete_name)
357
+ nesting.each do |namespace|
358
+ prefix = "#{namespace}::"
359
+ shortened_name = insertion_text.delete_prefix(prefix)
360
+
361
+ # If a different entry exists for the shortened name, then there's a conflict and we should not shorten it
362
+ conflict_name = "#{@node_context.fully_qualified_name}::#{shortened_name}"
363
+ break if real_name != conflict_name && @index[conflict_name]
364
+
365
+ insertion_text = shortened_name
366
+
367
+ # If the user is typing a fully qualified name `Foo::Bar::Baz`, then we should not use the short name (e.g.:
368
+ # `Baz`) as filtering. So we only shorten the filter text if the user is not including the namespaces in
369
+ # their typing
370
+ filter_text.delete_prefix!(prefix) unless incomplete_name.start_with?(prefix)
371
+ end
280
372
  end
281
373
 
282
374
  # When using a top level constant reference (e.g.: `::Bar`), the editor includes the `::` as part of the filter.
@@ -286,7 +378,7 @@ module RubyLsp
286
378
  label: real_name,
287
379
  filter_text: filter_text,
288
380
  text_edit: Interface::TextEdit.new(
289
- range: range_from_node(node),
381
+ range: range,
290
382
  new_text: insertion_text,
291
383
  ),
292
384
  kind: kind,
@@ -309,8 +401,9 @@ module RubyLsp
309
401
  # ```
310
402
  sig { params(entry_name: String).returns(T::Boolean) }
311
403
  def top_level?(entry_name)
312
- @nesting.length.downto(0).each do |i|
313
- prefix = T.must(@nesting[0...i]).join("::")
404
+ nesting = @node_context.nesting
405
+ nesting.length.downto(0).each do |i|
406
+ prefix = T.must(nesting[0...i]).join("::")
314
407
  full_name = prefix.empty? ? entry_name : "#{prefix}::#{entry_name}"
315
408
  next if full_name == entry_name
316
409