Skip to content

feat(tabs): close all, other, and other-database tabs from the File menu (#1972) - #1977

Merged
datlechin merged 1 commit into
mainfrom
fix/1972-close-all-tabs
Jul 27, 2026
Merged

feat(tabs): close all, other, and other-database tabs from the File menu (#1972)#1977
datlechin merged 1 commit into
mainfrom
fix/1972-close-all-tabs

Conversation

@datlechin

Copy link
Copy Markdown
Member

Closes #1972.

Problem

Two asks in the issue:

  1. There is no way to close a group of tabs in one step, and no "Close all tabs" anywhere.
  2. Switching the active database leaves the previous database's tabs open, which the reporter wanted closed automatically.

What this does

Adds three commands to the File menu, after Close Tab:

Command Scope
Close Other Tabs the current window's tab group
Close Tabs for Other Databases the current connection, across windows
Close All Tabs the current window's tab group, survivor kept blank

None gets a shortcut out of the box. All three are rebindable in Settings > Keyboard under Navigation, which matches DataGrip (only Cmd+W of its six close commands is bound) and TablePlus.

Close All Tabs closes every sibling window and empties the last one, landing on the "No tabs open" state that already existed in MainEditorContentView. The window stays open and the connection stays live, which is the blank page the issue asked for.

Close Tabs for Other Databases closes only windows where every tab was opened against a database other than the active one. It never touches the current window and is limited to the current connection. On engines that switch schemas rather than databases (BigQuery, Oracle) it reads Close Tabs for Other Schemas.

The reporter asked for a tab right-click menu. That is not buildable: NSWindowTab exposes only title, attributedTitle, toolTip, and accessoryView, NSWindowTabGroup exposes no menu API, and NSWindowDelegate has no tab callbacks. The File menu is the documented home, and the HIG's context-menu guidance points the same way ("in macOS, an app's menu bar menus list all the app's commands, including those in various context menus"). Option-clicking a tab's close button already closes the other tabs natively; the docs now say so.

Why ask 2 is not implemented as asked

Auto-closing tabs on a database switch is deliberately not built. It was in this codebase and was removed twice:

Staleness is already handled: handleTabChange re-points the connection to the selected tab's database, and executeQueryInternal does it again before running SQL. No comparable client auto-closes either. TablePlus shipped it, had it reported, and removed it two releases later; Sequel Ace, Sequel Pro, Postico and DataGrip do not. DataGrip's one auto-closing mechanism is its longest-running open complaint (IJPL-59306, open since 2018).

Close Tabs for Other Databases gives the same outcome on the user's command instead of as a side effect of navigation.

Refactor

closeTab() was synchronous and fired a detached Task for its save prompt, and performClose() inferred "am I the last window in my group" from a live window.tabbedWindows count. Neither can express "close N windows, one prompt at a time, with a caller-chosen survivor", so the close path is now one primitive:

  • closeWindowAwaiting(asBatchSurvivor:) async -> WindowCloseOutcome. nil reproduces the old self-deciding behaviour; a batch passes true for the window it keeps blank and false for the ones it tears down. closeTab() is a two-line wrapper, so Cmd+W is unchanged.
  • clearTabsInPlace() extracted from the old third branch.
  • TabBatchClosePlanner is pure, Foundation only, and owns which windows close and which survives.

The auto-detect path is equivalent to what it replaced: if A { close } else if B { close } else { clear } became if A || B { close } else { clear }, and captureClosingTabsForRecovery() still runs once, before any teardown, on every path.

A batch closes siblings first and the survivor last, so cancelling part-way leaves the window the user is looking at untouched. Each window keeps the ordinary Save / Don't Save / Cancel prompt rather than a new bulk alert, and every closed tab still lands in Recently Closed, so a bulk close is undoable with Cmd+Shift+T.

Fixed along the way

closeTab() gated its prompt on hasUnsavedWorkInSelectedTab(), so closing a window that holds more than one tab (Redis key tabs, all-tables metadata) could discard unsaved work in a tab that was not on screen. It now uses the existing hasAnyUnsavedWork(), which is a strict superset and only ever adds a prompt.

Tests

  • TabBatchClosePlannerTests: survivor selection, ordering, self-exclusion, and the database-match rules (foreign-only closes; mixed and unnamed windows are kept; an unknown active database closes nothing).
  • CommandActionsBulkCloseTests: the survivor empties in place, database scoping across two coordinators on one connection, other connections stay out of scope, and menu enablement.
  • SwitchDatabaseTests reconciled. It defined a hand-rolled simulateDatabaseSwitch that did tabs = [] and asserted tabs get cleared, without ever calling production code. It asserted behaviour removed by fix(coordinator): keep open tabs when switching schemas #1677 and feat(editor): per-tab database picker and tab interaction improvements #1776, so it would have misled anyone into restoring the wipe. Replaced with two tests that drive the real switchDatabase and lock the shipped behaviour.

Not covered, and why: the multi-window alert sequencing needs real dirty on-screen sibling windows and simulated alert clicks. AlertHelper is a static wrapper over NSAlert with no injection seam, and native tab-group membership has no XCUITest query surface. That path had no coverage before this change either; this does not widen the gap.

swiftlint lint --strict passes on every changed file.

Docs

docs/features/tabs.mdx documented "Close other tabs: right-click the tab (native macOS menu)". Closing is not among the selectors AppKit adds to the system tab menu, so that row is corrected to the Option-click gesture and the new command, alongside rows for the other two. docs/features/keyboard-shortcuts.mdx gains the three actions under Tabs and Windows.

Test plan

  • Open several tabs on one connection. File > Close All Tabs closes them all and leaves one blank window with the connection still live; Cmd+T works from there.
  • Repeat with a tab holding unsaved data edits. The prompt appears once for that window; Cancel stops the rest and leaves the current window's tabs intact.
  • Close Other Tabs leaves the current tab and its contents alone.
  • Open tabs against two databases on one MySQL connection. Close Tabs for Other Databases closes only the foreign ones and never the current tab.
  • Confirm the item is greyed out when every tab is on the active database, and hidden from use on SQLite.
  • On BigQuery or Oracle, confirm the item reads Close Tabs for Other Schemas.
  • Cmd+Shift+T reopens tabs closed by a bulk close.
  • Bind shortcuts to the three actions in Settings > Keyboard and confirm they fire.
  • With Group all connections in one window on, confirm Close All Tabs behaves sensibly on a mixed-connection tab group.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@mintlify

mintlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jul 27, 2026, 3:24 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@datlechin
datlechin merged commit f23fb68 into main Jul 27, 2026
2 of 3 checks passed
@datlechin
datlechin deleted the fix/1972-close-all-tabs branch July 27, 2026 15:34
@mintlify

mintlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟡 Building Jul 27, 2026, 3:22 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: close all the tabs

1 participant