2023-10-26 10:27:20 +00:00
|
|
|
use std::{any::Any, sync::Arc};
|
|
|
|
|
2023-10-31 14:49:51 +00:00
|
|
|
use gpui2::{AnyView, AppContext, Subscription, Task, View, ViewContext, WindowContext};
|
2023-10-30 14:39:58 +00:00
|
|
|
use project2::search::SearchQuery;
|
2023-10-26 10:27:20 +00:00
|
|
|
|
2023-10-30 14:39:58 +00:00
|
|
|
use crate::{
|
|
|
|
item::{Item, WeakItemHandle},
|
|
|
|
ItemHandle,
|
|
|
|
};
|
2023-10-26 10:27:20 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum SearchEvent {
|
|
|
|
MatchesInvalidated,
|
|
|
|
ActiveMatchChanged,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
|
|
pub enum Direction {
|
|
|
|
Prev,
|
|
|
|
Next,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
|
|
pub struct SearchOptions {
|
|
|
|
pub case: bool,
|
|
|
|
pub word: bool,
|
|
|
|
pub regex: bool,
|
|
|
|
/// Specifies whether the item supports search & replace.
|
|
|
|
pub replacement: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait SearchableItem: Item {
|
|
|
|
type Match: Any + Sync + Send + Clone;
|
|
|
|
|
|
|
|
fn supported_options() -> SearchOptions {
|
|
|
|
SearchOptions {
|
|
|
|
case: true,
|
|
|
|
word: true,
|
|
|
|
regex: true,
|
|
|
|
replacement: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn to_search_event(
|
|
|
|
&mut self,
|
|
|
|
event: &Self::Event,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) -> Option<SearchEvent>;
|
|
|
|
fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
|
|
|
|
fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
|
|
|
|
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
|
|
|
|
fn activate_match(
|
|
|
|
&mut self,
|
|
|
|
index: usize,
|
|
|
|
matches: Vec<Self::Match>,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
);
|
|
|
|
fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
|
|
|
|
fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext<Self>);
|
|
|
|
fn match_index_for_direction(
|
|
|
|
&mut self,
|
|
|
|
matches: &Vec<Self::Match>,
|
|
|
|
current_index: usize,
|
|
|
|
direction: Direction,
|
|
|
|
count: usize,
|
|
|
|
_: &mut ViewContext<Self>,
|
|
|
|
) -> usize {
|
|
|
|
match direction {
|
|
|
|
Direction::Prev => {
|
|
|
|
let count = count % matches.len();
|
|
|
|
if current_index >= count {
|
|
|
|
current_index - count
|
|
|
|
} else {
|
|
|
|
matches.len() - (count - current_index)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Direction::Next => (current_index + count) % matches.len(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn find_matches(
|
|
|
|
&mut self,
|
|
|
|
query: Arc<SearchQuery>,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) -> Task<Vec<Self::Match>>;
|
|
|
|
fn active_match_index(
|
|
|
|
&mut self,
|
|
|
|
matches: Vec<Self::Match>,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) -> Option<usize>;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait SearchableItemHandle: ItemHandle {
|
|
|
|
fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
|
|
|
|
fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
|
|
|
|
fn supported_options(&self) -> SearchOptions;
|
|
|
|
fn subscribe_to_search_events(
|
|
|
|
&self,
|
|
|
|
cx: &mut WindowContext,
|
2023-10-31 14:49:51 +00:00
|
|
|
handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>,
|
2023-10-26 10:27:20 +00:00
|
|
|
) -> Subscription;
|
|
|
|
fn clear_matches(&self, cx: &mut WindowContext);
|
|
|
|
fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
|
|
|
|
fn query_suggestion(&self, cx: &mut WindowContext) -> String;
|
|
|
|
fn activate_match(
|
|
|
|
&self,
|
|
|
|
index: usize,
|
|
|
|
matches: &Vec<Box<dyn Any + Send>>,
|
|
|
|
cx: &mut WindowContext,
|
|
|
|
);
|
|
|
|
fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
|
|
|
|
fn replace(&self, _: &Box<dyn Any + Send>, _: &SearchQuery, _: &mut WindowContext);
|
|
|
|
fn match_index_for_direction(
|
|
|
|
&self,
|
|
|
|
matches: &Vec<Box<dyn Any + Send>>,
|
|
|
|
current_index: usize,
|
|
|
|
direction: Direction,
|
|
|
|
count: usize,
|
|
|
|
cx: &mut WindowContext,
|
|
|
|
) -> usize;
|
|
|
|
fn find_matches(
|
|
|
|
&self,
|
|
|
|
query: Arc<SearchQuery>,
|
|
|
|
cx: &mut WindowContext,
|
|
|
|
) -> Task<Vec<Box<dyn Any + Send>>>;
|
|
|
|
fn active_match_index(
|
|
|
|
&self,
|
|
|
|
matches: &Vec<Box<dyn Any + Send>>,
|
|
|
|
cx: &mut WindowContext,
|
|
|
|
) -> Option<usize>;
|
|
|
|
}
|
|
|
|
|
2023-10-31 17:03:01 +00:00
|
|
|
// todo!("here is where we need to use AnyWeakView");
|
2023-10-30 14:39:58 +00:00
|
|
|
impl<T: SearchableItem> SearchableItemHandle for View<T> {
|
2023-10-26 10:27:20 +00:00
|
|
|
fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
|
2023-10-31 14:49:51 +00:00
|
|
|
// Box::new(self.downgrade())
|
|
|
|
todo!()
|
2023-10-26 10:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
|
|
|
|
Box::new(self.clone())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn supported_options(&self) -> SearchOptions {
|
|
|
|
T::supported_options()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn subscribe_to_search_events(
|
|
|
|
&self,
|
|
|
|
cx: &mut WindowContext,
|
2023-10-31 14:49:51 +00:00
|
|
|
handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>,
|
2023-10-26 10:27:20 +00:00
|
|
|
) -> Subscription {
|
|
|
|
cx.subscribe(self, move |handle, event, cx| {
|
|
|
|
let search_event = handle.update(cx, |handle, cx| handle.to_search_event(event, cx));
|
|
|
|
if let Some(search_event) = search_event {
|
|
|
|
handler(search_event, cx)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clear_matches(&self, cx: &mut WindowContext) {
|
|
|
|
self.update(cx, |this, cx| this.clear_matches(cx));
|
|
|
|
}
|
|
|
|
fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
|
|
|
|
let matches = downcast_matches(matches);
|
|
|
|
self.update(cx, |this, cx| this.update_matches(matches, cx));
|
|
|
|
}
|
|
|
|
fn query_suggestion(&self, cx: &mut WindowContext) -> String {
|
|
|
|
self.update(cx, |this, cx| this.query_suggestion(cx))
|
|
|
|
}
|
|
|
|
fn activate_match(
|
|
|
|
&self,
|
|
|
|
index: usize,
|
|
|
|
matches: &Vec<Box<dyn Any + Send>>,
|
|
|
|
cx: &mut WindowContext,
|
|
|
|
) {
|
|
|
|
let matches = downcast_matches(matches);
|
|
|
|
self.update(cx, |this, cx| this.activate_match(index, matches, cx));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
|
|
|
|
let matches = downcast_matches(matches);
|
|
|
|
self.update(cx, |this, cx| this.select_matches(matches, cx));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn match_index_for_direction(
|
|
|
|
&self,
|
|
|
|
matches: &Vec<Box<dyn Any + Send>>,
|
|
|
|
current_index: usize,
|
|
|
|
direction: Direction,
|
|
|
|
count: usize,
|
|
|
|
cx: &mut WindowContext,
|
|
|
|
) -> usize {
|
|
|
|
let matches = downcast_matches(matches);
|
|
|
|
self.update(cx, |this, cx| {
|
|
|
|
this.match_index_for_direction(&matches, current_index, direction, count, cx)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
fn find_matches(
|
|
|
|
&self,
|
|
|
|
query: Arc<SearchQuery>,
|
|
|
|
cx: &mut WindowContext,
|
|
|
|
) -> Task<Vec<Box<dyn Any + Send>>> {
|
|
|
|
let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
|
2023-10-31 14:49:51 +00:00
|
|
|
cx.spawn_on_main(|cx| async {
|
2023-10-26 10:27:20 +00:00
|
|
|
let matches = matches.await;
|
|
|
|
matches
|
|
|
|
.into_iter()
|
|
|
|
.map::<Box<dyn Any + Send>, _>(|range| Box::new(range))
|
|
|
|
.collect()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
fn active_match_index(
|
|
|
|
&self,
|
|
|
|
matches: &Vec<Box<dyn Any + Send>>,
|
|
|
|
cx: &mut WindowContext,
|
|
|
|
) -> Option<usize> {
|
|
|
|
let matches = downcast_matches(matches);
|
|
|
|
self.update(cx, |this, cx| this.active_match_index(matches, cx))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn replace(&self, matches: &Box<dyn Any + Send>, query: &SearchQuery, cx: &mut WindowContext) {
|
|
|
|
let matches = matches.downcast_ref().unwrap();
|
|
|
|
self.update(cx, |this, cx| this.replace(matches, query, cx))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T> {
|
|
|
|
matches
|
|
|
|
.iter()
|
|
|
|
.map(|range| range.downcast_ref::<T>().cloned())
|
|
|
|
.collect::<Option<Vec<_>>>()
|
|
|
|
.expect(
|
|
|
|
"SearchableItemHandle function called with vec of matches of a different type than expected",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-10-31 14:49:51 +00:00
|
|
|
impl From<Box<dyn SearchableItemHandle>> for AnyView {
|
|
|
|
fn from(this: Box<dyn SearchableItemHandle>) -> Self {
|
|
|
|
this.to_any().clone()
|
|
|
|
}
|
|
|
|
}
|
2023-10-26 10:27:20 +00:00
|
|
|
|
2023-10-31 14:49:51 +00:00
|
|
|
impl From<&Box<dyn SearchableItemHandle>> for AnyView {
|
|
|
|
fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
|
|
|
|
this.to_any().clone()
|
|
|
|
}
|
|
|
|
}
|
2023-10-26 10:27:20 +00:00
|
|
|
|
|
|
|
impl PartialEq for Box<dyn SearchableItemHandle> {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
2023-10-31 14:49:51 +00:00
|
|
|
self.id() == other.id()
|
2023-10-26 10:27:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for Box<dyn SearchableItemHandle> {}
|
|
|
|
|
|
|
|
pub trait WeakSearchableItemHandle: WeakItemHandle {
|
|
|
|
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
|
|
|
|
|
2023-10-31 14:49:51 +00:00
|
|
|
// fn into_any(self) -> AnyWeakView;
|
2023-10-26 10:27:20 +00:00
|
|
|
}
|
|
|
|
|
2023-10-30 14:39:58 +00:00
|
|
|
// todo!()
|
2023-10-31 14:49:51 +00:00
|
|
|
// impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
|
2023-10-30 14:39:58 +00:00
|
|
|
// fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
|
|
|
|
// Some(Box::new(self.upgrade(cx)?))
|
|
|
|
// }
|
2023-10-26 10:27:20 +00:00
|
|
|
|
2023-10-31 14:49:51 +00:00
|
|
|
// // fn into_any(self) -> AnyView {
|
|
|
|
// // self.into_any()
|
|
|
|
// // }
|
2023-10-30 14:39:58 +00:00
|
|
|
// }
|
2023-10-26 10:27:20 +00:00
|
|
|
|
|
|
|
impl PartialEq for Box<dyn WeakSearchableItemHandle> {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
2023-10-31 14:49:51 +00:00
|
|
|
self.id() == other.id()
|
2023-10-26 10:27:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for Box<dyn WeakSearchableItemHandle> {}
|
|
|
|
|
|
|
|
impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
|
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
2023-10-31 14:49:51 +00:00
|
|
|
self.id().hash(state)
|
2023-10-26 10:27:20 +00:00
|
|
|
}
|
|
|
|
}
|