diff options
author | Rory Dudley | 2024-12-12 17:59:32 -0700 |
---|---|---|
committer | Rory Dudley | 2024-12-12 18:05:00 -0700 |
commit | fa9ec0c377305126f4c810be198bdca68d71a275 (patch) | |
tree | bdcd76287cf46384762454aad4ae841ac7a2539b /src/main.rs | |
parent | 07e75b3f63001ca509df8d5b5a33336ab994814f (diff) | |
download | dwarvish-fa9ec0c377305126f4c810be198bdca68d71a275.tar.gz |
Rewrite of the buffer code + proper UTF-8 support
This patch rewrites much of the code in src/buffer.rs to (utlimately),
be less buggy. It also changed getchar() to have proper support for
UTF-8 characters. The autocomplete function was also enhanced to support
completions with filenames that have spaces in their paths. It handles
these by placing a backslash ('\') before each space in the filename.
There is not yet any completion support with quote ('), nor double-quote
characters ("). The buffer is still navigable with arrow keys, so
arbitrary deletions and insertions are still possible. Deletions and
insertions with multi-width UTF-8 characters work as expected.
Signed-off-by: Rory Dudley <rory@netc.lu>
Notes
Notes:
The complection function only works if the cursor is at the end of the
buffer. Pressing tab anywhere else will result in functionally a no op.
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 58 |
1 files changed, 45 insertions, 13 deletions
diff --git a/src/main.rs b/src/main.rs index 8cfd680..a73c2c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ mod path; mod poem; use poem::{read::Readable, recite::Reciteable, Poem}; mod compose; -use buffer::{getline, STDIN}; +use buffer::{getline, Key, STDIN}; use compose::Environment; use termios::{tcsetattr, Termios, ECHO, ECHOE, ICANON, TCSANOW}; @@ -27,8 +27,11 @@ use termios::{tcsetattr, Termios, ECHO, ECHOE, ICANON, TCSANOW}; /// ``` fn repl( away: &mut Arc<Mutex<bool>>, - buffer: &mut Arc<Mutex<Vec<u8>>>, + buffer: &mut Arc<Mutex<Vec<char>>>, pos: &mut Arc<Mutex<usize>>, + comp_pos: &mut Arc<Mutex<usize>>, + comp_len: &mut Arc<Mutex<usize>>, + last_key: &mut Arc<Mutex<Key>>, env: &mut Environment, ) { // Setup termios flags @@ -39,9 +42,6 @@ fn repl( // Main shell loop loop { - // Clear the buffer - buffer.lock().unwrap().clear(); - // Get the prompt let prompt = match env::var("PS1") { Ok(val) => val, @@ -60,7 +60,7 @@ fn repl( tcsetattr(STDIN, TCSANOW, &mut termios).unwrap(); // Wait for user input - let bytes = getline(buffer, pos); + let bytes = getline(buffer, pos, comp_pos, comp_len, last_key); // Check if we've reached EOF (i.e. <C-d>) if bytes == 0 { @@ -69,9 +69,7 @@ fn repl( } // Convert buffer to a string and trim it - let poetry = String::from_utf8_lossy(&buffer.lock().unwrap()) - .trim() - .to_string(); + let poetry = buffer.lock().unwrap().iter().collect::<String>(); // Skip parsing if there is no poetry if poetry.is_empty() { @@ -86,7 +84,7 @@ fn repl( *away.lock().unwrap() = true; // Parse the poem - let poem = Poem::read(poetry, env); + let poem = Poem::read(poetry.to_string(), env); let poem = match poem { Ok(poem) => poem, Err(e) => { @@ -151,17 +149,43 @@ fn main() { // Compose the environment for dwvsh let mut env = compose::env(); - // Handle signals + // Set when we are not on the buffer let mut away = Arc::new(Mutex::new(true)); - let mut buffer: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(vec![])); + + // Any text in the current buffer + let mut buffer: Arc<Mutex<Vec<char>>> = Arc::new(Mutex::new(Vec::new())); + + // Position in the buffer. Subject to change based on input from the user. Typing a character + // increments the position, while backspacing will decrement it. The user may also move it + // manually using the arrow keys to insert or delete at an arbitrary location in the buffer. let mut pos: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); + + // Position in the autocomplete list. This value gets reset if a new autocomplete list is + // generated (for instance, if the user preses '/' to start autocomplete in a new directory). + let mut comp_pos: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); + + // Keep track of the length of the last buffer from [crate::buffer::comp]. + let mut comp_len: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); + + // Keep track of the last key for autocomplete, as we may need to add or sub additionally from + // [comp_pos] before calling [crate::buffer::comp] (i.e. swapping directions (tab vs. shift + + // tab)). + let mut last_key: Arc<Mutex<Key>> = Arc::new(Mutex::new(Key::Ignored)); + + // Handle signals unsafe { let away = Arc::clone(&away); let buffer = Arc::clone(&buffer); let pos = Arc::clone(&pos); + let comp_pos = Arc::clone(&comp_pos); + let comp_len = Arc::clone(&comp_len); + let last_key = Arc::clone(&last_key); signal_hook::low_level::register(signal_hook::consts::SIGINT, move || { buffer.lock().unwrap().clear(); *pos.lock().unwrap() = 0; + *comp_pos.lock().unwrap() = 0; + *comp_len.lock().unwrap() = 0; + *last_key.lock().unwrap() = Key::Ignored; if *away.lock().unwrap() { println!(); } else { @@ -180,5 +204,13 @@ fn main() { options(&mut env); // Begin evaluating commands - repl(&mut away, &mut buffer, &mut pos, &mut env); + repl( + &mut away, + &mut buffer, + &mut pos, + &mut comp_pos, + &mut comp_len, + &mut last_key, + &mut env, + ); } |