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    ItemHandle,
 12    item::{Item, WeakItemHandle},
 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        direction: Direction,
154        matches: &[Self::Match],
155        window: &mut Window,
156        cx: &mut Context<Self>,
157    ) -> Option<usize>;
158}
159
160pub trait SearchableItemHandle: ItemHandle {
161    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
162    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
163    fn supported_options(&self, cx: &App) -> SearchOptions;
164    fn subscribe_to_search_events(
165        &self,
166        window: &mut Window,
167        cx: &mut App,
168        handler: Box<dyn Fn(&SearchEvent, &mut Window, &mut App) + Send>,
169    ) -> Subscription;
170    fn clear_matches(&self, window: &mut Window, cx: &mut App);
171    fn update_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App);
172    fn query_suggestion(&self, window: &mut Window, cx: &mut App) -> String;
173    fn activate_match(
174        &self,
175        index: usize,
176        matches: &AnyVec<dyn Send>,
177        window: &mut Window,
178        cx: &mut App,
179    );
180    fn select_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App);
181    fn replace(
182        &self,
183        _: any_vec::element::ElementRef<'_, dyn Send>,
184        _: &SearchQuery,
185        _window: &mut Window,
186        _: &mut App,
187    );
188    fn replace_all(
189        &self,
190        matches: &mut dyn Iterator<Item = any_vec::element::ElementRef<'_, dyn Send>>,
191        query: &SearchQuery,
192        window: &mut Window,
193        cx: &mut App,
194    );
195    fn match_index_for_direction(
196        &self,
197        matches: &AnyVec<dyn Send>,
198        current_index: usize,
199        direction: Direction,
200        count: usize,
201        window: &mut Window,
202        cx: &mut App,
203    ) -> usize;
204    fn find_matches(
205        &self,
206        query: Arc<SearchQuery>,
207        window: &mut Window,
208        cx: &mut App,
209    ) -> Task<AnyVec<dyn Send>>;
210    fn active_match_index(
211        &self,
212        direction: Direction,
213        matches: &AnyVec<dyn Send>,
214        window: &mut Window,
215        cx: &mut App,
216    ) -> Option<usize>;
217    fn search_bar_visibility_changed(&self, visible: bool, window: &mut Window, cx: &mut App);
218
219    fn toggle_filtered_search_ranges(&mut self, enabled: bool, window: &mut Window, cx: &mut App);
220}
221
222impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
223    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
224        Box::new(self.downgrade())
225    }
226
227    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
228        Box::new(self.clone())
229    }
230
231    fn supported_options(&self, cx: &App) -> SearchOptions {
232        self.read(cx).supported_options()
233    }
234
235    fn subscribe_to_search_events(
236        &self,
237        window: &mut Window,
238        cx: &mut App,
239        handler: Box<dyn Fn(&SearchEvent, &mut Window, &mut App) + Send>,
240    ) -> Subscription {
241        window.subscribe(self, cx, move |_, event: &SearchEvent, window, cx| {
242            handler(event, window, cx)
243        })
244    }
245
246    fn clear_matches(&self, window: &mut Window, cx: &mut App) {
247        self.update(cx, |this, cx| this.clear_matches(window, cx));
248    }
249    fn update_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App) {
250        let matches = matches.downcast_ref().unwrap();
251        self.update(cx, |this, cx| {
252            this.update_matches(matches.as_slice(), window, cx)
253        });
254    }
255    fn query_suggestion(&self, window: &mut Window, cx: &mut App) -> String {
256        self.update(cx, |this, cx| this.query_suggestion(window, cx))
257    }
258    fn activate_match(
259        &self,
260        index: usize,
261        matches: &AnyVec<dyn Send>,
262        window: &mut Window,
263        cx: &mut App,
264    ) {
265        let matches = matches.downcast_ref().unwrap();
266        self.update(cx, |this, cx| {
267            this.activate_match(index, matches.as_slice(), window, cx)
268        });
269    }
270
271    fn select_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App) {
272        let matches = matches.downcast_ref().unwrap();
273        self.update(cx, |this, cx| {
274            this.select_matches(matches.as_slice(), window, cx)
275        });
276    }
277
278    fn match_index_for_direction(
279        &self,
280        matches: &AnyVec<dyn Send>,
281        current_index: usize,
282        direction: Direction,
283        count: usize,
284        window: &mut Window,
285        cx: &mut App,
286    ) -> usize {
287        let matches = matches.downcast_ref().unwrap();
288        self.update(cx, |this, cx| {
289            this.match_index_for_direction(
290                matches.as_slice(),
291                current_index,
292                direction,
293                count,
294                window,
295                cx,
296            )
297        })
298    }
299    fn find_matches(
300        &self,
301        query: Arc<SearchQuery>,
302        window: &mut Window,
303        cx: &mut App,
304    ) -> Task<AnyVec<dyn Send>> {
305        let matches = self.update(cx, |this, cx| this.find_matches(query, window, cx));
306        window.spawn(cx, async |_| {
307            let matches = matches.await;
308            let mut any_matches = AnyVec::with_capacity::<T::Match>(matches.len());
309            {
310                let mut any_matches = any_matches.downcast_mut::<T::Match>().unwrap();
311                for mat in matches {
312                    any_matches.push(mat);
313                }
314            }
315            any_matches
316        })
317    }
318    fn active_match_index(
319        &self,
320        direction: Direction,
321        matches: &AnyVec<dyn Send>,
322        window: &mut Window,
323        cx: &mut App,
324    ) -> Option<usize> {
325        let matches = matches.downcast_ref()?;
326        self.update(cx, |this, cx| {
327            this.active_match_index(direction, matches.as_slice(), window, cx)
328        })
329    }
330
331    fn replace(
332        &self,
333        mat: any_vec::element::ElementRef<'_, dyn Send>,
334        query: &SearchQuery,
335        window: &mut Window,
336        cx: &mut App,
337    ) {
338        let mat = mat.downcast_ref().unwrap();
339        self.update(cx, |this, cx| this.replace(mat, query, window, cx))
340    }
341
342    fn replace_all(
343        &self,
344        matches: &mut dyn Iterator<Item = any_vec::element::ElementRef<'_, dyn Send>>,
345        query: &SearchQuery,
346        window: &mut Window,
347        cx: &mut App,
348    ) {
349        self.update(cx, |this, cx| {
350            this.replace_all(
351                &mut matches.map(|m| m.downcast_ref().unwrap()),
352                query,
353                window,
354                cx,
355            );
356        })
357    }
358
359    fn search_bar_visibility_changed(&self, visible: bool, window: &mut Window, cx: &mut App) {
360        self.update(cx, |this, cx| {
361            this.search_bar_visibility_changed(visible, window, cx)
362        });
363    }
364
365    fn toggle_filtered_search_ranges(&mut self, enabled: bool, window: &mut Window, cx: &mut App) {
366        self.update(cx, |this, cx| {
367            this.toggle_filtered_search_ranges(enabled, window, cx)
368        });
369    }
370}
371
372impl From<Box<dyn SearchableItemHandle>> for AnyView {
373    fn from(this: Box<dyn SearchableItemHandle>) -> Self {
374        this.to_any()
375    }
376}
377
378impl From<&Box<dyn SearchableItemHandle>> for AnyView {
379    fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
380        this.to_any()
381    }
382}
383
384impl PartialEq for Box<dyn SearchableItemHandle> {
385    fn eq(&self, other: &Self) -> bool {
386        self.item_id() == other.item_id()
387    }
388}
389
390impl Eq for Box<dyn SearchableItemHandle> {}
391
392pub trait WeakSearchableItemHandle: WeakItemHandle {
393    fn upgrade(&self, cx: &App) -> Option<Box<dyn SearchableItemHandle>>;
394
395    fn into_any(self) -> AnyWeakEntity;
396}
397
398impl<T: SearchableItem> WeakSearchableItemHandle for WeakEntity<T> {
399    fn upgrade(&self, _cx: &App) -> Option<Box<dyn SearchableItemHandle>> {
400        Some(Box::new(self.upgrade()?))
401    }
402
403    fn into_any(self) -> AnyWeakEntity {
404        self.into()
405    }
406}
407
408impl PartialEq for Box<dyn WeakSearchableItemHandle> {
409    fn eq(&self, other: &Self) -> bool {
410        self.id() == other.id()
411    }
412}
413
414impl Eq for Box<dyn WeakSearchableItemHandle> {}
415
416impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
417    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
418        self.id().hash(state)
419    }
420}