ruby-lsp 0.10.1 → 0.11.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-check +1 -1
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +40 -5
  6. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +141 -5
  7. data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +66 -18
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +23 -0
  9. data/lib/ruby_indexer/test/configuration_test.rb +2 -0
  10. data/lib/ruby_indexer/test/constant_test.rb +202 -0
  11. data/lib/ruby_indexer/test/index_test.rb +20 -0
  12. data/lib/ruby_lsp/{extension.rb → addon.rb} +27 -25
  13. data/lib/ruby_lsp/check_docs.rb +7 -8
  14. data/lib/ruby_lsp/document.rb +35 -38
  15. data/lib/ruby_lsp/event_emitter.rb +239 -77
  16. data/lib/ruby_lsp/executor.rb +45 -55
  17. data/lib/ruby_lsp/internal.rb +2 -3
  18. data/lib/ruby_lsp/listener.rb +8 -7
  19. data/lib/ruby_lsp/parameter_scope.rb +33 -0
  20. data/lib/ruby_lsp/requests/base_request.rb +3 -3
  21. data/lib/ruby_lsp/requests/code_action_resolve.rb +14 -14
  22. data/lib/ruby_lsp/requests/code_lens.rb +39 -63
  23. data/lib/ruby_lsp/requests/completion.rb +54 -32
  24. data/lib/ruby_lsp/requests/definition.rb +30 -27
  25. data/lib/ruby_lsp/requests/diagnostics.rb +26 -3
  26. data/lib/ruby_lsp/requests/document_highlight.rb +18 -19
  27. data/lib/ruby_lsp/requests/document_link.rb +50 -9
  28. data/lib/ruby_lsp/requests/document_symbol.rb +82 -75
  29. data/lib/ruby_lsp/requests/folding_ranges.rb +199 -222
  30. data/lib/ruby_lsp/requests/formatting.rb +5 -6
  31. data/lib/ruby_lsp/requests/hover.rb +33 -22
  32. data/lib/ruby_lsp/requests/inlay_hints.rb +2 -3
  33. data/lib/ruby_lsp/requests/selection_ranges.rb +65 -40
  34. data/lib/ruby_lsp/requests/semantic_highlighting.rb +187 -145
  35. data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -4
  36. data/lib/ruby_lsp/requests/support/annotation.rb +18 -17
  37. data/lib/ruby_lsp/requests/support/common.rb +17 -26
  38. data/lib/ruby_lsp/requests/support/dependency_detector.rb +67 -42
  39. data/lib/ruby_lsp/requests/support/highlight_target.rb +64 -45
  40. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -4
  41. data/lib/ruby_lsp/requests/support/selection_range.rb +5 -4
  42. data/lib/ruby_lsp/requests/support/sorbet.rb +2 -57
  43. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +7 -1
  44. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -1
  45. data/lib/ruby_lsp/server.rb +6 -44
  46. data/lib/ruby_lsp/utils.rb +2 -12
  47. metadata +11 -30
@@ -2,10 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
- require "syntax_tree"
6
5
  require "yarp"
7
6
  require "language_server-protocol"
8
- require "benchmark"
9
7
  require "bundler"
10
8
  require "uri"
11
9
  require "cgi"
@@ -14,11 +12,12 @@ require "ruby-lsp"
14
12
  require "ruby_indexer/ruby_indexer"
15
13
  require "core_ext/uri"
16
14
  require "ruby_lsp/utils"
15
+ require "ruby_lsp/parameter_scope"
17
16
  require "ruby_lsp/server"
18
17
  require "ruby_lsp/executor"
19
18
  require "ruby_lsp/event_emitter"
20
19
  require "ruby_lsp/requests"
21
20
  require "ruby_lsp/listener"
22
21
  require "ruby_lsp/store"
23
- require "ruby_lsp/extension"
22
+ require "ruby_lsp/addon"
24
23
  require "ruby_lsp/requests/support/rubocop_runner"
@@ -31,7 +31,7 @@ module RubyLsp
31
31
  def _response; end
