You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

340 lines
9.5 KiB

#![allow(unused)]
use std::{io::Read, mem::MaybeUninit};
use crate::reader::esc_parse::{InputChar, bell, hide_cursor, show_cursor};
pub mod esc_parse;
#[derive(Default)]
pub struct ReaderState {
prompt: String,
line: String,
cursor: usize,
max_len: usize,
history: Vec<String>,
history_inx: Option<usize>,
history_saved: Option<String>,
}
#[derive(Debug)]
pub enum InitTTYError {
NotATTY
}
#[derive(Debug)]
pub enum ReadError {
EscapeError(esc_parse::AcceptError),
Exited,
}
#[derive(Debug)]
pub enum EditCommand {
NewLine,
HistoryPrevious,
HistoryNext,
CursorBack,
CursorForward,
DeleteBack,
Exit,
}
macro_rules! edit_cmd {
($id:ident) => {
Result::<EditCommand, ReadError>::Ok(EditCommand::$id)
};
($id:ident, $cmd:expr) => {
Ok(EditCommand::$id($cmd))
};
}
impl ReaderState {
pub fn new(prompt: &str) -> Self {
ReaderState { prompt: String::from(prompt), max_len: prompt.len(), ..Default::default() }
}
pub fn print(&mut self, txt: String) {
eprint!("{}", txt);
self.max_len += txt.len();
}
fn clear(&mut self) {
esc_parse::clear_line();
self.max_len = self.prompt.len();
}
pub fn empty(&mut self) {
self.set_line(String::from(""));
}
pub fn init_tty() -> Result<(), InitTTYError> {
unsafe {
use libc::{isatty, termios, ECHO, ICANON, INPCK, TCSANOW, tcgetattr, tcsetattr};
if !(isatty(0) == 1) {
return Result::Err(InitTTYError::NotATTY);
}
let mut termios: termios = MaybeUninit::zeroed().assume_init();
tcgetattr(0, &mut termios);
termios.c_lflag &= !(ECHO | ICANON);
termios.c_iflag &= !(INPCK);
tcsetattr(0, TCSANOW, &termios);
return Ok(());
}
}
pub fn insert_at_cursor(&mut self, txt: String) {
self.line.insert_str(self.cursor, txt.as_str());
self.cursor += txt.len();
self.max_len += txt.len();
self.redraw();
}
fn current_hist(&mut self) -> String {
let i: usize = self.history_inx.unwrap();
let line = self.history.get(self.history.len().saturating_sub(i+1));
return line.unwrap().to_string();
}
fn set_current_hist(&mut self, str: String) {
let i: usize = self.history_inx.unwrap();
let len = self.history.len();
self.history[len.saturating_sub(i+1)] = str;
}
fn insert_at_cursor_hist(&mut self, txt: String) {
let mut line = self.current_hist();
line.insert_str(self.cursor, txt.as_str());
self.history_reset();
self.set_line(line.clone());
self.redraw();
}
fn input_char(&mut self, c: char) {
if self.history_inx.is_some() {
self.insert_at_cursor_hist(String::from(c));
} else {
self.insert_at_cursor(String::from(c));
}
self.redraw();
}
fn input_line(&mut self) -> Result<EditCommand, ReadError> {
let mut esc_buf: [u8; 1] = [0];
let mut read_stat = esc_parse::SequenceState::new();
let mut out_buf: Vec<esc_parse::InputChar> = Vec::new();
unsafe {
use libc::{read, c_void};
self.redraw();
loop {
read(0, esc_buf.as_mut_ptr() as *mut c_void, 1);
let res = read_stat.accept_char(esc_buf[0]);
if res.is_err() {
return Result::Err(ReadError::EscapeError(res.err().unwrap()));
} else {
if read_stat.has_next() {
let chr = read_stat.next().unwrap();
out_buf.push(chr.clone());
if let InputChar::Text(c) = chr {
self.input_char(c);
}
if let InputChar::CursorMotion(m) = chr {
use esc_parse::Direction::*;
return match m {
Up => edit_cmd!(HistoryPrevious),
Down => edit_cmd!(HistoryNext),
Left => edit_cmd!(CursorBack),
Right => edit_cmd!(CursorForward),
}
}
if chr == InputChar::NewLine {
break;
}
if chr == InputChar::Backspace {
return edit_cmd!(DeleteBack);
}
if chr == InputChar::EoT {
return edit_cmd!(Exit);
}
}
}
}
edit_cmd!(NewLine)
}
}
pub fn redraw(&mut self) {
hide_cursor();
self.clear();
self.print(format!("{}{}", self.prompt, self.line));
self.move_at(self.cursor);
show_cursor();
}
pub fn submit_line(&mut self, line: String) {
if self.history_inx.is_none() {
self.history_append(line.clone());
} else {
self.history_reset();
}
self.empty();
}
pub fn read_line(&mut self) -> Result<String, ReadError> {
use EditCommand::*;
loop {
let cmd = self.input_line();
if cmd.is_err() {
return Err(cmd.err().unwrap());
}
let cmd = cmd.ok().unwrap();
match cmd {
NewLine => {
let line = self.line.clone();
self.submit_line(line.clone());
return Ok(line)
},
CursorBack => {
self.move_left()
},
CursorForward => {
self.move_right();
},
DeleteBack => {
self.backspace();
},
HistoryPrevious => {
self.history_back();
},
HistoryNext => {
self.history_front();
},
Exit => return Err(ReadError::Exited)
}
}
}
pub fn set_line(&mut self, line: String) {
self.line = line.clone();
self.clear();
self.cursor_at(line.len());
}
pub fn history_append(&mut self, line: String) {
self.history.push(line);
self.history_inx = None;
}
fn history_reset(&mut self) {
self.history_inx = None;
self.history_saved = None;
}
pub fn set_prompt(&mut self, prompt: String) {
self.prompt = prompt;
self.redraw();
}
pub fn history_back(&mut self) {
let h = self.history.clone();
let mut i = self.history_inx;
if i.map_or(false, |v| v+1 == h.len()) {
bell();
return;
}
if i.is_none() {
i = Some(0);
self.history_saved = Some(self.line.clone());
} else {
i = i.map(|v| { v + 1 });
}
self.history_inx = i;
let i: usize = i.unwrap();
let line = h.get(h.len().saturating_sub(i+1));
if line.is_some() {
self.set_line(line.unwrap().to_string());
} else {
bell();
self.redraw();
}
}
fn saved_restore(&mut self) {
self.set_line(self.history_saved.clone().expect("Missing saved value"));
}
pub fn history_front(&mut self) {
let h = self.history.clone();
let mut i = self.history_inx;
if i.is_none() {
bell();
return;
} else if i.map_or(false, |v| v == 0) {
self.saved_restore();
self.history_reset();
return;
} else {
i = i.map(|v| { v - 1 });
self.history_inx = i;
}
let i = i.unwrap();
let line = h.get(h.len().saturating_sub(i+1));
if line.is_some() {
self.set_line(line.unwrap().to_string());
} else {
bell();
self.redraw();
}
}
pub fn move_left(&mut self) {
use esc_parse::{cursor_left, bell};
if self.cursor > 0 {
self.cursor -= 1;
cursor_left();
self.redraw();
} else {
bell();
}
}
pub fn move_right(&mut self) {
use esc_parse::{cursor_right, bell};
if self.cursor < self.line.len() {
self.cursor += 1;
cursor_right();
self.redraw();
} else {
bell();
}
}
fn move_at(&mut self, inx: usize) {
self.cursor = inx;
if self.max_len < inx {
self.max_len = inx;
}
esc_parse::cursor_left_nr(self.max_len);
esc_parse::cursor_right_nr(inx+self.prompt.len());
}
pub fn cursor_at(&mut self, inx: usize) {
self.move_at(inx);
self.redraw();
}
pub fn backspace(&mut self) {
if self.history_inx.is_some() {
if self.cursor > 0 {
let mut line = self.current_hist();
line.remove(self.cursor-1);
self.move_left();
self.history_reset();
self.set_line(line);
} else {
bell();
}
} else {
if self.cursor > 0 {
self.line.remove(self.cursor-1);
self.move_left();
} else {
bell();
}
}
}
}