summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRory Dudley2024-09-01 04:18:43 -0600
committerRory Dudley2024-09-01 04:18:43 -0600
commit0596517643d9daffa1c4b7b3b0f913ac6d1ab9cd (patch)
treed40a2364e89ece18aa9c26e4cf8ee321d94805f9 /src
parent398631015cac0174aeec5fd351fcfcaf51eb6ef0 (diff)
downloaddwarvish-0596517643d9daffa1c4b7b3b0f913ac6d1ab9cd.tar.gz
Slight refactor of getchar() and more handling for getline()
The getchar() function was changed so that it is able to detect some ANSI escape sequences. To better handle this, getchar() now returns a value from the Key enum, indicating whether or not an escape sequence was found. Currently, the only escape sequences the function deals with are arrow keys. Handling of the left and right arrow keys were added to the getline() function, in order to allow a user to go back and edit their command inplace. Up and down arrow keys are also handled, but they are just ignored for now (i.e. they do not move the cursor around anymore). The local 'pos' variable became an Arc<Mutex<usize>>, since it needs to be reset to 0 if ctrl-c is pressed (the handler for which is outside the scope of getline()).
Diffstat (limited to 'src')
-rw-r--r--src/buffer.rs148
-rw-r--r--src/main.rs14
2 files changed, 129 insertions, 33 deletions
diff --git a/src/buffer.rs b/src/buffer.rs
index eedb694..8668249 100644
--- a/src/buffer.rs
+++ b/src/buffer.rs
@@ -4,14 +4,47 @@ use std::sync::{Arc, Mutex};
// STDIN is file descriptor (fd) 0 on Linux and other UN*X-likes
pub const STDIN: i32 = 0;
+// Key input types from the user
+enum Key {
+ Up,
+ Down,
+ Right,
+ Left,
+ Else(u8),
+ Ignored,
+}
+
/// Retrieve a single byte of input
///
/// Requires some setup beforehand (see beginning of repl())
-fn getchar() -> u8 {
+fn getchar() -> Key {
let mut b = [0; 1];
io::stdout().lock().flush().unwrap();
io::stdin().read_exact(&mut b).unwrap();
- b[0]
+
+ // Might me an ASNI escape sequence
+ if b[0] == 27 {
+ io::stdin().read_exact(&mut b).unwrap();
+
+ if b[0] == 91 {
+ io::stdin().read_exact(&mut b).unwrap();
+
+ match b[0] {
+ // Arrow keys
+ 65 => return Key::Up,
+ 66 => return Key::Down,
+ 67 => return Key::Right,
+ 68 => return Key::Left,
+
+ // Everything else
+ _ => return Key::Ignored,
+ }
+ }
+
+ return Key::Ignored;
+ }
+
+ Key::Else(b[0])
}
/// Handle user input at the repl prompt
@@ -23,46 +56,101 @@ fn getchar() -> u8 {
/// 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 {
- // Keep track of position for backspaces
- let mut pos: usize = 0;
-
+pub fn getline(buffer: &mut Arc<Mutex<Vec<u8>>>, pos: &mut Arc<Mutex<usize>>) -> usize {
// Loop over characters until there is a newline
loop {
let c = getchar();
match c {
- // enter/return
- b'\n' => break,
-
- // tab
- b'\t' => {
- pos += 1;
- print!(" ");
- buffer.lock().unwrap().push(b' ');
+ Key::Up => {
+ continue;
}
-
- // ctrl-d
- 4 => return 0,
-
- // backspace
- 127 => {
- if pos == 0 {
+ Key::Down => {
+ continue;
+ }
+ Key::Right => {
+ if *pos.lock().unwrap() >= buffer.lock().unwrap().len() {
continue;
}
- pos -= 1;
- buffer.lock().unwrap().pop();
- print!("\u{8} \u{8}");
+ print!("\x1b[1C");
+ *pos.lock().unwrap() += 1;
}
-
- // everything else
- _ => {
- pos += 1;
- print!("{}", c as char);
- buffer.lock().unwrap().push(c);
+ Key::Left => {
+ if *pos.lock().unwrap() == 0 {
+ continue;
+ }
+ print!("\u{8}");
+ *pos.lock().unwrap() -= 1;
+ }
+ Key::Ignored => {
+ continue;
}
+ Key::Else(c) => match c {
+ // enter/return
+ b'\n' => break,
+
+ // tab
+ b'\t' => {
+ *pos.lock().unwrap() += 1;
+ print!(" ");
+ buffer.lock().unwrap().push(b' ');
+ }
+
+ // ctrl-d
+ 4 => return 0,
+
+ // backspace
+ 127 => {
+ if *pos.lock().unwrap() == 0 {
+ continue;
+ }
+ *pos.lock().unwrap() -= 1;
+
+ if *pos.lock().unwrap() == buffer.lock().unwrap().len() {
+ buffer.lock().unwrap().pop();
+ print!("\u{8} \u{8}");
+ } else {
+ buffer.lock().unwrap().remove(*pos.lock().unwrap());
+ print!(
+ "\u{8}{} ",
+ String::from_utf8_lossy(
+ &buffer.lock().unwrap()[*pos.lock().unwrap()..]
+ )
+ );
+ for _ in *pos.lock().unwrap()..buffer.lock().unwrap().len() + 1 {
+ print!("\u{8}");
+ }
+ }
+ }
+
+ // everything else
+ _ => {
+ // Print out the character as the user is typing
+ print!("{}", c as char);
+
+ // Insert the character onto the buffer at whatever *pos.lock().unwrap()ition the cursor is at
+ buffer.lock().unwrap().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.lock().unwrap().len() {
+ print!(
+ "{}",
+ String::from_utf8_lossy(
+ &buffer.lock().unwrap()[*pos.lock().unwrap()..]
+ )
+ );
+ for _ in *pos.lock().unwrap()..buffer.lock().unwrap().len() {
+ print!("\u{8}");
+ }
+ }
+ }
+ },
}
}
+ *pos.lock().unwrap() = 0;
println!();
buffer.lock().unwrap().push(b'\n');
buffer.lock().unwrap().len()
diff --git a/src/main.rs b/src/main.rs
index d5147ff..527fb8c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -25,7 +25,12 @@ use termios::{tcsetattr, Termios, ECHO, ICANON, TCSANOW};
/// repl(&mut away, &mut env);
/// }
/// ```
-fn repl(away: &mut Arc<Mutex<bool>>, buffer: &mut Arc<Mutex<Vec<u8>>>, env: &mut Environment) {
+fn repl(
+ away: &mut Arc<Mutex<bool>>,
+ buffer: &mut Arc<Mutex<Vec<u8>>>,
+ pos: &mut Arc<Mutex<usize>>,
+ env: &mut Environment,
+) {
// Setup termios flags
let mut termios = Termios::from_fd(STDIN).unwrap();
termios.c_lflag &= !(ICANON | ECHO);
@@ -55,7 +60,7 @@ fn repl(away: &mut Arc<Mutex<bool>>, buffer: &mut Arc<Mutex<Vec<u8>>>, env: &mut
*away.lock().unwrap() = false;
// Wait for user input
- let bytes = getline(buffer);
+ let bytes = getline(buffer, pos);
// Check if we've reached EOF (i.e. <C-d>)
if bytes == 0 {
@@ -145,11 +150,14 @@ fn main() {
// Handle signals
let mut away = Arc::new(Mutex::new(true));
let mut buffer: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(vec![]));
+ let mut pos: Arc<Mutex<usize>> = Arc::new(Mutex::new(0));
unsafe {
let away = Arc::clone(&away);
let buffer = Arc::clone(&buffer);
+ let pos = Arc::clone(&pos);
signal_hook::low_level::register(signal_hook::consts::SIGINT, move || {
buffer.lock().unwrap().clear();
+ *pos.lock().unwrap() = 0;
if *away.lock().unwrap() {
println!();
} else {
@@ -168,5 +176,5 @@ fn main() {
options(&mut env);
// Begin evaluating commands
- repl(&mut away, &mut buffer, &mut env);
+ repl(&mut away, &mut buffer, &mut pos, &mut env);
}