From e5dc2f06c9e625cb95658a8fcaad35627ade360a Mon Sep 17 00:00:00 2001 From: Om Chillure Date: Thu, 19 Mar 2026 20:49:03 +0530 Subject: [PATCH] search: Fix replace all being silently dropped (#50852) Fixes #50848 ### Problem When Replace All was triggered with a stale search query (i.e., the query text had changed since the last completed search), the old code ould restart the search and immediately return, silently discarding the replace-all intent. This caused the Replace All action to appear to do nothing on the first try, only working on subsequent attempts once results were already loaded. ### Fix : Fix this by introducing a `pending_replace_all` flag on ProjectSearchView. When Replace All is invoked while a search is in flight or the query is stale, the flag is set and the action is deferred. Once the search completes and `entity_changed` is called, the flag is checked and `replace_all` is automatically dispatched. Also disable the Replace Next button in the UI while a search is underway, since it cannot meaningfully act without up-to-date results. ### Release Notes: - Fixed "Replace All" in project search not working on the first attempt when the search query was changed or results hadn't loaded yet. --- crates/search/src/project_search.rs | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 59d279d8ccacc63a37e3adcbdeb5f1d16ec501d9..2c86c67f2574364698f4ee6d24eaf8aa0da5882f 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -266,6 +266,7 @@ pub struct ProjectSearchView { excluded_files_editor: Entity, filters_enabled: bool, replace_enabled: bool, + pending_replace_all: bool, included_opened_only: bool, regex_language: Option>, _subscriptions: Vec, @@ -797,6 +798,9 @@ impl ProjectSearchView { } fn replace_next(&mut self, _: &ReplaceNext, window: &mut Window, cx: &mut Context) { + if self.entity.read(cx).pending_search.is_some() { + return; + } if let Some(last_search_query_text) = &self.entity.read(cx).last_search_query_text && self.query_editor.read(cx).text(cx) != *last_search_query_text { @@ -824,14 +828,24 @@ impl ProjectSearchView { self.select_match(Direction::Next, window, cx) } } + fn replace_all(&mut self, _: &ReplaceAll, window: &mut Window, cx: &mut Context) { - if let Some(last_search_query_text) = &self.entity.read(cx).last_search_query_text - && self.query_editor.read(cx).text(cx) != *last_search_query_text - { - // search query has changed, restart search and bail + if self.entity.read(cx).pending_search.is_some() { + self.pending_replace_all = true; + return; + } + let query_text = self.query_editor.read(cx).text(cx); + let query_is_stale = + self.entity.read(cx).last_search_query_text.as_deref() != Some(query_text.as_str()); + if query_is_stale { + self.pending_replace_all = true; self.search(cx); + if self.entity.read(cx).pending_search.is_none() { + self.pending_replace_all = false; + } return; } + self.pending_replace_all = false; if self.active_match_index.is_none() { return; } @@ -1043,6 +1057,7 @@ impl ProjectSearchView { excluded_files_editor, filters_enabled, replace_enabled: false, + pending_replace_all: false, included_opened_only: false, regex_language: None, _subscriptions: subscriptions, @@ -1583,6 +1598,10 @@ impl ProjectSearchView { cx.emit(ViewEvent::UpdateTab); cx.notify(); + + if self.pending_replace_all && self.entity.read(cx).pending_search.is_none() { + self.replace_all(&ReplaceAll, window, cx); + } } fn update_match_index(&mut self, cx: &mut Context) { @@ -2300,14 +2319,13 @@ impl Render for ProjectSearchBar { .child(render_text_input(&search.replacement_editor, None, cx)); let focus_handle = search.replacement_editor.read(cx).focus_handle(cx); - let replace_actions = h_flex() .min_w_64() .gap_1() .child(render_action_button( "project-search-replace-button", IconName::ReplaceNext, - Default::default(), + is_search_underway.then_some(ActionButtonState::Disabled), "Replace Next Match", &ReplaceNext, focus_handle.clone(),