From a127ff4a4f207214b0cf17e64a97c991b3d7d032 Mon Sep 17 00:00:00 2001 From: Finn Evers Date: Thu, 8 May 2025 08:42:40 +0200 Subject: [PATCH] search: Do not consider filters if they are toggled off (#30162) Closes #30134 This PR ensures that path filters are only applied to searches when the filters are actually enabled (and visible). Release Notes: - Fixed the project search considering included and excluded filters after toggling them off. --- crates/search/src/project_search.rs | 190 +++++++++++++++++++++++----- 1 file changed, 160 insertions(+), 30 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 2c8d6273b2..2ef51b9141 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1010,41 +1010,51 @@ impl ProjectSearchView { } else { None }; - let included_files = - match Self::parse_path_matches(&self.included_files_editor.read(cx).text(cx)) { - Ok(included_files) => { - let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Include); - if should_unmark_error { - cx.notify(); + let included_files = self + .filters_enabled + .then(|| { + match Self::parse_path_matches(&self.included_files_editor.read(cx).text(cx)) { + Ok(included_files) => { + let should_unmark_error = + self.panels_with_errors.remove(&InputPanel::Include); + if should_unmark_error { + cx.notify(); + } + included_files + } + Err(_e) => { + let should_mark_error = self.panels_with_errors.insert(InputPanel::Include); + if should_mark_error { + cx.notify(); + } + PathMatcher::default() } - included_files } - Err(_e) => { - let should_mark_error = self.panels_with_errors.insert(InputPanel::Include); - if should_mark_error { - cx.notify(); - } - PathMatcher::default() - } - }; - let excluded_files = - match Self::parse_path_matches(&self.excluded_files_editor.read(cx).text(cx)) { - Ok(excluded_files) => { - let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Exclude); - if should_unmark_error { - cx.notify(); - } + }) + .unwrap_or_default(); + let excluded_files = self + .filters_enabled + .then(|| { + match Self::parse_path_matches(&self.excluded_files_editor.read(cx).text(cx)) { + Ok(excluded_files) => { + let should_unmark_error = + self.panels_with_errors.remove(&InputPanel::Exclude); + if should_unmark_error { + cx.notify(); + } - excluded_files - } - Err(_e) => { - let should_mark_error = self.panels_with_errors.insert(InputPanel::Exclude); - if should_mark_error { - cx.notify(); + excluded_files + } + Err(_e) => { + let should_mark_error = self.panels_with_errors.insert(InputPanel::Exclude); + if should_mark_error { + cx.notify(); + } + PathMatcher::default() } - PathMatcher::default() } - }; + }) + .unwrap_or_default(); // If the project contains multiple visible worktrees, we match the // include/exclude patterns against full paths to allow them to be @@ -2645,6 +2655,126 @@ pub mod tests { }).unwrap(); } + #[gpui::test] + async fn test_filters_consider_toggle_state(cx: &mut TestAppContext) { + init_test(cx); + + let fs = FakeFs::new(cx.background_executor.clone()); + 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(), ["/dir".as_ref()], cx).await; + let window = cx.add_window(|window, cx| Workspace::test_new(project, window, cx)); + let workspace = window; + let search_bar = window.build_entity(cx, |_, _| ProjectSearchBar::new()); + + window + .update(cx, move |workspace, window, cx| { + workspace.panes()[0].update(cx, |pane, cx| { + pane.toolbar() + .update(cx, |toolbar, cx| toolbar.add_item(search_bar, window, cx)) + }); + + ProjectSearchView::deploy_search( + workspace, + &workspace::DeploySearch::find(), + window, + cx, + ) + }) + .unwrap(); + + let Some(search_view) = cx.read(|cx| { + workspace + .read(cx) + .unwrap() + .active_pane() + .read(cx) + .active_item() + .and_then(|item| item.downcast::()) + }) else { + panic!("Search view expected to appear after new search event trigger") + }; + + cx.spawn(|mut cx| async move { + window + .update(&mut cx, |_, window, cx| { + window.dispatch_action(ToggleFocus.boxed_clone(), cx) + }) + .unwrap(); + }) + .detach(); + cx.background_executor.run_until_parked(); + + window + .update(cx, |_, window, cx| { + search_view.update(cx, |search_view, cx| { + search_view.query_editor.update(cx, |query_editor, cx| { + query_editor.set_text("const FOUR", window, cx) + }); + search_view.toggle_filters(cx); + search_view + .excluded_files_editor + .update(cx, |exclude_editor, cx| { + exclude_editor.set_text("four.rs", window, cx) + }); + search_view.search(cx); + }); + }) + .unwrap(); + cx.background_executor.run_until_parked(); + window + .update(cx, |_, _, cx| { + search_view.update(cx, |search_view, cx| { + let results_text = search_view + .results_editor + .update(cx, |editor, cx| editor.display_text(cx)); + assert!( + results_text.is_empty(), + "Search view for query with the only match in an excluded file should have no results but got '{results_text}'" + ); + }); + }).unwrap(); + + cx.spawn(|mut cx| async move { + window.update(&mut cx, |_, window, cx| { + window.dispatch_action(ToggleFocus.boxed_clone(), cx) + }) + }) + .detach(); + cx.background_executor.run_until_parked(); + + window + .update(cx, |_, _, cx| { + search_view.update(cx, |search_view, cx| { + search_view.toggle_filters(cx); + search_view.search(cx); + }); + }) + .unwrap(); + cx.background_executor.run_until_parked(); + window + .update(cx, |_, _, cx| { + search_view.update(cx, |search_view, cx| { + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.display_text(cx)), + "\n\nconst FOUR: usize = one::ONE + three::THREE;", + "Search view results should contain the queried result in the previously excluded file with filters toggled off" + ); + }); + }) + .unwrap(); + } + #[gpui::test] async fn test_new_project_search_focus(cx: &mut TestAppContext) { init_test(cx);