32
32
  end
33
33
 
34
- # ExtensibleListener is an abstract class to be used by requests that accept extensions.
34
+ # ExtensibleListener is an abstract class to be used by requests that accept addons.
35
35
  class ExtensibleListener < Listener
36
36
  extend T::Sig
37
37
  extend T::Generic
@@ -48,7 +48,7 @@ module RubyLsp
48
48
  super
49
49
  @response_merged = T.let(false, T::Boolean)
50
50
  @external_listeners = T.let(
51
- Extension.extensions.filter_map do |ext|
51
+ Addon.addons.filter_map do |ext|
52
52
  initialize_external_listener(ext)
53
53
  end,
54
54
  T::Array[RubyLsp::Listener[ResponseType]],
@@ -56,7 +56,7 @@ module RubyLsp
56
56
  end
57
57
 
58
58
  # Merge responses from all external listeners into the base listener's response. We do this to return a single
59
- # response to the editor including the results of all extensions
59
+ # response to the editor including the results of all addons
60
60
  sig { void }
61
61
  def merge_external_listeners_responses!
62
62
  @external_listeners.each { |l| merge_response!(l) }
@@ -69,14 +69,15 @@ module RubyLsp
69
69
  end
70
70
 
71
71
  sig do
72
- abstract.params(extension: RubyLsp::Extension).returns(T.nilable(RubyLsp::Listener[ResponseType]))
72
+ abstract.params(addon: RubyLsp::Addon).returns(T.nilable(RubyLsp::Listener[ResponseType]))
73
73
  end
74
- def initialize_external_listener(extension); end
74
+ def initialize_external_listener(addon); end
75
75
 
76
- # Does nothing by default. Requests that accept extensions should override this method to define how to merge
77
- # responses coming from external listeners
76
+ # Does nothing by default. Requests that accept addons should override this method to define how to merge responses
77
+ # coming from external listeners
78
78
  sig { abstract.params(other: Listener[T.untyped]).returns(T.self_type) }
79
79
  def merge_response!(other)
80
80
  end
81
81
  end
82
+ private_constant(:ExtensibleListener)
82
83
  end
@@ -0,0 +1,33 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ class ParameterScope
6
+ extend T::Sig
7
+
8
+ sig { returns(T.nilable(ParameterScope)) }
9
+ attr_reader :parent
10
+
11
+ sig { params(parent: T.nilable(ParameterScope)).void }
12
+ def initialize(parent = nil)
13
+ @parent = parent
14
+ @parameters = T.let(Set.new, T::Set[Symbol])
15
+ end
16
+
17
+ sig { params(name: T.any(String, Symbol)).void }
18
+ def <<(name)
19
+ @parameters << name.to_sym
20
+ end
21
+
22
+ sig { params(name: T.any(Symbol, String)).returns(Symbol) }
23
+ def type_for(name)
24
+ parameter?(name) ? :parameter : :variable
25
+ end
26
+
27
+ sig { params(name: T.any(Symbol, String)).returns(T::Boolean) }
28
+ def parameter?(name)
29
+ sym = name.to_sym
30
+ @parameters.include?(sym) || (!@parent.nil? && @parent.parameter?(sym))
31
+ end
32
+ end
33
+ end
@@ -4,7 +4,7 @@
4
4
  module RubyLsp
5
5
  module Requests
6
6
  # :nodoc:
7
- class BaseRequest < SyntaxTree::Visitor
7
+ class BaseRequest < YARP::Visitor
8
8
  extend T::Sig
9
9
  extend T::Helpers
10
10
  include Support::Common
@@ -20,10 +20,10 @@ module RubyLsp
20
20
  sig { abstract.returns(Object) }
21
21
  def run; end
22
22
 
23
- # Syntax Tree implements `visit_all` using `map` instead of `each` for users who want to use the pattern
23
+ # YARP implements `visit_all` using `map` instead of `each` for users who want to use the pattern
24
24
  # `result = visitor.visit(tree)`. However, we don't use that pattern and should avoid producing a new array for
25
25
  # every single node visited
26
- sig { params(nodes: T::Array[T.nilable(SyntaxTree::Node)]).void }
26
+ sig { params(nodes: T::Array[T.nilable(YARP::Node)]).void }
27
27
  def visit_all(nodes)
28
28
  nodes.each { |node| visit(node) }
29
29
  end
@@ -43,11 +43,11 @@ module RubyLsp
43
43
 
44
44
  sig { override.returns(T.any(Interface::CodeAction, Error)) }
45
45
  def run
46
+ return Error::EmptySelection if @document.source.empty?
47
+
46
48
  source_range = @code_action.dig(:data, :range)
47
49
  return Error::EmptySelection if source_range[:start] == source_range[:end]
48
50
 
49
- return Error::InvalidTargetRange if @document.syntax_error?
50
-
51
51
  scanner = @document.create_scanner
52
52
  start_index = scanner.find_char_position(source_range[:start])
53
53
  end_index = scanner.find_char_position(source_range[:end])
@@ -55,26 +55,23 @@ module RubyLsp
55
55
 
56
56
  # Find the closest statements node, so that we place the refactor in a valid position
57
57
  closest_statements, parent_statements = @document
58
- .locate(T.must(@document.tree), start_index, node_types: [SyntaxTree::Statements])
59
- return Error::InvalidTargetRange if closest_statements.nil?
58
+ .locate(@document.tree, start_index, node_types: [YARP::StatementsNode, YARP::BlockNode])
59
+
60
+ return Error::InvalidTargetRange if closest_statements.nil? || closest_statements.child_nodes.compact.empty?
60
61
 
61
62
  # Find the node with the end line closest to the requested position, so that we can place the refactor
62
63
  # immediately after that closest node
63
- closest_node = closest_statements.child_nodes.compact.min_by do |node|
64
+ closest_node = T.must(closest_statements.child_nodes.compact.min_by do |node|
64
65
  distance = source_range.dig(:start, :line) - (node.location.end_line - 1)
65
66
  distance <= 0 ? Float::INFINITY : distance
66
- end
67
+ end)
67
68
 
68
- # Find the parent expression of the closest node
69
- parent_expression = parent_statements.child_nodes.compact.find do |node|
70
- loc = node.location
71
- loc.start_line - 1 <= source_range.dig(:start, :line) && loc.end_line - 1 >= source_range.dig(:end, :line)
72
- end if parent_statements
69
+ return Error::InvalidTargetRange if closest_node.is_a?(YARP::MissingNode)
73
70
 
74
71
  closest_node_loc = closest_node.location
75
72
  # If the parent expression is a single line block, then we have to extract it inside of the oneline block
76
- if parent_expression.is_a?(SyntaxTree::MethodAddBlock) &&
77
- parent_expression.location.start_line == parent_expression.location.end_line
73
+ if parent_statements.is_a?(YARP::BlockNode) &&
74
+ parent_statements.location.start_line == parent_statements.location.end_line
78
75
 
79
76
  variable_source = " #{NEW_VARIABLE_NAME} = #{extracted_source};"
80
77
  character = source_range.dig(:start, :character) - 1
@@ -105,7 +102,10 @@ module RubyLsp
105
102
  end: { line: target_line, character: indentation },
106
103
  }
