searchable.rs

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