searchable.rs

  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}