use std::env;
use std::fs::OpenOptions;
use std::io::Write;
use color_eyre::eyre::Result;
use crossterm::{ExecutableCommand, QueueableCommand, cursor};
use unicode_width::UnicodeWidthStr;
use crate::cursor::query_cursor_position;
fn visible_width(s: &str) -> usize {
let stripped_ansi = strip_ansi_escapes::strip(s);
let clean = String::from_utf8_lossy(&stripped_ansi);
UnicodeWidthStr::width(clean.as_ref())
}
pub struct PromptGuard {
tty: std::fs::File,
cursor_line: u16,
cursor_column: u16,
prompt: String,
input: String,
}
impl PromptGuard {
pub fn try_new() -> Result<Self> {
let tty = OpenOptions::new().write(true).open("/dev/tty")?;
let prompt = env::var("LEADR_PROMPT")?;
let input = env::var("LEADR_CURRENT_INPUT")?;
let (cursor_column, cursor_line) = query_cursor_position()?;
Ok(Self {
tty,
cursor_line,
cursor_column,
prompt,
input,
})
}
pub fn redraw(&mut self) -> Result<()> {
let prompt_width = visible_width(&self.prompt) as u16;
self.tty
.queue(crossterm::cursor::MoveTo(0, self.cursor_line))?
.queue(crossterm::terminal::Clear(
crossterm::terminal::ClearType::CurrentLine,
))?
.queue(crossterm::style::Print(format!(
"{}{}",
self.prompt, self.input
)))?
.queue(crossterm::cursor::MoveTo(
prompt_width + self.cursor_column,
self.cursor_line,
))?
.flush()?;
Ok(())
}
}
impl Drop for PromptGuard {
fn drop(&mut self) {
let _ = self.tty.execute(cursor::MoveTo(0, self.cursor_line));
let _ = self.tty.execute(crossterm::terminal::Clear(
crossterm::terminal::ClearType::CurrentLine,
));
}
}