107
104
 
108
- variable_source = if T.must(lines[target_line]).strip.empty?
105
+ line = lines[target_line]
106
+ return Error::InvalidTargetRange unless line
107
+
108
+ variable_source = if line.strip.empty?
109
109
  "\n#{" " * indentation}#{NEW_VARIABLE_NAME} = #{extracted_source}"
110
110
  else
111
111
  "#{NEW_VARIABLE_NAME} = #{extracted_source}\n#{" " * indentation}"
@@ -31,10 +31,9 @@ module RubyLsp
31
31
  sig { override.returns(ResponseType) }
32
32
  attr_reader :_response
33
33
 
34
- sig { params(uri: URI::Generic, emitter: EventEmitter, message_queue: Thread::Queue, test_library: String).void }
35
- def initialize(uri, emitter, message_queue, test_library)
34
+ sig { params(uri: URI::Generic, emitter: EventEmitter, message_queue: Thread::Queue).void }
35
+ def initialize(uri, emitter, message_queue)
36
36
  @uri = T.let(uri, URI::Generic)
37
- @test_library = T.let(test_library, String)
38
37
  @_response = T.let([], ResponseType)
39
38
  @path = T.let(uri.to_standardized_path, T.nilable(String))
40
39
  # visibility_stack is a stack of [current_visibility, previous_visibility]
