diff --git a/crates/auto_update_ui/src/auto_update_ui.rs b/crates/auto_update_ui/src/auto_update_ui.rs index fa44df7542..25d64bc3e8 100644 --- a/crates/auto_update_ui/src/auto_update_ui.rs +++ b/crates/auto_update_ui/src/auto_update_ui.rs @@ -82,7 +82,10 @@ fn view_release_notes_locally( .update_in(cx, |workspace, window, cx| { let project = workspace.project().clone(); let buffer = project.update(cx, |project, cx| { - project.create_local_buffer("", markdown, cx) + let buffer = project.create_local_buffer("", markdown, cx); + project + .mark_buffer_as_non_searchable(buffer.read(cx).remote_id(), cx); + buffer }); buffer.update(cx, |buffer, cx| { buffer.edit([(0..0, body.release_notes)], None, cx) diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index 8d54cd046e..b2c21abcdb 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -39,6 +39,7 @@ pub struct BufferStore { path_to_buffer_id: HashMap, downstream_client: Option<(AnyProtoClient, u64)>, shared_buffers: HashMap>, + non_searchable_buffers: HashSet, } #[derive(Hash, Eq, PartialEq, Clone)] @@ -726,6 +727,7 @@ impl BufferStore { path_to_buffer_id: Default::default(), shared_buffers: Default::default(), loading_buffers: Default::default(), + non_searchable_buffers: Default::default(), worktree_store, } } @@ -750,6 +752,7 @@ impl BufferStore { path_to_buffer_id: Default::default(), loading_buffers: Default::default(), shared_buffers: Default::default(), + non_searchable_buffers: Default::default(), worktree_store, } } @@ -1059,7 +1062,9 @@ impl BufferStore { let mut unnamed_buffers = Vec::new(); for handle in self.buffers() { let buffer = handle.read(cx); - if let Some(entry_id) = buffer.entry_id(cx) { + if self.non_searchable_buffers.contains(&buffer.remote_id()) { + continue; + } else if let Some(entry_id) = buffer.entry_id(cx) { open_buffers.insert(entry_id); } else { limit = limit.saturating_sub(1); @@ -1665,6 +1670,10 @@ impl BufferStore { } serialized_transaction } + + pub(crate) fn mark_buffer_as_non_searchable(&mut self, buffer_id: BufferId) { + self.non_searchable_buffers.insert(buffer_id); + } } impl OpenBuffer { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index d0f266134e..cb636ba71e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5068,6 +5068,12 @@ impl Project { pub fn agent_location(&self) -> Option { self.agent_location.clone() } + + pub fn mark_buffer_as_non_searchable(&self, buffer_id: BufferId, cx: &mut Context) { + self.buffer_store.update(cx, |buffer_store, _| { + buffer_store.mark_buffer_as_non_searchable(buffer_id) + }); + } } pub struct PathMatchCandidateSet { diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index fc41d79ad0..cab6ccc0fb 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -5336,6 +5336,132 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { ); } +#[gpui::test] +async fn test_search_with_buffer_exclusions(cx: &mut gpui::TestAppContext) { + init_test(cx); + + let search_query = "file"; + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree( + path!("/dir"), + json!({ + "one.rs": r#"// Rust file one"#, + "one.ts": r#"// TypeScript file one"#, + "two.rs": r#"// Rust file two"#, + "two.ts": r#"// TypeScript file two"#, + }), + ) + .await; + + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; + let _buffer = project.update(cx, |project, cx| { + let buffer = project.create_local_buffer("file", None, cx); + project.mark_buffer_as_non_searchable(buffer.read(cx).remote_id(), cx); + buffer + }); + + assert_eq!( + search( + &project, + SearchQuery::text( + search_query, + false, + true, + false, + Default::default(), + PathMatcher::new(&["*.odd".to_owned()]).unwrap(), + false, + None, + ) + .unwrap(), + cx + ) + .await + .unwrap(), + HashMap::from_iter([ + (path!("dir/one.rs").to_string(), vec![8..12]), + (path!("dir/one.ts").to_string(), vec![14..18]), + (path!("dir/two.rs").to_string(), vec![8..12]), + (path!("dir/two.ts").to_string(), vec![14..18]), + ]), + "If no exclusions match, all files should be returned" + ); + + assert_eq!( + search( + &project, + SearchQuery::text( + search_query, + false, + true, + false, + Default::default(), + PathMatcher::new(&["*.rs".to_owned()]).unwrap(), + false, + None, + ) + .unwrap(), + cx + ) + .await + .unwrap(), + HashMap::from_iter([ + (path!("dir/one.ts").to_string(), vec![14..18]), + (path!("dir/two.ts").to_string(), vec![14..18]), + ]), + "Rust exclusion search should give only TypeScript files" + ); + + assert_eq!( + search( + &project, + SearchQuery::text( + search_query, + false, + true, + false, + Default::default(), + PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(), + false, + None, + ) + .unwrap(), + cx + ) + .await + .unwrap(), + HashMap::from_iter([ + (path!("dir/one.rs").to_string(), vec![8..12]), + (path!("dir/two.rs").to_string(), vec![8..12]), + ]), + "TypeScript exclusion search should give only Rust files, even if other exclusions don't match anything" + ); + + assert!( + search( + &project, + SearchQuery::text( + search_query, + false, + true, + false, + Default::default(), + PathMatcher::new(&["*.rs".to_owned(), "*.ts".to_owned(), "*.odd".to_owned()]) + .unwrap(), + false, + None, + ) + .unwrap(), + cx + ) + .await + .unwrap() + .is_empty(), + "Rust and typescript exclusion should give no files, even if other exclusions don't match anything" + ); +} + #[gpui::test] async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContext) { init_test(cx); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 3ed7f73ebd..79ae18fcfe 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -164,7 +164,7 @@ pub fn init(cx: &mut App) { .detach(); } -fn is_contains_uppercase(str: &str) -> bool { +fn contains_uppercase(str: &str) -> bool { str.chars().any(|c| c.is_uppercase()) } @@ -767,7 +767,7 @@ impl ProjectSearchView { let query = this.search_query_text(cx); if !query.is_empty() && this.search_options.contains(SearchOptions::CASE_SENSITIVE) - != is_contains_uppercase(&query) + != contains_uppercase(&query) { this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx); } @@ -1323,7 +1323,7 @@ impl ProjectSearchView { if EditorSettings::get_global(cx).use_smartcase_search && !query.is_empty() && self.search_options.contains(SearchOptions::CASE_SENSITIVE) - != is_contains_uppercase(query) + != contains_uppercase(query) { self.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx) }