From 97a6a281fd9780ecdccfcf30079f339e305fdc78 Mon Sep 17 00:00:00 2001 From: Jonas Maier <> Date: Mon, 20 Apr 2026 10:27:04 +0200 Subject: move ansi escape code parsing into own module --- src/main.rs | 213 ++++++++++++++---------------------------------------------- 1 file changed, 49 insertions(+), 164 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 9286653..e0d9e84 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex}; use std::thread::sleep; use std::time::Duration; +pub mod ansi; pub mod basedir; pub mod completion; pub mod ctrlc; @@ -273,12 +274,6 @@ impl Session { } } -fn read1() -> u8 { - let mut buf = [0]; - io::stdin().lock().read_exact(&mut buf).unwrap(); - buf[0] -} - fn event_loop() { history::setup(); @@ -316,56 +311,31 @@ fn event_loop() { let _ctrlc = ctrlc::setup(session.clone()); loop { - let mut buf = [0u8; 1]; - - let Ok(_) = stdin.lock().read_exact(&mut buf) else { - break; - }; - let mut se = session.lock().unwrap(); - if se.debug_keystrokes { - println!("{}", buf.escape_ascii()); - } - - match buf[0] { - // Ctrl+A - 1 => { - se.move_to_begin(); - } + use ansi::KeyboardInput as Kb; - // Ctrl+E - 5 => { - se.move_to_end(); - } - - // Ctrl+C - 3 => { + match ansi::read(se.debug_keystrokes) { + Kb::Eof => todo!(), + Kb::CtrlA => se.move_to_begin(), + Kb::CtrlB => todo!(), + Kb::CtrlC => { se.clear_prompt(); se.history_visit = 0; } - - // EOF - 4 => { - break; - } - - // apparently another backspace, don't do anything. - 8 => {} - - // Ctrl+L - 12 => { + Kb::CtrlE => se.move_to_end(), + Kb::CtrlD => break, + Kb::CtrlL => { clear_screen(); print!("{}", se.prompt()); io::stdout().write_all(&se.line.into_bytes()).unwrap(); cursor::move_cursor(Direction::Left, se.line.distance_from_right_end()); io::stdout().lock().flush().unwrap(); } - - // Ctrl+R - 18 => {} - - // Enter - b'\r' => { + Kb::CtrlR => { + println!("search is not yet implemented"); + se.reprint_prompt(); + } + Kb::Key(b'\r' | b'\n') => { let line = se.line.into_bytes(); if !line.is_empty() { @@ -391,19 +361,7 @@ fn event_loop() { run::run(session.clone(), parsed); } } - - // Backspace (127 on most systems) - 127 => { - if se.line.is_empty() && !se.line.is_dirty() && !se.history.is_empty() { - // take previous command for editing - let cmd = se.history[se.history.len() - 1].cmd.clone(); - se.type_bytes(&cmd); - } else { - se.del_left(); - } - } - - b'\t' => { + Kb::Key(b'\t') => { let cmd = se.line.pre().to_vec(); drop(se); @@ -422,117 +380,44 @@ fn event_loop() { se.reprint_prompt(); } } - - // Escape sequence - 27 => { - let mut seq = vec![read1()]; - - if seq[0] == b'[' { - // still more - while { - let last = seq[seq.len() - 1]; - !(0x40..=0x7E).contains(&last) || seq.len() == 1 - } { - seq.push(read1()); - } - - if se.debug_keystrokes { - println!("escape: {}", seq.escape_ascii()); + Kb::Arrow(dir) => match dir { + Direction::Up => se.history_up(), + Direction::Down => se.history_down(), + Direction::Left => { + if se.line.left() { + move_cursor(Direction::Left, 1); + io::stdout().lock().flush().unwrap(); } - - match seq[1] { - b'A' => { - // up - se.history_up(); - } - b'B' => { - // down - se.history_down(); - } - b'C' => { - if se.line.right() { - move_cursor(Direction::Right, 1); - io::stdout().lock().flush().unwrap(); - } - } - b'D' => { - if se.line.left() { - move_cursor(Direction::Left, 1); - io::stdout().lock().flush().unwrap(); - } - } - b'3' => { - if seq.len() > 2 && seq[2] == b'~' { - se.del_right(); - } else { - todo!("unhandled: {}", seq.escape_ascii()); - } - } - - // HOME button - b'H' => { - se.move_to_begin(); - } - - // END button - b'F' => { - se.move_to_end(); - } - - // Ctrl Arrow - b'1' => { - if seq[1..].starts_with(b"1;5") { - if seq.len() == 4 { - todo!("idk what this is."); - } - match seq[4] { - b'A' => { - println!("Ctrl+Up"); - se.reprint_prompt(); - continue; - } - b'B' => { - println!("Ctrl+Down"); - se.reprint_prompt(); - continue; - } - b'C' => { - se.move_right_word(); - } - b'D' => { - se.move_left_word(); - } - _ => todo!("unhandled {}", seq.escape_ascii()), - } - continue; - } - todo!("unhandled {}", seq[1..].escape_ascii()) - } - - _ => todo!("escape characters {}", seq[1..].escape_ascii()), + } + Direction::Right => { + if se.line.right() { + move_cursor(Direction::Right, 1); + io::stdout().lock().flush().unwrap(); } + } + }, + Kb::CtrlArrow(dir) => match dir { + Direction::Left => se.move_left_word(), + Direction::Right => se.move_right_word(), + _ => { + println!("Ctrl+{dir:?} not implemented"); + se.reprint_prompt(); + } + }, + Kb::DeleteLeft => { + if se.line.is_empty() && !se.line.is_dirty() && !se.history.is_empty() { + // take previous command for editing + let cmd = se.history[se.history.len() - 1].cmd.clone(); + se.type_bytes(&cmd); } else { - if se.debug_keystrokes { - println!("escape: {}", seq.escape_ascii()); - } - if seq[0] == b'd' { - se.del_right_word(); - } + se.del_left(); } } - - b'|' if se.line.is_empty() && !se.history.is_empty() => { - let mut cmd = se.history[se.history.len() - 1].cmd.clone(); - cmd.extend_from_slice(b" | "); - io::stdout().write_all(&cmd).unwrap(); - io::stdout().flush().unwrap(); - se.line.set_content(cmd); - } - - // Normal character - x => { - se.type_byte(x); - } + Kb::DeleteRight => se.del_right(), + Kb::CtrlDeleteRight => se.del_right_word(), + Kb::Home => se.move_to_begin(), + Kb::End => se.move_to_end(), + Kb::Key(x) => se.type_byte(x), } } -- cgit v1.2.3