From 82b0415d926e06346ac15547899fb4b0b9b2a2a9 Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 16 Oct 2021 07:37:58 +1300 Subject: [PATCH] Try out select completions from dialoguer --- Cargo.lock | 79 ++++++++++++++++++++++---- Cargo.toml | 1 + crates/nu-command/src/filters/lines.rs | 5 +- src/main.rs | 59 +++++++++++++++++-- 4 files changed, 127 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65709e251..9831ee895 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,10 +155,25 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.8.2" +name = "console" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "regex", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "crossbeam-channel" @@ -240,6 +255,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dialoguer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61579ada4ec0c6031cfac3f86fdba0d195a7ebeb5e36693bd53cb5999a25beeb" +dependencies = [ + "console", + "fuzzy-matcher", + "lazy_static", + "tempfile", + "zeroize", +] + [[package]] name = "diff" version = "0.1.12" @@ -291,12 +319,19 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "engine-q" version = "0.1.0" dependencies = [ "assert_cmd", "crossterm", + "dialoguer", "miette", "nu-cli", "nu-command", @@ -312,6 +347,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + [[package]] name = "getrandom" version = "0.2.3" @@ -718,9 +762,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741" [[package]] name = "predicates" @@ -763,9 +807,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" dependencies = [ "unicode-xid", ] @@ -866,7 +910,7 @@ dependencies = [ [[package]] name = "reedline" version = "0.2.0" -source = "git+https://github.com/nushell/reedline?branch=main#6fedafffb7a783949b5e9a86149286014eddba15" +source = "git+https://github.com/nushell/reedline?branch=main#68a6ab4e5b1ada6d4e0f64bddd305ef1c852fb39" dependencies = [ "chrono", "crossterm", @@ -1058,9 +1102,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.20.4" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffff4a02fa61eee51f95210fc9c98ea6eeb46bb071adeafd61e1a0b9b22c6a6d" +checksum = "e223c65cd36b485a34c2ce6e38efa40777d31c4166d9076030c74cdcf971679f" dependencies = [ "cfg-if", "core-foundation-sys", @@ -1142,6 +1186,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + [[package]] name = "time" version = "0.1.44" @@ -1258,3 +1311,9 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970" diff --git a/Cargo.toml b/Cargo.toml index 3d819c062..b9294c0a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = ["crates/nu-cli", "crates/nu-engine", "crates/nu-parser", "crates/nu-c [dependencies] reedline = { git = "https://github.com/nushell/reedline", branch = "main" } crossterm = "0.21.*" +dialoguer = { version = "0.9.0", features = ["fuzzy-select"] } nu-cli = { path="./crates/nu-cli" } nu-command = { path="./crates/nu-command" } nu-engine = { path="./crates/nu-engine" } diff --git a/crates/nu-command/src/filters/lines.rs b/crates/nu-command/src/filters/lines.rs index f3517c8e6..87721e8df 100644 --- a/crates/nu-command/src/filters/lines.rs +++ b/crates/nu-command/src/filters/lines.rs @@ -28,6 +28,7 @@ impl Command for Lines { call: &Call, input: Value, ) -> Result { + let span = call.head; match input { #[allow(clippy::needless_collect)] // Collect is needed because the string may not live long enough for @@ -49,7 +50,7 @@ impl Command for Lines { Ok(Value::Stream { stream: ValueStream(Rc::new(RefCell::new(iter))), - span: Span::unknown(), + span, }) } Value::Stream { stream, span: _ } => { @@ -80,7 +81,7 @@ impl Command for Lines { Ok(Value::Stream { stream: ValueStream(Rc::new(RefCell::new(iter))), - span: Span::unknown(), + span, }) } val => Err(ShellError::UnsupportedInput( diff --git a/src/main.rs b/src/main.rs index 0ca6f3b04..4437d5fc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use std::{cell::RefCell, io::Write, rc::Rc}; +use dialoguer::{theme::ColorfulTheme, Select}; use miette::{IntoDiagnostic, Result}; use nu_cli::{report_error, NuCompleter, NuHighlighter, NuValidator, NushellPrompt}; use nu_command::create_default_context; @@ -10,7 +11,7 @@ use nu_protocol::{ engine::{EngineState, EvaluationContext, Stack, StateWorkingSet}, ShellError, Value, }; -use reedline::{DefaultPrompt, Prompt}; +use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt}; #[cfg(test)] mod tests; @@ -18,6 +19,51 @@ mod tests; // Name of environment variable where the prompt could be stored const PROMPT_COMMAND: &str = "PROMPT_COMMAND"; +struct FuzzyCompletion { + completer: Box, +} + +impl CompletionActionHandler for FuzzyCompletion { + fn handle(&mut self, present_buffer: &mut LineBuffer) { + let completions = self + .completer + .complete(present_buffer.get_buffer(), present_buffer.offset()); + + if completions.is_empty() { + // do nothing + } else if completions.len() == 1 { + let span = completions[0].0; + + let mut offset = present_buffer.offset(); + offset += completions[0].1.len() - (span.end - span.start); + + // TODO improve the support for multiline replace + present_buffer.replace(span.start..span.end, &completions[0].1); + present_buffer.set_insertion_point(offset); + } else { + let selections: Vec<_> = completions.iter().map(|(_, string)| string).collect(); + + let _ = crossterm::terminal::disable_raw_mode(); + println!(); + let result = Select::with_theme(&ColorfulTheme::default()) + .default(0) + .items(&selections[..]) + .interact() + .unwrap(); + let _ = crossterm::terminal::enable_raw_mode(); + + let span = completions[result].0; + + let mut offset = present_buffer.offset(); + offset += completions[result].1.len() - (span.end - span.start); + + // TODO improve the support for multiline replace + present_buffer.replace(span.start..span.end, &completions[result].1); + present_buffer.set_insertion_point(offset); + } + } +} + fn main() -> Result<()> { miette::set_panic_hook(); let miette_hook = std::panic::take_hook(); @@ -66,7 +112,7 @@ fn main() -> Result<()> { Ok(()) } else { - use reedline::{FileBackedHistory, ListCompletionHandler, Reedline, Signal}; + use reedline::{FileBackedHistory, Reedline, Signal}; let completer = NuCompleter::new(engine_state.clone()); let mut entry_num = 0; @@ -80,9 +126,12 @@ fn main() -> Result<()> { .with_highlighter(Box::new(NuHighlighter { engine_state: engine_state.clone(), })) - .with_completion_action_handler(Box::new( - ListCompletionHandler::default().with_completer(Box::new(completer)), - )) + .with_completion_action_handler(Box::new(FuzzyCompletion { + completer: Box::new(completer), + })) + // .with_completion_action_handler(Box::new( + // ListCompletionHandler::default().with_completer(Box::new(completer)), + // )) .with_validator(Box::new(NuValidator { engine_state: engine_state.clone(), }));