Skip to main content

code_moniker_workspace/snapshot/view/
symbols.rs

1// code-moniker: ignore-file[smell-clone-reflex]
2// Snapshot views are owned read-model projections from borrowed symbol records.
3use rustc_hash::FxHashMap;
4
5use super::model::{SymbolDetail, SymbolSummary};
6use crate::snapshot::model::{ChangeStatus, SymbolId, SymbolRecord, WorkspaceSnapshot};
7
8pub struct SymbolView<'a> {
9	snapshot: &'a WorkspaceSnapshot,
10}
11
12impl<'a> SymbolView<'a> {
13	pub(super) fn new(snapshot: &'a WorkspaceSnapshot) -> Self {
14		Self { snapshot }
15	}
16
17	pub fn all(&self) -> Vec<SymbolSummary> {
18		let counts = self.navigable_child_counts();
19		let mut symbols = self
20			.snapshot
21			.index
22			.symbols
23			.iter()
24			.filter(|symbol| symbol.navigable)
25			.map(|symbol| self.summary_with_children(symbol, &counts))
26			.collect::<Vec<_>>();
27		symbols.sort_by(|left, right| {
28			left.source
29				.cmp(&right.source)
30				.then_with(|| left.identity.cmp(&right.identity))
31		});
32		symbols
33	}
34
35	fn navigable_child_counts(&self) -> FxHashMap<&SymbolId, usize> {
36		let mut counts = FxHashMap::default();
37		for symbol in self.snapshot.index.symbols.iter() {
38			if symbol.navigable
39				&& let Some(parent) = symbol.parent.as_ref()
40			{
41				*counts.entry(parent).or_default() += 1;
42			}
43		}
44		counts
45	}
46
47	fn summary_with_children(
48		&self,
49		symbol: &SymbolRecord,
50		counts: &FxHashMap<&SymbolId, usize>,
51	) -> SymbolSummary {
52		SymbolSummary {
53			id: symbol.id,
54			source: symbol.source,
55			identity: symbol.identity.clone(),
56			name: symbol.name.clone(),
57			kind: symbol.kind.clone(),
58			line_range: symbol.line_range,
59			child_count: counts.get(&symbol.id).copied().unwrap_or(0),
60			change: self.change_status_for_symbol(&symbol.id),
61		}
62	}
63
64	pub fn roots_for_source(&self, source: &crate::snapshot::SourceId) -> Vec<SymbolSummary> {
65		self.snapshot
66			.index
67			.symbols
68			.iter()
69			.filter(|symbol| {
70				symbol.navigable && &symbol.source == source && symbol.parent.is_none()
71			})
72			.map(|symbol| self.summary(symbol))
73			.collect()
74	}
75
76	pub fn children(&self, parent: &SymbolId) -> Vec<SymbolSummary> {
77		self.snapshot
78			.index
79			.symbols
80			.iter()
81			.filter(|symbol| symbol.navigable && symbol.parent.as_ref() == Some(parent))
82			.map(|symbol| self.summary(symbol))
83			.collect()
84	}
85
86	pub fn detail(&self, symbol: &SymbolId) -> Option<SymbolDetail> {
87		let record = self.find(symbol)?;
88		Some(SymbolDetail {
89			symbol: self.summary(record),
90			children: self.children(symbol),
91		})
92	}
93
94	pub fn find(&self, id: &SymbolId) -> Option<&'a SymbolRecord> {
95		let record = self
96			.snapshot
97			.index
98			.symbols
99			.file_records(id.file())
100			.get(id.def());
101		if let Some(record) = record
102			&& &record.id == id
103		{
104			return Some(record);
105		}
106		self.snapshot
107			.index
108			.symbols
109			.iter()
110			.find(|symbol| &symbol.id == id)
111	}
112
113	pub(super) fn summary(&self, symbol: &SymbolRecord) -> SymbolSummary {
114		SymbolSummary {
115			id: symbol.id,
116			source: symbol.source,
117			identity: symbol.identity.clone(),
118			name: symbol.name.clone(),
119			kind: symbol.kind.clone(),
120			line_range: symbol.line_range,
121			child_count: self
122				.snapshot
123				.index
124				.symbols
125				.iter()
126				.filter(|child| child.navigable && child.parent.as_ref() == Some(&symbol.id))
127				.count(),
128			change: self.change_status_for_symbol(&symbol.id),
129		}
130	}
131
132	fn change_status_for_symbol(&self, symbol: &SymbolId) -> Option<ChangeStatus> {
133		self.snapshot
134			.changes
135			.changes
136			.iter()
137			.find(|change| change.symbol.as_ref() == Some(symbol))
138			.map(|change| change.status)
139	}
140}