summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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);
}