From b0138d6473808d6b75b920884b0f8c5b29c2b69c Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 4 Mar 2023 20:02:51 -0600 Subject: [PATCH] working on moving left this is the first actual line-editing and ansi escape handling w00t --- src/error.rs | 7 ++++++ src/input.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++------ src/line.rs | 25 ++++++++++++++------ src/main.rs | 26 +++++++++++++++----- 4 files changed, 105 insertions(+), 20 deletions(-) diff --git a/src/error.rs b/src/error.rs index 19b87e1..46fcee1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,9 @@ pub enum Error { #[error("i/o error: {0}")] IOError(#[from] io::Error), + + #[error("input error: {0}")] + InputError(String), } impl Error { @@ -23,4 +26,8 @@ impl Error { Err(Error::last_error()) } } + + pub fn input_error>(msg: S) -> Self { + Error::InputError(msg.into()) + } } diff --git a/src/input.rs b/src/input.rs index 8813707..0e961ba 100644 --- a/src/input.rs +++ b/src/input.rs @@ -145,8 +145,20 @@ impl Reader { }) } - pub fn next(&mut self) -> Result { - // All buffered items have been processed, ask the OS for new event records + pub fn next(&mut self) -> Result { + let rec = self.next_rec()?; + if rec.EventType as u32 == Console::KEY_EVENT { + unsafe { + let event = rec.Event.KeyEvent; + if event.wVirtualKeyCode == 0 && event.uChar.UnicodeChar == 27 { + return Ok(self.next_escape_sequence()?); + } + } + } + Ok(rec.into()) + } + + fn next_rec(&mut self) -> Result { if self.buf_idx as u32 >= self.buf_len { unsafe { Error::check(Console::ReadConsoleInputA( @@ -162,6 +174,49 @@ impl Reader { self.buf_idx += 1; return Ok(rec); } + + fn next_escape_sequence(&mut self) -> Result { + self.take_bracket()?; + match self.next_escape_char()? { + 'D' => Ok(Event::Left), + e => Err(Error::input_error(format!("unexpected escape char: {}", e)).into()), + } + } + + fn take_bracket(&mut self) -> Result<()> { + let rec = self.next_rec()?; + if rec.EventType as u32 != Console::KEY_EVENT { + Err(Error::input_error("failed to read escape sequence: not a key event").into()) + } else { + unsafe { + let event = rec.Event.KeyEvent; + if event.wVirtualKeyCode == 0 && event.uChar.UnicodeChar == 91 { + Ok(()) + } else { + Err(Error::input_error("failed to read escape sequence: not a [").into()) + } + } + } + } + + fn next_escape_char(&mut self) -> Result { + let rec = self.next_rec()?; + if rec.EventType as u32 != Console::KEY_EVENT { + Err( + Error::input_error("failed to read char in escape sequence: not a key event") + .into(), + ) + } else { + unsafe { + let n = rec.Event.KeyEvent.uChar.UnicodeChar as u32; + let c = char::from_u32(n); + c.ok_or_else(|| { + let msg = format!("escape key value is not a valid unicode character: {}", n); + Error::input_error(msg).into() + }) + } + } + } } #[derive(Debug)] @@ -169,11 +224,9 @@ pub enum Event { Focus(bool), Menu(u32), Key(key::Event), - Mouse{ - x: i16, - y: i16, - }, + Mouse { x: i16, y: i16 }, Size, + Left, } const ALT_KEYS: u32 = 0x0002 | 0x0001; @@ -221,7 +274,7 @@ impl From for Event { // pub dwButtonState: u32, // pub dwControlKeyState: u32, // pub dwEventFlags: u32, - Event::Mouse{ + Event::Mouse { x: event.dwMousePosition.X, y: event.dwMousePosition.Y, } diff --git a/src/line.rs b/src/line.rs index 935200a..9d109ae 100644 --- a/src/line.rs +++ b/src/line.rs @@ -5,20 +5,20 @@ use windows::Win32::System::Console; pub struct Line { chars: Vec, + cursor: usize, } impl Line { pub fn new() -> Self { - Self { chars: Vec::new() } - } - - /// adds a character to the end of the line - pub fn append(&mut self, c: char) { - self.chars.push(c) + Self { + chars: Vec::new(), + cursor: 0, + } } pub fn clear(&mut self) { - self.chars.clear() + self.cursor = 0; + self.chars.clear(); } pub fn print(&self) -> Result<()> { @@ -33,4 +33,15 @@ impl Line { } Ok(()) } + + pub fn back(&mut self) { + if self.cursor > 0 { + self.cursor -= 1; + } + } + + pub fn insert(&mut self, c: char) { + self.chars.insert(self.cursor, c); + self.cursor += 1; + } } diff --git a/src/main.rs b/src/main.rs index 13e7898..e634902 100644 --- a/src/main.rs +++ b/src/main.rs @@ -161,14 +161,12 @@ fn main() -> Result<()> { prompt.print()?; loop { - let rec = input.next()?; - let event: input::Event = rec.into(); - match event { + match input.next()? { input::Event::Key(event) => { + debug!("Key Event: {}", event); if event.down { continue; } - debug!("Key Event: {}", event); if event.code == key::ENTER { newline(stdout)?; @@ -195,12 +193,18 @@ fn main() -> Result<()> { continue; } + // TODO: something better here, this is crappy. I should be checking characters + // based on their unicode categories, not this garbo if !event.char.is_control() { + line.insert(event.char); + let mut buf = [0 as u8; 8]; let s = event.char.encode_utf8(&mut buf); - s.chars().for_each(|c| line.append(c)); + + // write everything from the current line cursor out to the output buffer. Then + // rewind the cursor in the output buffer to where it was. unsafe { - Error::check(Console::WriteConsoleW( + Error::check(Console::WriteConsoleA( stdout, s.as_bytes(), None, @@ -209,7 +213,17 @@ fn main() -> Result<()> { } continue; } + + debug!("Unhandled Keyboard Event: {}", event); } + input::Event::Left => { + debug!("back!"); + line.back(); + unsafe { + let text = "\x1b[D"; + Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?; + } + }, input::Event::Focus(true) => {}, input::Event::Focus(false) => {}, input::Event::Menu(_command_id) => {},