use std::io::Write;
use std::panic;
use std::sync::Once;
use crossterm::{
cursor, execute,
terminal::{self, LeaveAlternateScreen},
};
static PANIC_HOOK_INSTALLED: Once = Once::new();
pub fn restore_terminal() {
let mut stdout = std::io::stdout();
let _ = terminal::disable_raw_mode();
let _ = execute!(
stdout,
LeaveAlternateScreen,
cursor::Show,
crossterm::event::DisableMouseCapture,
crossterm::event::DisableBracketedPaste,
crossterm::event::DisableFocusChange,
);
let _ = stdout.flush();
}
pub fn install_panic_hook() {
PANIC_HOOK_INSTALLED.call_once(|| {
let original_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
restore_terminal();
eprintln!(
"\n\x1b[1;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m"
);
eprintln!("\x1b[1;31mPanic occurred! Terminal state has been restored.\x1b[0m");
eprintln!(
"\x1b[1;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n"
);
original_hook(panic_info);
}));
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_restore_terminal_doesnt_panic() {
restore_terminal();
}
#[test]
fn test_install_panic_hook_idempotent() {
install_panic_hook();
install_panic_hook();
}
}