Fix CloseProject not switching to neighbor in multi-project workspace (#60524)
Fixes #54772 When "Workspace: close project" was used in a multi-project window, the handler pre-created an empty "get started" workspace and activated it before removing the old project group. This prevented `remove_project_group` from detecting that the active workspace was being removed, so the neighbor-fallback never ran — leaving the user on the empty workspace even though other projects were still open. The fix delegates entirely to `remove_project_group`, which already handles: - Computing a neighboring project group - Prompting for unsaved changes via `prepare_to_close` - Activating the neighbor (or creating an empty workspace if none remains) This matches the behavior of the sidebar "Remove Project" action, which uses the same `remove_project_group` path. Release Notes: - Fixed "Workspace: close project" action not switching to another project in multi-project workspace --------- Co-authored-by: Eric Holk <eric@zed.dev>
This commit is contained in:
1 parent
f7e2380479
commit
b1b7041cbf
1 file changed
+94
-48
+94
-48
@@ -1291,59 +1291,17 @@ fn register_actions(
|
||||
}
|
||||
})
|
||||
.register_action({
|
||||
let app_state = app_state.clone();
|
||||
move |workspace, _: &CloseProject, window, cx| {
|
||||
let Some(window_handle) = window.window_handle().downcast::<MultiWorkspace>() else {
|
||||
return;
|
||||
};
|
||||
let app_state = app_state.clone();
|
||||
let old_group_key = workspace.project_group_key(cx);
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let should_continue = this
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.prepare_to_close(
|
||||
CloseIntent::ReplaceWindow,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await?;
|
||||
if should_continue {
|
||||
let task = cx.update(|_window, cx| {
|
||||
open_new(
|
||||
workspace::OpenOptions {
|
||||
requesting_window: Some(window_handle),
|
||||
..Default::default()
|
||||
},
|
||||
app_state,
|
||||
cx,
|
||||
|workspace, window, cx| {
|
||||
cx.activate(true);
|
||||
let project = workspace.project().clone();
|
||||
let buffer = project.update(cx, |project, cx| {
|
||||
project.create_local_buffer("", None, true, cx)
|
||||
});
|
||||
let editor = cx.new(|cx| {
|
||||
Editor::for_buffer(buffer, Some(project), window, cx)
|
||||
});
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(editor),
|
||||
None,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
},
|
||||
)
|
||||
})?;
|
||||
task.await?;
|
||||
window_handle.update(cx, |mw, window, cx| {
|
||||
mw.remove_project_group(&old_group_key, window, cx)
|
||||
})?.await.log_err();
|
||||
Ok::<(), anyhow::Error>(())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
let task = window_handle.update(cx, |multi_workspace, window, cx| {
|
||||
multi_workspace.remove_project_group(&old_group_key, window, cx)
|
||||
})?;
|
||||
task.await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
@@ -7092,4 +7050,92 @@ mod tests {
|
||||
"project group should be removed after CloseProject: {keys:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_close_project_switches_to_neighbor_in_multi_project(cx: &mut TestAppContext) {
|
||||
use workspace::OpenMode;
|
||||
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
.insert_tree(path!("/project-a"), json!({}))
|
||||
.await;
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
.insert_tree(path!("/project-b"), json!({}))
|
||||
.await;
|
||||
|
||||
let workspace::OpenResult {
|
||||
window,
|
||||
workspace: workspace_a,
|
||||
..
|
||||
} = cx
|
||||
.update(|cx| {
|
||||
workspace::Workspace::new_local(
|
||||
vec![path!("/project-a").into()],
|
||||
app_state.clone(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
OpenMode::Activate,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let project_b = Project::test(app_state.fs.clone(), [Path::new("/project-b")], cx).await;
|
||||
|
||||
window
|
||||
.update(cx, |multi_workspace, window, cx| {
|
||||
multi_workspace.test_add_workspace(project_b, window, cx);
|
||||
})
|
||||
.unwrap();
|
||||
cx.background_executor.run_until_parked();
|
||||
|
||||
// Reactivate workspace A so we close it via CloseProject.
|
||||
window
|
||||
.update(cx, |multi_workspace, window, cx| {
|
||||
multi_workspace.activate(workspace_a, None, window, cx);
|
||||
})
|
||||
.unwrap();
|
||||
cx.background_executor.run_until_parked();
|
||||
|
||||
let keys_before = window
|
||||
.read_with(cx, |multi_workspace, _| {
|
||||
multi_workspace.project_group_keys()
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
keys_before.len(),
|
||||
2,
|
||||
"should have 2 project groups before CloseProject: {keys_before:?}"
|
||||
);
|
||||
|
||||
cx.dispatch_action(window.into(), CloseProject);
|
||||
cx.background_executor.run_until_parked();
|
||||
|
||||
let keys_after = window
|
||||
.read_with(cx, |multi_workspace, _| {
|
||||
multi_workspace.project_group_keys()
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
keys_after.len(),
|
||||
1,
|
||||
"one project group should remain after CloseProject: {keys_after:?}"
|
||||
);
|
||||
|
||||
let active_paths = window
|
||||
.read_with(cx, |multi_workspace, cx| {
|
||||
multi_workspace.workspace().read(cx).root_paths(cx)
|
||||
})
|
||||
.unwrap();
|
||||
assert!(
|
||||
!active_paths.is_empty(),
|
||||
"active workspace should contain the remaining project, not be empty: {active_paths:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user