pub const FRAME: std::time::Duration = std::time::Duration::from_millis(33);
pub mod difffeed;
pub mod difftool;
pub mod highlight;
pub mod input;
pub mod widgets;
use ratatui::DefaultTerminal;
use ratatui::crossterm::event::{
KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
};
use ratatui::crossterm::terminal::supports_keyboard_enhancement;
use ratatui::crossterm::{ExecutableCommand, execute};
use std::io::stdout;
#[cfg(unix)]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(unix)]
use std::sync::{Arc, OnceLock};
pub fn push_keyboard_enhancement() -> bool {
if supports_keyboard_enhancement().unwrap_or(false) {
let _ = execute!(
stdout(),
PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES)
);
true
} else {
false
}
}
pub fn pop_keyboard_enhancement() {
let _ = stdout().execute(PopKeyboardEnhancementFlags);
}
pub fn suspended<T>(terminal: &mut DefaultTerminal, enhanced: bool, run: impl FnOnce() -> T) -> T {
if enhanced {
pop_keyboard_enhancement();
}
ratatui::restore();
let out = interrupts_to_child(run);
*terminal = ratatui::init();
if enhanced {
push_keyboard_enhancement();
}
out
}
#[cfg(unix)]
fn interrupts_to_child<T>(run: impl FnOnce() -> T) -> T {
let ours = interrupts_end_us();
ours.store(false, Ordering::SeqCst);
let out = run();
ours.store(true, Ordering::SeqCst);
out
}
#[cfg(not(unix))]
fn interrupts_to_child<T>(run: impl FnOnce() -> T) -> T {
run()
}
#[cfg(unix)]
fn interrupts_end_us() -> &'static Arc<AtomicBool> {
use signal_hook::consts::{SIGINT, SIGQUIT};
static OURS: OnceLock<Arc<AtomicBool>> = OnceLock::new();
OURS.get_or_init(|| {
let ours = Arc::new(AtomicBool::new(true));
for signal in [SIGINT, SIGQUIT] {
let _ = signal_hook::flag::register_conditional_default(signal, ours.clone());
}
ours
})
}
pub fn pane_width(terminal: &DefaultTerminal) -> u16 {
terminal
.size()
.map(|s| s.width.saturating_sub(2))
.unwrap_or(120)
}
pub fn clamp_scroll(scroll: u16, total_lines: usize, viewport: u16) -> u16 {
let max = (total_lines.min(u16::MAX as usize) as u16).saturating_sub(viewport);
scroll.min(max)
}
pub fn clamp_hscroll(hscroll: u16, max_line: usize, cell_w: u16) -> u16 {
let max = (max_line.min(u16::MAX as usize) as u16).saturating_sub(cell_w);
hscroll.min(max)
}