@@ -48,18 +47,15 @@ module RubyLsp
48
47
  :on_class,
49
48
  :after_class,
50
49
  :on_def,
51
- :on_command,
52
- :after_command,
53
50
  :on_call,
54
51
  :after_call,
55
- :on_vcall,
56
52
  )
57
53
  end
58
54
 
59
- sig { params(node: SyntaxTree::ClassDeclaration).void }
55
+ sig { params(node: YARP::ClassNode).void }
60
56
  def on_class(node)
61
57
  @visibility_stack.push(["public", "public"])
62
- class_name = node.constant.constant.value
58
+ class_name = node.constant_path.slice
63
59
  @class_stack.push(class_name)
64
60
 
65
61
  if @path && class_name.end_with?("Test")
@@ -72,20 +68,20 @@ module RubyLsp
72
68
  end
73
69
  end
74
70
 
75
- sig { params(node: SyntaxTree::ClassDeclaration).void }
71
+ sig { params(node: YARP::ClassNode).void }
76
72
  def after_class(node)
77
73
  @visibility_stack.pop
78
74
  @class_stack.pop
79
75
  end
80
76
 
81
- sig { params(node: SyntaxTree::DefNode).void }
77
+ sig { params(node: YARP::DefNode).void }
82
78
  def on_def(node)
83
79
  class_name = @class_stack.last
84
80
  return unless class_name&.end_with?("Test")
85
81
 
86
82
  visibility, _ = @visibility_stack.last
87
83
  if visibility == "public"
88
- method_name = node.name.value
84
+ method_name = node.name.to_s
89
85
  if @path && method_name.start_with?("test_")
