diff options
author | Rory Dudley | 2024-09-28 01:09:03 -0600 |
---|---|---|
committer | Rory Dudley | 2024-09-28 01:09:03 -0600 |
commit | 4f62017709220bbc6d60f51720f13fe1ecb19058 (patch) | |
tree | eaf42cfa6b199259f35e77912bbc32e4a610b09d | |
parent | 2590315b9e5280ca0c69cce35b4f4bea794790d4 (diff) | |
download | dwarvish-4f62017709220bbc6d60f51720f13fe1ecb19058.tar.gz |
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.
-rw-r--r-- | src/buffer.rs | 89 |
1 files changed, 57 insertions, 32 deletions
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<dyn std::error::Error>> { - // 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<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> usize { + // Keep track of the last key + let mut last: Option<Key> = 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<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> *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<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> } 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<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> } } 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<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> } 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<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> _ => { 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<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> } 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<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> 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; |