Skip to main content

cc_switch_lib/cli/
terminal.rs

1use std::io::{self, IsTerminal, Write};
2use std::sync::Once;
3
4static DISABLE_BRACKETED_PASTE: Once = Once::new();
5
6/// Disables bracketed paste mode to work around inquire dropping paste events.
7///
8/// When bracketed paste mode is enabled (common in zsh/fish, tmux/zellij, and some terminals),
9/// paste events are sent as `Event::Paste(String)` by crossterm. However, inquire's event
10/// handler only processes `Event::Key(...)` and drops all other events, causing paste to
11/// appear broken.
12///
13/// This function sends the ANSI escape sequence `CSI ?2004 l` to disable bracketed paste mode,
14/// causing the terminal to send paste content as regular character events that inquire can handle.
15///
16/// **Side effects:**
17/// - This changes terminal state and may affect bracketed paste behavior after the program exits.
18/// - Most shells (zsh/fish) will re-enable it on the next prompt, but this is not guaranteed.
19///
20/// **Implementation notes:**
21/// - Uses `Once` to ensure the sequence is only sent once per process.
22/// - This is a best-effort operation that silently fails if stderr is not a terminal.
23/// - On Windows, the ANSI sequence may appear as garbage in legacy consoles that don't support VT.
24pub fn disable_bracketed_paste_mode_best_effort() {
25    DISABLE_BRACKETED_PASTE.call_once(|| {
26        if !io::stderr().is_terminal() {
27            return;
28        }
29
30        let mut stderr = io::stderr();
31        // CSI ?2004 l - Disable bracketed paste mode
32        if let Err(e) = stderr.write_all(b"\x1b[?2004l") {
33            log::debug!("Failed to disable bracketed paste mode: {}", e);
34            return;
35        }
36        if let Err(e) = stderr.flush() {
37            log::debug!(
38                "Failed to flush stderr after disabling bracketed paste: {}",
39                e
40            );
41        }
42    });
43}