code_moniker_workspace/snapshot/view/
references.rs1use std::collections::BTreeSet;
4
5use super::model::{
6 ReferenceDirection, ReferenceSet, ReferenceSetSummary, ReferenceSummary, SymbolReferences,
7};
8use super::symbols::SymbolView;
9use crate::snapshot::model::{ReferenceId, ReferenceRecord, SymbolId, WorkspaceSnapshot};
10
11pub struct ReferenceView<'a> {
12 snapshot: &'a WorkspaceSnapshot,
13}
14
15impl<'a> ReferenceView<'a> {
16 pub(super) fn new(snapshot: &'a WorkspaceSnapshot) -> Self {
17 Self { snapshot }
18 }
19
20 pub fn for_symbol(&self, symbol: &SymbolId) -> Option<SymbolReferences> {
21 let symbols = SymbolView::new(self.snapshot);
22 let record = symbols.find(symbol)?;
23 Some(SymbolReferences {
24 symbol: symbols.summary(record),
25 incoming: self.reference_set(&self.incoming_ids(symbol), ReferenceDirection::Incoming),
26 outgoing: self.reference_set(&self.outgoing_ids(symbol), ReferenceDirection::Outgoing),
27 })
28 }
29
30 pub fn incoming_ids(&self, symbol: &SymbolId) -> Vec<ReferenceId> {
31 if let Some(index) = self.snapshot.linkage.read_index.get() {
32 return index.incoming(symbol).to_vec();
33 }
34 self.snapshot
35 .linkage
36 .resolved
37 .iter()
38 .filter(|edge| &edge.target == symbol)
39 .map(|edge| edge.reference)
40 .collect()
41 }
42
43 pub fn outgoing_ids(&self, symbol: &SymbolId) -> Vec<ReferenceId> {
44 self.snapshot
45 .index
46 .references
47 .file_records(symbol.file())
48 .iter()
49 .filter(|reference| &reference.source_symbol == symbol)
50 .map(|reference| reference.id)
51 .collect()
52 }
53
54 pub fn reference_set(
55 &self,
56 refs: &[ReferenceId],
57 direction: ReferenceDirection,
58 ) -> ReferenceSet {
59 let groups = refs
60 .iter()
61 .filter_map(|reference| self.reference(reference))
62 .map(|reference| reference_summary(self.snapshot, reference, direction))
63 .collect::<Vec<_>>();
64 let files = groups
65 .iter()
66 .filter_map(|group| group.source)
67 .collect::<BTreeSet<_>>()
68 .len();
69 let contexts = groups
70 .iter()
71 .filter_map(|group| group.context)
72 .collect::<BTreeSet<_>>()
73 .len();
74 ReferenceSet {
75 summary: ReferenceSetSummary {
76 refs: groups.len(),
77 files,
78 contexts,
79 },
80 groups,
81 }
82 }
83
84 pub fn reference(&self, id: &ReferenceId) -> Option<&'a ReferenceRecord> {
85 let record = self
86 .snapshot
87 .index
88 .references
89 .file_records(id.file())
90 .get(id.reference());
91 if let Some(record) = record
92 && &record.id == id
93 {
94 return Some(record);
95 }
96 self.snapshot
97 .index
98 .references
99 .iter()
100 .find(|reference| &reference.id == id)
101 }
102}
103
104fn reference_summary(
105 snapshot: &WorkspaceSnapshot,
106 reference: &ReferenceRecord,
107 direction: ReferenceDirection,
108) -> ReferenceSummary {
109 let symbols = SymbolView::new(snapshot);
110 let source_symbol = symbols.find(&reference.source_symbol);
111 let resolved_target = resolved_target(snapshot, &reference.id);
112 let external_target = external_target(snapshot, &reference.id);
113 ReferenceSummary {
114 reference: reference.id,
115 source: Some(reference.source),
116 context: source_symbol.map(|symbol| symbol.id),
117 actor: source_symbol
118 .map(|symbol| symbol.name.clone())
119 .unwrap_or_else(|| reference.source_symbol.to_string()),
120 endpoint_label: match direction {
121 ReferenceDirection::Incoming => "source",
122 ReferenceDirection::Outgoing => "target",
123 },
124 endpoint: resolved_target
125 .as_ref()
126 .and_then(|symbol| symbols.find(symbol))
127 .map(|symbol| symbol.identity.to_string())
128 .or(external_target)
129 .unwrap_or_else(|| reference.target_identity.to_string()),
130 kind: reference.kind.clone(),
131 line_range: reference.line_range,
132 }
133}
134
135fn resolved_target(snapshot: &WorkspaceSnapshot, reference: &ReferenceId) -> Option<SymbolId> {
136 if let Some(index) = snapshot.linkage.read_index.get() {
137 return index.resolved_target(reference).cloned();
138 }
139 snapshot
140 .linkage
141 .resolved
142 .iter()
143 .find(|edge| &edge.reference == reference)
144 .map(|edge| edge.target)
145}
146
147fn external_target(snapshot: &WorkspaceSnapshot, reference: &ReferenceId) -> Option<String> {
148 snapshot
149 .linkage
150 .external
151 .iter()
152 .find(|external| &external.reference == reference)
153 .map(|external| external.target_identity.to_string())
154}