From f1fcc7ce42eaa91531ae84be3688224fa349acaf Mon Sep 17 00:00:00 2001 From: vms Date: Sun, 9 Aug 2020 14:48:18 +0300 Subject: [PATCH] improve REPL command completer --- .gitignore | 3 + Cargo.lock | 308 ++++++++++++++++++++++++++++++++++++++++- tools/repl/Cargo.toml | 2 +- tools/repl/src/main.rs | 77 +++++++++-- tools/repl/src/repl.rs | 2 +- 5 files changed, 375 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index f77bb23e..ea40430a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ target/ *.wasm *.wat +# REPL history files +*.repl_history + # Temporary file of ipfs node example /examples/ipfs_node/wasm/artifacts/ipfs_rpc_file diff --git a/Cargo.lock b/Cargo.lock index 970914ec..fefd8c93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +[[package]] +name = "aho-corasick" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +dependencies = [ + "memchr", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -145,6 +154,17 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "chrono" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + [[package]] name = "clap" version = "2.33.2" @@ -154,7 +174,7 @@ dependencies = [ "ansi_term", "atty", "bitflags", - "strsim", + "strsim 0.8.0", "textwrap", "unicode-width", "vec_map", @@ -309,6 +329,66 @@ dependencies = [ "syn", ] +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" +dependencies = [ + "darling", + "derive_builder_core", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.8.1" @@ -327,6 +407,17 @@ dependencies = [ "generic-array 0.14.3", ] +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dirs-next" version = "1.0.1" @@ -354,6 +445,19 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "env_logger" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "erased-serde" version = "0.3.12" @@ -557,6 +661,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "frepl" version = "0.1.0" @@ -572,6 +682,15 @@ dependencies = [ "wasmer-wasi-fl", ] +[[package]] +name = "fuzzy-matcher" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda28c4acb13182d935e0ab6bc12329d1d22134d69801d0836d1ae4b47054f2a" +dependencies = [ + "thread_local", +] + [[package]] name = "gcc" version = "0.3.55" @@ -678,12 +797,27 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + [[package]] name = "id-arena" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indexmap" version = "1.5.0" @@ -837,6 +971,19 @@ dependencies = [ "serde", ] +[[package]] +name = "nix" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "void", +] + [[package]] name = "nix" version = "0.15.0" @@ -874,6 +1021,25 @@ dependencies = [ "version_check", ] +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -962,6 +1128,12 @@ dependencies = [ "parity-wasm", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.7" @@ -1081,6 +1253,24 @@ dependencies = [ "rust-argon2", ] +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + [[package]] name = "rust-argon2" version = "0.7.0" @@ -1127,9 +1317,10 @@ dependencies = [ "memchr", "nix 0.17.0", "scopeguard", + "skim", "unicode-segmentation", "unicode-width", - "utf8parse", + "utf8parse 0.2.0", "winapi", ] @@ -1226,6 +1417,37 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + +[[package]] +name = "skim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb226ef1f69c44352626532a14c0692be118eb9f93e91010150d165a9d693efa" +dependencies = [ + "bitflags", + "chrono", + "clap", + "derive_builder", + "env_logger", + "fuzzy-matcher", + "lazy_static", + "log", + "nix 0.14.1", + "rayon", + "regex", + "shlex", + "time", + "timer", + "tuikit", + "unicode-width", + "vte", +] + [[package]] name = "smallvec" version = "1.4.1" @@ -1244,6 +1466,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + [[package]] name = "subtle" version = "2.2.3" @@ -1279,6 +1507,26 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" +[[package]] +name = "term" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +dependencies = [ + "byteorder", + "dirs", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +dependencies = [ + "winapi-util", +] + [[package]] name = "test-record" version = "0.1.0" @@ -1315,6 +1563,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + [[package]] name = "time" version = "0.1.43" @@ -1325,6 +1582,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "timer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" +dependencies = [ + "chrono", +] + [[package]] name = "toml" version = "0.5.6" @@ -1334,6 +1600,20 @@ dependencies = [ "serde", ] +[[package]] +name = "tuikit" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "846bc187c93656d5ef5389809d1b6950fd1364906af5e9953a9de129c295c3a5" +dependencies = [ + "bitflags", + "lazy_static", + "log", + "nix 0.14.1", + "term", + "unicode-width", +] + [[package]] name = "typenum" version = "1.12.0" @@ -1382,6 +1662,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "utf8parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" + [[package]] name = "utf8parse" version = "0.2.0" @@ -1415,6 +1701,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "vte" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf" +dependencies = [ + "utf8parse 0.1.1", +] + [[package]] name = "walrus" version = "0.17.0" @@ -1660,6 +1955,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/tools/repl/Cargo.toml b/tools/repl/Cargo.toml index 58802b79..17844864 100644 --- a/tools/repl/Cargo.toml +++ b/tools/repl/Cargo.toml @@ -19,7 +19,7 @@ clap = "2.33.1" serde_json = "1.0.57" wasmer-wasi = { package = "wasmer-wasi-fl", version = "0.17.0"} -rustyline = "6.1.2" +rustyline = { version = "6.1.2", features = ["with-fuzzy"] } rustyline-derive = "0.3.1" rustop = "1.1.0" uuid = { version = "0.8.1", features = ["v4"] } diff --git a/tools/repl/src/main.rs b/tools/repl/src/main.rs index 4460d41d..d4e6889d 100644 --- a/tools/repl/src/main.rs +++ b/tools/repl/src/main.rs @@ -40,6 +40,9 @@ use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyPress use rustyline_derive::Helper; use std::borrow::Cow::{self, Borrowed, Owned}; +use std::collections::HashSet; + +const HISTORY_FILE_PATH: &str = ".repl_history"; pub(crate) type Result = std::result::Result; @@ -52,22 +55,28 @@ fn main() -> Result<()> { let config = Config::builder() .history_ignore_space(true) - .completion_type(CompletionType::List) + .completion_type(CompletionType::Fuzzy) .edit_mode(EditMode::Emacs) .output_stream(OutputStreamType::Stdout) .build(); - let h = MyHelper { + + let repl_hinter = REPLHinter { + commands_hints: commands_hints(), + history_hinter: HistoryHinter {}, + }; + let repl_helper = REPLHelper { completer: FilenameCompleter::new(), highlighter: MatchingBracketHighlighter::new(), - hinter: HistoryHinter {}, + hinter: repl_hinter, colored_prompt: "".to_owned(), validator: MatchingBracketValidator::new(), }; + let mut rl = Editor::with_config(config); - rl.set_helper(Some(h)); + rl.set_helper(Some(repl_helper)); rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward); rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward); - let _ = rl.load_history("history.txt"); + let _ = rl.load_history(HISTORY_FILE_PATH); println!("Welcome to the Fluence FaaS REPL:"); @@ -75,7 +84,7 @@ fn main() -> Result<()> { let mut count = 1; loop { - let p = format!("{}> ", count); + let p = format!("\n{}> ", count); rl.helper_mut().expect("No helper").colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p); let readline = rl.readline(&p); match readline { @@ -99,19 +108,29 @@ fn main() -> Result<()> { count += 1; } + if let Err(e) = rl.save_history(HISTORY_FILE_PATH) { + eprintln!("failed to save history: {}", e); + } + Ok(()) } #[derive(Helper)] -struct MyHelper { +struct REPLHelper { completer: FilenameCompleter, highlighter: MatchingBracketHighlighter, validator: MatchingBracketValidator, - hinter: HistoryHinter, + hinter: REPLHinter, colored_prompt: String, } -impl Completer for MyHelper { +/// Tries to find hint from history if its failed from supported command list. +struct REPLHinter { + commands_hints: HashSet, + history_hinter: HistoryHinter, +} + +impl Completer for REPLHelper { type Candidate = Pair; fn complete( @@ -124,13 +143,33 @@ impl Completer for MyHelper { } } -impl Hinter for MyHelper { +impl Hinter for REPLHelper { fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option { - self.hinter.hint(line, pos, ctx) + if pos < line.len() { + return None; + } + + if let Some(hint) = self.hinter.history_hinter.hint(line, pos, ctx) { + return Some(hint); + } + + self.hinter + .commands_hints + .iter() + .filter_map(|hint| { + // expect hint after word complete, like redis cli, add condition: + // line.ends_with(" ") + if pos > 0 && hint.starts_with(&line[..pos]) { + Some(hint[pos..].to_owned()) + } else { + None + } + }) + .next() } } -impl Highlighter for MyHelper { +impl Highlighter for REPLHelper { fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { self.highlighter.highlight(line, pos) } @@ -156,7 +195,7 @@ impl Highlighter for MyHelper { } } -impl Validator for MyHelper { +impl Validator for REPLHelper { fn validate( &self, ctx: &mut validate::ValidationContext<'_>, @@ -168,3 +207,15 @@ impl Validator for MyHelper { self.validator.validate_while_typing() } } + +fn commands_hints() -> HashSet { + let mut set = HashSet::new(); + set.insert(String::from("load")); + set.insert(String::from("unload")); + set.insert(String::from("call")); + set.insert(String::from("envs")); + set.insert(String::from("fs")); + set.insert(String::from("interface")); + set.insert(String::from("help")); + set +} diff --git a/tools/repl/src/repl.rs b/tools/repl/src/repl.rs index 3da39fc5..e8f2691f 100644 --- a/tools/repl/src/repl.rs +++ b/tools/repl/src/repl.rs @@ -129,7 +129,7 @@ impl REPL { envs - to print environment variables of module with module_name\n\ fs - to print filesystem state of module with module_name\n\ h/help - to print this message\n\ - Ctrl-C - to exit" + Ctrl-C - to exit" ); } _ => {