diff options
| author | Jonas Maier <> | 2026-04-20 10:27:04 +0200 |
|---|---|---|
| committer | Jonas Maier <> | 2026-04-20 10:27:04 +0200 |
| commit | 97a6a281fd9780ecdccfcf30079f339e305fdc78 (patch) | |
| tree | 7a8ef069e742bd36f287bd4b8244d65bb9d2e296 /src/ansi.rs | |
| parent | 8fa52302ae6d0cb15daff9384f43744d4201d8ee (diff) | |
| download | pish-97a6a281fd9780ecdccfcf30079f339e305fdc78.tar.gz | |
move ansi escape code parsing into own module
Diffstat (limited to 'src/ansi.rs')
| -rw-r--r-- | src/ansi.rs | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/src/ansi.rs b/src/ansi.rs new file mode 100644 index 0000000..e04f6d9 --- /dev/null +++ b/src/ansi.rs @@ -0,0 +1,137 @@ +use std::io::Read; + +use crate::cursor::Direction; + +pub enum KeyboardInput { + Eof, + Key(u8), + CtrlA, + CtrlB, + CtrlC, + CtrlE, + CtrlD, + CtrlL, + CtrlR, + Arrow(Direction), + CtrlArrow(Direction), + DeleteLeft, + DeleteRight, + CtrlDeleteRight, + Home, + End, +} + +fn read1() -> Option<u8> { + let mut buf = [0]; + match std::io::stdin().lock().read_exact(&mut buf) { + Ok(_) => Some(buf[0]), + Err(_) => None, + } +} + +fn byte_to_dir(b: u8) -> Option<Direction> { + use Direction::*; + match b { + b'A' => Some(Up), + b'B' => Some(Down), + b'C' => Some(Right), + b'D' => Some(Left), + _ => None, + } +} + +fn read_escape(debug: bool) -> KeyboardInput { + use Direction::*; + use KeyboardInput::*; + + let mut seq = vec![match read1() { + Some(x) => x, + None => return Eof, + }]; + + if seq[0] == b'[' { + // still more + while { + let last = seq[seq.len() - 1]; + !(0x40..=0x7E).contains(&last) || seq.len() == 1 + } { + seq.push(match read1() { + Some(x) => x, + None => return Eof, + }); + } + + if debug { + println!("escape: {}", seq.escape_ascii()); + } + + match seq[1] { + b'3' => { + if seq.len() > 2 && seq[2] == b'~' { + DeleteRight + } else { + todo!("unhandled: {}", seq.escape_ascii()); + } + } + b'H' => Home, + b'F' => End, + b'd' => CtrlDeleteRight, + + // 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' => CtrlArrow(Up), + b'B' => CtrlArrow(Down), + b'C' => CtrlArrow(Right), + b'D' => CtrlArrow(Left), + _ => todo!("unhandled {}", seq.escape_ascii()), + } + } else { + todo!("unhandled {}", seq[1..].escape_ascii()) + } + } + + x => { + if let Some(dir) = byte_to_dir(x) { + Arrow(dir) + } else { + todo!("escape characters {}", seq[1..].escape_ascii()) + } + } + } + } else { + if debug { + println!("escape: {}", seq.escape_ascii()); + } + match seq[0] { + b'd' => CtrlDeleteRight, + x => todo!("unhandled escape code: ESC {x}"), + } + } +} + +pub fn read(debug: bool) -> KeyboardInput { + use KeyboardInput::*; + + let Some(x) = read1() else { + return KeyboardInput::Eof; + }; + + match x { + 1 => CtrlA, + 2 => CtrlB, + 3 => CtrlC, + 4 => CtrlD, + 8 | 127 => DeleteLeft, + 12 => CtrlL, + 18 => CtrlR, + 27 => read_escape(debug), + b'\r' => Key(x), + x if !x.is_ascii_control() => Key(x), + x => todo!("unimplemented control code: {x}"), + } +} |
