summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRory Dudley2024-09-30 23:07:30 -0600
committerRory Dudley2024-09-30 23:08:20 -0600
commit07e75b3f63001ca509df8d5b5a33336ab994814f (patch)
tree3283fea468a166fea194c9bc487ef4bcdbca4bde
parent41335c1618d3c7c1c5950a228ea15e2d0c3c2d8d (diff)
downloaddwarvish-main.tar.gz
Fix Tab/Shift+Tab autocomplete behaviorHEADmain
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. Signed-off-by: Rory Dudley <rory@netc.lu>
-rw-r--r--src/buffer.rs89
1 files changed, 57 insertions, 32 deletions
diff --git a/src/buffer.rs b/src/buffer.rs
index 4d00d34..7ece258 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(),
@@ -185,14 +183,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;
}
@@ -214,6 +219,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}");
@@ -222,6 +233,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;
@@ -233,6 +245,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}");
@@ -241,6 +260,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;
@@ -300,6 +320,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(),
@@ -319,7 +368,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(_) => {
@@ -351,36 +400,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;