use crate::{config, context, file_paths, helper};
use std::{error, io, mem, path};
pub fn is_terminal_stdout() -> bool {
std::io::IsTerminal::is_terminal(&std::io::stdout())
}
pub fn is_terminal_stdin() -> bool {
std::io::IsTerminal::is_terminal(&std::io::stdin())
}
pub struct PromptState<'a> {
rl: rustyline::Editor<helper::Helper<'a>, rustyline::history::FileHistory>,
config: &'a config::Config,
history_path: Option<path::PathBuf>,
}
pub fn init_prompt<'a>(
config: &'a config::Config,
context: &context::Context<'a>,
) -> Result<PromptState<'a>, Box<dyn error::Error>> {
use rustyline::{
Cmd, Editor, KeyCode, KeyEvent, Modifiers, Movement, config::Builder, history::FileHistory,
};
let mut rl = Editor::<helper::Helper<'_>, FileHistory>::with_config(
Builder::new()
.history_ignore_space(true)
.auto_add_history(true)
.max_history_size(config.max_history_size)?
.build(),
)?;
rl.set_helper(Some(helper::Helper::new(context.clone(), config)));
rl.bind_sequence(
KeyEvent(KeyCode::Right, Modifiers::NONE),
Cmd::Move(Movement::ForwardChar(1)),
);
let history_path = match file_paths::get_history_file_location(file_paths::DirMode::DontCreate)
{
Ok(history_path) => {
mem::drop(rl.load_history(history_path.as_path()));
Some(history_path)
}
Err(_) => None,
};
Ok(PromptState {
rl,
config,
history_path,
})
}
pub enum ReadLineError {
Interrupted, Eof,
Error(Box<dyn error::Error>),
}
impl From<rustyline::error::ReadlineError> for ReadLineError {
fn from(err: rustyline::error::ReadlineError) -> Self {
match err {
rustyline::error::ReadlineError::Interrupted => ReadLineError::Interrupted,
rustyline::error::ReadlineError::Eof => ReadLineError::Eof,
err => ReadLineError::Error(err.into()),
}
}
}
fn save_history(
rl: &mut rustyline::Editor<helper::Helper<'_>, rustyline::history::FileHistory>,
path: Option<&path::PathBuf>,
) -> io::Result<()> {
if let Some(history_path) = path {
file_paths::get_state_dir(file_paths::DirMode::Create)?;
if rl.save_history(history_path.as_path()).is_err() {
}
}
Ok(())
}
impl PromptState<'_> {
pub fn read_line(&mut self) -> Result<String, ReadLineError> {
let res = self.rl.readline(self.config.prompt.as_str());
mem::drop(save_history(&mut self.rl, self.history_path.as_ref()));
Ok(res?)
}
}