Merge pull request #236 from nushell/select_completions

Try out select completions from dialoguer
This commit is contained in:
JT 2021-10-16 07:50:20 +13:00 committed by GitHub
commit 2be26127c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 20 deletions

71
Cargo.lock generated
View File

@ -170,10 +170,25 @@ dependencies = [
] ]
[[package]] [[package]]
name = "core-foundation-sys" name = "console"
version = "0.8.2" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
@ -261,7 +276,20 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9dd058f8b65922819fabb4a41e7d1964e56344042c26efbccd465202c23fa0c" checksum = "c9dd058f8b65922819fabb4a41e7d1964e56344042c26efbccd465202c23fa0c"
dependencies = [ dependencies = [
"console", "console 0.14.1",
"lazy_static",
"tempfile",
"zeroize",
]
[[package]]
name = "dialoguer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61579ada4ec0c6031cfac3f86fdba0d195a7ebeb5e36693bd53cb5999a25beeb"
dependencies = [
"console 0.15.0",
"fuzzy-matcher",
"lazy_static", "lazy_static",
"tempfile", "tempfile",
"zeroize", "zeroize",
@ -330,6 +358,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"assert_cmd", "assert_cmd",
"crossterm", "crossterm",
"dialoguer 0.9.0",
"miette", "miette",
"nu-cli", "nu-cli",
"nu-command", "nu-command",
@ -345,6 +374,15 @@ dependencies = [
"tempfile", "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]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.3" version = "0.2.3"
@ -574,7 +612,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bytesize", "bytesize",
"chrono", "chrono",
"dialoguer", "dialoguer 0.8.0",
"glob", "glob",
"lscolors", "lscolors",
"nu-engine", "nu-engine",
@ -752,9 +790,9 @@ dependencies = [
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.10" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741"
[[package]] [[package]]
name = "predicates" name = "predicates"
@ -797,9 +835,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.29" version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
@ -900,7 +938,7 @@ dependencies = [
[[package]] [[package]]
name = "reedline" name = "reedline"
version = "0.2.0" 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 = [ dependencies = [
"chrono", "chrono",
"crossterm", "crossterm",
@ -1092,9 +1130,9 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.20.4" version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffff4a02fa61eee51f95210fc9c98ea6eeb46bb071adeafd61e1a0b9b22c6a6d" checksum = "e223c65cd36b485a34c2ce6e38efa40777d31c4166d9076030c74cdcf971679f"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"core-foundation-sys", "core-foundation-sys",
@ -1176,6 +1214,15 @@ dependencies = [
"syn", "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]] [[package]]
name = "time" name = "time"
version = "0.1.44" version = "0.1.44"

View File

@ -11,6 +11,7 @@ members = ["crates/nu-cli", "crates/nu-engine", "crates/nu-parser", "crates/nu-c
[dependencies] [dependencies]
reedline = { git = "https://github.com/nushell/reedline", branch = "main" } reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
crossterm = "0.21.*" crossterm = "0.21.*"
dialoguer = { version = "0.9.0", features = ["fuzzy-select"] }
nu-cli = { path="./crates/nu-cli" } nu-cli = { path="./crates/nu-cli" }
nu-command = { path="./crates/nu-command" } nu-command = { path="./crates/nu-command" }
nu-engine = { path="./crates/nu-engine" } nu-engine = { path="./crates/nu-engine" }

View File

@ -3,7 +3,7 @@ use std::rc::Rc;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext}; use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{ShellError, Signature, Span, Value, ValueStream}; use nu_protocol::{ShellError, Signature, Value, ValueStream};
pub struct Lines; pub struct Lines;
@ -28,6 +28,7 @@ impl Command for Lines {
call: &Call, call: &Call,
input: Value, input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> { ) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let span = call.head;
match input { match input {
#[allow(clippy::needless_collect)] #[allow(clippy::needless_collect)]
// Collect is needed because the string may not live long enough for // Collect is needed because the string may not live long enough for
@ -49,7 +50,7 @@ impl Command for Lines {
Ok(Value::Stream { Ok(Value::Stream {
stream: ValueStream(Rc::new(RefCell::new(iter))), stream: ValueStream(Rc::new(RefCell::new(iter))),
span: Span::unknown(), span,
}) })
} }
Value::Stream { stream, span: _ } => { Value::Stream { stream, span: _ } => {
@ -80,7 +81,7 @@ impl Command for Lines {
Ok(Value::Stream { Ok(Value::Stream {
stream: ValueStream(Rc::new(RefCell::new(iter))), stream: ValueStream(Rc::new(RefCell::new(iter))),
span: Span::unknown(), span,
}) })
} }
val => Err(ShellError::UnsupportedInput( val => Err(ShellError::UnsupportedInput(

View File

@ -1,5 +1,6 @@
use std::{cell::RefCell, io::Write, rc::Rc}; use std::{cell::RefCell, io::Write, rc::Rc};
use dialoguer::{theme::ColorfulTheme, Select};
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use nu_cli::{report_error, NuCompleter, NuHighlighter, NuValidator, NushellPrompt}; use nu_cli::{report_error, NuCompleter, NuHighlighter, NuValidator, NushellPrompt};
use nu_command::create_default_context; use nu_command::create_default_context;
@ -10,7 +11,7 @@ use nu_protocol::{
engine::{EngineState, EvaluationContext, Stack, StateWorkingSet}, engine::{EngineState, EvaluationContext, Stack, StateWorkingSet},
ShellError, Value, ShellError, Value,
}; };
use reedline::{DefaultPrompt, Prompt}; use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -18,6 +19,51 @@ mod tests;
// Name of environment variable where the prompt could be stored // Name of environment variable where the prompt could be stored
const PROMPT_COMMAND: &str = "PROMPT_COMMAND"; const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
struct FuzzyCompletion {
completer: Box<dyn Completer>,
}
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<()> { fn main() -> Result<()> {
miette::set_panic_hook(); miette::set_panic_hook();
let miette_hook = std::panic::take_hook(); let miette_hook = std::panic::take_hook();
@ -66,7 +112,7 @@ fn main() -> Result<()> {
Ok(()) Ok(())
} else { } else {
use reedline::{FileBackedHistory, ListCompletionHandler, Reedline, Signal}; use reedline::{FileBackedHistory, Reedline, Signal};
let completer = NuCompleter::new(engine_state.clone()); let completer = NuCompleter::new(engine_state.clone());
let mut entry_num = 0; let mut entry_num = 0;
@ -80,9 +126,12 @@ fn main() -> Result<()> {
.with_highlighter(Box::new(NuHighlighter { .with_highlighter(Box::new(NuHighlighter {
engine_state: engine_state.clone(), engine_state: engine_state.clone(),
})) }))
.with_completion_action_handler(Box::new( .with_completion_action_handler(Box::new(FuzzyCompletion {
ListCompletionHandler::default().with_completer(Box::new(completer)), completer: Box::new(completer),
)) }))
// .with_completion_action_handler(Box::new(
// ListCompletionHandler::default().with_completer(Box::new(completer)),
// ))
.with_validator(Box::new(NuValidator { .with_validator(Box::new(NuValidator {
engine_state: engine_state.clone(), engine_state: engine_state.clone(),
})); }));