From ae74513e8891ee8c57191d4ec5bed159b33cc480 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 3 Sep 2019 11:48:40 -0400 Subject: [PATCH] Rework input logic to remove throttling --- src/editor.rs | 189 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 113 insertions(+), 76 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 17fa90a..35d7b5b 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -122,107 +122,144 @@ impl Editor { // Terminal // ------------------------------------------------------------------------ + /// Convert stdin to specific keypresses fn read_key(&mut self) -> Option> { - /* - TODO: Read 1 byte by default, and read additional bytes - if the first byte is an escape character, to resolve the - unintentional input throttling - */ + // -------------------------------------------------------------------- + // Match single character + // -------------------------------------------------------------------- let stdin = io::stdin(); let stdin = stdin.lock(); - let mut in_str = String::new(); - let mut buffer = BufReader::with_capacity(4, stdin); - buffer.read_to_string(&mut in_str).unwrap(); + let mut br = BufReader::with_capacity(5, stdin); + + let mut first_read = [0; 1]; + br.read_exact(&mut first_read); + let first_str = String::from_utf8(first_read.to_vec()); + if first_str.is_err() { + return None; + } + let first_str = first_str.unwrap(); + + // Read the first character, if it isn't escape, just return it + let mut chars = first_str.chars(); + let char = chars.next(); + if char.is_none() { + return None; + } + let char = char.unwrap(); + match char { + '\0' => return None, + '\x1b' => (), + '\x08' => return Some(Backspace), + '\x7f' => return Some(Backspace), + '\r' => return Some(Enter), + c => return Some(OtherKey(c)), + } + + // -------------------------------------------------------------------- + // Match escape sequence + // -------------------------------------------------------------------- + let mut seq = [0;4]; + br.read_exact(&mut seq); + let seq_str = String::from_utf8(seq.to_vec()); + + // On error, just continue the input loop + if seq_str.is_err() { + return None; + } + let seq_str = seq_str.unwrap(); let mut input: Vec> = vec![]; - for char in in_str.chars() { + for char in seq_str.chars() { + // Since the fixed array is always filled, there + // will be null characters. Ignore these. + if char == '\0' { + continue; + } + input.push(match char { - '\x08' => Backspace, - '\x7f' => Backspace, '\x1b' => Escape, - '\r' => Enter, _ => OtherKey(char), }); } + // Since we matched Escape earlier, if the input is empty, + // this must be the escape key if input.is_empty() { - return None; + return Some(Escape); } - if input[0].eq(&Escape) { - match input.len() { - 5 => { - // Escape code of the form `^[[NM~` - if input[4].eq(&OtherKey('~')) { - let action = match (input[2].unwrap(), input[3].unwrap()) { - ('1', '5') => Function('5'), - ('1', '7') => Function('6'), - ('1', '8') => Function('7'), - ('1', '9') => Function('8'), - ('2', '0') => Function('9'), - ('2', '1') => Function('X'), // F10 - ('2', '4') => Function('T'), // F12 + match input.len() { + 4 => { + // Escape code of the form `^[[NM~` + if input[3].eq(&OtherKey('~')) { + let action = match (input[1].unwrap(), input[2].unwrap()) { + ('1', '5') => Function('5'), + ('1', '7') => Function('6'), + ('1', '8') => Function('7'), + ('1', '9') => Function('8'), + ('2', '0') => Function('9'), + ('2', '1') => Function('X'), // F10 + ('2', '4') => Function('T'), // F12 + _ => Escape, + }; + + return Some(action); + } + } + 3 => { + // Escape code of the form `^[[N~` + if input[2].eq(&OtherKey('~')) { + let action = match input[1].unwrap() { + '1' => HomeKey, + '3' => DeleteKey, + '4' => EndKey, + '5' => PageUp, + '6' => PageDown, + '7' => HomeKey, + '8' => EndKey, + _ => input[1], // Escape, + }; + + return Some(action); + } + } + 2 => { + match input[0] { + // Escape code of the form `^[[X` + OtherKey('[') => { + let action = match input[1].unwrap() { + 'A' => ArrowUp, + 'B' => ArrowDown, + 'C' => ArrowRight, + 'D' => ArrowLeft, + 'H' => HomeKey, + 'F' => EndKey, + + // Eh, just return escape otherwise _ => Escape, }; return Some(action); } - } - 4 => { - // Escape code of the form `^[[N~` - if input[3].eq(&OtherKey('~')) { - let action = match input[2].unwrap() { - '1' => HomeKey, - '3' => DeleteKey, - '4' => EndKey, - '5' => PageUp, - '6' => PageDown, - '7' => HomeKey, - '8' => EndKey, - _ => input[2], // Escape, + // Escape code of the form `^[OX` + OtherKey('O') => { + let action = match input[1].unwrap() { + 'H' => HomeKey, + 'F' => EndKey, + 'P' => Function('1'), + 'Q' => Function('2'), + 'R' => Function('3'), + 'S' => Function('4'), + _ => Escape, }; return Some(action); } + _ => return Some(Escape), } - 3 => { - match input[1] { - // Escape code of the form `^[[X` - OtherKey('[') => { - let action = match input[2].unwrap() { - 'A' => ArrowUp, - 'B' => ArrowDown, - 'C' => ArrowRight, - 'D' => ArrowLeft, - 'H' => HomeKey, - 'F' => EndKey, - - // Eh, just return escape otherwise - _ => input[2], //Escape, - }; - - return Some(action); - } - // Escape code of the form `^[OX` - OtherKey('O') => { - let action = match input[2].unwrap() { - 'H' => HomeKey, - 'F' => EndKey, - 'P' => Function('1'), - 'Q' => Function('2'), - 'R' => Function('3'), - 'S' => Function('4'), - _ => input[2], //Escape, - }; - - return Some(action); - } - _ => return Some(input[1]), - } - } - _ => return Some(input[0]), } + _ => return Some(input[0]), } // If the character doesn't match any escape sequences, just @@ -344,6 +381,7 @@ impl Editor { self.cursor_x = self.rows[self.cursor_y].chars.len(); } } + Function(_) => (), OtherKey(c) => { if c.is_ascii_control() { if c == ctrl_key('q') { @@ -373,7 +411,6 @@ impl Editor { self.insert_char(c); } } - _ => (), }; self.quit_times = KILO_QUIT_TIMES;