-
Notifications
You must be signed in to change notification settings - Fork 265
Expand file tree
/
Copy pathreferences.rb
More file actions
136 lines (118 loc) · 5.59 KB
/
Copy pathreferences.rb
File metadata and controls
136 lines (118 loc) · 5.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# typed: strict
# frozen_string_literal: true
module RubyLsp
module Requests
# The
# [references](https://microsoft.github.io/language-server-protocol/specification#textDocument_references)
# request finds all references for the selected symbol.
class References < Request
include Support::Common
#: (GlobalState global_state, Store store, (RubyDocument | ERBDocument) document, Hash[Symbol, untyped] params) -> void
def initialize(global_state, store, document, params)
super()
@global_state = global_state
@store = store
@document = document
@params = params
@locations = [] #: Array[Interface::Location]
end
# @override
#: -> Array[Interface::Location]
def perform
position = @params[:position]
char_position, _ = @document.find_index_by_position(position)
node_context = RubyDocument.locate(
@document.ast,
char_position,
node_types: [
Prism::ConstantReadNode,
Prism::ConstantPathNode,
Prism::ConstantPathTargetNode,
Prism::InstanceVariableAndWriteNode,
Prism::InstanceVariableOperatorWriteNode,
Prism::InstanceVariableOrWriteNode,
Prism::InstanceVariableReadNode,
Prism::InstanceVariableTargetNode,
Prism::InstanceVariableWriteNode,
Prism::CallNode,
Prism::DefNode,
],
code_units_cache: @document.code_units_cache,
)
target = node_context.node
parent = node_context.parent
return @locations if !target || target.is_a?(Prism::ProgramNode)
if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
target = determine_target(
target,
parent,
position,
)
end
target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode | Prism::InstanceVariableAndWriteNode | Prism::InstanceVariableOperatorWriteNode | Prism::InstanceVariableOrWriteNode | Prism::InstanceVariableReadNode | Prism::InstanceVariableTargetNode | Prism::InstanceVariableWriteNode | Prism::CallNode | Prism::DefNode,
reference_target = create_reference_target(target, node_context)
return @locations unless reference_target
Dir.glob("**/*.rb", base: @global_state.workspace_path).each do |relative_path|
path = File.join(@global_state.workspace_path, relative_path)
uri = URI::Generic.from_path(path: path)
# If the document is being managed by the client, then we should use whatever is present in the store instead
# of reading from disk
next if @store.key?(uri)
parse_result = Prism.parse_lex_file(path)
collect_references(reference_target, parse_result, uri)
rescue Errno::EISDIR, Errno::ENOENT
# If `path` is a directory, just ignore it and continue. If the file doesn't exist, then we also ignore it.
end
@store.each do |_uri, document|
collect_references(reference_target, document.parse_result, document.uri)
end
@locations
end
private
#: ((Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode | Prism::InstanceVariableAndWriteNode | Prism::InstanceVariableOperatorWriteNode | Prism::InstanceVariableOrWriteNode | Prism::InstanceVariableReadNode | Prism::InstanceVariableTargetNode | Prism::InstanceVariableWriteNode | Prism::CallNode | Prism::DefNode) target_node, NodeContext node_context) -> RubyIndexer::ReferenceFinder::Target?
def create_reference_target(target_node, node_context)
case target_node
when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode
name = RubyIndexer::Index.constant_name(target_node)
return unless name
entries = @global_state.index.resolve(name, node_context.nesting)
return unless entries
fully_qualified_name = entries.first #: as !nil
.name
RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
when
Prism::InstanceVariableAndWriteNode,
Prism::InstanceVariableOperatorWriteNode,
Prism::InstanceVariableOrWriteNode,
Prism::InstanceVariableReadNode,
Prism::InstanceVariableTargetNode,
Prism::InstanceVariableWriteNode
receiver_type = @global_state.type_inferrer.infer_receiver_type(node_context)
return unless receiver_type
ancestors = @global_state.index.linearized_ancestors_of(receiver_type.name)
RubyIndexer::ReferenceFinder::InstanceVariableTarget.new(target_node.name.to_s, ancestors)
when Prism::CallNode, Prism::DefNode
RubyIndexer::ReferenceFinder::MethodTarget.new(target_node.name.to_s)
end
end
#: (RubyIndexer::ReferenceFinder::Target target, Prism::LexResult parse_result, URI::Generic uri) -> void
def collect_references(target, parse_result, uri)
dispatcher = Prism::Dispatcher.new
finder = RubyIndexer::ReferenceFinder.new(
target,
@global_state.index,
dispatcher,
uri,
include_declarations: @params.dig(:context, :includeDeclaration) || true,
)
dispatcher.visit(parse_result.value.first)
finder.references.each do |reference|
@locations << Interface::Location.new(
uri: uri.to_s,
range: range_from_location(reference.location),
)
end
end
end
end
end