Reputation: 193
I am writing a minimal Lisp with a classic terminal-based REPL environment project in Rust.
How do I read in user input from the arrow keys, allowing them to move back and forth on their current line of input at least before they have pressed enter
? Ideally, I will be able to extend functionality to include moving "back" to retrieve old inputs like you would in any terminal or any REPL. Here's an image of the behavior for clarity:
I have played around with the standard library's io
module and the termion
crate but have not figured this functionality out.
Here is my current working code. It effectively takes input and immediately prints it back to the user in addition to quitting as expected with quit()
.
use std::io::prelude::*;
use std::io;
fn main() {
println!("Rispy Version 0.0.1");
println!("Enter `quit()` to Exit");
let mut input: String;
// continuous input til ctrl-c or quit()
loop {
print!("rispy>> ");
io::stdout().flush().unwrap();
input = String::new();
io::stdin().read_line(&mut input)
.expect("Error reading line");
print!("input: {}", input);
match input.as_ref() {
"quit()\n" => {
println!("\nGoodbye");
break;
},
_ => continue,
}
}
}
Upvotes: 2
Views: 1898
Reputation: 431689
As an out-of-the-box answer, I occasionally use rlwrap, a small wrapper around any command line program that adds basic readline capabilities.
Running rlwrap cargo run
, your original program now has editing like you asked, as well as command history and history searching, and probably a lot of other things.
Upvotes: 2
Reputation: 193
It's funny how some basic/fundamental crates are not suggested elsewhere more readily for problems like this but thankfully @kazemakase answered the question by suggesting a crate I had not found up to this point: rustyline
A slight edit to the example code on the readme yields the results I want, with history, the ability to navigate left/right with the arrow keys, and even the use of key strokes like ctrl-d, ctrl-c, home, etcetera. Here it is to couple with the question as asked:
extern crate rustyline;
use rustyline::Editor;
use rustyline::error::ReadlineError;
fn main() {
println!("Rispy Version 0.0.1");
println!("Enter `quit()` to Exit");
let mut reader = Editor::<()>::new();
if let Err(_) = reader.load_history("rispy_history.txt") {
println!("No previous history.");
}
// continuous input
loop {
let readline = reader.readline("rispy>> ");
match readline {
Ok(line) => {
reader.add_history_entry(&line);
println!("input: {}", line);
},
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
println!("Goodbye");
break
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
println!("Goodbye");
break
},
Err(err) => {
println!("Error: {:?}", err);
break
}
}
}
reader.save_history("rispy_history.txt").unwrap();
}
Upvotes: 5