Cargo.lock 🔗
@@ -4175,6 +4175,7 @@ dependencies = [
"log",
"postage",
"project",
+ "serde_json",
"theme",
"unindent",
"util",
Antonio Scandurra created
Cargo.lock | 1
crates/project/src/project.rs | 3
crates/search/Cargo.toml | 1
crates/search/src/project_search.rs | 156 ++++++++++++++++++++++++++++++
4 files changed, 157 insertions(+), 4 deletions(-)
@@ -4175,6 +4175,7 @@ dependencies = [
"log",
"postage",
"project",
+ "serde_json",
"theme",
"unindent",
"util",
@@ -2063,6 +2063,9 @@ impl Project {
let background = cx.background().clone();
let path_count: usize = snapshots.iter().map(|s| s.visible_file_count()).sum();
+ if path_count == 0 {
+ return Task::ready(Ok(Default::default()));
+ }
let workers = background.num_cpus().min(path_count);
let (matching_paths_tx, mut matching_paths_rx) = smol::channel::bounded(1024);
cx.background()
@@ -22,5 +22,6 @@ postage = { version = "0.4.1", features = ["futures-traits"] }
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
+serde_json = { version = "1.0.64", features = ["preserve_order"] }
workspace = { path = "../workspace", features = ["test-support"] }
unindent = "0.1"
@@ -150,7 +150,7 @@ impl Item for ProjectSearch {
nav_history: ItemNavHistory,
cx: &mut gpui::ViewContext<Self::View>,
) -> Self::View {
- ProjectSearchView::new(model, nav_history, workspace.settings(), cx)
+ ProjectSearchView::new(model, Some(nav_history), workspace.settings(), cx)
}
fn project_path(&self) -> Option<project::ProjectPath> {
@@ -316,7 +316,12 @@ impl ItemView for ProjectSearchView {
Self: Sized,
{
let model = self.model.update(cx, |model, cx| model.clone(cx));
- Some(Self::new(model, nav_history, self.settings.clone(), cx))
+ Some(Self::new(
+ model,
+ Some(nav_history),
+ self.settings.clone(),
+ cx,
+ ))
}
fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) {
@@ -332,7 +337,7 @@ impl ItemView for ProjectSearchView {
impl ProjectSearchView {
fn new(
model: ModelHandle<ProjectSearch>,
- nav_history: ItemNavHistory,
+ nav_history: Option<ItemNavHistory>,
settings: watch::Receiver<Settings>,
cx: &mut ViewContext<Self>,
) -> Self {
@@ -370,7 +375,7 @@ impl ProjectSearchView {
let results_editor = cx.add_view(|cx| {
let mut editor = Editor::for_buffer(excerpts, Some(project), settings.clone(), cx);
editor.set_searchable(false);
- editor.set_nav_history(Some(nav_history));
+ editor.set_nav_history(nav_history);
editor
});
cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab))
@@ -698,3 +703,146 @@ impl ProjectSearchView {
.boxed()
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use editor::DisplayPoint;
+ use gpui::{color::Color, TestAppContext};
+ use project::FakeFs;
+ use serde_json::json;
+ use std::sync::Arc;
+
+ #[gpui::test]
+ async fn test_project_search(mut cx: TestAppContext) {
+ let fonts = cx.font_cache();
+ let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default());
+ theme.search.match_background = Color::red();
+ let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap();
+ let settings = watch::channel_with(settings).1;
+
+ let fs = FakeFs::new(cx.background());
+ fs.insert_tree(
+ "/dir",
+ json!({
+ "one.rs": "const ONE: usize = 1;",
+ "two.rs": "const TWO: usize = one::ONE + one::ONE;",
+ "three.rs": "const THREE: usize = one::ONE + two::TWO;",
+ "four.rs": "const FOUR: usize = one::ONE + three::THREE;",
+ }),
+ )
+ .await;
+ let project = Project::test(fs.clone(), &mut cx);
+ let (tree, _) = project
+ .update(&mut cx, |project, cx| {
+ project.find_or_create_local_worktree("/dir", false, cx)
+ })
+ .await
+ .unwrap();
+ cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
+ .await;
+
+ let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
+ let search_view = cx.add_view(Default::default(), |cx| {
+ ProjectSearchView::new(search.clone(), None, settings, cx)
+ });
+
+ search_view.update(&mut cx, |search_view, cx| {
+ search_view
+ .query_editor
+ .update(cx, |query_editor, cx| query_editor.set_text("TWO", cx));
+ search_view.search(&Search, cx);
+ });
+ search_view.next_notification(&cx).await;
+ search_view.update(&mut cx, |search_view, cx| {
+ assert_eq!(
+ search_view
+ .results_editor
+ .update(cx, |editor, cx| editor.display_text(cx)),
+ "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;"
+ );
+ assert_eq!(
+ search_view
+ .results_editor
+ .update(cx, |editor, cx| editor.all_highlighted_ranges(cx)),
+ &[
+ (
+ DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35),
+ Color::red()
+ ),
+ (
+ DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40),
+ Color::red()
+ ),
+ (
+ DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9),
+ Color::red()
+ )
+ ]
+ );
+ assert_eq!(search_view.active_match_index, Some(0));
+ assert_eq!(
+ search_view
+ .results_editor
+ .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+ [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)]
+ );
+
+ search_view.select_match(&SelectMatch(Direction::Next), cx);
+ });
+
+ search_view.update(&mut cx, |search_view, cx| {
+ assert_eq!(search_view.active_match_index, Some(1));
+ assert_eq!(
+ search_view
+ .results_editor
+ .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+ [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)]
+ );
+ search_view.select_match(&SelectMatch(Direction::Next), cx);
+ });
+
+ search_view.update(&mut cx, |search_view, cx| {
+ assert_eq!(search_view.active_match_index, Some(2));
+ assert_eq!(
+ search_view
+ .results_editor
+ .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+ [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)]
+ );
+ search_view.select_match(&SelectMatch(Direction::Next), cx);
+ });
+
+ search_view.update(&mut cx, |search_view, cx| {
+ assert_eq!(search_view.active_match_index, Some(0));
+ assert_eq!(
+ search_view
+ .results_editor
+ .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+ [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)]
+ );
+ search_view.select_match(&SelectMatch(Direction::Prev), cx);
+ });
+
+ search_view.update(&mut cx, |search_view, cx| {
+ assert_eq!(search_view.active_match_index, Some(2));
+ assert_eq!(
+ search_view
+ .results_editor
+ .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+ [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)]
+ );
+ search_view.select_match(&SelectMatch(Direction::Prev), cx);
+ });
+
+ search_view.update(&mut cx, |search_view, cx| {
+ assert_eq!(search_view.active_match_index, Some(1));
+ assert_eq!(
+ search_view
+ .results_editor
+ .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+ [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)]
+ );
+ });
+ }
+}