1use std::{any::Any, sync::Arc};
2
3use gpui::{
4 AnyView, AppContext, EventEmitter, Subscription, Task, View, ViewContext, WindowContext,
5};
6use project2::search::SearchQuery;
7
8use crate::{
9 item::{Item, WeakItemHandle},
10 ItemHandle,
11};
12
13#[derive(Debug)]
14pub enum SearchEvent {
15 MatchesInvalidated,
16 ActiveMatchChanged,
17}
18
19#[derive(Clone, Copy, PartialEq, Eq, Debug)]
20pub enum Direction {
21 Prev,
22 Next,
23}
24
25#[derive(Clone, Copy, Debug, Default)]
26pub struct SearchOptions {
27 pub case: bool,
28 pub word: bool,
29 pub regex: bool,
30 /// Specifies whether the item supports search & replace.
31 pub replacement: bool,
32}
33
34pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
35 type Match: Any + Sync + Send + Clone;
36
37 fn supported_options() -> SearchOptions {
38 SearchOptions {
39 case: true,
40 word: true,
41 regex: true,
42 replacement: true,
43 }
44 }
45
46 fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
47 fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
48 fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
49 fn activate_match(
50 &mut self,
51 index: usize,
52 matches: Vec<Self::Match>,
53 cx: &mut ViewContext<Self>,
54 );
55 fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
56 fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext<Self>);
57 fn match_index_for_direction(
58 &mut self,
59 matches: &Vec<Self::Match>,
60 current_index: usize,
61 direction: Direction,
62 count: usize,
63 _: &mut ViewContext<Self>,
64 ) -> usize {
65 match direction {
66 Direction::Prev => {
67 let count = count % matches.len();
68 if current_index >= count {
69 current_index - count
70 } else {
71 matches.len() - (count - current_index)
72 }
73 }
74 Direction::Next => (current_index + count) % matches.len(),
75 }
76 }
77 fn find_matches(
78 &mut self,
79 query: Arc<SearchQuery>,
80 cx: &mut ViewContext<Self>,
81 ) -> Task<Vec<Self::Match>>;
82 fn active_match_index(
83 &mut self,
84 matches: Vec<Self::Match>,
85 cx: &mut ViewContext<Self>,
86 ) -> Option<usize>;
87}
88
89pub trait SearchableItemHandle: ItemHandle {
90 fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
91 fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
92 fn supported_options(&self) -> SearchOptions;
93 fn subscribe_to_search_events(
94 &self,
95 cx: &mut WindowContext,
96 handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
97 ) -> Subscription;
98 fn clear_matches(&self, cx: &mut WindowContext);
99 fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
100 fn query_suggestion(&self, cx: &mut WindowContext) -> String;
101 fn activate_match(
102 &self,
103 index: usize,
104 matches: &Vec<Box<dyn Any + Send>>,
105 cx: &mut WindowContext,
106 );
107 fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
108 fn replace(&self, _: &Box<dyn Any + Send>, _: &SearchQuery, _: &mut WindowContext);
109 fn match_index_for_direction(
110 &self,
111 matches: &Vec<Box<dyn Any + Send>>,
112 current_index: usize,
113 direction: Direction,
114 count: usize,
115 cx: &mut WindowContext,
116 ) -> usize;
117 fn find_matches(
118 &self,
119 query: Arc<SearchQuery>,
120 cx: &mut WindowContext,
121 ) -> Task<Vec<Box<dyn Any + Send>>>;
122 fn active_match_index(
123 &self,
124 matches: &Vec<Box<dyn Any + Send>>,
125 cx: &mut WindowContext,
126 ) -> Option<usize>;
127}
128
129// todo!("here is where we need to use AnyWeakView");
130impl<T: SearchableItem> SearchableItemHandle for View<T> {
131 fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
132 // Box::new(self.downgrade())
133 todo!()
134 }
135
136 fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
137 Box::new(self.clone())
138 }
139
140 fn supported_options(&self) -> SearchOptions {
141 T::supported_options()
142 }
143
144 fn subscribe_to_search_events(
145 &self,
146 cx: &mut WindowContext,
147 handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
148 ) -> Subscription {
149 cx.subscribe(self, move |_, event: &SearchEvent, cx| handler(event, cx))
150 }
151
152 fn clear_matches(&self, cx: &mut WindowContext) {
153 self.update(cx, |this, cx| this.clear_matches(cx));
154 }
155 fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
156 let matches = downcast_matches(matches);
157 self.update(cx, |this, cx| this.update_matches(matches, cx));
158 }
159 fn query_suggestion(&self, cx: &mut WindowContext) -> String {
160 self.update(cx, |this, cx| this.query_suggestion(cx))
161 }
162 fn activate_match(
163 &self,
164 index: usize,
165 matches: &Vec<Box<dyn Any + Send>>,
166 cx: &mut WindowContext,
167 ) {
168 let matches = downcast_matches(matches);
169 self.update(cx, |this, cx| this.activate_match(index, matches, cx));
170 }
171
172 fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
173 let matches = downcast_matches(matches);
174 self.update(cx, |this, cx| this.select_matches(matches, cx));
175 }
176
177 fn match_index_for_direction(
178 &self,
179 matches: &Vec<Box<dyn Any + Send>>,
180 current_index: usize,
181 direction: Direction,
182 count: usize,
183 cx: &mut WindowContext,
184 ) -> usize {
185 let matches = downcast_matches(matches);
186 self.update(cx, |this, cx| {
187 this.match_index_for_direction(&matches, current_index, direction, count, cx)
188 })
189 }
190 fn find_matches(
191 &self,
192 query: Arc<SearchQuery>,
193 cx: &mut WindowContext,
194 ) -> Task<Vec<Box<dyn Any + Send>>> {
195 let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
196 cx.spawn(|cx| async {
197 let matches = matches.await;
198 matches
199 .into_iter()
200 .map::<Box<dyn Any + Send>, _>(|range| Box::new(range))
201 .collect()
202 })
203 }
204 fn active_match_index(
205 &self,
206 matches: &Vec<Box<dyn Any + Send>>,
207 cx: &mut WindowContext,
208 ) -> Option<usize> {
209 let matches = downcast_matches(matches);
210 self.update(cx, |this, cx| this.active_match_index(matches, cx))
211 }
212
213 fn replace(&self, matches: &Box<dyn Any + Send>, query: &SearchQuery, cx: &mut WindowContext) {
214 let matches = matches.downcast_ref().unwrap();
215 self.update(cx, |this, cx| this.replace(matches, query, cx))
216 }
217}
218
219fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T> {
220 matches
221 .iter()
222 .map(|range| range.downcast_ref::<T>().cloned())
223 .collect::<Option<Vec<_>>>()
224 .expect(
225 "SearchableItemHandle function called with vec of matches of a different type than expected",
226 )
227}
228
229impl From<Box<dyn SearchableItemHandle>> for AnyView {
230 fn from(this: Box<dyn SearchableItemHandle>) -> Self {
231 this.to_any().clone()
232 }
233}
234
235impl From<&Box<dyn SearchableItemHandle>> for AnyView {
236 fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
237 this.to_any().clone()
238 }
239}
240
241impl PartialEq for Box<dyn SearchableItemHandle> {
242 fn eq(&self, other: &Self) -> bool {
243 self.item_id() == other.item_id()
244 }
245}
246
247impl Eq for Box<dyn SearchableItemHandle> {}
248
249pub trait WeakSearchableItemHandle: WeakItemHandle {
250 fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
251
252 // fn into_any(self) -> AnyWeakView;
253}
254
255// todo!()
256// impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
257// fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
258// Some(Box::new(self.upgrade(cx)?))
259// }
260
261// // fn into_any(self) -> AnyView {
262// // self.into_any()
263// // }
264// }
265
266impl PartialEq for Box<dyn WeakSearchableItemHandle> {
267 fn eq(&self, other: &Self) -> bool {
268 self.id() == other.id()
269 }
270}
271
272impl Eq for Box<dyn WeakSearchableItemHandle> {}
273
274impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
275 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
276 self.id().hash(state)
277 }
278}