searchable.rs

  1use std::any::Any;
  2
  3use gpui::{
  4    AnyViewHandle, AnyWeakViewHandle, AppContext, MutableAppContext, Subscription, Task,
  5    ViewContext, ViewHandle, WeakViewHandle,
  6};
  7use project::search::SearchQuery;
  8
  9use crate::{Item, ItemHandle, WeakItemHandle};
 10
 11#[derive(Debug)]
 12pub enum SearchEvent {
 13    MatchesInvalidated,
 14    ActiveMatchChanged,
 15}
 16
 17#[derive(Clone, Copy, PartialEq, Eq)]
 18pub enum Direction {
 19    Prev,
 20    Next,
 21}
 22
 23pub trait SearchableItem: Item {
 24    type Match: Any + Sync + Send + Clone;
 25
 26    fn to_search_event(event: &Self::Event) -> Option<SearchEvent>;
 27    fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
 28    fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
 29    fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
 30    fn activate_next_match(
 31        &mut self,
 32        index: usize,
 33        direction: Direction,
 34        matches: Vec<Self::Match>,
 35        cx: &mut ViewContext<Self>,
 36    );
 37    fn activate_match_at_index(
 38        &mut self,
 39        index: usize,
 40        matches: Vec<Self::Match>,
 41        cx: &mut ViewContext<Self>,
 42    );
 43    fn find_matches(
 44        &mut self,
 45        query: SearchQuery,
 46        cx: &mut ViewContext<Self>,
 47    ) -> Task<Vec<Self::Match>>;
 48    fn active_match_index(
 49        &mut self,
 50        matches: Vec<Self::Match>,
 51        cx: &mut ViewContext<Self>,
 52    ) -> Option<usize>;
 53}
 54
 55pub trait SearchableItemHandle: ItemHandle {
 56    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
 57    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
 58    fn subscribe(
 59        &self,
 60        cx: &mut MutableAppContext,
 61        handler: Box<dyn Fn(SearchEvent, &mut MutableAppContext)>,
 62    ) -> Subscription;
 63    fn clear_highlights(&self, cx: &mut MutableAppContext);
 64    fn highlight_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut MutableAppContext);
 65    fn query_suggestion(&self, cx: &mut MutableAppContext) -> String;
 66    fn select_next_match_in_direction(
 67        &self,
 68        index: usize,
 69        direction: Direction,
 70        matches: &Vec<Box<dyn Any + Send>>,
 71        cx: &mut MutableAppContext,
 72    );
 73    fn select_match_by_index(
 74        &self,
 75        index: usize,
 76        matches: &Vec<Box<dyn Any + Send>>,
 77        cx: &mut MutableAppContext,
 78    );
 79    fn matches(
 80        &self,
 81        query: SearchQuery,
 82        cx: &mut MutableAppContext,
 83    ) -> Task<Vec<Box<dyn Any + Send>>>;
 84    fn active_match_index(
 85        &self,
 86        matches: &Vec<Box<dyn Any + Send>>,
 87        cx: &mut MutableAppContext,
 88    ) -> Option<usize>;
 89}
 90
 91impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
 92    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
 93        Box::new(self.downgrade())
 94    }
 95
 96    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
 97        Box::new(self.clone())
 98    }
 99
100    fn subscribe(
101        &self,
102        cx: &mut MutableAppContext,
103        handler: Box<dyn Fn(SearchEvent, &mut MutableAppContext)>,
104    ) -> Subscription {
105        cx.subscribe(self, move |_, event, cx| {
106            if let Some(search_event) = T::to_search_event(event) {
107                handler(search_event, cx)
108            }
109        })
110    }
111
112    fn clear_highlights(&self, cx: &mut MutableAppContext) {
113        self.update(cx, |this, cx| this.clear_matches(cx));
114    }
115    fn highlight_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut MutableAppContext) {
116        let matches = downcast_matches(matches);
117        self.update(cx, |this, cx| this.update_matches(matches, cx));
118    }
119    fn query_suggestion(&self, cx: &mut MutableAppContext) -> String {
120        self.update(cx, |this, cx| this.query_suggestion(cx))
121    }
122    fn select_next_match_in_direction(
123        &self,
124        index: usize,
125        direction: Direction,
126        matches: &Vec<Box<dyn Any + Send>>,
127        cx: &mut MutableAppContext,
128    ) {
129        let matches = downcast_matches(matches);
130        self.update(cx, |this, cx| {
131            this.activate_next_match(index, direction, matches, cx)
132        });
133    }
134    fn select_match_by_index(
135        &self,
136        index: usize,
137        matches: &Vec<Box<dyn Any + Send>>,
138        cx: &mut MutableAppContext,
139    ) {
140        let matches = downcast_matches(matches);
141        self.update(cx, |this, cx| {
142            this.activate_match_at_index(index, matches, cx)
143        });
144    }
145    fn matches(
146        &self,
147        query: SearchQuery,
148        cx: &mut MutableAppContext,
149    ) -> Task<Vec<Box<dyn Any + Send>>> {
150        let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
151        cx.foreground().spawn(async {
152            let matches = matches.await;
153            matches
154                .into_iter()
155                .map::<Box<dyn Any + Send>, _>(|range| Box::new(range))
156                .collect()
157        })
158    }
159    fn active_match_index(
160        &self,
161        matches: &Vec<Box<dyn Any + Send>>,
162        cx: &mut MutableAppContext,
163    ) -> Option<usize> {
164        let matches = downcast_matches(matches);
165        self.update(cx, |this, cx| this.active_match_index(matches, cx))
166    }
167}
168
169fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T> {
170    matches
171        .iter()
172        .map(|range| range.downcast_ref::<T>().cloned())
173        .collect::<Option<Vec<_>>>()
174        .expect(
175            "SearchableItemHandle function called with vec of matches of a different type than expected",
176        )
177}
178
179impl From<Box<dyn SearchableItemHandle>> for AnyViewHandle {
180    fn from(this: Box<dyn SearchableItemHandle>) -> Self {
181        this.to_any()
182    }
183}
184
185impl From<&Box<dyn SearchableItemHandle>> for AnyViewHandle {
186    fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
187        this.to_any()
188    }
189}
190
191impl PartialEq for Box<dyn SearchableItemHandle> {
192    fn eq(&self, other: &Self) -> bool {
193        self.id() == other.id() && self.window_id() == other.window_id()
194    }
195}
196
197impl Eq for Box<dyn SearchableItemHandle> {}
198
199pub trait WeakSearchableItemHandle: WeakItemHandle {
200    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
201
202    fn to_any(self) -> AnyWeakViewHandle;
203}
204
205impl<T: SearchableItem> WeakSearchableItemHandle for WeakViewHandle<T> {
206    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
207        Some(Box::new(self.upgrade(cx)?))
208    }
209
210    fn to_any(self) -> AnyWeakViewHandle {
211        self.into()
212    }
213}
214
215impl PartialEq for Box<dyn WeakSearchableItemHandle> {
216    fn eq(&self, other: &Self) -> bool {
217        self.id() == other.id() && self.window_id() == other.window_id()
218    }
219}
220
221impl Eq for Box<dyn WeakSearchableItemHandle> {}
222
223impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
224    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
225        (self.id(), self.window_id()).hash(state)
226    }
227}