searchable.rs

  1use std::{any::Any, sync::Arc};
  2
  3use any_vec::AnyVec;
  4use gpui::{
  5    AnyView, AnyWeakEntity, App, Context, Entity, EventEmitter, Subscription, Task, WeakEntity,
  6    Window,
  7};
  8use project::search::SearchQuery;
  9
 10use crate::{
 11    item::{Item, WeakItemHandle},
 12    ItemHandle,
 13};
 14
 15#[derive(Clone, Debug)]
 16pub enum SearchEvent {
 17    MatchesInvalidated,
 18    ActiveMatchChanged,
 19}
 20
 21#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
 22pub enum Direction {
 23    Prev,
 24    #[default]
 25    Next,
 26}
 27
 28impl Direction {
 29    pub fn opposite(&self) -> Self {
 30        match self {
 31            Direction::Prev => Direction::Next,
 32            Direction::Next => Direction::Prev,
 33        }
 34    }
 35}
 36
 37#[derive(Clone, Copy, Debug, Default)]
 38pub struct SearchOptions {
 39    pub case: bool,
 40    pub word: bool,
 41    pub regex: bool,
 42    /// Specifies whether the  supports search & replace.
 43    pub replacement: bool,
 44    pub selection: bool,
 45}
 46
 47pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
 48    type Match: Any + Sync + Send + Clone;
 49
 50    fn supported_options() -> SearchOptions {
 51        SearchOptions {
 52            case: true,
 53            word: true,
 54            regex: true,
 55            replacement: true,
 56            selection: true,
 57        }
 58    }
 59
 60    fn search_bar_visibility_changed(
 61        &mut self,
 62        _visible: bool,
 63        _window: &mut Window,
 64        _cx: &mut Context<Self>,
 65    ) {
 66    }
 67
 68    fn has_filtered_search_ranges(&mut self) -> bool {
 69        Self::supported_options().selection
 70    }
 71
 72    fn toggle_filtered_search_ranges(
 73        &mut self,
 74        _enabled: bool,
 75        _window: &mut Window,
 76        _cx: &mut Context<Self>,
 77    ) {
 78    }
 79
 80    fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec<Self::Match> {
 81        Vec::new()
 82    }
 83    fn clear_matches(&mut self, window: &mut Window, cx: &mut Context<Self>);
 84    fn update_matches(
 85        &mut self,
 86        matches: &[Self::Match],
 87        window: &mut Window,
 88        cx: &mut Context<Self>,
 89    );
 90    fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String;
 91    fn activate_match(
 92        &mut self,
 93        index: usize,
 94        matches: &[Self::Match],
 95        window: &mut Window,
 96        cx: &mut Context<Self>,
 97    );
 98    fn select_matches(
 99        &mut self,
100        matches: &[Self::Match],
101        window: &mut Window,
102        cx: &mut Context<Self>,
103    );
104    fn replace(
105        &mut self,
106        _: &Self::Match,
107        _: &SearchQuery,
108        _window: &mut Window,
109        _: &mut Context<Self>,
110    );
111    fn replace_all(
112        &mut self,
113        matches: &mut dyn Iterator<Item = &Self::Match>,
114        query: &SearchQuery,
115        window: &mut Window,
116        cx: &mut Context<Self>,
117    ) {
118        for item in matches {
119            self.replace(item, query, window, cx);
120        }
121    }
122    fn match_index_for_direction(
123        &mut self,
124        matches: &[Self::Match],
125        current_index: usize,
126        direction: Direction,
127        count: usize,
128        _window: &mut Window,
129        _: &mut Context<Self>,
130    ) -> usize {
131        match direction {
132            Direction::Prev => {
133                let count = count % matches.len();
134                if current_index >= count {
135                    current_index - count
136                } else {
137                    matches.len() - (count - current_index)
138                }
139            }
140            Direction::Next => (current_index + count) % matches.len(),
141        }
142    }
143    fn find_matches(
144        &mut self,
145        query: Arc<SearchQuery>,
146        window: &mut Window,
147        cx: &mut Context<Self>,
148    ) -> Task<Vec<Self::Match>>;
149    fn active_match_index(
150        &mut self,
151        matches: &[Self::Match],
152        window: &mut Window,
153        cx: &mut Context<Self>,
154    ) -> Option<usize>;
155}
156
157pub trait SearchableItemHandle: ItemHandle {
158    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
159    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
160    fn supported_options(&self) -> SearchOptions;
161    fn subscribe_to_search_events(
162        &self,
163        window: &mut Window,
164        cx: &mut App,
165        handler: Box<dyn Fn(&SearchEvent, &mut Window, &mut App) + Send>,
166    ) -> Subscription;
167    fn clear_matches(&self, window: &mut Window, cx: &mut App);
168    fn update_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App);
169    fn query_suggestion(&self, window: &mut Window, cx: &mut App) -> String;
170    fn activate_match(
171        &self,
172        index: usize,
173        matches: &AnyVec<dyn Send>,
174        window: &mut Window,
175        cx: &mut App,
176    );
177    fn select_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App);
178    fn replace(
179        &self,
180        _: any_vec::element::ElementRef<'_, dyn Send>,
181        _: &SearchQuery,
182        _window: &mut Window,
183        _: &mut App,
184    );
185    fn replace_all(
186        &self,
187        matches: &mut dyn Iterator<Item = any_vec::element::ElementRef<'_, dyn Send>>,
188        query: &SearchQuery,
189        window: &mut Window,
190        cx: &mut App,
191    );
192    fn match_index_for_direction(
193        &self,
194        matches: &AnyVec<dyn Send>,
195        current_index: usize,
196        direction: Direction,
197        count: usize,
198        window: &mut Window,
199        cx: &mut App,
200    ) -> usize;
201    fn find_matches(
202        &self,
203        query: Arc<SearchQuery>,
204        window: &mut Window,
205        cx: &mut App,
206    ) -> Task<AnyVec<dyn Send>>;
207    fn active_match_index(
208        &self,
209        matches: &AnyVec<dyn Send>,
210        window: &mut Window,
211        cx: &mut App,
212    ) -> Option<usize>;
213    fn search_bar_visibility_changed(&self, visible: bool, window: &mut Window, cx: &mut App);
214
215    fn toggle_filtered_search_ranges(&mut self, enabled: bool, window: &mut Window, cx: &mut App);
216}
217
218impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
219    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
220        Box::new(self.downgrade())
221    }
222
223    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
224        Box::new(self.clone())
225    }
226
227    fn supported_options(&self) -> SearchOptions {
228        T::supported_options()
229    }
230
231    fn subscribe_to_search_events(
232        &self,
233        window: &mut Window,
234        cx: &mut App,
235        handler: Box<dyn Fn(&SearchEvent, &mut Window, &mut App) + Send>,
236    ) -> Subscription {
237        window.subscribe(self, cx, move |_, event: &SearchEvent, window, cx| {
238            handler(event, window, cx)
239        })
240    }
241
242    fn clear_matches(&self, window: &mut Window, cx: &mut App) {
243        self.update(cx, |this, cx| this.clear_matches(window, cx));
244    }
245    fn update_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App) {
246        let matches = matches.downcast_ref().unwrap();
247        self.update(cx, |this, cx| {
248            this.update_matches(matches.as_slice(), window, cx)
249        });
250    }
251    fn query_suggestion(&self, window: &mut Window, cx: &mut App) -> String {
252        self.update(cx, |this, cx| this.query_suggestion(window, cx))
253    }
254    fn activate_match(
255        &self,
256        index: usize,
257        matches: &AnyVec<dyn Send>,
258        window: &mut Window,
259        cx: &mut App,
260    ) {
261        let matches = matches.downcast_ref().unwrap();
262        self.update(cx, |this, cx| {
263            this.activate_match(index, matches.as_slice(), window, cx)
264        });
265    }
266
267    fn select_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App) {
268        let matches = matches.downcast_ref().unwrap();
269        self.update(cx, |this, cx| {
270            this.select_matches(matches.as_slice(), window, cx)
271        });
272    }
273
274    fn match_index_for_direction(
275        &self,
276        matches: &AnyVec<dyn Send>,
277        current_index: usize,
278        direction: Direction,
279        count: usize,
280        window: &mut Window,
281        cx: &mut App,
282    ) -> usize {
283        let matches = matches.downcast_ref().unwrap();
284        self.update(cx, |this, cx| {
285            this.match_index_for_direction(
286                matches.as_slice(),
287                current_index,
288                direction,
289                count,
290                window,
291                cx,
292            )
293        })
294    }
295    fn find_matches(
296        &self,
297        query: Arc<SearchQuery>,
298        window: &mut Window,
299        cx: &mut App,
300    ) -> Task<AnyVec<dyn Send>> {
301        let matches = self.update(cx, |this, cx| this.find_matches(query, window, cx));
302        window.spawn(cx, |_| async {
303            let matches = matches.await;
304            let mut any_matches = AnyVec::with_capacity::<T::Match>(matches.len());
305            {
306                let mut any_matches = any_matches.downcast_mut::<T::Match>().unwrap();
307                for mat in matches {
308                    any_matches.push(mat);
309                }
310            }
311            any_matches
312        })
313    }
314    fn active_match_index(
315        &self,
316        matches: &AnyVec<dyn Send>,
317        window: &mut Window,
318        cx: &mut App,
319    ) -> Option<usize> {
320        let matches = matches.downcast_ref()?;
321        self.update(cx, |this, cx| {
322            this.active_match_index(matches.as_slice(), window, cx)
323        })
324    }
325
326    fn replace(
327        &self,
328        mat: any_vec::element::ElementRef<'_, dyn Send>,
329        query: &SearchQuery,
330        window: &mut Window,
331        cx: &mut App,
332    ) {
333        let mat = mat.downcast_ref().unwrap();
334        self.update(cx, |this, cx| this.replace(mat, query, window, cx))
335    }
336
337    fn replace_all(
338        &self,
339        matches: &mut dyn Iterator<Item = any_vec::element::ElementRef<'_, dyn Send>>,
340        query: &SearchQuery,
341        window: &mut Window,
342        cx: &mut App,
343    ) {
344        self.update(cx, |this, cx| {
345            this.replace_all(
346                &mut matches.map(|m| m.downcast_ref().unwrap()),
347                query,
348                window,
349                cx,
350            );
351        })
352    }
353
354    fn search_bar_visibility_changed(&self, visible: bool, window: &mut Window, cx: &mut App) {
355        self.update(cx, |this, cx| {
356            this.search_bar_visibility_changed(visible, window, cx)
357        });
358    }
359
360    fn toggle_filtered_search_ranges(&mut self, enabled: bool, window: &mut Window, cx: &mut App) {
361        self.update(cx, |this, cx| {
362            this.toggle_filtered_search_ranges(enabled, window, cx)
363        });
364    }
365}
366
367impl From<Box<dyn SearchableItemHandle>> for AnyView {
368    fn from(this: Box<dyn SearchableItemHandle>) -> Self {
369        this.to_any().clone()
370    }
371}
372
373impl From<&Box<dyn SearchableItemHandle>> for AnyView {
374    fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
375        this.to_any().clone()
376    }
377}
378
379impl PartialEq for Box<dyn SearchableItemHandle> {
380    fn eq(&self, other: &Self) -> bool {
381        self.item_id() == other.item_id()
382    }
383}
384
385impl Eq for Box<dyn SearchableItemHandle> {}
386
387pub trait WeakSearchableItemHandle: WeakItemHandle {
388    fn upgrade(&self, cx: &App) -> Option<Box<dyn SearchableItemHandle>>;
389
390    fn into_any(self) -> AnyWeakEntity;
391}
392
393impl<T: SearchableItem> WeakSearchableItemHandle for WeakEntity<T> {
394    fn upgrade(&self, _cx: &App) -> Option<Box<dyn SearchableItemHandle>> {
395        Some(Box::new(self.upgrade()?))
396    }
397
398    fn into_any(self) -> AnyWeakEntity {
399        self.into()
400    }
401}
402
403impl PartialEq for Box<dyn WeakSearchableItemHandle> {
404    fn eq(&self, other: &Self) -> bool {
405        self.id() == other.id()
406    }
407}
408
409impl Eq for Box<dyn WeakSearchableItemHandle> {}
410
411impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
412    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
413        self.id().hash(state)
414    }
415}