From 389bb9bf66c3b8ec944e579867beeaf71f9eb665 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 31 Jan 2022 21:02:36 +0800 Subject: [PATCH] Add history recall to repl. --- CHANGELOG.md | 25 ++++++++- src/bin/rhai-repl.rs | 131 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 128 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8595e784..0afcd1a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,9 +29,32 @@ New features 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. * 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 diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 9a6f68d3..c23eab2e 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -44,9 +44,13 @@ fn print_error(input: &str, mut err: EvalAltResult) { /// Print help text. fn print_help() { println!("help => print this help"); - println!("keys => print list of key bindings"); println!("quit, exit => quit"); + println!("keys => print list of key bindings"); println!("history => print lines history"); + println!("!! => repeat the last history line"); + println!("!<#> => repeat a particular history line"); + println!("! => repeat the last history line starting with some text"); + println!("!? => repeat the last history line containing some text"); println!("scope => print all variables in the scope"); println!("strict => toggle on/off Strict Variables Mode"); #[cfg(not(feature = "no_optimize"))] @@ -311,6 +315,10 @@ fn main() { // REPL loop 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 ast_u = AST::empty(); let mut ast = AST::empty(); @@ -318,35 +326,53 @@ fn main() { print_help(); 'main_loop: loop { - input.clear(); - - loop { - let prompt = if input.is_empty() { - "rhai-repl> " + if let Some(replace) = replacement.take() { + input = replace; + if rl.add_history_entry(input.clone()) { + history_offset += 1; + } + if input.contains('\n') { + println!("[{}] ~~~~", replacement_index); + println!("{}", input); + println!("~~~~"); } else { - " > " - }; + println!("[{}] {}", replacement_index, input); + } + replacement_index = 0; + } else { + input.clear(); - match rl.readline(prompt) { - // Line continuation - Ok(mut line) if line.ends_with("\\") => { - line.pop(); - input += line.trim_end(); - input.push('\n'); - } - Ok(line) => { - input += line.trim_end(); - if !input.is_empty() { - rl.add_history_entry(input.clone()); + loop { + let prompt = if input.is_empty() { + "rhai-repl> " + } else { + " > " + }; + + match rl.readline(prompt) { + // Line continuation + Ok(mut line) if line.ends_with("\\") => { + 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) => { - eprintln!("Error: {:?}", err); - break 'main_loop; + Err(err) => { + eprintln!("Error: {:?}", err); + break 'main_loop; + } } } } @@ -371,10 +397,10 @@ fn main() { "history" => { for (i, h) in rl.history().iter().enumerate() { match &h.split('\n').collect::>()[..] { - [line] => println!("[{}] {}", i + 1, line), + [line] => println!("[{}] {}", history_offset + i, line), lines => { for (x, line) in lines.iter().enumerate() { - let number = format!("[{}]", i + 1); + let number = format!("[{}]", history_offset + i); if x == 0 { println!("{} {}", number, line.trim_end()); } else { @@ -447,6 +473,57 @@ fn main() { ); 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::() { + 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; + } _ => (), }