ruby-lsp 0.10.1 → 0.11.0

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