You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(terminal): make live-vs-scrollback genuine per-split state
The previous fix made the *rendering* decision per-split but the mode
itself was still tracked by two coupled window/buffer fields that could
only describe one terminal at a time: `Window::terminal_mode: bool` and
`TerminalBuffer::mode` (a per-buffer Live/Scrollback). So only the
focused split could ever be in scrollback — an unfocused split snapped
back to the live grid the moment it lost focus, and two splits on one
terminal could never hold two different scroll positions.
Replace both with a single source of truth: `TerminalBuffer` now owns a
`scrollback_splits: HashSet<LeafId>`. A split showing a terminal is live
iff its LeafId is absent and scrollback iff present. This makes illegal
states unrepresentable:
- Live has exactly one representation (absence), so no split can be in
a contradictory live+scrollback state.
- The set lives on `TerminalBuffer`, so only terminals can carry a
mode at all.
- It is keyed per split, so each split's mode is independent and
survives losing focus.
Everything else derives from it. `Window::terminal_mode` is deleted;
`focused_terminal_live()` is `key_context == Terminal`, which
`sync_terminal_mode_flags` — the single derivation authority — projects
from the set for the focused split (also owning the `editing_disabled`
edge via `sync_terminal_to_buffer`). The ~20 transition sites that used
to hand-maintain the `(mode, terminal_mode, key_context)` trio inline
now just flip the set and re-derive, collapsing into
`enter_terminal_mode()` / `enter_terminal_scrollback()`. Rendering
(`render_terminal_splits`, `resize_visible_terminals`, the orchestration
scrollbar gate) reads the set directly; the renderer is handed the
`scrollback_view_splits` set instead of a window flag.
Lifecycle:
- Splits close: `forget_split_terminal_modes` prunes the LeafId from
every terminal (wired into all split-close paths).
- Process exit: every split showing the dead terminal is moved to
scrollback (no live grid for a gone PTY).
- Workspace restore: terminals come back live by default (empty set);
the read-only → live completion on focus is unchanged.
Behaviour preserved: single-split terminal mode, #2485 (re-focus keeps a
terminal's remembered mode — now per split), #2029 (file-explorer focus
stops PTY routing), and remote reconnect all keep working. New: any
number of splits can sit in independent scrollback while others stream
live, focused or not.
Tests: `test_unfocused_split_retains_scrollback_independently` drives the
new guarantee end-to-end (screen-only asserts); the existing #2595
reproducer still passes. `issue_2029` and `remote_auto_reconnect` tests
updated to the derived accessors. Docs note the per-split behaviour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DouV1Nq3wzFPmtR5yNSsHA
0 commit comments