diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 76639e74fdfd856e896dbf74d645a2d2059d40ed..90e38bfdd9e9a123ccf02379f048e694cfd18353 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -653,6 +653,27 @@ impl TerminalPanel { window: &mut Window, cx: &mut Context, ) { + let center_pane = workspace.active_pane(); + let center_pane_has_focus = center_pane.focus_handle(cx).contains_focused(window, cx); + let active_center_item_is_terminal = center_pane + .read(cx) + .active_item() + .is_some_and(|item| item.downcast::().is_some()); + + if center_pane_has_focus && active_center_item_is_terminal { + let working_directory = default_working_directory(workspace, cx); + let local = action.local; + Self::add_center_terminal(workspace, window, cx, move |project, cx| { + if local { + project.create_local_terminal(cx) + } else { + project.create_terminal_shell(working_directory, cx) + } + }) + .detach_and_log_err(cx); + return; + } + let Some(terminal_panel) = workspace.panel::(cx) else { return; }; @@ -1883,6 +1904,436 @@ mod tests { ); } + async fn init_workspace_with_panel( + cx: &mut TestAppContext, + ) -> (gpui::WindowHandle, Entity) { + let fs = FakeFs::new(cx.executor()); + let project = Project::test(fs, [], cx).await; + let window_handle = + cx.add_window(|window, cx| MultiWorkspace::test_new(project, window, cx)); + + let terminal_panel = window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + let panel = cx.new(|cx| TerminalPanel::new(workspace, window, cx)); + workspace.add_panel(panel.clone(), window, cx); + panel + }) + }) + .expect("Failed to initialize workspace with terminal panel"); + + (window_handle, terminal_panel) + } + + #[gpui::test] + async fn test_new_terminal_opens_in_panel_by_default(cx: &mut TestAppContext) { + cx.executor().allow_parking(); + init_test(cx); + + let (window_handle, terminal_panel) = init_workspace_with_panel(cx).await; + + let panel_items_before = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + let center_items_before = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + TerminalPanel::new_terminal( + workspace, + &workspace::NewTerminal::default(), + window, + cx, + ); + }) + }) + .expect("Failed to dispatch new_terminal"); + + cx.run_until_parked(); + + let panel_items_after = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + let center_items_after = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + + assert_eq!( + panel_items_after, + panel_items_before + 1, + "Terminal should be added to the panel when no center terminal is focused" + ); + assert_eq!( + center_items_after, center_items_before, + "Center pane should not gain a new terminal" + ); + } + + #[gpui::test] + async fn test_new_terminal_opens_in_center_when_center_terminal_focused( + cx: &mut TestAppContext, + ) { + cx.executor().allow_parking(); + init_test(cx); + + let (window_handle, terminal_panel) = init_workspace_with_panel(cx).await; + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + TerminalPanel::add_center_terminal(workspace, window, cx, |project, cx| { + project.create_terminal_shell(None, cx) + }) + }) + }) + .expect("Failed to update workspace") + .await + .expect("Failed to create center terminal"); + cx.run_until_parked(); + + let center_items_before = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + assert_eq!(center_items_before, 1, "Center pane should have 1 terminal"); + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + let active_item = workspace + .active_pane() + .read(cx) + .active_item() + .expect("Center pane should have an active item"); + let terminal_view = active_item + .downcast::() + .expect("Active center item should be a TerminalView"); + window.focus(&terminal_view.focus_handle(cx), cx); + }) + }) + .expect("Failed to focus terminal view"); + cx.run_until_parked(); + + let panel_items_before = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + TerminalPanel::new_terminal( + workspace, + &workspace::NewTerminal::default(), + window, + cx, + ); + }) + }) + .expect("Failed to dispatch new_terminal"); + cx.run_until_parked(); + + let center_items_after = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + let panel_items_after = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + + assert_eq!( + center_items_after, + center_items_before + 1, + "New terminal should be added to the center pane" + ); + assert_eq!( + panel_items_after, panel_items_before, + "Terminal panel should not gain a new terminal" + ); + } + + #[gpui::test] + async fn test_new_terminal_opens_in_panel_when_panel_focused(cx: &mut TestAppContext) { + cx.executor().allow_parking(); + init_test(cx); + + let (window_handle, terminal_panel) = init_workspace_with_panel(cx).await; + + window_handle + .update(cx, |_, window, cx| { + terminal_panel.update(cx, |panel, cx| { + panel.add_terminal_shell(None, RevealStrategy::Always, window, cx) + }) + }) + .expect("Failed to update workspace") + .await + .expect("Failed to create panel terminal"); + cx.run_until_parked(); + + window_handle + .update(cx, |_, window, cx| { + window.focus(&terminal_panel.read(cx).focus_handle(cx), cx); + }) + .expect("Failed to focus terminal panel"); + cx.run_until_parked(); + + let panel_items_before = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + + let center_items_before = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + TerminalPanel::new_terminal( + workspace, + &workspace::NewTerminal::default(), + window, + cx, + ); + }) + }) + .expect("Failed to dispatch new_terminal"); + cx.run_until_parked(); + + let panel_items_after = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + let center_items_after = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + + assert_eq!( + panel_items_after, + panel_items_before + 1, + "New terminal should be added to the panel when panel is focused" + ); + assert_eq!( + center_items_after, center_items_before, + "Center pane should not gain a new terminal" + ); + } + + #[gpui::test] + async fn test_new_local_terminal_opens_in_center_when_center_terminal_focused( + cx: &mut TestAppContext, + ) { + cx.executor().allow_parking(); + init_test(cx); + + let (window_handle, terminal_panel) = init_workspace_with_panel(cx).await; + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + TerminalPanel::add_center_terminal(workspace, window, cx, |project, cx| { + project.create_terminal_shell(None, cx) + }) + }) + }) + .expect("Failed to update workspace") + .await + .expect("Failed to create center terminal"); + cx.run_until_parked(); + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + let active_item = workspace + .active_pane() + .read(cx) + .active_item() + .expect("Center pane should have an active item"); + let terminal_view = active_item + .downcast::() + .expect("Active center item should be a TerminalView"); + window.focus(&terminal_view.focus_handle(cx), cx); + }) + }) + .expect("Failed to focus terminal view"); + cx.run_until_parked(); + + let center_items_before = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + let panel_items_before = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + TerminalPanel::new_terminal( + workspace, + &workspace::NewTerminal { local: true }, + window, + cx, + ); + }) + }) + .expect("Failed to dispatch new_terminal with local=true"); + cx.run_until_parked(); + + let center_items_after = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + let panel_items_after = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + + assert_eq!( + center_items_after, + center_items_before + 1, + "New local terminal should be added to the center pane" + ); + assert_eq!( + panel_items_after, panel_items_before, + "Terminal panel should not gain a new terminal" + ); + } + + #[gpui::test] + async fn test_new_terminal_opens_in_panel_when_panel_focused_and_center_has_terminal( + cx: &mut TestAppContext, + ) { + cx.executor().allow_parking(); + init_test(cx); + + let (window_handle, terminal_panel) = init_workspace_with_panel(cx).await; + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + TerminalPanel::add_center_terminal(workspace, window, cx, |project, cx| { + project.create_terminal_shell(None, cx) + }) + }) + }) + .expect("Failed to update workspace") + .await + .expect("Failed to create center terminal"); + cx.run_until_parked(); + + window_handle + .update(cx, |_, window, cx| { + terminal_panel.update(cx, |panel, cx| { + panel.add_terminal_shell(None, RevealStrategy::Always, window, cx) + }) + }) + .expect("Failed to update workspace") + .await + .expect("Failed to create panel terminal"); + cx.run_until_parked(); + + window_handle + .update(cx, |_, window, cx| { + window.focus(&terminal_panel.read(cx).focus_handle(cx), cx); + }) + .expect("Failed to focus terminal panel"); + cx.run_until_parked(); + + let panel_items_before = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + let center_items_before = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + + window_handle + .update(cx, |multi_workspace, window, cx| { + multi_workspace.workspace().update(cx, |workspace, cx| { + TerminalPanel::new_terminal( + workspace, + &workspace::NewTerminal::default(), + window, + cx, + ); + }) + }) + .expect("Failed to dispatch new_terminal"); + cx.run_until_parked(); + + let panel_items_after = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + let center_items_after = window_handle + .read_with(cx, |multi_workspace, cx| { + multi_workspace + .workspace() + .read(cx) + .active_pane() + .read(cx) + .items_len() + }) + .expect("Failed to read center pane items"); + + assert_eq!( + panel_items_after, + panel_items_before + 1, + "New terminal should go to panel when panel is focused, even if center has a terminal" + ); + assert_eq!( + center_items_after, center_items_before, + "Center pane should not gain a new terminal when panel is focused" + ); + } + fn set_max_tabs(cx: &mut TestAppContext, value: Option) { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings(cx, |settings| {