ruby-lsp 0.18.4 → 0.19.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/VERSION +1 -1
  3. data/exe/ruby-lsp-check +1 -1
  4. data/lib/core_ext/uri.rb +9 -4
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
  6. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +66 -8
  7. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +63 -32
  8. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +8 -5
  9. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +38 -4
  10. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +324 -0
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -0
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +23 -0
  13. data/lib/ruby_indexer/test/constant_test.rb +8 -0
  14. data/lib/ruby_indexer/test/enhancements_test.rb +2 -0
  15. data/lib/ruby_indexer/test/index_test.rb +3 -0
  16. data/lib/ruby_indexer/test/instance_variables_test.rb +12 -0
  17. data/lib/ruby_indexer/test/method_test.rb +10 -0
  18. data/lib/ruby_indexer/test/rbs_indexer_test.rb +14 -0
  19. data/lib/ruby_indexer/test/reference_finder_test.rb +242 -0
  20. data/lib/ruby_lsp/addon.rb +69 -7
  21. data/lib/ruby_lsp/erb_document.rb +9 -3
  22. data/lib/ruby_lsp/global_state.rb +8 -0
  23. data/lib/ruby_lsp/internal.rb +4 -0
  24. data/lib/ruby_lsp/listeners/completion.rb +1 -1
  25. data/lib/ruby_lsp/listeners/hover.rb +19 -0
  26. data/lib/ruby_lsp/requests/code_action_resolve.rb +9 -3
  27. data/lib/ruby_lsp/requests/completion.rb +1 -0
  28. data/lib/ruby_lsp/requests/completion_resolve.rb +29 -0
  29. data/lib/ruby_lsp/requests/definition.rb +1 -0
  30. data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
  31. data/lib/ruby_lsp/requests/hover.rb +1 -0
  32. data/lib/ruby_lsp/requests/range_formatting.rb +55 -0
  33. data/lib/ruby_lsp/requests/references.rb +146 -0
  34. data/lib/ruby_lsp/requests/rename.rb +196 -0
  35. data/lib/ruby_lsp/requests/signature_help.rb +6 -1
  36. data/lib/ruby_lsp/requests/support/formatter.rb +3 -0
  37. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +6 -0
  38. data/lib/ruby_lsp/requests/support/source_uri.rb +8 -1
  39. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +8 -0
  40. data/lib/ruby_lsp/ruby_document.rb +23 -8
  41. data/lib/ruby_lsp/server.rb +98 -10
  42. data/lib/ruby_lsp/static_docs.rb +15 -0
  43. data/lib/ruby_lsp/store.rb +12 -0
  44. data/lib/ruby_lsp/test_helper.rb +1 -1
  45. data/lib/ruby_lsp/type_inferrer.rb +6 -1
  46. data/static_docs/yield.md +81 -0
  47. metadata +20 -7
@@ -32,6 +32,8 @@ module RubyLsp
32
32
 
33
33
  AddonNotFoundError = Class.new(StandardError)
34
34
 
35
+ class IncompatibleApiError < StandardError; end
36
+
35
37
  class << self
36
38
  extend T::Sig
37
39
 
@@ -53,13 +55,28 @@ module RubyLsp
53
55
 
54
56
  # Discovers and loads all add-ons. Returns a list of errors when trying to require add-ons
55
57
  sig do
56
- params(global_state: GlobalState, outgoing_queue: Thread::Queue).returns(T::Array[StandardError])
58
+ params(
59
+ global_state: GlobalState,
60
+ outgoing_queue: Thread::Queue,
61
+ include_project_addons: T::Boolean,
62
+ ).returns(T::Array[StandardError])
57
63
  end
58
- def load_addons(global_state, outgoing_queue)
64
+ def load_addons(global_state, outgoing_queue, include_project_addons: true)
59
65
  # Require all add-ons entry points, which should be placed under
