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; // 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() -> Key { let mut b = [0; 1]; io::stdout().lock().flush().unwrap(); io::stdin().read_exact(&mut b).unwrap(); // 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 /// /// This is required instead of io::stdin().read_line(), because certain /// keys like `` and `` 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>>, pos: &mut Arc>) -> usize { // Loop over characters until there is a newline loop { let c = getchar(); match c { Key::Up => { continue; } Key::Down => { continue; } Key::Right => { if *pos.lock().unwrap() >= buffer.lock().unwrap().len() { continue; } print!("\x1b[1C"); *pos.lock().unwrap() += 1; } 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() }