aboutsummaryrefslogtreecommitdiffstats
path: root/src/ansi.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ansi.rs')
-rw-r--r--src/ansi.rs137
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}"),
+ }
+}