60
- # `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
61
- errors = Gem.find_files("ruby_lsp/**/addon.rb").filter_map do |addon|
62
- require File.expand_path(addon)
66
+ # `some_gem/lib/ruby_lsp/your_gem_name/addon.rb` or in the workspace under
67
+ # `your_project/ruby_lsp/project_name/addon.rb`
68
+ addon_files = Gem.find_files("ruby_lsp/**/addon.rb")
69
+
70
+ if include_project_addons
71
+ addon_files.concat(Dir.glob(File.join(global_state.workspace_path, "**", "ruby_lsp/**/addon.rb")))
72
+ end
73
+
74
+ errors = addon_files.filter_map do |addon_path|
75
+ # Avoid requiring this file twice. This may happen if you're working on the Ruby LSP itself and at the same
76
+ # time have `ruby-lsp` installed as a vendored gem
77
+ next if File.basename(File.dirname(addon_path)) == "ruby_lsp"
78
+
79
+ require File.expand_path(addon_path)
63
80
  nil
64
81
  rescue => e
65
82
  e
@@ -80,13 +97,53 @@ module RubyLsp
80
97
  errors
81
98
  end
82
99
 
83
- sig { params(addon_name: String).returns(Addon) }
84
- def get(addon_name)
100
+ # Get a reference to another add-on object by name and version. If an add-on exports an API that can be used by
101
+ # other add-ons, this is the way to get access to that API.
102
+ #
103
+ # Important: if the add-on is not found, AddonNotFoundError will be raised. If the add-on is found, but its
104
+ # current version does not satisfy the given version constraint, then IncompatibleApiError will be raised. It is
105
+ # the responsibility of the add-ons using this API to handle these errors appropriately.
106
+ sig { params(addon_name: String, version_constraints: String).returns(Addon) }
107
+ def get(addon_name, *version_constraints)
108
+ if version_constraints.empty?
109
+ raise IncompatibleApiError, "Must specify version constraints when accessing other add-ons"
110
+ end
111
+
85
112
  addon = addons.find { |addon| addon.name == addon_name }
86
113
  raise AddonNotFoundError, "Could not find add-on '#{addon_name}'" unless addon
87
114
 
115
+ version_object = Gem::Version.new(addon.version)
116
+
117
+ unless version_constraints.all? { |constraint| Gem::Requirement.new(constraint).satisfied_by?(version_object) }
118
+ raise IncompatibleApiError,
119
+ "Constraints #{version_constraints.inspect} is incompatible with #{addon_name} version #{addon.version}"
120
+ end
121
+
88
122
  addon
89
123
  end
124
+
125
+ # Depend on a specific version of the Ruby LSP. This method should only be used if the add-on is distributed in a
126
+ # gem that does not have a runtime dependency on the ruby-lsp gem. This method should be invoked at the top of the
127
+ # `addon.rb` file before defining any classes or requiring any files. For example:
128
+ #
129
+ # ```ruby
130
+ # RubyLsp::Addon.depend_on_ruby_lsp!(">= 0.18.0")
131
+ #
132
+ # module MyGem
133
+ # class MyAddon < RubyLsp::Addon
134
+ # # ...
135
+ # end
136
+ # end
137
+ # ```
138
+ sig { params(version_constraints: String).void }
139
+ def depend_on_ruby_lsp!(*version_constraints)
140
+ version_object = Gem::Version.new(RubyLsp::VERSION)
141
+
142
+ unless version_constraints.all? { |constraint| Gem::Requirement.new(constraint).satisfied_by?(version_object) }
143
+ raise IncompatibleApiError,
144
+ "Add-on is not compatible with this version of the Ruby LSP. Skipping its activation"
145
+ end
146
+ end
90
147
  end
91
148
 
92
149
  sig { void }
@@ -132,6 +189,11 @@ module RubyLsp
132
189
  sig { abstract.returns(String) }
133
190
  def name; end
134
191
 
192
+ # Add-ons should override the `version` method to return a semantic version string representing the add-on's
193
+ # version. This is used for compatibility checks
194
+ sig { abstract.returns(String) }
195
+ def version; end
196
+
135
197
  # Creates a new CodeLens listener. This method is invoked on every CodeLens request
136
198
  sig do
