perf(next): Cmd-W on an idle terminal closes in its key's turn (the program is read before the key) (SHORTCUT LATENCY SL-3)
Merged
LA
2d ago+471+471+012345678901234567890123456789-84-84-01234567890123456789
Summary
Cmd-W on a live terminal now takes the tab out of the strip in the key's own turn, while the terminal stays alive. Before, the tab left only after a daemon round trip (SHORTCUT FRAMES F6: the first changed frame came 4-6 display refreshes after the key at 120 Hz). A program is never ended without the question.
Design (lead's rules on #31875):
- The key's turn.
TabCloseGuard.closehides the closing tabs with a close intent and sends nothing (HiddenTabClose, CmuxNextDaemonService), so the terminals stay alive. - The read. The daemon reads the foreground program off the main thread, as before (
DaemonService.runningPrograms(of:unanswered:)).- Nothing runs: the hide is lifted in the same turn that the real close's own intent takes the tabs out (no flash), and the close goes to the daemon.
- A program runs, an agent works, or the read gives no answer: the hide is rolled back (
rejectIntent), the tabs come back at their places with each pane's selection (applySelection, no focus taken from another pane), and the normal question shows. Only a confirmed "no program", or the person's Close, ends a process.
- Content. While a tab is hidden, the store still lists it for its gone-tab watchers (
DaemonStore.holdTabs;liveTreeLists(tab:)is the read thatGoneTabWatch,AgentTabStoreandInternalPageTabStoreshare). Their terminal views and pages stay, so a Cancel brings back the SAME view and page (no reload; form input and scroll stay). - Keys. The close lane opens before the hide and, on the hide path, takes every key typed in the window until the answer (
CreationInputBuffer.openClose(takesEveryKey:)). The question parks them as before: Close sends them to the neighbour, Cancel sends them to the terminal that stays, in order. A typed Return never presses Close. - Second Cmd-W during the read. It acts at once on the neighbour the hide shows, and the first close's held keys keep waiting (
CreationInputBuffer.isClosingis true while a hidden close is open; before, they flushed into the neighbour, #29409). - No answer asks on every close through
TabCloseGuard(rule 4), hidden or not. A stand-in terminal still opening is never asked. A tab group's close (tabGroup.close) still uses the old read, which is unchanged in this PR. - Not hidden. Tabs whose page decides its own close (beforeunload), and a close that would leave a pane with no tab (the pane animates out after the read, as before).
- The per-key input path is not changed. Arch: no count rises.
Testing
CmdWIdleTerminalClosesInItsTurnTests (CmuxNextAppTests, same-tree daemon, real OSC 133 marks from the shell):
- idle terminal: the tab leaves in the key's turn and closes after the read, with no question;
- (a)
sleep, Ctrl-Z, the prompt's A, thenfgwith no OSC 133 C: Cmd-W asks, the tab is back, andsleepstill runs before and after Cancel; - (b) the prompt's A, then a command with no C, just before Cmd-W: the same;
- H1 terminal: a cancelled close brings back the SAME terminal view (
TabContentCache.existingTerminalobject), which also stays during the read; - H1 browser: Close Other Tabs with a running terminal and a page; Cancel brings back the SAME page entry (never released, so never reloaded);
- H2: keys typed during the read never run in the neighbour and do not reach the closing terminal while the question shows; a Return typed then does not press Close; after Cancel the whole line comes back, in order, to the terminal that stays.
- second Cmd-W (3 tabs): Cmd-W, a line, Cmd-W, more keys with the read held: no key runs in a neighbour, and after Cancel the line comes back to the terminal that stays.
Results (fleet):
- Red, read-ahead design: 5ed9ec75a58, step fe8ce53800e1. (a), (b) and the idle test fail.
- Red, first hide design (22b65947a25 + the H1/H2 tests): never-merge
cp-scratch/sl-cmdw-h2-red-never-mergee9b91d41fe8, step ac5350ee0ecd. H1 terminal (a new view comes back), H1 browser (the page is released) and H2 (the keys reach the closing terminal before the answer) fail. - Red, second Cmd-W test on 76579fdaa17: 7bc3a8c814f, step 0461101c723a. The held keys ran in the neighbour (
sh-3.2$ dw129912). - Green at b804e95f19d, step 5dd0312bc5a3: the 7 Cmd-W tests and the close suites (27 tests) pass.
- 5/5 at the head 6351d9b10e8: one step runs the 6 suites (with CreationInputHoldTests) five times, step ea5e6b9aa7da: 30 of 30 suite runs pass. An earlier 5x at 76579fdaa17 was 23/25; both failures were the same test set-up race (the view test read the terminal view before the pane had made it), now waited for.
Known, not changed here (review Lows): a close of another, unfocused terminal during a hidden read takes the held keys (the base does the same on the path that does not hide); an unanswered terminal's question names its tab title.
Observed / Not run
- Observed: the fleet runs above.
- Not run: the key-to-commit A/B on a mini; the SHORTCUT FRAMES film of the after.
Changelog
- Changed: Cmd-W takes a terminal tab away at once. The terminal ends only after the app confirms that no program runs in it; a running program brings the tab back with the usual question.