@@ -37,7 +37,7 @@ use ui::{
Icon, IconButton, IconButtonShape, IconName, KeyBinding, Label, LabelCommon, LabelSize,
Toggleable, Tooltip, h_flex, prelude::*, utils::SearchInputWidth, v_flex,
};
-use util::paths::PathMatcher;
+use util::{ResultExt as _, paths::PathMatcher};
use workspace::{
DeploySearch, ItemNavHistory, NewSearch, ToolbarItemEvent, ToolbarItemLocation,
ToolbarItemView, Workspace, WorkspaceId,
@@ -72,15 +72,18 @@ pub fn init(cx: &mut App) {
);
register_workspace_action(
workspace,
- move |search_bar, _: &ToggleCaseSensitive, _, cx| {
- search_bar.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx);
+ move |search_bar, _: &ToggleCaseSensitive, window, cx| {
+ search_bar.toggle_search_option(SearchOptions::CASE_SENSITIVE, window, cx);
},
);
- register_workspace_action(workspace, move |search_bar, _: &ToggleWholeWord, _, cx| {
- search_bar.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
- });
- register_workspace_action(workspace, move |search_bar, _: &ToggleRegex, _, cx| {
- search_bar.toggle_search_option(SearchOptions::REGEX, cx);
+ register_workspace_action(
+ workspace,
+ move |search_bar, _: &ToggleWholeWord, window, cx| {
+ search_bar.toggle_search_option(SearchOptions::WHOLE_WORD, window, cx);
+ },
+ );
+ register_workspace_action(workspace, move |search_bar, _: &ToggleRegex, window, cx| {
+ search_bar.toggle_search_option(SearchOptions::REGEX, window, cx);
});
register_workspace_action(
workspace,
@@ -1032,6 +1035,61 @@ impl ProjectSearchView {
});
}
+ fn prompt_to_save_if_dirty_then_search(
+ &mut self,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Task<anyhow::Result<()>> {
+ use workspace::AutosaveSetting;
+
+ let project = self.entity.read(cx).project.clone();
+
+ let can_autosave = self.results_editor.can_autosave(cx);
+ let autosave_setting = self.results_editor.workspace_settings(cx).autosave;
+
+ let will_autosave = can_autosave
+ && matches!(
+ autosave_setting,
+ AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
+ );
+
+ let is_dirty = self.is_dirty(cx);
+
+ let should_confirm_save = !will_autosave && is_dirty;
+
+ cx.spawn_in(window, async move |this, cx| {
+ let should_search = if should_confirm_save {
+ let options = &["Save", "Don't Save", "Cancel"];
+ let result_channel = this.update_in(cx, |_, window, cx| {
+ window.prompt(
+ gpui::PromptLevel::Warning,
+ "Project search buffer contains unsaved edits. Do you want to save it?",
+ None,
+ options,
+ cx,
+ )
+ })?;
+ let result = result_channel.await?;
+ let should_save = result == 0;
+ if should_save {
+ this.update_in(cx, |this, window, cx| this.save(true, project, window, cx))?
+ .await
+ .log_err();
+ }
+ let should_search = result != 2;
+ should_search
+ } else {
+ true
+ };
+ if should_search {
+ this.update(cx, |this, cx| {
+ this.search(cx);
+ })?;
+ }
+ anyhow::Ok(())
+ })
+ }
+
fn search(&mut self, cx: &mut Context<Self>) {
if let Some(query) = self.build_search_query(cx) {
self.entity.update(cx, |model, cx| model.search(query, cx));
@@ -1503,7 +1561,9 @@ impl ProjectSearchBar {
.is_focused(window)
{
cx.stop_propagation();
- search_view.search(cx);
+ search_view
+ .prompt_to_save_if_dirty_then_search(window, cx)
+ .detach_and_log_err(cx);
}
});
}
@@ -1570,19 +1630,39 @@ impl ProjectSearchBar {
});
}
- fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut Context<Self>) -> bool {
- if let Some(search_view) = self.active_project_search.as_ref() {
- search_view.update(cx, |search_view, cx| {
- search_view.toggle_search_option(option, cx);
- if search_view.entity.read(cx).active_query.is_some() {
- search_view.search(cx);
- }
- });
- cx.notify();
- true
- } else {
- false
+ fn toggle_search_option(
+ &mut self,
+ option: SearchOptions,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> bool {
+ if self.active_project_search.is_none() {
+ return false;
}
+
+ cx.spawn_in(window, async move |this, cx| {
+ let task = this.update_in(cx, |this, window, cx| {
+ let search_view = this.active_project_search.as_ref()?;
+ search_view.update(cx, |search_view, cx| {
+ search_view.toggle_search_option(option, cx);
+ search_view
+ .entity
+ .read(cx)
+ .active_query
+ .is_some()
+ .then(|| search_view.prompt_to_save_if_dirty_then_search(window, cx))
+ })
+ })?;
+ if let Some(task) = task {
+ task.await?;
+ }
+ this.update(cx, |_, cx| {
+ cx.notify();
+ })?;
+ anyhow::Ok(())
+ })
+ .detach();
+ true
}
fn toggle_replace(&mut self, _: &ToggleReplace, window: &mut Window, cx: &mut Context<Self>) {
@@ -1621,19 +1701,33 @@ impl ProjectSearchBar {
}
fn toggle_opened_only(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
- if let Some(search_view) = self.active_project_search.as_ref() {
- search_view.update(cx, |search_view, cx| {
- search_view.toggle_opened_only(window, cx);
- if search_view.entity.read(cx).active_query.is_some() {
- search_view.search(cx);
- }
- });
-
- cx.notify();
- true
- } else {
- false
+ if self.active_project_search.is_none() {
+ return false;
}
+
+ cx.spawn_in(window, async move |this, cx| {
+ let task = this.update_in(cx, |this, window, cx| {
+ let search_view = this.active_project_search.as_ref()?;
+ search_view.update(cx, |search_view, cx| {
+ search_view.toggle_opened_only(window, cx);
+ search_view
+ .entity
+ .read(cx)
+ .active_query
+ .is_some()
+ .then(|| search_view.prompt_to_save_if_dirty_then_search(window, cx))
+ })
+ })?;
+ if let Some(task) = task {
+ task.await?;
+ }
+ this.update(cx, |_, cx| {
+ cx.notify();
+ })?;
+ anyhow::Ok(())
+ })
+ .detach();
+ true
}
fn is_opened_only_enabled(&self, cx: &App) -> bool {
@@ -1860,22 +1954,22 @@ impl Render for ProjectSearchBar {
.child(SearchOptions::CASE_SENSITIVE.as_button(
self.is_option_enabled(SearchOptions::CASE_SENSITIVE, cx),
focus_handle.clone(),
- cx.listener(|this, _, _, cx| {
- this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx);
+ cx.listener(|this, _, window, cx| {
+ this.toggle_search_option(SearchOptions::CASE_SENSITIVE, window, cx);
}),
))
.child(SearchOptions::WHOLE_WORD.as_button(
self.is_option_enabled(SearchOptions::WHOLE_WORD, cx),
focus_handle.clone(),
- cx.listener(|this, _, _, cx| {
- this.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
+ cx.listener(|this, _, window, cx| {
+ this.toggle_search_option(SearchOptions::WHOLE_WORD, window, cx);
}),
))
.child(SearchOptions::REGEX.as_button(
self.is_option_enabled(SearchOptions::REGEX, cx),
focus_handle.clone(),
- cx.listener(|this, _, _, cx| {
- this.toggle_search_option(SearchOptions::REGEX, cx);
+ cx.listener(|this, _, window, cx| {
+ this.toggle_search_option(SearchOptions::REGEX, window, cx);
}),
)),
);
@@ -2147,8 +2241,12 @@ impl Render for ProjectSearchBar {
.search_options
.contains(SearchOptions::INCLUDE_IGNORED),
focus_handle.clone(),
- cx.listener(|this, _, _, cx| {
- this.toggle_search_option(SearchOptions::INCLUDE_IGNORED, cx);
+ cx.listener(|this, _, window, cx| {
+ this.toggle_search_option(
+ SearchOptions::INCLUDE_IGNORED,
+ window,
+ cx,
+ );
}),
),
),
@@ -2188,11 +2286,11 @@ impl Render for ProjectSearchBar {
.on_action(cx.listener(|this, action, window, cx| {
this.toggle_replace(action, window, cx);
}))
- .on_action(cx.listener(|this, _: &ToggleWholeWord, _, cx| {
- this.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
+ .on_action(cx.listener(|this, _: &ToggleWholeWord, window, cx| {
+ this.toggle_search_option(SearchOptions::WHOLE_WORD, window, cx);
}))
- .on_action(cx.listener(|this, _: &ToggleCaseSensitive, _, cx| {
- this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx);
+ .on_action(cx.listener(|this, _: &ToggleCaseSensitive, window, cx| {
+ this.toggle_search_option(SearchOptions::CASE_SENSITIVE, window, cx);
}))
.on_action(cx.listener(|this, action, window, cx| {
if let Some(search) = this.active_project_search.as_ref() {
@@ -2209,8 +2307,8 @@ impl Render for ProjectSearchBar {
}
}))
.when(search.filters_enabled, |this| {
- this.on_action(cx.listener(|this, _: &ToggleIncludeIgnored, _, cx| {
- this.toggle_search_option(SearchOptions::INCLUDE_IGNORED, cx);
+ this.on_action(cx.listener(|this, _: &ToggleIncludeIgnored, window, cx| {
+ this.toggle_search_option(SearchOptions::INCLUDE_IGNORED, window, cx);
}))
})
.on_action(cx.listener(Self::select_next_match))