137
199
  overridable.params(
@@ -27,8 +27,9 @@ module RubyLsp
27
27
  scanner = ERBScanner.new(@source)
28
28
  scanner.scan
29
29
  @host_language_source = scanner.host_language
30
- # assigning empty scopes to turn Prism into eval mode
31
- @parse_result = Prism.parse(scanner.ruby, scopes: [[]])
30
+ # Use partial script to avoid syntax errors in ERB files where keywords may be used without the full context in
31
+ # which they will be evaluated
32
+ @parse_result = Prism.parse(scanner.ruby, partial_script: true)
32
33
  true
33
34
  end
34
35
 
@@ -49,7 +50,12 @@ module RubyLsp
49
50
  ).returns(NodeContext)
50
51
  end
51
52
  def locate_node(position, node_types: [])
52
- RubyDocument.locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
53
+ RubyDocument.locate(
54
+ @parse_result.value,
55
+ create_scanner.find_char_position(position),
56
+ node_types: node_types,
57
+ encoding: @encoding,
58
+ )
53
59
  end
54
60
 
55
61
  sig { params(char_position: Integer).returns(T.nilable(T::Boolean)) }
@@ -23,6 +23,9 @@ module RubyLsp
23
23
  sig { returns(T::Boolean) }
24
24
  attr_reader :supports_watching_files, :experimental_features, :supports_request_delegation
25
25
 
26
+ sig { returns(T::Array[String]) }
27
+ attr_reader :supported_resource_operations
28
+
26
29
  sig { returns(TypeInferrer) }
27
30
  attr_reader :type_inferrer
28
31
 
@@ -42,6 +45,7 @@ module RubyLsp
42
45
  @type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
43
46
  @addon_settings = T.let({}, T::Hash[String, T.untyped])
44
47
  @supports_request_delegation = T.let(false, T::Boolean)
48
+ @supported_resource_operations = T.let([], T::Array[String])
45
49
  end
46
50
 
47
51
  sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
@@ -117,6 +121,7 @@ module RubyLsp
117
121
  else
118
122
  Encoding::UTF_32
119
123
  end
124
+ @index.configuration.encoding = @encoding
120
125
 
121
126
  file_watching_caps = options.dig(:capabilities, :workspace, :didChangeWatchedFiles)
122
127
  if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
@@ -132,6 +137,9 @@ module RubyLsp
132
137
  end
133
138
 
134
139
  @supports_request_delegation = options.dig(:capabilities, :experimental, :requestDelegation) || false
140
+ supported_resource_operations = options.dig(:capabilities, :workspace, :workspaceEdit, :resourceOperations)
141
+ @supported_resource_operations = supported_resource_operations if supported_resource_operations
142
+
135
143
  notifications
136
144
  end
137
145
 
@@ -26,6 +26,7 @@ require "ruby_lsp/base_server"
26
26
  require "ruby_indexer/ruby_indexer"
27
27
  require "core_ext/uri"
28
28
  require "ruby_lsp/utils"
29
+ require "ruby_lsp/static_docs"
29
30
  require "ruby_lsp/scope"
30
31
  require "ruby_lsp/global_state"
31
32
  require "ruby_lsp/server"
@@ -74,6 +75,9 @@ require "ruby_lsp/requests/hover"
74
75
  require "ruby_lsp/requests/inlay_hints"
75
76
  require "ruby_lsp/requests/on_type_formatting"
76
77
  require "ruby_lsp/requests/prepare_type_hierarchy"
78
+ require "ruby_lsp/requests/range_formatting"
79
+ require "ruby_lsp/requests/references"
80
+ require "ruby_lsp/requests/rename"
77
81
  require "ruby_lsp/requests/selection_ranges"
78
82
  require "ruby_lsp/requests/semantic_highlighting"
79
83
  require "ruby_lsp/requests/show_syntax_tree"
@@ -438,7 +438,7 @@ module RubyLsp
438
438
  text_edit: Interface::TextEdit.new(range: range, new_text: keyword),
439
439
  kind: Constant::CompletionItemKind::KEYWORD,
440
440
  data: {
441
- skip_resolve: true,
441
+ keyword: true,
442
442
  },
443
443
  )
