summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRory Dudley2024-09-28 01:09:03 -0600
committerRory Dudley2024-09-28 01:09:03 -0600
commit4f62017709220bbc6d60f51720f13fe1ecb19058 (patch)
treeeaf42cfa6b199259f35e77912bbc32e4a610b09d
parent2590315b9e5280ca0c69cce35b4f4bea794790d4 (diff)
downloaddwarvish-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.rs89
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;