Switch project search deploy behavior to be isolated to a pane (#4072)

This adjusts the solution in
https://github.com/zed-industries/zed/pull/4014 to fix the double-focus
issue, allowing each pane's project search to work independently.

Release Notes:

- Changed the name of the `workspace::DeploySearch` action to
`pane::DeploySearch` and changed it's behavior to open a new search OR
focus an existing project search in the current pane.
(https://github.com/zed-industries/community/issues/2395)
This commit is contained in:
Mikayla Maki 2024-01-16 10:41:57 -08:00 committed by GitHub
commit d00067cd86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 31 deletions

View file

@ -402,7 +402,7 @@
"cmd-r": "workspace::ToggleRightDock", "cmd-r": "workspace::ToggleRightDock",
"cmd-j": "workspace::ToggleBottomDock", "cmd-j": "workspace::ToggleBottomDock",
"alt-cmd-y": "workspace::CloseAllDocks", "alt-cmd-y": "workspace::CloseAllDocks",
"cmd-shift-f": "workspace::DeploySearch", "cmd-shift-f": "pane::DeploySearch",
"cmd-k cmd-t": "theme_selector::Toggle", "cmd-k cmd-t": "theme_selector::Toggle",
"cmd-k cmd-s": "zed::OpenKeymap", "cmd-k cmd-s": "zed::OpenKeymap",
"cmd-t": "project_symbols::Toggle", "cmd-t": "project_symbols::Toggle",

View file

@ -54,14 +54,10 @@ actions!(
[SearchInNew, ToggleFocus, NextField, ToggleFilters] [SearchInNew, ToggleFocus, NextField, ToggleFilters]
); );
#[derive(Default)]
struct ActiveSearches(HashMap<WeakModel<Project>, WeakView<ProjectSearchView>>);
#[derive(Default)] #[derive(Default)]
struct ActiveSettings(HashMap<WeakModel<Project>, ProjectSearchSettings>); struct ActiveSettings(HashMap<WeakModel<Project>, ProjectSearchSettings>);
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
cx.set_global(ActiveSearches::default());
cx.set_global(ActiveSettings::default()); cx.set_global(ActiveSettings::default());
cx.observe_new_views(|workspace: &mut Workspace, _cx| { cx.observe_new_views(|workspace: &mut Workspace, _cx| {
workspace workspace
@ -948,25 +944,19 @@ impl ProjectSearchView {
}); });
} }
// Re-activate the most recently activated search or the most recent if it has been closed. // Re-activate the most recently activated search in this pane or the most recent if it has been closed.
// If no search exists in the workspace, create a new one. // If no search exists in the workspace, create a new one.
fn deploy_search( fn deploy_search(
workspace: &mut Workspace, workspace: &mut Workspace,
_: &workspace::DeploySearch, _: &workspace::DeploySearch,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) { ) {
let active_search = cx let existing = workspace
.global::<ActiveSearches>() .active_pane()
.0 .read(cx)
.get(&workspace.project().downgrade()); .items()
let existing = active_search .find_map(|item| item.downcast::<ProjectSearchView>());
.and_then(|active_search| {
workspace
.items_of_type::<ProjectSearchView>(cx)
.filter(|search| &search.downgrade() == active_search)
.last()
})
.or_else(|| workspace.item_of_type::<ProjectSearchView>(cx));
Self::existing_or_new_search(workspace, existing, cx) Self::existing_or_new_search(workspace, existing, cx)
} }
@ -984,11 +974,6 @@ impl ProjectSearchView {
existing: Option<View<ProjectSearchView>>, existing: Option<View<ProjectSearchView>>,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) { ) {
// Clean up entries for dropped projects
cx.update_global(|state: &mut ActiveSearches, _cx| {
state.0.retain(|project, _| project.is_upgradable())
});
let query = workspace.active_item(cx).and_then(|item| { let query = workspace.active_item(cx).and_then(|item| {
let editor = item.act_as::<Editor>(cx)?; let editor = item.act_as::<Editor>(cx)?;
let query = editor.query_suggestion(cx); let query = editor.query_suggestion(cx);
@ -1020,6 +1005,7 @@ impl ProjectSearchView {
workspace.add_item(Box::new(view.clone()), cx); workspace.add_item(Box::new(view.clone()), cx);
view view
}; };
search.update(cx, |search, cx| { search.update(cx, |search, cx| {
if let Some(query) = query { if let Some(query) = query {
search.set_query(&query, cx); search.set_query(&query, cx);
@ -3118,6 +3104,7 @@ pub mod tests {
async fn test_deploy_search_with_multiple_panes(cx: &mut TestAppContext) { async fn test_deploy_search_with_multiple_panes(cx: &mut TestAppContext) {
init_test(cx); init_test(cx);
// Setup 2 panes, both with a file open and one with a project search.
let fs = FakeFs::new(cx.background_executor.clone()); let fs = FakeFs::new(cx.background_executor.clone());
fs.insert_tree( fs.insert_tree(
"/dir", "/dir",
@ -3176,6 +3163,8 @@ pub mod tests {
} }
}) })
.unwrap(); .unwrap();
// Add a project search item to the second pane
window window
.update(cx, { .update(cx, {
let search_bar = search_bar.clone(); let search_bar = search_bar.clone();
@ -3195,6 +3184,8 @@ pub mod tests {
cx.run_until_parked(); cx.run_until_parked();
assert_eq!(cx.update(|cx| second_pane.read(cx).items_len()), 2); assert_eq!(cx.update(|cx| second_pane.read(cx).items_len()), 2);
assert_eq!(cx.update(|cx| first_pane.read(cx).items_len()), 1); assert_eq!(cx.update(|cx| first_pane.read(cx).items_len()), 1);
// Focus the first pane
window window
.update(cx, |workspace, cx| { .update(cx, |workspace, cx| {
assert_eq!(workspace.active_pane(), &second_pane); assert_eq!(workspace.active_pane(), &second_pane);
@ -3213,20 +3204,47 @@ pub mod tests {
assert_eq!(second_pane.read(cx).items_len(), 2); assert_eq!(second_pane.read(cx).items_len(), 2);
}) })
.unwrap(); .unwrap();
// Deploy a new search
cx.dispatch_action(window.into(), DeploySearch); cx.dispatch_action(window.into(), DeploySearch);
// We should have same # of items in workspace, the only difference being that // Both panes should now have a project search in them
// the search we've deployed previously should now be focused.
window window
.update(cx, |workspace, cx| { .update(cx, |workspace, cx| {
assert_eq!(workspace.active_pane(), &second_pane); assert_eq!(workspace.active_pane(), &first_pane);
second_pane.update(cx, |this, _| { first_pane.update(cx, |this, _| {
assert_eq!(this.active_item_index(), 1); assert_eq!(this.active_item_index(), 1);
assert_eq!(this.items_len(), 2); assert_eq!(this.items_len(), 2);
}); });
first_pane.update(cx, |this, cx| { second_pane.update(cx, |this, cx| {
assert!(!cx.focus_handle().contains_focused(cx)); assert!(!cx.focus_handle().contains_focused(cx));
assert_eq!(this.items_len(), 1); assert_eq!(this.items_len(), 2);
});
})
.unwrap();
// Focus the second pane's non-search item
window
.update(cx, |_workspace, cx| {
second_pane.update(cx, |pane, cx| pane.activate_next_item(true, cx));
})
.unwrap();
// Deploy a new search
cx.dispatch_action(window.into(), DeploySearch);
// The project search view should now be focused in the second pane
// And the number of items should be unchanged.
window
.update(cx, |_workspace, cx| {
second_pane.update(cx, |pane, _cx| {
assert!(pane
.active_item()
.unwrap()
.downcast::<ProjectSearchView>()
.is_some());
assert_eq!(pane.items_len(), 2);
}); });
}) })
.unwrap(); .unwrap();
@ -3236,7 +3254,7 @@ pub mod tests {
cx.update(|cx| { cx.update(|cx| {
let settings = SettingsStore::test(cx); let settings = SettingsStore::test(cx);
cx.set_global(settings); cx.set_global(settings);
cx.set_global(ActiveSearches::default());
SemanticIndexSettings::register(cx); SemanticIndexSettings::register(cx);
theme::init(theme::LoadThemes::JustBase, cx); theme::init(theme::LoadThemes::JustBase, cx);

View file

@ -99,6 +99,7 @@ actions!(
CloseItemsToTheLeft, CloseItemsToTheLeft,
CloseItemsToTheRight, CloseItemsToTheRight,
GoBack, GoBack,
DeploySearch,
GoForward, GoForward,
ReopenClosedItem, ReopenClosedItem,
SplitLeft, SplitLeft,

View file

@ -108,7 +108,6 @@ actions!(
NewCenterTerminal, NewCenterTerminal,
ToggleTerminalFocus, ToggleTerminalFocus,
NewSearch, NewSearch,
DeploySearch,
Feedback, Feedback,
Restart, Restart,
Welcome, Welcome,