1use std::{any::Any, sync::Arc};
2
3use gpui::{
4 AnyView, AppContext, EventEmitter, Subscription, Task, View, ViewContext, WeakView,
5 WindowContext,
6};
7use project2::search::SearchQuery;
8
9use crate::{
10 item::{Item, WeakItemHandle},
11 ItemHandle,
12};
13
14#[derive(Debug)]
15pub enum SearchEvent {
16 MatchesInvalidated,
17 ActiveMatchChanged,
18}
19
20#[derive(Clone, Copy, PartialEq, Eq, Debug)]
21pub enum Direction {
22 Prev,
23 Next,
24}
25
26#[derive(Clone, Copy, Debug, Default)]
27pub struct SearchOptions {
28 pub case: bool,
29 pub word: bool,
30 pub regex: bool,
31 /// Specifies whether the item supports search & replace.
32 pub replacement: bool,
33}
34
35pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
36 type Match: Any + Sync + Send + Clone;
37
38 fn supported_options() -> SearchOptions {
39 SearchOptions {
40 case: true,
41 word: true,
42 regex: true,
43 replacement: true,
44 }
45 }
46
47 fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
48 fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
49 fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
50 fn activate_match(
51 &mut self,
52 index: usize,
53 matches: Vec<Self::Match>,
54 cx: &mut ViewContext<Self>,
55 );
56 fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
57 fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext<Self>);
58 fn match_index_for_direction(
59 &mut self,
60 matches: &Vec<Self::Match>,
61 current_index: usize,
62 direction: Direction,
63 count: usize,
64 _: &mut ViewContext<Self>,
65 ) -> usize {
66 match direction {
67 Direction::Prev => {
68 let count = count % matches.len();
69 if current_index >= count {
70 current_index - count
71 } else {
72 matches.len() - (count - current_index)
73 }
74 }
75 Direction::Next => (current_index + count) % matches.len(),
76 }
77 }
78 fn find_matches(
79 &mut self,
80 query: Arc<SearchQuery>,
81 cx: &mut ViewContext<Self>,
82 ) -> Task<Vec<Self::Match>>;
83 fn active_match_index(
84 &mut self,
85 matches: Vec<Self::Match>,
86 cx: &mut ViewContext<Self>,
87 ) -> Option<usize>;
88}
89
90pub trait SearchableItemHandle: ItemHandle {
91 fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
92 fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
93 fn supported_options(&self) -> SearchOptions;
94 fn subscribe_to_search_events(
95 &self,
96 cx: &mut WindowContext,
97 handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
98 ) -> Subscription;
99 fn clear_matches(&self, cx: &mut WindowContext);
100 fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
101 fn query_suggestion(&self, cx: &mut WindowContext) -> String;
102 fn activate_match(
103 &self,
104 index: usize,
105 matches: &Vec<Box<dyn Any + Send>>,
106 cx: &mut WindowContext,
107 );
108 fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
109 fn replace(&self, _: &Box<dyn Any + Send>, _: &SearchQuery, _: &mut WindowContext);
110 fn match_index_for_direction(
111 &self,
112 matches: &Vec<Box<dyn Any + Send>>,
113 current_index: usize,
114 direction: Direction,
115 count: usize,
116 cx: &mut WindowContext,
117 ) -> usize;
118 fn find_matches(
119 &self,
120 query: Arc<SearchQuery>,
121 cx: &mut WindowContext,
122 ) -> Task<Vec<Box<dyn Any + Send>>>;
123 fn active_match_index(
124 &self,
125 matches: &Vec<Box<dyn Any + Send>>,
126 cx: &mut WindowContext,
127 ) -> Option<usize>;
128}
129
130// todo!("here is where we need to use AnyWeakView");
131impl<T: SearchableItem> SearchableItemHandle for View<T> {
132 fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
133 Box::new(self.downgrade())
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
255impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
256 fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
257 Some(Box::new(self.upgrade()?))
258 }
259
260 // fn into_any(self) -> AnyView {
261 // self.into_any()
262 // }
263}
264
265impl PartialEq for Box<dyn WeakSearchableItemHandle> {
266 fn eq(&self, other: &Self) -> bool {
267 self.id() == other.id()
268 }
269}
270
271impl Eq for Box<dyn WeakSearchableItemHandle> {}
272
273impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
274 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
275 self.id().hash(state)
276 }
277}