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