1use crate::symbols::{SymbolKind, SymbolLocation, Utf16Range};
14use std::collections::BTreeMap;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct ResultSetId(u64);
19
20impl ResultSetId {
21 pub fn get(self) -> u64 {
23 self.0
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ResultSetKind {
30 References,
32 CallHierarchy,
34 TypeHierarchy,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct HierarchyItem {
41 pub name: String,
43 pub detail: Option<String>,
45 pub kind: SymbolKind,
47 pub location: SymbolLocation,
49 pub selection_range: Utf16Range,
51 pub data_json: Option<String>,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct ReferencesResultSet {
58 pub title: String,
60 pub locations: Vec<SymbolLocation>,
62 pub is_stale: bool,
64}
65
66impl ReferencesResultSet {
67 fn mentions_uri(&self, uri: &str) -> bool {
68 self.locations.iter().any(|loc| loc.uri == uri)
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct CallHierarchyIncomingCall {
75 pub from: HierarchyItem,
77 pub from_ranges: Vec<Utf16Range>,
79}
80
81impl CallHierarchyIncomingCall {
82 fn mentions_uri(&self, uri: &str) -> bool {
83 self.from.location.uri == uri
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
89pub struct CallHierarchyOutgoingCall {
90 pub to: HierarchyItem,
92 pub from_ranges: Vec<Utf16Range>,
94}
95
96impl CallHierarchyOutgoingCall {
97 fn mentions_uri(&self, uri: &str) -> bool {
98 self.to.location.uri == uri
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct CallHierarchyResultSet {
105 pub title: String,
107 pub roots: Vec<HierarchyItem>,
109 pub incoming: Vec<CallHierarchyIncomingCall>,
111 pub outgoing: Vec<CallHierarchyOutgoingCall>,
113 pub is_stale: bool,
115}
116
117impl CallHierarchyResultSet {
118 fn mentions_uri(&self, uri: &str) -> bool {
119 self.roots.iter().any(|it| it.location.uri == uri)
120 || self.incoming.iter().any(|c| c.mentions_uri(uri))
121 || self.outgoing.iter().any(|c| c.mentions_uri(uri))
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct TypeHierarchyResultSet {
128 pub title: String,
130 pub roots: Vec<HierarchyItem>,
132 pub supertypes: Vec<HierarchyItem>,
134 pub subtypes: Vec<HierarchyItem>,
136 pub is_stale: bool,
138}
139
140impl TypeHierarchyResultSet {
141 fn mentions_uri(&self, uri: &str) -> bool {
142 self.roots.iter().any(|it| it.location.uri == uri)
143 || self.supertypes.iter().any(|it| it.location.uri == uri)
144 || self.subtypes.iter().any(|it| it.location.uri == uri)
145 }
146}
147
148#[derive(Debug, Clone, PartialEq, Eq)]
150pub enum IntelligenceResultSet {
151 References(ReferencesResultSet),
153 CallHierarchy(CallHierarchyResultSet),
155 TypeHierarchy(TypeHierarchyResultSet),
157}
158
159impl IntelligenceResultSet {
160 pub fn kind(&self) -> ResultSetKind {
162 match self {
163 Self::References(_) => ResultSetKind::References,
164 Self::CallHierarchy(_) => ResultSetKind::CallHierarchy,
165 Self::TypeHierarchy(_) => ResultSetKind::TypeHierarchy,
166 }
167 }
168
169 pub fn title(&self) -> &str {
171 match self {
172 Self::References(s) => s.title.as_str(),
173 Self::CallHierarchy(s) => s.title.as_str(),
174 Self::TypeHierarchy(s) => s.title.as_str(),
175 }
176 }
177
178 pub fn is_stale(&self) -> bool {
180 match self {
181 Self::References(s) => s.is_stale,
182 Self::CallHierarchy(s) => s.is_stale,
183 Self::TypeHierarchy(s) => s.is_stale,
184 }
185 }
186
187 pub fn mark_stale(&mut self) {
189 match self {
190 Self::References(s) => s.is_stale = true,
191 Self::CallHierarchy(s) => s.is_stale = true,
192 Self::TypeHierarchy(s) => s.is_stale = true,
193 }
194 }
195
196 fn mentions_uri(&self, uri: &str) -> bool {
197 match self {
198 Self::References(s) => s.mentions_uri(uri),
199 Self::CallHierarchy(s) => s.mentions_uri(uri),
200 Self::TypeHierarchy(s) => s.mentions_uri(uri),
201 }
202 }
203}
204
205#[derive(Debug, Default)]
207pub struct WorkspaceIntelligence {
208 next_id: u64,
209 sets: BTreeMap<ResultSetId, IntelligenceResultSet>,
210}
211
212impl WorkspaceIntelligence {
213 pub fn len(&self) -> usize {
215 self.sets.len()
216 }
217
218 pub fn is_empty(&self) -> bool {
220 self.sets.is_empty()
221 }
222
223 pub fn ids(&self) -> Vec<ResultSetId> {
225 self.sets.keys().cloned().collect()
226 }
227
228 pub fn get(&self, id: ResultSetId) -> Option<&IntelligenceResultSet> {
230 self.sets.get(&id)
231 }
232
233 pub fn get_mut(&mut self, id: ResultSetId) -> Option<&mut IntelligenceResultSet> {
235 self.sets.get_mut(&id)
236 }
237
238 pub fn remove(&mut self, id: ResultSetId) -> Option<IntelligenceResultSet> {
240 self.sets.remove(&id)
241 }
242
243 pub fn clear(&mut self) {
245 self.sets.clear();
246 }
247
248 pub fn create(&mut self, set: IntelligenceResultSet) -> ResultSetId {
250 let id = ResultSetId(self.next_id);
251 self.next_id = self.next_id.saturating_add(1);
252 self.sets.insert(id, set);
253 id
254 }
255
256 pub fn create_references(
258 &mut self,
259 title: impl Into<String>,
260 locations: Vec<SymbolLocation>,
261 ) -> ResultSetId {
262 self.create(IntelligenceResultSet::References(ReferencesResultSet {
263 title: title.into(),
264 locations,
265 is_stale: false,
266 }))
267 }
268
269 pub fn mark_stale_for_uri(&mut self, uri: &str) -> bool {
273 let mut changed = false;
274 for set in self.sets.values_mut() {
275 if set.is_stale() {
276 continue;
277 }
278 if set.mentions_uri(uri) {
279 set.mark_stale();
280 changed = true;
281 }
282 }
283 changed
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290 use crate::symbols::{Utf16Position, Utf16Range};
291
292 #[test]
293 fn mark_stale_for_uri_marks_matching_sets_only() {
294 let mut intel = WorkspaceIntelligence::default();
295
296 let a_loc = SymbolLocation {
297 uri: "file:///a.rs".to_string(),
298 range: Utf16Range::new(Utf16Position::new(0, 0), Utf16Position::new(0, 1)),
299 };
300 let b_loc = SymbolLocation {
301 uri: "file:///b.rs".to_string(),
302 range: Utf16Range::new(Utf16Position::new(1, 0), Utf16Position::new(1, 1)),
303 };
304
305 let a_id = intel.create_references("refs a", vec![a_loc.clone()]);
306 let b_id = intel.create_references("refs b", vec![b_loc.clone()]);
307
308 assert!(!intel.get(a_id).unwrap().is_stale());
309 assert!(!intel.get(b_id).unwrap().is_stale());
310
311 assert!(intel.mark_stale_for_uri("file:///a.rs"));
312 assert!(intel.get(a_id).unwrap().is_stale());
313 assert!(!intel.get(b_id).unwrap().is_stale());
314
315 assert!(!intel.mark_stale_for_uri("file:///a.rs"));
317 }
318}