90
86
  add_test_code_lens(
91
87
  node,
@@ -97,58 +93,44 @@ module RubyLsp
97
93
  end
98
94
  end
99
95
 
100
- sig { params(node: SyntaxTree::Command).void }
101
- def on_command(node)
102
- node_message = node.message.value
103
- if ACCESS_MODIFIERS.include?(node_message) && node.arguments.parts.any?
104
- visibility, _ = @visibility_stack.pop
105
- @visibility_stack.push([node_message, visibility])
106
- elsif @path&.include?("Gemfile") && node_message.include?("gem") && node.arguments.parts.any?
107
- remote = resolve_gem_remote(node)
108
- return unless remote
96
+ sig { params(node: YARP::CallNode).void }
97
+ def on_call(node)
98
+ name = node.name
99
+ arguments = node.arguments
100
+
101
+ # If we found `private` by itself or `private def foo`
102
+ if ACCESS_MODIFIERS.include?(name)
103
+ if arguments.nil?
104
+ @visibility_stack.pop
105
+ @visibility_stack.push([name, name])
106
+ elsif arguments.arguments.first.is_a?(YARP::DefNode)
107
+ visibility, _ = @visibility_stack.pop
108
+ @visibility_stack.push([name, visibility])
109
+ end
109
110
 
110
- add_open_gem_remote_code_lens(node, remote)
111
+ return
111
112
  end
112
- end
113
113
 
114
- sig { params(node: SyntaxTree::Command).void }
115
- def after_command(node)
116
- _, prev_visibility = @visibility_stack.pop
117
- @visibility_stack.push([prev_visibility, prev_visibility])
118
- end
114
+ if @path&.include?("Gemfile") && name == "gem" && arguments
115
+ first_argument = arguments.arguments.first
116
+ return unless first_argument.is_a?(YARP::StringNode)
119
117
 
120
- sig { params(node: SyntaxTree::CallNode).void }
121
- def on_call(node)
122
- ident = node.message if node.message.is_a?(SyntaxTree::Ident)
118
+ remote = resolve_gem_remote(first_argument)
119
+ return unless remote
123
120
 
124
- if ident
125
- ident_value = T.cast(ident, SyntaxTree::Ident).value
126
- if ACCESS_MODIFIERS.include?(ident_value)
127
- visibility, _ = @visibility_stack.pop
128
- @visibility_stack.push([ident_value, visibility])
129
- end
121
+ add_open_gem_remote_code_lens(node, remote)
130
122
  end
131
123
  end
132
124
 
133
- sig { params(node: SyntaxTree::CallNode).void }
125
+ sig { params(node: YARP::CallNode).void }
134
126
  def after_call(node)
135
127
  _, prev_visibility = @visibility_stack.pop
136
128
  @visibility_stack.push([prev_visibility, prev_visibility])
137
129
  end
138
130
 
139
- sig { params(node: SyntaxTree::VCall).void }
140
- def on_vcall(node)
141
- vcall_value = node.value.value
142
-
143
- if ACCESS_MODIFIERS.include?(vcall_value)
144
- @visibility_stack.pop
145
- @visibility_stack.push([vcall_value, vcall_value])
146
- end
147
- end
148
-
149
- sig { override.params(extension: RubyLsp::Extension).returns(T.nilable(Listener[ResponseType])) }
150
- def initialize_external_listener(extension)
151
- extension.create_code_lens_listener(@uri, @emitter, @message_queue)
131
+ sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
132
+ def initialize_external_listener(addon)
133
+ addon.create_code_lens_listener(@uri, @emitter, @message_queue)
152
134
  end
153
135
 
154
136
  sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
@@ -159,10 +141,10 @@ module RubyLsp
159
141
 
160
142
  private
161
143
 
162
- sig { params(node: SyntaxTree::Node, name: String, command: String, kind: Symbol).void }
144
+ sig { params(node: YARP::Node, name: String, command: String, kind: Symbol).void }
163
145
  def add_test_code_lens(node, name:, command:, kind:)
164
146
  # don't add code lenses if the test library is not supported or unknown
165
- return unless SUPPORTED_TEST_LIBRARIES.include?(@test_library) && @path
147
+ return unless SUPPORTED_TEST_LIBRARIES.include?(DependencyDetector.instance.detected_test_library) && @path
166
148
 
167
149
  arguments = [
168
150
  @path,
@@ -201,15 +183,9 @@ module RubyLsp
201
183
  )
202
184
  end
203
185
 
204
- sig { params(node: SyntaxTree::Command).returns(T.nilable(String)) }
205
- def resolve_gem_remote(node)
206
- gem_statement = node.arguments.parts.first
207
- return unless gem_statement.is_a?(SyntaxTree::StringLiteral)
208
-
209
- gem_name = gem_statement.parts.first
210
- return unless gem_name.is_a?(SyntaxTree::TStringContent)
211
-
212
- spec = Gem::Specification.stubs.find { |gem| gem.name == gem_name.value }&.to_spec
186
+ sig { params(gem_name: YARP::StringNode).returns(T.nilable(String)) }
187
+ def resolve_gem_remote(gem_name)
188
+ spec = Gem::Specification.stubs.find { |gem| gem.name == gem_name.content }&.to_spec
213
189
  return if spec.nil?
214
190
 
215
191
  [spec.homepage, spec.metadata["source_code_uri"]].compact.find do |page|
@@ -221,7 +197,7 @@ module RubyLsp
221
197
  def generate_test_command(class_name:, method_name: nil)
222
198
  command = BASE_COMMAND + T.must(@path)
223
199
 
224
- case @test_library
200
+ case DependencyDetector.instance.detected_test_library
225
201
  when "minitest"
226
202
  command += if method_name
227
203
  " --name " + "/#{Shellwords.escape(class_name + "#" + method_name)}/"
@@ -239,7 +215,7 @@ module RubyLsp
239
215
  command
240
216
  end
241
217
 
242
- sig { params(node: SyntaxTree::Command, remote: String).void }
218
+ sig { params(node: YARP::CallNode, remote: String).void }
243
219
  def add_open_gem_remote_code_lens(node, remote)
244
220
  @_response << create_code_lens(
245
221
  node,
@@ -40,58 +40,80 @@ module RubyLsp
40
40
  @index = index
41
41
  @nesting = nesting
42
42
 
43
- emitter.register(self, :on_tstring_content, :on_const_path_ref, :on_const, :on_top_const_ref)
43
+ emitter.register(self, :on_string, :on_constant_path, :on_constant_read)
44
44
  end
45
45
 
46
- sig { params(node: SyntaxTree::TStringContent).void }
47
- def on_tstring_content(node)
48
- @index.search_require_paths(node.value).map!(&:require_path).sort!.each do |path|
46
+ sig { params(node: YARP::StringNode).void }
47
+ def on_string(node)
48
+ @index.search_require_paths(node.content).map!(&:require_path).sort!.each do |path|
49
49
  @_response << build_completion(T.must(path), node)
50
50
  end
51
51
  end
52
52
 
53
53
  # Handle completion on regular constant references (e.g. `Bar`)
54
- sig { params(node: SyntaxTree::Const).void }
55
- def on_const(node)
56
- return if DependencyDetector::HAS_TYPECHECKER
54
+ sig { params(node: YARP::ConstantReadNode).void }
55
+ def on_constant_read(node)
56
+ return if DependencyDetector.instance.typechecker
57
57
 
58
- name = node.value
58
+ name = node.slice
59
59
  candidates = @index.prefix_search(name, @nesting)
60
60
  candidates.each do |entries|
61
- @_response << build_entry_completion(name, node, entries, top_level?(T.must(entries.first).name, candidates))
61
+ complete_name = T.must(entries.first).name
62
+ @_response << build_entry_completion(complete_name, node, entries, top_level?(complete_name, candidates))
62
63
  end
63
64
  end
64
65
 
65
66
  # Handle completion on namespaced constant references (e.g. `Foo::Bar`)
66
- sig { params(node: SyntaxTree::ConstPathRef).void }
67
- def on_const_path_ref(node)
68
- return if DependencyDetector::HAS_TYPECHECKER
67
+ sig { params(node: YARP::ConstantPathNode).void }
68
+ def on_constant_path(node)
69
+ return if DependencyDetector.instance.typechecker
69
70
 
70
- name = full_constant_name(node)
71
- candidates = @index.prefix_search(name, @nesting)
72
- candidates.each do |entries|
73
- @_response << build_entry_completion(name, node, entries, top_level?(T.must(entries.first).name, candidates))
71
+ name = node.slice
72
+
73
+ top_level_reference = if name.start_with?("::")
74
+ name = name.delete_prefix("::")
75
+ true
76
+ else
77
+ false
74
78
  end
75
- end
76
79
 
77
- # Handle completion on top level constant references (e.g. `::Bar`)
78
- sig { params(node: SyntaxTree::TopConstRef).void }
79
- def on_top_const_ref(node)
80
- return if DependencyDetector::HAS_TYPECHECKER
80
+ # If we're trying to provide completion for an aliased namespace, we need to first discover it's real name in
81
+ # order to find which possible constants match the desired search
82
+ *namespace, incomplete_name = name.split("::")
83
+ aliased_namespace = namespace.join("::")
84
+ namespace_entries = @index.resolve(aliased_namespace, @nesting)
85
+ return unless namespace_entries
86
+
87
+ real_namespace = @index.follow_aliased_namespace(T.must(namespace_entries.first).name)
81
88
 
82
- name = full_constant_name(node)
83
- candidates = @index.prefix_search(name, [])
84
- candidates.each { |entries| @_response << build_entry_completion(name, node, entries, true) }
89
+ candidates = @index.prefix_search("#{real_namespace}::#{incomplete_name}", top_level_reference ? [] : @nesting)
90
+ candidates.each do |entries|
91
+ # The only time we may have a private constant reference from outside of the namespace is if we're dealing
92
+ # with ConstantPath and the entry name doesn't start with the current nesting
93
+ first_entry = T.must(entries.first)
94
+ next if first_entry.visibility == :private && !first_entry.name.start_with?("#{@nesting}::")
95
+
96
+ constant_name = T.must(first_entry.name.split("::").last)
97
+
98
+ full_name = aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}"
99
+
100
+ @_response << build_entry_completion(
101
+ full_name,
102
+ node,
103
+ entries,
104
+ top_level_reference || top_level?(T.must(entries.first).name, candidates),
105
+ )
106
+ end
85
107
  end
86
108
 
87
109
  private
88
110
 
89
- sig { params(label: String, node: SyntaxTree::TStringContent).returns(Interface::CompletionItem) }
111
+ sig { params(label: String, node: YARP::StringNode).returns(Interface::CompletionItem) }
90
112
  def build_completion(label, node)
91
113
  Interface::CompletionItem.new(
92
114
  label: label,
93
115
  text_edit: Interface::TextEdit.new(
94
- range: range_from_syntax_tree_node(node),
116
+ range: range_from_node(node),
95
117
  new_text: label,
96
118
  ),
97
119
  kind: Constant::CompletionItemKind::REFERENCE,
@@ -101,7 +123,7 @@ module RubyLsp
101
123
  sig do
102
124
  params(
103
125
  name: String,
104
- node: SyntaxTree::Node,
126
+ node: YARP::Node,
105
127
  entries: T::Array[RubyIndexer::Index::Entry],
106
128
  top_level: T::Boolean,
107
129
  ).returns(Interface::CompletionItem)
@@ -119,7 +141,7 @@ module RubyLsp
119
141
  Constant::CompletionItemKind::REFERENCE
120
142
  end
121
143
 
122
- insertion_text = first_entry.name.dup
144
+ insertion_text = name.dup
123
145
 
124
146
  # If we have two entries with the same name inside the current namespace and the user selects the top level
125
147
  # option, we have to ensure it's prefixed with `::` or else we're completing the wrong constant. For example:
@@ -143,17 +165,17 @@ module RubyLsp
143
165
  # For these top level references, we need to include the `::` as part of the filter text or else it won't match
144
166
  # the right entries in the index
145
167
  Interface::CompletionItem.new(
146
- label: first_entry.name,
147
- filter_text: top_level ? "::#{first_entry.name}" : first_entry.name,
168
+ label: name,
169
+ filter_text: top_level ? "::#{name}" : name,
148
170
  text_edit: Interface::TextEdit.new(
149
- range: range_from_syntax_tree_node(node),
171
+ range: range_from_node(node),
150
172
  new_text: insertion_text,
151
173
  ),
152
174
  kind: kind,
153
175
  label_details: Interface::CompletionItemLabelDetails.new(
154
176
  description: entries.map(&:file_name).join(","),
155
177
  ),
156
- documentation: markdown_from_index_entries(first_entry.name, entries),
178
+ documentation: markdown_from_index_entries(name, entries),
157
179
  )
158
180
  end
159
181
 
@@ -43,12 +43,11 @@ module RubyLsp
43
43
 
44
44
  super(emitter, message_queue)
45
45
 
46
- emitter.register(self, :on_command, :on_const, :on_const_path_ref)
46
+ emitter.register(self, :on_call, :on_constant_read, :on_constant_path)
47
47
  end
48
-
49
- sig { override.params(ext: Extension).returns(T.nilable(RubyLsp::Listener[ResponseType])) }
50
- def initialize_external_listener(ext)
51
- ext.create_definition_listener(@uri, @nesting, @index, @emitter, @message_queue)
48
+ sig { override.params(addon: Addon).returns(T.nilable(RubyLsp::Listener[ResponseType])) }
49
+ def initialize_external_listener(addon)
50
+ addon.create_definition_listener(@uri, @nesting, @index, @emitter, @message_queue)
52
51
  end
53
52
 
54
53
  sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
@@ -67,32 +66,21 @@ module RubyLsp
67
66
  self
68
67
  end
69
68
 
70
- sig { params(node: SyntaxTree::ConstPathRef).void }
71
- def on_const_path_ref(node)
72
- name = full_constant_name(node)
73
- find_in_index(name)
74
- end
75
-
76
- sig { params(node: SyntaxTree::Const).void }
77
- def on_const(node)
78
- find_in_index(node.value)
79
- end
80
-
81
- sig { params(node: SyntaxTree::Command).void }
82
- def on_command(node)
83
- message = node.message.value
69
+ sig { params(node: YARP::CallNode).void }
70
+ def on_call(node)
71
+ message = node.name
84
72
  return unless message == "require" || message == "require_relative"
85
73
 
86
- argument = node.arguments.parts.first
87
- return unless argument.is_a?(SyntaxTree::StringLiteral)
74
+ arguments = node.arguments
75
+ return unless arguments
88
76
 
89
- string = argument.parts.first
90
- return unless string.is_a?(SyntaxTree::TStringContent)
77
+ argument = arguments.arguments.first
78
+ return unless argument.is_a?(YARP::StringNode)
91
79
 
92
80
  case message
93
81
  when "require"
94
- entry = @index.search_require_paths(string.value).find do |indexable_path|
95
- indexable_path.require_path == string.value
82
+ entry = @index.search_require_paths(argument.content).find do |indexable_path|
83
+ indexable_path.require_path == argument.content
96
84
  end
97
85
 
98
86
  if entry
@@ -107,7 +95,7 @@ module RubyLsp
107
95
  )
108
96
  end
109
97
  when "require_relative"
110
- required_file = "#{string.value}.rb"
98
+ required_file = "#{argument.content}.rb"
111
99
  path = @uri.to_standardized_path
112
100
  current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : Dir.pwd
113
101
  candidate = File.expand_path(File.join(current_folder, required_file))
@@ -122,6 +110,16 @@ module RubyLsp
122
110
  end
123
111
  end
124
112
 
113
+ sig { params(node: YARP::ConstantPathNode).void }
114
+ def on_constant_path(node)
115
+ find_in_index(node.slice)
116
+ end
117
+
118
+ sig { params(node: YARP::ConstantReadNode).void }
119
+ def on_constant_read(node)
120
+ find_in_index(node.slice)
121
+ end
122
+
125
123
  private
126
124
 
127
125
  sig { params(value: String).void }
@@ -129,6 +127,11 @@ module RubyLsp
129
127
  entries = @index.resolve(value, @nesting)
130
128
  return unless entries
131
129
 
130
+ # We should only allow jumping to the definition of private constants if the constant is defined in the same
131
+ # namespace as the reference
132
+ first_entry = T.must(entries.first)
133
+ return if first_entry.visibility == :private && first_entry.name != "#{@nesting.join("::")}::#{value}"
134
+
132
135
  bundle_path = begin
133
136
  Bundler.bundle_path.to_s
134
137
  rescue Bundler::GemfileNotFound
@@ -141,7 +144,7 @@ module RubyLsp
141
144
  # additional behavior on top of jumping to RBIs. Sorbet can already handle go to definition for all constants
142
145
  # in the project, even if the files are typed false
143
146
  file_path = entry.file_path
144
- if DependencyDetector::HAS_TYPECHECKER && bundle_path && !file_path.start_with?(bundle_path) &&
147
+ if DependencyDetector.instance.typechecker && bundle_path && !file_path.start_with?(bundle_path) &&
145
148
  !file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
146
149
 
147
150
  next