444
444
  end
@@ -24,6 +24,7 @@ module RubyLsp
24
24
  Prism::InterpolatedStringNode,
25
25
  Prism::SuperNode,
26
26
  Prism::ForwardingSuperNode,
27
+ Prism::YieldNode,
27
28
  ],
28
29
  T::Array[T.class_of(Prism::Node)],
29
30
  )
@@ -71,6 +72,7 @@ module RubyLsp
71
72
  :on_forwarding_super_node_enter,
72
73
  :on_string_node_enter,
73
74
  :on_interpolated_string_node_enter,
75
+ :on_yield_node_enter,
74
76
  )
75
77
  end
76
78
 
@@ -166,6 +168,11 @@ module RubyLsp
166
168
  handle_super_node_hover
167
169
  end
168
170
 
171
+ sig { params(node: Prism::YieldNode).void }
172
+ def on_yield_node_enter(node)
173
+ handle_keyword_documentation(node.keyword)
174
+ end
175
+
169
176
  private
170
177
 
171
178
  sig { params(node: T.any(Prism::InterpolatedStringNode, Prism::StringNode)).void }
@@ -193,6 +200,18 @@ module RubyLsp
193
200
  end
194
201
  end
195
202
 
203
+ sig { params(keyword: String).void }
204
+ def handle_keyword_documentation(keyword)
205
+ content = KEYWORD_DOCS[keyword]
206
+ return unless content
207
+
208
+ doc_path = File.join(STATIC_DOCS_PATH, "#{keyword}.md")
209
+
210
+ @response_builder.push("```ruby\n#{keyword}\n```", category: :title)
211
+ @response_builder.push("[Read more](#{doc_path})", category: :links)
212
+ @response_builder.push(content, category: :documentation)
213
+ end
214
+
196
215
  sig { void }
197
216
  def handle_super_node_hover
198
217
  # Sorbet can handle super hover on typed true or higher
@@ -23,10 +23,11 @@ module RubyLsp
23
23
  end
24
24
  end
25
25
 
26
- sig { params(document: RubyDocument, code_action: T::Hash[Symbol, T.untyped]).void }
27
- def initialize(document, code_action)
26
+ sig { params(document: RubyDocument, global_state: GlobalState, code_action: T::Hash[Symbol, T.untyped]).void }
27
+ def initialize(document, global_state, code_action)
28
28
  super()
29
29
  @document = document
30
+ @global_state = global_state
30
31
  @code_action = code_action
31
32
  end
32
33
 
@@ -191,7 +192,12 @@ module RubyLsp
191
192
  extracted_source = T.must(@document.source[start_index...end_index])
192
193
 
193
194
  # Find the closest method declaration node, so that we place the refactor in a valid position
194
- node_context = RubyDocument.locate(@document.parse_result.value, start_index, node_types: [Prism::DefNode])
195
+ node_context = RubyDocument.locate(
196
+ @document.parse_result.value,
197
+ start_index,
198
+ node_types: [Prism::DefNode],
199
+ encoding: @global_state.encoding,
200
+ )
195
201
  closest_node = node_context.node
196
202
  return Error::InvalidTargetRange unless closest_node
197
203
 
@@ -57,6 +57,7 @@ module RubyLsp
57
57
  Prism::InstanceVariableTargetNode,
58
58
  Prism::InstanceVariableWriteNode,
59
59
  ],
60
+ encoding: global_state.encoding,
60
61
  )
61
62
  @response_builder = T.let(
62
63
  ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem].new,
@@ -40,6 +40,8 @@ module RubyLsp
40
40
  # For example, forgetting to return the `insertText` included in the original item will make the editor use the
41
41
  # `label` for the text edit instead
42
42
  label = @item[:label].dup
43
+ return keyword_resolve(@item) if @item.dig(:data, :keyword)
44
+
43
45
  entries = @index[label] || []
44
46
 
45
47
  owner_name = @item.dig(:data, :owner_name)
@@ -72,6 +74,33 @@ module RubyLsp
72
74
 
73
75
  @item
74
76
  end
