1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! Shared ratatui terminal setup/teardown for every full-screen TUI surface.
//!
//! Why: three surfaces now enter raw mode and the alternate screen — the
//! coordinator chat, the `project_ctl` multipane dashboard, and (#7224) the
//! `tm ls` session TUI. The first two each carried their own private
//! `TerminalGuard` with byte-identical `Drop` bodies. A third copy would make
//! the restore sequence a thing to keep in step across three files instead of
//! one, and leaving a terminal in raw mode is the worst failure any of them can
//! have: the operator's shell stops echoing and Ctrl-C stops working.
//!
//! What: [`TerminalGuard`] is the `Drop`-based safety net (normal return AND
//! panic unwind); [`enter`] builds a ratatui terminal on stdout already in raw
//! mode and on the alternate screen; [`suspend`] hands the real screen back
//! WITHOUT dropping the terminal, so a caller can shell out (a tmux attach,
//! say) and then [`resume`] onto the same terminal value.
//!
//! Test: `terminal_guard_drop_is_idempotent`,
//! `enter_with_restores_the_terminal_when_the_screen_fails`,
//! `enter_with_leaves_the_terminal_alone_when_raw_mode_fails` and
//! `term_supports_raw_mode_rejects_dumb_and_missing` in `super::tests`; the
//! escape sequences themselves are side-effect-only and verified by launching a
//! TUI.
use ;
use ;
use ;
/// The ratatui terminal type every full-screen surface in this crate uses.
pub type TuiTerminal = ;
/// Restores cooked mode, the main screen, and the cursor however a TUI exits.
///
/// Why: an early `?` return or a panic unwind would otherwise leave the
/// operator's shell in raw mode on the alternate screen. A `Drop` guard covers
/// both paths that a trailing teardown call misses.
/// What: hold one for the lifetime of the TUI. Its `Drop` is best-effort and
/// idempotent — running it after [`suspend`] has already restored the screen
/// is a no-op in practice, which is what lets a suspend/resume cycle keep the
/// guard installed across the hand-off.
/// Test: `terminal_guard_drop_is_idempotent`.
;
/// Restore cooked mode, the main screen, and the cursor — best effort.
///
/// Why: the one place the restore sequence is written, so the `Drop` guard and
/// the explicit [`suspend`] can never drift apart on the order of operations.
/// What: `disable_raw_mode`, then `LeaveAlternateScreen` + `Show` on stdout;
/// every error is dropped for the reason [`TerminalGuard::drop`] gives.
/// Test: side-effect-only; covered by launching a TUI.
/// May a full-screen TUI take this `TERM` over, or must the caller print statically?
///
/// Why: a `dumb` (or absent) `TERM` has no cursor addressing, so a redraw smears
/// the frame down the screen instead of repainting it — and raw mode has already
/// taken the operator's echo away by then. One predicate so every gate that
/// guards [`enter`] answers this the same way; a second copy is how `tm ls` and
/// `tm f` came to disagree (#7224).
/// What: true only for a `TERM` that is present, non-empty, and not `dumb`
/// (case-insensitively). `NO_COLOR` is deliberately not consulted: it suppresses
/// color, not cursor addressing.
/// Test: `term_supports_raw_mode_rejects_dumb_and_missing`,
/// `term_supports_raw_mode_accepts_a_real_terminal`.
/// Enter raw mode + the alternate screen and build the ratatui terminal.
///
/// Why: every caller wants the same four steps in the same order, and getting
/// the order wrong (building the backend before the alternate screen) leaves a
/// frame of the shell's own scrollback painted over.
/// What: `enable_raw_mode`, `EnterAlternateScreen` on stdout, then a
/// `CrosstermBackend` terminal over that same stdout — atomically, so a failure
/// at either later step restores the terminal before propagating (#7224).
/// Callers construct their [`TerminalGuard`] only after this returns `Ok`, so
/// until then this function is the only thing that can undo the raw mode it
/// just took.
/// Test: the atomicity by `enter_with_restores_the_terminal_when_the_screen_fails`
/// and `enter_with_leaves_the_terminal_alone_when_raw_mode_fails`; the real
/// escape sequences are side-effect-only and covered by launching a TUI.
/// [`enter`] with its two terminal side effects injected, so failure is testable.
///
/// Why: crossterm's raw-mode calls act on the process's real controlling
/// terminal, which a unit test can neither fake nor safely disturb. Taking the
/// screen writer, the raw-mode enable, and the restore as parameters lets a test
/// hand in a writer that fails on `EnterAlternateScreen` and then assert the
/// restore actually ran — the property that was missing (#7224).
/// What: enables raw mode, enters the alternate screen on `screen`, and builds
/// the backend over it. On a failure at either step AFTER raw mode engaged it
/// calls `restore_terminal` and propagates the error; a failure of `enable`
/// itself changed nothing, so nothing is restored. The step-3 restore goes
/// through `restore_terminal` rather than `screen` because `Terminal::new`
/// consumes the writer on the error path.
/// Test: `enter_with_restores_the_terminal_when_the_screen_fails`,
/// `enter_with_leaves_the_terminal_alone_when_raw_mode_fails`.
pub
/// Hand the real screen back while keeping the terminal value alive (#7224).
///
/// Why: the `tm ls` TUI opens a session by shelling out to `tmux attach`, which
/// needs a cooked terminal on the MAIN screen — and control comes back here
/// when the operator detaches, at which point the TUI has to reappear. A
/// `Drop`-only teardown cannot express that: it ends the terminal for good.
/// What: `disable_raw_mode`, then `LeaveAlternateScreen` + `Show` through the
/// terminal's own backend, so the buffered ratatui state stays intact and
/// [`resume`] can pick it back up. Errors propagate: unlike the `Drop` path,
/// this one has a caller that can report them.
/// Test: side-effect-only; the hand-off is verified by the tmux smoke run.
/// Take the screen back after a [`suspend`] (#7224).
///
/// Why: the operator detached from the tmux session the TUI handed them to, so
/// control is back here and the list has to reappear where it was.
/// What: re-enters raw mode and the alternate screen through the SAME backend,
/// then clears ratatui's cached buffer — without the clear, the first frame
/// diffs against what the pre-suspend screen held and paints nothing.
/// Test: side-effect-only; verified by the tmux smoke run.