1use std::any::Any;
2
3use gpui::{
4 AnyViewHandle, AnyWeakViewHandle, AppContext, MutableAppContext, Subscription, Task,
5 ViewContext, ViewHandle, WeakViewHandle,
6};
7use project::search::SearchQuery;
8
9use crate::{Item, ItemHandle, WeakItemHandle};
10
11#[derive(Debug)]
12pub enum SearchEvent {
13 ContentsUpdated,
14 SelectionsChanged,
15}
16
17#[derive(Clone, Copy, PartialEq, Eq)]
18pub enum Direction {
19 Prev,
20 Next,
21}
22
23pub trait SearchableItem: Item {
24 type Match: Any + Sync + Send + Clone;
25
26 fn to_search_event(event: &Self::Event) -> Option<SearchEvent>;
27 fn clear_highlights(&mut self, cx: &mut ViewContext<Self>);
28 fn highlight_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
29 fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
30 fn select_next_match_in_direction(
31 &mut self,
32 index: usize,
33 direction: Direction,
34 matches: Vec<Self::Match>,
35 cx: &mut ViewContext<Self>,
36 );
37 fn select_match_by_index(
38 &mut self,
39 index: usize,
40 matches: Vec<Self::Match>,
41 cx: &mut ViewContext<Self>,
42 );
43 fn matches(&mut self, query: SearchQuery, cx: &mut ViewContext<Self>)
44 -> Task<Vec<Self::Match>>;
45 fn active_match_index(
46 &mut self,
47 matches: Vec<Self::Match>,
48 cx: &mut ViewContext<Self>,
49 ) -> Option<usize>;
50}
51
52pub trait SearchableItemHandle: ItemHandle {
53 fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
54 fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
55 fn subscribe(
56 &self,
57 cx: &mut MutableAppContext,
58 handler: Box<dyn Fn(SearchEvent, &mut MutableAppContext)>,
59 ) -> Subscription;
60 fn clear_highlights(&self, cx: &mut MutableAppContext);
61 fn highlight_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut MutableAppContext);
62 fn query_suggestion(&self, cx: &mut MutableAppContext) -> String;
63 fn select_next_match_in_direction(
64 &self,
65 index: usize,
66 direction: Direction,
67 matches: &Vec<Box<dyn Any + Send>>,
68 cx: &mut MutableAppContext,
69 );
70 fn select_match_by_index(
71 &self,
72 index: usize,
73 matches: &Vec<Box<dyn Any + Send>>,
74 cx: &mut MutableAppContext,
75 );
76 fn matches(
77 &self,
78 query: SearchQuery,
79 cx: &mut MutableAppContext,
80 ) -> Task<Vec<Box<dyn Any + Send>>>;
81 fn active_match_index(
82 &self,
83 matches: &Vec<Box<dyn Any + Send>>,
84 cx: &mut MutableAppContext,
85 ) -> Option<usize>;
86}
87
88impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
89 fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
90 Box::new(self.downgrade())
91 }
92
93 fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
94 Box::new(self.clone())
95 }
96
97 fn subscribe(
98 &self,
99 cx: &mut MutableAppContext,
100 handler: Box<dyn Fn(SearchEvent, &mut MutableAppContext)>,
101 ) -> Subscription {
102 cx.subscribe(self, move |_, event, cx| {
103 if let Some(search_event) = T::to_search_event(event) {
104 handler(search_event, cx)
105 }
106 })
107 }
108
109 fn clear_highlights(&self, cx: &mut MutableAppContext) {
110 self.update(cx, |this, cx| this.clear_highlights(cx));
111 }
112 fn highlight_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut MutableAppContext) {
113 let matches = downcast_matches(matches);
114 self.update(cx, |this, cx| this.highlight_matches(matches, cx));
115 }
116 fn query_suggestion(&self, cx: &mut MutableAppContext) -> String {
117 self.update(cx, |this, cx| this.query_suggestion(cx))
118 }
119 fn select_next_match_in_direction(
120 &self,
121 index: usize,
122 direction: Direction,
123 matches: &Vec<Box<dyn Any + Send>>,
124 cx: &mut MutableAppContext,
125 ) {
126 let matches = downcast_matches(matches);
127 self.update(cx, |this, cx| {
128 this.select_next_match_in_direction(index, direction, matches, cx)
129 });
130 }
131 fn select_match_by_index(
132 &self,
133 index: usize,
134 matches: &Vec<Box<dyn Any + Send>>,
135 cx: &mut MutableAppContext,
136 ) {
137 let matches = downcast_matches(matches);
138 self.update(cx, |this, cx| {
139 this.select_match_by_index(index, matches, cx)
140 });
141 }
142 fn matches(
143 &self,
144 query: SearchQuery,
145 cx: &mut MutableAppContext,
146 ) -> Task<Vec<Box<dyn Any + Send>>> {
147 let matches = self.update(cx, |this, cx| this.matches(query, cx));
148 cx.foreground().spawn(async {
149 let matches = matches.await;
150 matches
151 .into_iter()
152 .map::<Box<dyn Any + Send>, _>(|range| Box::new(range))
153 .collect()
154 })
155 }
156 fn active_match_index(
157 &self,
158 matches: &Vec<Box<dyn Any + Send>>,
159 cx: &mut MutableAppContext,
160 ) -> Option<usize> {
161 let matches = downcast_matches(matches);
162 self.update(cx, |this, cx| this.active_match_index(matches, cx))
163 }
164}
165
166fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T> {
167 matches
168 .iter()
169 .map(|range| range.downcast_ref::<T>().cloned())
170 .collect::<Option<Vec<_>>>()
171 .expect(
172 "SearchableItemHandle function called with vec of matches of a different type than expected",
173 )
174}
175
176impl From<Box<dyn SearchableItemHandle>> for AnyViewHandle {
177 fn from(this: Box<dyn SearchableItemHandle>) -> Self {
178 this.to_any()
179 }
180}
181
182impl From<&Box<dyn SearchableItemHandle>> for AnyViewHandle {
183 fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
184 this.to_any()
185 }
186}
187
188impl PartialEq for Box<dyn SearchableItemHandle> {
189 fn eq(&self, other: &Self) -> bool {
190 self.id() == other.id() && self.window_id() == other.window_id()
191 }
192}
193
194impl Eq for Box<dyn SearchableItemHandle> {}
195
196pub trait WeakSearchableItemHandle: WeakItemHandle {
197 fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
198
199 fn to_any(self) -> AnyWeakViewHandle;
200}
201
202impl<T: SearchableItem> WeakSearchableItemHandle for WeakViewHandle<T> {
203 fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
204 Some(Box::new(self.upgrade(cx)?))
205 }
206
207 fn to_any(self) -> AnyWeakViewHandle {
208 self.into()
209 }
210}
211
212impl PartialEq for Box<dyn WeakSearchableItemHandle> {
213 fn eq(&self, other: &Self) -> bool {
214 self.id() == other.id() && self.window_id() == other.window_id()
215 }
216}
217
218impl Eq for Box<dyn WeakSearchableItemHandle> {}
219
220impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
221 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
222 (self.id(), self.window_id()).hash(state)
223 }
224}