125 lines
3.9 KiB
Rust
125 lines
3.9 KiB
Rust
use crossterm::{cursor, event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, execute, terminal};
|
|
use std::io::{self, Write};
|
|
use thiserror::Error;
|
|
|
|
use crate::{commands::Command, session::{self, Pse}};
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum InputHandleError {
|
|
#[error("UserExit")]
|
|
UserExit,
|
|
#[error("Sigterm")]
|
|
Sigterm,
|
|
#[error("Render failure: {0}")]
|
|
Write(io::Error),
|
|
#[error("Flush failure: {0}")]
|
|
Flush(io::Error),
|
|
#[error("Disabling the terminal's raw mode failed: {0}")]
|
|
EnableRaw(io::Error),
|
|
#[error("Enabling the terminal's raw mode failed: {0}")]
|
|
DisableRaw(io::Error),
|
|
#[error("key input failure: {0}")]
|
|
Key(KeyCode),
|
|
}
|
|
type InputResult<T> = Result<T, InputHandleError>;
|
|
|
|
trait SpecificKeybinds {
|
|
const TERM_ID_1: &str;
|
|
fn key_ctrl(&mut self, input_key: KeyEvent, keycode: KeyCode) -> InputResult<()>;
|
|
fn key_enter(&mut self) -> InputResult<()>;
|
|
fn key_backspace(&mut self) -> InputResult<()>;
|
|
fn key_arrow_right(&mut self) -> InputResult<()>;
|
|
fn key_arrow_left(&mut self) -> InputResult<()>;
|
|
}
|
|
impl SpecificKeybinds for Pse {
|
|
const TERM_ID_1: &str = "exit";
|
|
|
|
fn key_ctrl(&mut self, input_key: KeyEvent, keycode: KeyCode) -> InputResult<()> {
|
|
if input_key.modifiers.contains(KeyModifiers::CONTROL) {
|
|
match keycode {
|
|
KeyCode::Char('c') => Err(InputHandleError::Sigterm),
|
|
_ => Ok(())
|
|
}
|
|
} else {
|
|
self.term_render(keycode.to_string())
|
|
}
|
|
}
|
|
|
|
fn key_enter(&mut self) -> InputResult<()> {
|
|
if self.rt.input == Self::TERM_ID_1 { return Err(InputHandleError::UserExit) };
|
|
|
|
terminal::disable_raw_mode().map_err(InputHandleError::DisableRaw)?;
|
|
Command::new(&self.rt.input).exec(&mut self.history);
|
|
self.rt.input.clear();
|
|
Ok(())
|
|
}
|
|
|
|
fn key_backspace(&mut self) -> InputResult<()> {
|
|
if self.rt.input.pop().is_some() {
|
|
execute!(
|
|
io::stdout(),
|
|
cursor::MoveLeft(1),
|
|
terminal::Clear(terminal::ClearType::UntilNewLine)
|
|
).map_err(InputHandleError::Flush)
|
|
} else {
|
|
//the string is empty, do terminal beep
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn key_arrow_right(&mut self) -> InputResult<()> {
|
|
execute!(io::stdout(), cursor::MoveRight(1)).map_err(InputHandleError::Flush)
|
|
}
|
|
|
|
fn key_arrow_left(&mut self) -> InputResult<()> {
|
|
execute!(io::stdout(), cursor::MoveLeft(1)).map_err(InputHandleError::Flush)
|
|
}
|
|
}
|
|
|
|
pub trait TermProcessor {
|
|
fn term_render(&mut self, def_string: String) -> InputResult<()>;
|
|
fn term_input_handler(&mut self, input_key: KeyEvent) -> Option<()>;
|
|
fn term_input_mainthread(&mut self) -> io::Result<()>;
|
|
fn term_input_processor(&mut self) -> io::Result<()>;
|
|
}
|
|
impl TermProcessor for Pse {
|
|
fn term_render(&mut self, def_string: String) -> InputResult<()> {
|
|
self.rt.input.push_str(&def_string);
|
|
write!(io::stdout(), "{def_string}").map_err(InputHandleError::Write)?;
|
|
io::stdout().flush().map_err(InputHandleError::Flush)
|
|
}
|
|
|
|
fn term_input_handler(&mut self, input_key: KeyEvent) -> Option<()> {
|
|
let input_handle = match input_key.code {
|
|
KeyCode::Enter => self.key_enter(),
|
|
KeyCode::Backspace => self.key_backspace(),
|
|
KeyCode::Tab => todo!(),
|
|
KeyCode::Right => self.key_arrow_right(),
|
|
KeyCode::Left => self.key_arrow_left(),
|
|
KeyCode::Up => todo!(),
|
|
KeyCode::Down => todo!(),
|
|
keycode => self.key_ctrl(input_key, keycode)
|
|
};
|
|
input_handle.map_or_else(|inp_err| match inp_err {
|
|
InputHandleError::UserExit => None,
|
|
InputHandleError::Sigterm => self.term_render("^C".to_owned()).ok(),
|
|
input_err => session::shell_error_none(input_err)
|
|
}, Some)
|
|
}
|
|
|
|
fn term_input_mainthread(&mut self) -> io::Result<()> {
|
|
execute!(io::stdout(), event::EnableBracketedPaste)?;
|
|
loop {
|
|
terminal::enable_raw_mode()?;
|
|
if let Event::Key(event) = event::read()? {
|
|
if self.term_input_handler(event).is_none() { break Ok(()) }
|
|
}
|
|
}
|
|
}
|
|
|
|
fn term_input_processor(&mut self) -> io::Result<()> {
|
|
self.term_input_mainthread()?;
|
|
terminal::disable_raw_mode()?;
|
|
execute!(io::stdout(), event::DisableBracketedPaste)
|
|
}
|
|
} |