From 36c006828e403bc4ba6a41a6670509e336a5ad08 Mon Sep 17 00:00:00 2001 From: vipex <101529155+vipexv@users.noreply.github.com> Date: Tue, 21 Oct 2025 03:19:40 +0200 Subject: [PATCH] pane: Ignore max tabs on terminal pane (#40740) Closes #39901 I'm unsure as to which direction the team wants to go with this, but this is the behavior of VSCode which is what this feature is based off so i'm going with this. Changes: 1. Introduced a new argument to the `new` method on the Pane called `ignore_max_tabs` that forces the `max_tabs` to None if it's true. 2. Added a new test `test_bypass_max_tabs_limit`. Release Notes: - Fixed: `max_tabs` Setting affecting the terminal pane. --------- Co-authored-by: Joseph T. Lyons --- crates/debugger_ui/src/session/running.rs | 1 + crates/terminal_view/src/terminal_panel.rs | 51 ++++++++++++++++++++++ crates/workspace/src/pane.rs | 12 ++++- crates/workspace/src/workspace.rs | 2 + 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index 8f25ee7fa4..7340f25916 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -386,6 +386,7 @@ pub(crate) fn new_debugger_pane( Default::default(), None, NoAction.boxed_clone(), + true, window, cx, ); diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index cb80d58c13..de66bb1ed6 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -1103,6 +1103,7 @@ pub fn new_terminal_pane( Default::default(), None, NewTerminal.boxed_clone(), + false, window, cx, ); @@ -1752,6 +1753,8 @@ impl Render for InlineAssistTabBarButton { #[cfg(test)] mod tests { + use std::num::NonZero; + use super::*; use gpui::{TestAppContext, UpdateGlobal as _}; use pretty_assertions::assert_eq; @@ -1808,6 +1811,46 @@ mod tests { .unwrap(); } + #[gpui::test] + async fn test_bypass_max_tabs_limit(cx: &mut TestAppContext) { + init_test(cx); + + let fs = FakeFs::new(cx.executor()); + let project = Project::test(fs, [], cx).await; + let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx)); + + let (window_handle, terminal_panel) = workspace + .update(cx, |workspace, window, cx| { + let window_handle = window.window_handle(); + let terminal_panel = cx.new(|cx| TerminalPanel::new(workspace, window, cx)); + (window_handle, terminal_panel) + }) + .unwrap(); + + set_max_tabs(cx, Some(3)); + + for _ in 0..5 { + let task = window_handle + .update(cx, |_, window, cx| { + terminal_panel.update(cx, |panel, cx| { + panel.add_terminal_shell(None, RevealStrategy::Always, window, cx) + }) + }) + .unwrap(); + task.await.unwrap(); + } + + cx.run_until_parked(); + + let item_count = + terminal_panel.read_with(cx, |panel, cx| panel.active_pane.read(cx).items_len()); + + assert_eq!( + item_count, 5, + "Terminal panel should bypass max_tabs limit and have all 5 terminals" + ); + } + // A complex Unix command won't be properly parsed by the Windows terminal hence omit the test there. #[cfg(unix)] #[gpui::test] @@ -1922,6 +1965,14 @@ mod tests { .unwrap(); } + fn set_max_tabs(cx: &mut TestAppContext, value: Option) { + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings(cx, |settings| { + settings.workspace.max_tabs = value.map(|v| NonZero::new(v).unwrap()) + }); + }); + } + pub fn init_test(cx: &mut TestAppContext) { cx.update(|cx| { let store = SettingsStore::test(cx); diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 0784f30739..3dcba4aa4e 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -376,6 +376,7 @@ pub struct Pane { render_tab_bar: Rc) -> AnyElement>, show_tab_bar_buttons: bool, max_tabs: Option, + use_max_tabs: bool, _subscriptions: Vec, tab_bar_scroll_handle: ScrollHandle, /// This is set to true if a user scroll has occurred more recently than a system scroll @@ -473,10 +474,16 @@ impl Pane { next_timestamp: Arc, can_drop_predicate: Option bool + 'static>>, double_click_dispatch_action: Box, + use_max_tabs: bool, window: &mut Window, cx: &mut Context, ) -> Self { let focus_handle = cx.focus_handle(); + let max_tabs = if use_max_tabs { + WorkspaceSettings::get_global(cx).max_tabs + } else { + None + }; let subscriptions = vec![ cx.on_focus(&focus_handle, window, Pane::focus_in), @@ -498,7 +505,8 @@ impl Pane { zoomed: false, active_item_index: 0, preview_item_id: None, - max_tabs: WorkspaceSettings::get_global(cx).max_tabs, + max_tabs, + use_max_tabs, last_focus_handle_by_item: Default::default(), nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState { mode: NavigationMode::Normal, @@ -706,7 +714,7 @@ impl Pane { self.preview_item_id = None; } - if new_max_tabs != self.max_tabs { + if self.use_max_tabs && new_max_tabs != self.max_tabs { self.max_tabs = new_max_tabs; self.close_items_on_settings_change(window, cx); } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 53f416ae80..3c8c94ce0e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1331,6 +1331,7 @@ impl Workspace { pane_history_timestamp.clone(), None, NewFile.boxed_clone(), + true, window, cx, ); @@ -3235,6 +3236,7 @@ impl Workspace { self.pane_history_timestamp.clone(), None, NewFile.boxed_clone(), + true, window, cx, );