summaryrefslogtreecommitdiffstats
path: root/src/buffer.rs
diff options
context:
space:
mode:
authorRory Dudley2024-08-24 16:06:23 -0600
committerRory Dudley2024-08-24 16:06:23 -0600
commit3c3cea0c7c494c998f05f21317a7c1bfa078a80e (patch)
tree0ff5661646e86d5c88463743fe96c102d9590f2d /src/buffer.rs
parent6328666624e59574946f7af1570c5676aa54d0ac (diff)
downloaddwarvish-3c3cea0c7c494c998f05f21317a7c1bfa078a80e.tar.gz
Replace io::stdin().read_line() with custom function
Added the termios crate to facilitate the changing of certain terminal options. It is a wrapper around the termios C library, so 'man 3 termios' for more details. Added the custom getchar() function, with retrieves characters from STDIN as they are typed by the user (as opposed to waiting for a newline, like io::stdin().read_line()). This is necessary, since keys like <tab> and <up> have special functionality, which needs to be acted on before command submission. Added the custom getline() function, which uses getchar() to read characters as they are typed. The getline() function contains the logic for the various key presses. For most characters, we simply push the byte to a buffer, and print it out to the screen (since getline() assumes ECHO is off).
Notes
Notes: For now, <tab> autocomplete is not finished, so hitting the tab key only replaces the tabs with spaces in the inbut buffer. Also, some edge cases are unhandled in getline(). For instance, using the arrow keys appears to move the cursor keys. The parser gets upset when you move the cursor then try to submit a command, so this needs to be fixed.
Diffstat (limited to 'src/buffer.rs')
-rw-r--r--src/buffer.rs59
1 files changed, 59 insertions, 0 deletions
diff --git a/src/buffer.rs b/src/buffer.rs
new file mode 100644
index 0000000..48e2d85
--- /dev/null
+++ b/src/buffer.rs
@@ -0,0 +1,59 @@
+use std::io::{self, Read, Write};
+use std::sync::{Arc, Mutex};
+
+// STDIN is file descriptor (fd) 0 on Linux and other UN*X-likes
+pub const STDIN: i32 = 0;
+
+/// Retrieve a single byte of input
+///
+/// Requires some setup beforehand (see beginning of repl())
+fn getchar() -> u8 {
+ let mut b = [0; 1];
+ io::stdout().lock().flush().unwrap();
+ io::stdin().read_exact(&mut b).unwrap();
+ b[0]
+}
+
+/// Handle user input at the repl prompt
+///
+/// This is required instead of io::stdin().read_line(), because certain
+/// keys like `<tab>` and `<up>` have special functions (cycle through
+/// autocomplete options, and history, respectively). It leverages
+/// [getchar] to read each character as the user inputs it. This also
+/// means special cases for handling backspace, newlines, etc. Assumes
+/// that (ICANON and ECHO) are off. See the beginning of [crate::repl]
+/// for more details.
+pub fn getline(buffer: &mut Arc<Mutex<Vec<u8>>>) -> usize {
+ loop {
+ let c = getchar();
+ match c {
+ // enter/return
+ b'\n' => break,
+
+ // tab
+ b'\t' => {
+ print!(" ");
+ buffer.lock().unwrap().push(b' ');
+ }
+
+ // ctrl-d
+ 4 => return 0,
+
+ // backspace
+ 127 => {
+ buffer.lock().unwrap().pop();
+ print!("\u{8} \u{8}");
+ }
+
+ // everything else
+ _ => {
+ print!("{}", c as char);
+ buffer.lock().unwrap().push(c);
+ }
+ }
+ }
+
+ println!();
+ buffer.lock().unwrap().push(b'\n');
+ buffer.lock().unwrap().len()
+}