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