77
+
78
+ private
79
+
80
+ sig { params(item: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
81
+ def keyword_resolve(item)
82
+ keyword = item[:label]
83
+ content = KEYWORD_DOCS[keyword]
84
+
85
+ if content
86
+ doc_path = File.join(STATIC_DOCS_PATH, "#{keyword}.md")
87
+
88
+ @item[:documentation] = Interface::MarkupContent.new(
89
+ kind: "markdown",
90
+ value: <<~MARKDOWN.chomp,
91
+ ```ruby
92
+ #{keyword}
93
+ ```
94
+
95
+ [Read more](#{doc_path})
96
+
97
+ #{content}
98
+ MARKDOWN
99
+ )
100
+ end
101
+
102
+ item
103
+ end
75
104
  end
76
105
  end
77
106
  end
@@ -57,6 +57,7 @@ module RubyLsp
57
57
  Prism::SuperNode,
58
58
  Prism::ForwardingSuperNode,
59
59
  ],
60
+ encoding: global_state.encoding,
60
61
  )
61
62
 
62
63
  target = node_context.node
@@ -28,7 +28,7 @@ module RubyLsp
28
28
  char_position = document.create_scanner.find_char_position(position)
29
29
  delegate_request_if_needed!(global_state, document, char_position)
30
30
 
31
- node_context = RubyDocument.locate(document.parse_result.value, char_position)
31
+ node_context = RubyDocument.locate(document.parse_result.value, char_position, encoding: global_state.encoding)
32
32
 
33
33
  @response_builder = T.let(
34
34
  ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight].new,
@@ -41,6 +41,7 @@ module RubyLsp
41
41
  document.parse_result.value,
42
42
  char_position,
43
43
  node_types: Listeners::Hover::ALLOWED_TARGETS,
44
+ encoding: global_state.encoding,
44
45
  )
45
46
  target = node_context.node
46
47
  parent = node_context.parent
