Fix a bug where Zed incorrectly keeps a removed pane as the active pane (#56229)
Some checks are pending
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / orchestrate (push) Waiting to run
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions

This PR should fix the issue Ben ran into with Auto Watch. It isn’t a
bug in Auto Watch itself, but a bug in Zed’s active-pane bookkeeping
when Zed auto-closes a pane. Ben had a screen share in its own split;
when the share ended, the tab closed, and with it, the pane. In this
case, Zed was holding onto the removed pane as the active pane, so Auto
Watch was trying to open the next screen share in a pane that was no
longer part of the workspace.

Self-Review Checklist:

- [X] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [X] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [X] Tests cover the new/changed behavior
- [X] Performance impact has been considered and is acceptable

Release Notes:

- Fixed a bug where Zed incorrectly kept a removed pane as the active
pane.
This commit is contained in:
Joseph T. Lyons 2026-05-09 15:33:05 -04:00 committed by GitHub
parent ef65b5a283
commit c8f09caee4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -6768,14 +6768,17 @@ impl Workspace {
window: &mut Window,
cx: &mut Context<Workspace>,
) {
let removing_active_pane = self.active_pane() == pane;
self.panes.retain(|p| p != pane);
if let Some(focus_on) = focus_on {
if removing_active_pane {
self.set_active_pane(focus_on, window, cx);
}
focus_on.update(cx, |pane, cx| window.focus(&pane.focus_handle(cx), cx));
} else if self.active_pane() == pane {
} else if removing_active_pane {
let fallback_pane = self.panes.last().unwrap().clone();
if self.has_active_modal(window, cx) {
self.set_active_pane(&fallback_pane, window, cx);
} else {
self.set_active_pane(&fallback_pane, window, cx);
if !self.has_active_modal(window, cx) {
fallback_pane.update(cx, |pane, cx| window.focus(&pane.focus_handle(cx), cx));
}
}
@ -14829,6 +14832,47 @@ mod tests {
});
}
#[gpui::test]
async fn test_active_pane_updates_to_focus_target_on_removal(cx: &mut TestAppContext) {
assert_active_pane_is_replaced_after_removal(cx, true).await;
}
#[gpui::test]
async fn test_active_pane_updates_to_fallback_on_removal(cx: &mut TestAppContext) {
assert_active_pane_is_replaced_after_removal(cx, false).await;
}
async fn assert_active_pane_is_replaced_after_removal(
cx: &mut TestAppContext,
use_focus_target: bool,
) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, [], cx).await;
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
workspace.update_in(cx, |workspace, window, cx| {
let first_pane = workspace.active_pane().clone();
let second_pane =
workspace.split_pane(first_pane.clone(), SplitDirection::Right, window, cx);
workspace.set_active_pane(&second_pane, window, cx);
let focus_target = use_focus_target.then(|| first_pane.clone());
workspace.remove_pane(second_pane, focus_target, window, cx);
assert_eq!(workspace.active_pane(), &first_pane);
assert!(
workspace
.panes()
.iter()
.any(|pane| pane == workspace.active_pane()),
"active pane should be one of the remaining workspace panes"
);
});
}
#[gpui::test]
async fn test_moving_items_create_panes(cx: &mut TestAppContext) {
init_test(cx);