Skip to main content

code_moniker_workspace/snapshot/view/
references.rs

1// code-moniker: ignore-file[smell-clone-reflex]
2// Snapshot views are owned read-model projections from borrowed reference records.
3use 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}