@@ -0,0 +1,55 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # The [range formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_rangeFormatting)
7
+ # is used to format a selection or to format on paste.
8
+ class RangeFormatting < Request
9
+ extend T::Sig
10
+
11
+ sig { params(global_state: GlobalState, document: RubyDocument, params: T::Hash[Symbol, T.untyped]).void }
12
+ def initialize(global_state, document, params)
13
+ super()
14
+ @document = document
15
+ @uri = T.let(document.uri, URI::Generic)
16
+ @params = params
17
+ @active_formatter = T.let(global_state.active_formatter, T.nilable(Support::Formatter))
18
+ end
19
+
20
+ sig { override.returns(T.nilable(T::Array[Interface::TextEdit])) }
21
+ def perform
22
+ return unless @active_formatter
23
+ return if @document.syntax_error?
24
+
25
+ target = @document.locate_first_within_range(@params[:range])
26
+ return unless target
27
+
28
+ location = target.location
29
+
30
+ formatted_text = @active_formatter.run_range_formatting(
31
+ @uri,
32
+ target.slice,
33
+ location.start_column / 2,
34
+ )
35
+ return unless formatted_text
36
+
37
+ [
38
+ Interface::TextEdit.new(
39
+ range: Interface::Range.new(
40
+ start: Interface::Position.new(
41
+ line: location.start_line - 1,
42
+ character: location.start_code_units_column(@document.encoding),
43
+ ),
44
+ end: Interface::Position.new(
45
+ line: location.end_line - 1,
46
+ character: location.end_code_units_column(@document.encoding),
47
+ ),
48
+ ),
49
+ new_text: formatted_text.strip,
50
+ ),
51
+ ]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,146 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # The
7
+ # [references](https://microsoft.github.io/language-server-protocol/specification#textDocument_references)
8
+ # request finds all references for the selected symbol.
9
+ class References < Request
10
+ extend T::Sig
11
+ include Support::Common
12
+
13
+ sig do
14
+ params(
15
+ global_state: GlobalState,
16
+ store: Store,
17
+ document: T.any(RubyDocument, ERBDocument),
18
+ params: T::Hash[Symbol, T.untyped],
19
+ ).void
20
+ end
21
+ def initialize(global_state, store, document, params)
22
+ super()
23
+ @global_state = global_state
24
+ @store = store
25
+ @document = document
26
+ @params = params
27
+ @locations = T.let([], T::Array[Interface::Location])
28
+ end
29
+
30
+ sig { override.returns(T::Array[Interface::Location]) }
31
+ def perform
32
+ position = @params[:position]
33
+ char_position = @document.create_scanner.find_char_position(position)
34
+
35
+ node_context = RubyDocument.locate(
36
+ @document.parse_result.value,
37
+ char_position,
38
+ node_types: [
39
+ Prism::ConstantReadNode,
40
+ Prism::ConstantPathNode,
41
+ Prism::ConstantPathTargetNode,
42
+ Prism::CallNode,
43
+ Prism::DefNode,
44
+ ],
45
+ encoding: @global_state.encoding,
46
+ )
47
+ target = node_context.node
48
+ parent = node_context.parent
49
+ return @locations if !target || target.is_a?(Prism::ProgramNode)
50
+
51
+ if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
52
+ target = determine_target(
53
+ target,
54
+ parent,
55
+ position,
56
+ )
57
+ end
58
+
59
+ target = T.cast(
60
+ target,
61
+ T.any(
62
+ Prism::ConstantReadNode,
63
+ Prism::ConstantPathNode,
64
+ Prism::ConstantPathTargetNode,
65
+ Prism::CallNode,
66
+ Prism::DefNode,
67
+ ),
68
+ )
69
+
70
+ reference_target = create_reference_target(target, node_context)
71
+ return @locations unless reference_target
72
+
73
+ Dir.glob(File.join(@global_state.workspace_path, "**/*.rb")).each do |path|
74
+ uri = URI::Generic.from_path(path: path)
75
+ # If the document is being managed by the client, then we should use whatever is present in the store instead
76
+ # of reading from disk
77
+ next if @store.key?(uri)
78
+
79
+ parse_result = Prism.parse_file(path)
80
+ collect_references(reference_target, parse_result, uri)
81
+ end
82
+
83
+ @store.each do |_uri, document|
84
+ collect_references(reference_target, document.parse_result, document.uri)
85
+ end
86
+
87
+ @locations
88
+ end
89
+
90
+ private
91
+
92
+ sig do
93
+ params(
94
+ target_node: T.any(
95
+ Prism::ConstantReadNode,
96
+ Prism::ConstantPathNode,
97
+ Prism::ConstantPathTargetNode,
98
+ Prism::CallNode,
99
+ Prism::DefNode,
100
+ ),
101
+ node_context: NodeContext,
102
+ ).returns(T.nilable(RubyIndexer::ReferenceFinder::Target))
103
+ end
104
+ def create_reference_target(target_node, node_context)
105
+ case target_node
106
+ when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode
107
+ name = constant_name(target_node)
108
+ return unless name
109
+
110
+ entries = @global_state.index.resolve(name, node_context.nesting)
111
+ return unless entries
112
+
113
+ fully_qualified_name = T.must(entries.first).name
114
+ RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
115
+ when Prism::CallNode, Prism::DefNode
116
+ RubyIndexer::ReferenceFinder::MethodTarget.new(target_node.name.to_s)
117
+ end
118
+ end
119
+
120
+ sig do
121
+ params(
122
+ target: RubyIndexer::ReferenceFinder::Target,
123
+ parse_result: Prism::ParseResult,
124
+ uri: URI::Generic,
125
+ ).void
126
+ end
127
+ def collect_references(target, parse_result, uri)
128
+ dispatcher = Prism::Dispatcher.new
129
+ finder = RubyIndexer::ReferenceFinder.new(
130
+ target,
131
+ @global_state.index,
132
+ dispatcher,
133
+ include_declarations: @params.dig(:context, :includeDeclaration) || true,
134
+ )
135
+ dispatcher.visit(parse_result.value)
136
+
137
+ finder.references.each do |reference|
138
+ @locations << Interface::Location.new(
139
+ uri: uri.to_s,
140
+ range: range_from_location(reference.location),
141
+ )
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end