searchable.rs

  1use std::{any::Any, sync::Arc};
  2
  3use gpui2::{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
131impl<T: SearchableItem> SearchableItemHandle for View<T> {
132    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
133        // Box::new(self.downgrade())
134        todo!()
135    }
136
137    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
138        Box::new(self.clone())
139    }
140
141    fn supported_options(&self) -> SearchOptions {
142        T::supported_options()
143    }
144
145    fn subscribe_to_search_events(
146        &self,
147        cx: &mut WindowContext,
148        handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>,
149    ) -> Subscription {
150        cx.subscribe(self, move |handle, event, cx| {
151            let search_event = handle.update(cx, |handle, cx| handle.to_search_event(event, cx));
152            if let Some(search_event) = search_event {
153                handler(search_event, cx)
154            }
155        })
156    }
157
158    fn clear_matches(&self, cx: &mut WindowContext) {
159        self.update(cx, |this, cx| this.clear_matches(cx));
160    }
161    fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
162        let matches = downcast_matches(matches);
163        self.update(cx, |this, cx| this.update_matches(matches, cx));
164    }
165    fn query_suggestion(&self, cx: &mut WindowContext) -> String {
166        self.update(cx, |this, cx| this.query_suggestion(cx))
167    }
168    fn activate_match(
169        &self,
170        index: usize,
171        matches: &Vec<Box<dyn Any + Send>>,
172        cx: &mut WindowContext,
173    ) {
174        let matches = downcast_matches(matches);
175        self.update(cx, |this, cx| this.activate_match(index, matches, cx));
176    }
177
178    fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
179        let matches = downcast_matches(matches);
180        self.update(cx, |this, cx| this.select_matches(matches, cx));
181    }
182
183    fn match_index_for_direction(
184        &self,
185        matches: &Vec<Box<dyn Any + Send>>,
186        current_index: usize,
187        direction: Direction,
188        count: usize,
189        cx: &mut WindowContext,
190    ) -> usize {
191        let matches = downcast_matches(matches);
192        self.update(cx, |this, cx| {
193            this.match_index_for_direction(&matches, current_index, direction, count, cx)
194        })
195    }
196    fn find_matches(
197        &self,
198        query: Arc<SearchQuery>,
199        cx: &mut WindowContext,
200    ) -> Task<Vec<Box<dyn Any + Send>>> {
201        let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
202        cx.spawn_on_main(|cx| async {
203            let matches = matches.await;
204            matches
205                .into_iter()
206                .map::<Box<dyn Any + Send>, _>(|range| Box::new(range))
207                .collect()
208        })
209    }
210    fn active_match_index(
211        &self,
212        matches: &Vec<Box<dyn Any + Send>>,
213        cx: &mut WindowContext,
214    ) -> Option<usize> {
215        let matches = downcast_matches(matches);
216        self.update(cx, |this, cx| this.active_match_index(matches, cx))
217    }
218
219    fn replace(&self, matches: &Box<dyn Any + Send>, query: &SearchQuery, cx: &mut WindowContext) {
220        let matches = matches.downcast_ref().unwrap();
221        self.update(cx, |this, cx| this.replace(matches, query, cx))
222    }
223}
224
225fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T> {
226    matches
227        .iter()
228        .map(|range| range.downcast_ref::<T>().cloned())
229        .collect::<Option<Vec<_>>>()
230        .expect(
231            "SearchableItemHandle function called with vec of matches of a different type than expected",
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 From<&Box<dyn SearchableItemHandle>> for AnyView {
242    fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
243        this.to_any().clone()
244    }
245}
246
247impl PartialEq for Box<dyn SearchableItemHandle> {
248    fn eq(&self, other: &Self) -> bool {
249        self.id() == other.id()
250    }
251}
252
253impl Eq for Box<dyn SearchableItemHandle> {}
254
255pub trait WeakSearchableItemHandle: WeakItemHandle {
256    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
257
258    // fn into_any(self) -> AnyWeakView;
259}
260
261// todo!()
262// impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
263//     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
264//         Some(Box::new(self.upgrade(cx)?))
265//     }
266
267//     // fn into_any(self) -> AnyView {
268//     //     self.into_any()
269//     // }
270// }
271
272impl PartialEq for Box<dyn WeakSearchableItemHandle> {
273    fn eq(&self, other: &Self) -> bool {
274        self.id() == other.id()
275    }
276}
277
278impl Eq for Box<dyn WeakSearchableItemHandle> {}
279
280impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
281    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
282        self.id().hash(state)
283    }
284}