Add history recall to repl.

This commit is contained in:
Stephen Chung 2022-01-31 21:02:36 +08:00
parent f1458e79e0
commit 389bb9bf66
2 changed files with 128 additions and 28 deletions

View File

@ -29,9 +29,32 @@ New features
Enhancements Enhancements
------------ ------------
* `rhai-repl` tool has a few more commands, such as `strict` to turn on/off _Strict Variables Mode_ and `optimize` to turn on/off script optimization.
* Default features for dependencies (such as `ahash/std` and `num-traits/std`) are no longer required. * Default features for dependencies (such as `ahash/std` and `num-traits/std`) are no longer required.
* The `no_module` feature now eliminates large sections of code via feature gates. * The `no_module` feature now eliminates large sections of code via feature gates.
* Debug display of `AST` is improved.
REPL tool changes
-----------------
The REPL bin tool, `rhai-rpl`, has been enhanced.
### Build changes
* The `rustyline` feature is now required in order to build `rhai-repl`.
* Therefore, `rhai-repl` is no longer automatically built when using a simple `cargo build` with default features.
### Line editor
* `rhai-repl` now uses [`rustyline`](https://crates.io/crates/rustyline) as a line editor with history.
* Shift-Enter can now be used to enter multiple lines without having to attach the `\` continuation character the end of each line.
### New commands
* `strict` to turn on/off _Strict Variables Mode_.
* `optimize` to turn on/off script optimization.
* `history` to print lines history.
* `!!`, `!`_num_ and `!`_text_ to recall a history line.
* `keys` to print all key bindings.
Version 1.4.1 Version 1.4.1

View File

@ -44,9 +44,13 @@ fn print_error(input: &str, mut err: EvalAltResult) {
/// Print help text. /// Print help text.
fn print_help() { fn print_help() {
println!("help => print this help"); println!("help => print this help");
println!("keys => print list of key bindings");
println!("quit, exit => quit"); println!("quit, exit => quit");
println!("keys => print list of key bindings");
println!("history => print lines history"); println!("history => print lines history");
println!("!! => repeat the last history line");
println!("!<#> => repeat a particular history line");
println!("!<text> => repeat the last history line starting with some text");
println!("!?<text> => repeat the last history line containing some text");
println!("scope => print all variables in the scope"); println!("scope => print all variables in the scope");
println!("strict => toggle on/off Strict Variables Mode"); println!("strict => toggle on/off Strict Variables Mode");
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
@ -311,6 +315,10 @@ fn main() {
// REPL loop // REPL loop
let mut input = String::new(); let mut input = String::new();
let mut replacement = None;
let mut replacement_index = 0;
let mut history_offset = 1;
let mut main_ast = AST::empty(); let mut main_ast = AST::empty();
let mut ast_u = AST::empty(); let mut ast_u = AST::empty();
let mut ast = AST::empty(); let mut ast = AST::empty();
@ -318,35 +326,53 @@ fn main() {
print_help(); print_help();
'main_loop: loop { 'main_loop: loop {
input.clear(); if let Some(replace) = replacement.take() {
input = replace;
loop { if rl.add_history_entry(input.clone()) {
let prompt = if input.is_empty() { history_offset += 1;
"rhai-repl> " }
if input.contains('\n') {
println!("[{}] ~~~~", replacement_index);
println!("{}", input);
println!("~~~~");
} else { } else {
" > " println!("[{}] {}", replacement_index, input);
}; }
replacement_index = 0;
} else {
input.clear();
match rl.readline(prompt) { loop {
// Line continuation let prompt = if input.is_empty() {
Ok(mut line) if line.ends_with("\\") => { "rhai-repl> "
line.pop(); } else {
input += line.trim_end(); " > "
input.push('\n'); };
}
Ok(line) => { match rl.readline(prompt) {
input += line.trim_end(); // Line continuation
if !input.is_empty() { Ok(mut line) if line.ends_with("\\") => {
rl.add_history_entry(input.clone()); line.pop();
input += line.trim_end();
input.push('\n');
}
Ok(line) => {
input += line.trim_end();
if !input.is_empty() && !input.starts_with('!') && input.trim() != "history"
{
if rl.add_history_entry(input.clone()) {
history_offset += 1;
}
}
break;
} }
break;
}
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break 'main_loop, Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break 'main_loop,
Err(err) => { Err(err) => {
eprintln!("Error: {:?}", err); eprintln!("Error: {:?}", err);
break 'main_loop; break 'main_loop;
}
} }
} }
} }
@ -371,10 +397,10 @@ fn main() {
"history" => { "history" => {
for (i, h) in rl.history().iter().enumerate() { for (i, h) in rl.history().iter().enumerate() {
match &h.split('\n').collect::<Vec<_>>()[..] { match &h.split('\n').collect::<Vec<_>>()[..] {
[line] => println!("[{}] {}", i + 1, line), [line] => println!("[{}] {}", history_offset + i, line),
lines => { lines => {
for (x, line) in lines.iter().enumerate() { for (x, line) in lines.iter().enumerate() {
let number = format!("[{}]", i + 1); let number = format!("[{}]", history_offset + i);
if x == 0 { if x == 0 {
println!("{} {}", number, line.trim_end()); println!("{} {}", number, line.trim_end());
} else { } else {
@ -447,6 +473,57 @@ fn main() {
); );
continue; continue;
} }
"!!" => {
if let Some(line) = rl.history().last() {
replacement = Some(line.clone());
replacement_index = history_offset + rl.history().len() - 1;
} else {
eprintln!("No lines history!");
}
continue;
}
_ if script.starts_with("!?") => {
let text = script[2..].trim();
if let Some((n, line)) = rl
.history()
.iter()
.rev()
.enumerate()
.find(|&(_, h)| h.contains(text))
{
replacement = Some(line.clone());
replacement_index = history_offset + (rl.history().len() - 1 - n);
} else {
eprintln!("History line not found: {}", text);
}
continue;
}
_ if script.starts_with('!') => {
if let Ok(num) = script[1..].parse::<usize>() {
if num >= history_offset {
if let Some(line) = rl.history().get(num - history_offset) {
replacement = Some(line.clone());
replacement_index = num;
continue;
}
}
} else {
let prefix = script[1..].trim();
if let Some((n, line)) = rl
.history()
.iter()
.rev()
.enumerate()
.find(|&(_, h)| h.trim_start().starts_with(prefix))
{
replacement = Some(line.clone());
replacement_index = history_offset + (rl.history().len() - 1 - n);
continue;
}
}
eprintln!("History line not found: {}", &script[1..]);
continue;
}
_ => (), _ => (),
} }