From 4f62017709220bbc6d60f51720f13fe1ecb19058 Mon Sep 17 00:00:00 2001 From: Rory Dudley Date: Sat, 28 Sep 2024 01:09:03 -0600 Subject: Fix Tab/Shift+Tab autocomplete behavior Previously, switching between Tab and Shift+Tab while cycling through autocomplete options could temporarily mess up the order of items. This patch fixes that flaw by adding two new variables to help keep track of the state: - 'last', the last key that was recorded by getchar(), - and 'length', which keeps track of the length of the last buffer generated by the autocomplete() function. These variables are then checked first whenever the user uses Tab or Shift+Tab. The last know buffer 'length' is used to deal with overflow in the case of Shift+Tab. Finally, the logic for processing the autocomplete directory was moved below the code for handling the actual user input (i.e. appending to the getline() buffer). This is because the last character in the buffer (i.e. the last character the user typed) is important for properly updating the autcomplete working directory. --- src/buffer.rs | 89 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 32 deletions(-) (limited to 'src/buffer.rs') diff --git a/src/buffer.rs b/src/buffer.rs index c27946f..366f20a 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -10,6 +10,7 @@ use std::sync::{Arc, Mutex}; pub const STDIN: i32 = 0; // Key input types from the user +#[derive(PartialEq)] enum Key { Up, Down, @@ -79,9 +80,6 @@ fn autocomplete( index: usize, pwd: &PathBuf, ) -> Result<(String, usize), Box> { - // Get the present working directory - // let pwd = current_dir()?; - let buffer = buffer.lock().unwrap(); let word = match buffer.last() { Some(c) if *c == b' ' => "".to_string(), @@ -175,14 +173,21 @@ fn autocomplete( /// that (ICANON and ECHO) are off. See the beginning of [crate::repl] /// for more details. pub fn getline(buffer: &mut Arc>>, pos: &mut Arc>) -> usize { + // Keep track of the last key + let mut last: Option = None; + // Keep track of index for autocomplete let mut pwd = current_dir().unwrap_or(PathBuf::from(env!("HOME"))); let mut auindex = 0; let mut aulen = 0; + // Keep track of the length of the last buffer from autcomplete() + let mut length = 0; + // Loop over characters until there is a newline loop { - match getchar() { + let c = getchar(); + match c { Key::Up => { continue; } @@ -204,6 +209,12 @@ pub fn getline(buffer: &mut Arc>>, pos: &mut Arc>) -> *pos.lock().unwrap() -= 1; } Key::Tab => { + if last == Some(Key::ShiftTab) { + auindex += 2; + if auindex >= length { + auindex = 0; + } + } while aulen > 0 { buffer.lock().unwrap().pop(); print!("\u{8} \u{8}"); @@ -212,6 +223,7 @@ pub fn getline(buffer: &mut Arc>>, pos: &mut Arc>) -> } let (path, len) = autocomplete(buffer, auindex, &pwd).unwrap_or(("".to_string(), 0)); + length = len; for c in path.into_bytes().iter() { buffer.lock().unwrap().insert(*pos.lock().unwrap(), *c); *pos.lock().unwrap() += 1; @@ -223,6 +235,13 @@ pub fn getline(buffer: &mut Arc>>, pos: &mut Arc>) -> } } Key::ShiftTab => { + if last == Some(Key::Tab) { + if auindex.checked_sub(2) == None { + auindex = length - 1; + } else { + auindex -= 2; + } + } while aulen > 0 { buffer.lock().unwrap().pop(); print!("\u{8} \u{8}"); @@ -231,6 +250,7 @@ pub fn getline(buffer: &mut Arc>>, pos: &mut Arc>) -> } let (path, len) = autocomplete(buffer, auindex, &pwd).unwrap_or(("".to_string(), 0)); + length = len; for c in path.into_bytes().iter() { buffer.lock().unwrap().insert(*pos.lock().unwrap(), *c); *pos.lock().unwrap() += 1; @@ -290,6 +310,35 @@ pub fn getline(buffer: &mut Arc>>, pos: &mut Arc>) -> _ => { let mut buffer = buffer.lock().unwrap(); + // Print out the character as the user is typing + match buffer.last() { + Some(last) if *last == b'/' && c == b'/' => { + buffer.pop(); + *pos.lock().unwrap() -= 1; + } + Some(_) => print!("{}", c as char), + None => print!("{}", c as char), + } + + // Insert the character onto the buffer at whatever *pos.lock().unwrap()ition + // the cursor is at + buffer.insert(*pos.lock().unwrap(), c); + + // Increment our *pos.lock().unwrap()ition + *pos.lock().unwrap() += 1; + + // Reprint the end of the buffer if inserting at the front or middle + if *pos.lock().unwrap() != buffer.len() { + print!( + "{}", + String::from_utf8_lossy(&buffer[*pos.lock().unwrap()..]) + ); + for _ in *pos.lock().unwrap()..buffer.len() { + print!("\u{8}"); + } + } + + // Update directory for autocomplete let word = match buffer.last() { Some(c) if *c == b' ' => "".to_string(), None => "".to_string(), @@ -309,7 +358,7 @@ pub fn getline(buffer: &mut Arc>>, pos: &mut Arc>) -> } loop { match word.last() { - Some(c) if *c == b'/' || *c == b'.' => { + Some(c) if *c == b'/' || *c == b'.' || *c == b'~' => { break; } Some(_) => { @@ -341,36 +390,12 @@ pub fn getline(buffer: &mut Arc>>, pos: &mut Arc>) -> pwd = PathBuf::from(word); auindex = 0; aulen = 0; - - // Print out the character as the user is typing - match buffer.last() { - Some(last) if *last == b'/' && c == b'/' => { - buffer.pop(); - *pos.lock().unwrap() -= 1; - } - Some(_) => print!("{}", c as char), - None => print!("{}", c as char), - } - - // Insert the character onto the buffer at whatever *pos.lock().unwrap()ition the cursor is at - buffer.insert(*pos.lock().unwrap(), c); - - // Increment our *pos.lock().unwrap()ition - *pos.lock().unwrap() += 1; - - // Reprint the end of the buffer if inserting at the front or middle - if *pos.lock().unwrap() != buffer.len() { - print!( - "{}", - String::from_utf8_lossy(&buffer[*pos.lock().unwrap()..]) - ); - for _ in *pos.lock().unwrap()..buffer.len() { - print!("\u{8}"); - } - } } }, } + + // Update the last key + last = Some(c); } *pos.lock().unwrap() = 0; -- cgit v1.2.3