#![doc = include_str!("../README.md")]
pub use promkit_core;
pub use promkit_core::crossterm;
pub use promkit_widgets;
pub mod preset;
pub mod snapshot;
pub mod suggest;
pub mod switch;
pub mod validate;
use std::io;
use promkit_core::{
crossterm::{
cursor,
event::{self, Event},
execute,
terminal::{disable_raw_mode, enable_raw_mode},
},
terminal::Terminal,
Pane,
};
#[derive(Eq, PartialEq)]
pub enum PromptSignal {
Continue,
Quit,
}
pub trait Finalizer {
type Return;
fn finalize(&mut self) -> anyhow::Result<Self::Return>;
}
pub trait Renderer: Finalizer {
fn create_panes(&self, width: u16, height: u16) -> Vec<Pane>;
fn evaluate(&mut self, event: &Event) -> anyhow::Result<PromptSignal>;
}
pub struct Prompt<T: Renderer> {
pub renderer: T,
}
impl<T: Renderer> Drop for Prompt<T> {
fn drop(&mut self) {
execute!(
io::stdout(),
cursor::Show,
event::DisableMouseCapture,
cursor::MoveToNextLine(1),
)
.ok();
disable_raw_mode().ok();
}
}
impl<T: Renderer> Prompt<T> {
pub fn run(&mut self) -> anyhow::Result<T::Return> {
enable_raw_mode()?;
execute!(io::stdout(), cursor::Hide)?;
let size = crossterm::terminal::size()?;
let panes = self.renderer.create_panes(size.0, size.1);
let mut terminal = Terminal {
position: crossterm::cursor::position()?,
};
terminal.draw(&panes)?;
loop {
let ev = event::read()?;
match &ev {
Event::Resize(_, _) => {
terminal.position = (0, 0);
crossterm::execute!(
io::stdout(),
crossterm::terminal::Clear(crossterm::terminal::ClearType::Purge),
)?;
}
_ => {
if self.renderer.evaluate(&ev)? == PromptSignal::Quit {
terminal.draw(&self.renderer.create_panes(size.0, size.1))?;
break;
}
}
}
let size = crossterm::terminal::size()?;
terminal.draw(&self.renderer.create_panes(size.0, size.1))?;
}
disable_raw_mode()?;
self.renderer.finalize()
}
}