From f1b723973b1a15ab7b3aa596defda7d6d81c4477 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Fri, 26 Dec 2025 15:22:57 -0300 Subject: [PATCH] =?UTF-8?q?mac:=20Delay=20initial=20find=20pasteboard=20se?= =?UTF-8?q?arch=20until=20=E2=8C=98G=20or=20=E2=8C=98F=20(#45605)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow up to https://github.com/zed-industries/zed/pull/45311. Instead of searching for the string in the find pasteboard as soon as the pane is focused, we will now wait until the search bar is either deployed or `Select{Next|Prev}Match` is triggered. Release Notes: - N/A --- crates/search/src/buffer_search.rs | 97 ++++++++++++++------ crates/search/src/buffer_search/registrar.rs | 18 ++-- 2 files changed, 79 insertions(+), 36 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index be3331048bc78a91a8d3c5a3637d6bf6ea007e4d..7ca1d9d909feb20c2a5cf3e350958a35dca83668 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -4,6 +4,7 @@ use crate::{ FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOption, SearchOptions, SearchSource, SelectAllMatches, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive, ToggleRegex, ToggleReplace, ToggleSelection, ToggleWholeWord, + buffer_search::registrar::WithResultsOrExternalQuery, search_bar::{ActionButtonState, input_base_styles, render_action_button, render_text_input}, }; use any_vec::AnyVec; @@ -43,7 +44,7 @@ use workspace::{ }; pub use registrar::DivRegistrar; -use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar, WithResults}; +use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar}; const MAX_BUFFER_SEARCH_HISTORY_SIZE: usize = 50; @@ -110,6 +111,8 @@ pub struct BufferSearchBar { active_searchable_item_subscriptions: Option<[Subscription; 2]>, #[cfg(not(target_os = "macos"))] active_searchable_item_subscriptions: Option, + #[cfg(target_os = "macos")] + pending_external_query: Option<(String, SearchOptions)>, active_search: Option>, searchable_items_with_matches: HashMap, AnyVec>, pending_search: Option>, @@ -510,24 +513,27 @@ impl ToolbarItemView for BufferSearchBar { } cx.defer_in(window, |this, window, cx| { - if let Some(item) = cx.read_from_find_pasteboard() - && let Some(text) = item.text() - { - if this.query(cx) != text { - let search_options = item - .metadata() - .and_then(|m| m.parse().ok()) - .and_then(SearchOptions::from_bits) - .unwrap_or(this.search_options); - - drop(this.search( - &text, - Some(search_options), - true, - window, - cx, - )); - } + let Some(item) = cx.read_from_find_pasteboard() else { + return; + }; + let Some(text) = item.text() else { + return; + }; + + if this.query(cx) == text { + return; + } + + let search_options = item + .metadata() + .and_then(|m| m.parse().ok()) + .and_then(SearchOptions::from_bits) + .unwrap_or(this.search_options); + + if this.dismissed { + this.pending_external_query = Some((text, search_options)); + } else { + drop(this.search(&text, Some(search_options), true, window, cx)); } }); }), @@ -594,14 +600,16 @@ impl BufferSearchBar { cx.propagate(); } })); - registrar.register_handler(WithResults(|this, action: &SelectNextMatch, window, cx| { - if this.supported_options(cx).find_in_results { - cx.propagate(); - } else { - this.select_next_match(action, window, cx); - } - })); - registrar.register_handler(WithResults( + registrar.register_handler(WithResultsOrExternalQuery( + |this, action: &SelectNextMatch, window, cx| { + if this.supported_options(cx).find_in_results { + cx.propagate(); + } else { + this.select_next_match(action, window, cx); + } + }, + )); + registrar.register_handler(WithResultsOrExternalQuery( |this, action: &SelectPreviousMatch, window, cx| { if this.supported_options(cx).find_in_results { cx.propagate(); @@ -610,7 +618,7 @@ impl BufferSearchBar { } }, )); - registrar.register_handler(WithResults( + registrar.register_handler(WithResultsOrExternalQuery( |this, action: &SelectAllMatches, window, cx| { if this.supported_options(cx).find_in_results { cx.propagate(); @@ -707,6 +715,8 @@ impl BufferSearchBar { replacement_editor_focused: false, active_searchable_item: None, active_searchable_item_subscriptions: None, + #[cfg(target_os = "macos")] + pending_external_query: None, active_match_index: None, searchable_items_with_matches: Default::default(), default_options: search_options, @@ -852,11 +862,20 @@ impl BufferSearchBar { self.search(&suggestion, Some(self.default_options), true, window, cx) }); + #[cfg(target_os = "macos")] + let search = search.or_else(|| { + self.pending_external_query + .take() + .map(|(query, options)| self.search(&query, Some(options), true, window, cx)) + }); + if let Some(search) = search { cx.spawn_in(window, async move |this, cx| { if search.await.is_ok() { this.update_in(cx, |this, window, cx| { - this.activate_current_match(window, cx) + if !this.dismissed { + this.activate_current_match(window, cx) + } }) } else { Ok(()) @@ -1071,6 +1090,22 @@ impl BufferSearchBar { window: &mut Window, cx: &mut Context, ) { + #[cfg(target_os = "macos")] + if let Some((query, options)) = self.pending_external_query.take() { + let search_rx = self.search(&query, Some(options), true, window, cx); + cx.spawn_in(window, async move |this, cx| { + if search_rx.await.is_ok() { + this.update_in(cx, |this, window, cx| { + this.activate_current_match(window, cx); + }) + .ok(); + } + }) + .detach(); + + return; + } + if let Some(index) = self.active_match_index && let Some(searchable_item) = self.active_searchable_item.as_ref() && let Some(matches) = self @@ -1271,6 +1306,8 @@ impl BufferSearchBar { let (done_tx, done_rx) = oneshot::channel(); let query = self.query(cx); self.pending_search.take(); + #[cfg(target_os = "macos")] + self.pending_external_query.take(); if let Some(active_searchable_item) = self.active_searchable_item.as_ref() { self.query_error = None; @@ -1367,8 +1404,8 @@ impl BufferSearchBar { cx, ); } - let _ = done_tx.send(()); } + let _ = done_tx.send(()); cx.notify(); } }) diff --git a/crates/search/src/buffer_search/registrar.rs b/crates/search/src/buffer_search/registrar.rs index 2c640e67cee98fb5e56eda1484ecf6e2fab41976..2d0d58a71e7457b2f5ea47135b2d55a5372ce6bf 100644 --- a/crates/search/src/buffer_search/registrar.rs +++ b/crates/search/src/buffer_search/registrar.rs @@ -149,16 +149,16 @@ impl ActionExecutor for ForDeployed { } } -/// Run an action when the search bar has any matches, regardless of whether it -/// is visible or not. -pub struct WithResults(pub(super) SearchBarActionCallback); -impl Clone for WithResults { +/// Run an action when the search bar has any matches or a pending external query, +/// regardless of whether it is visible or not. +pub struct WithResultsOrExternalQuery(pub(super) SearchBarActionCallback); +impl Clone for WithResultsOrExternalQuery { fn clone(&self) -> Self { Self(self.0) } } -impl ActionExecutor for WithResults { +impl ActionExecutor for WithResultsOrExternalQuery { fn execute( &self, search_bar: &mut BufferSearchBar, @@ -166,7 +166,13 @@ impl ActionExecutor for WithResults { window: &mut Window, cx: &mut Context, ) -> DidHandleAction { - if search_bar.active_match_index.is_some() { + #[cfg(not(target_os = "macos"))] + let has_external_query = false; + + #[cfg(target_os = "macos")] + let has_external_query = search_bar.pending_external_query.is_some(); + + if has_external_query || search_bar.active_match_index.is_some() { self.0(search_bar, action, window, cx); true } else {