Extract completions into subcrate. (#3631)

This commit is contained in:
Andrés N. Robalino 2021-06-16 15:20:01 -05:00 committed by GitHub
parent 04c0e94349
commit 7c8fb060f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 160 additions and 110 deletions

19
Cargo.lock generated
View File

@ -3233,6 +3233,7 @@ dependencies = [
"itertools",
"nu-cli",
"nu-command",
"nu-completion",
"nu-data",
"nu-engine",
"nu-errors",
@ -3318,6 +3319,7 @@ dependencies = [
"meval",
"nu-ansi-term",
"nu-command",
"nu-completion",
"nu-data",
"nu-engine",
"nu-errors",
@ -3476,6 +3478,23 @@ dependencies = [
"zip",
]
[[package]]
name = "nu-completion"
version = "0.32.1"
dependencies = [
"directories-next",
"dirs-next",
"indexmap",
"nu-data",
"nu-engine",
"nu-errors",
"nu-parser",
"nu-protocol",
"nu-source",
"nu-test-support",
"rustyline",
]
[[package]]
name = "nu-data"
version = "0.32.1"

View File

@ -20,6 +20,7 @@ members = ["crates/*/"]
[dependencies]
nu-cli = { version = "0.32.1", path = "./crates/nu-cli", default-features = false }
nu-command = { version = "0.32.1", path = "./crates/nu-command" }
nu-completion = { version = "0.32.1", path = "./crates/nu-completion" }
nu-data = { version = "0.32.1", path = "./crates/nu-data" }
nu-engine = { version = "0.32.1", path = "./crates/nu-engine" }
nu-errors = { version = "0.32.1", path = "./crates/nu-errors" }

View File

@ -11,6 +11,7 @@ version = "0.32.1"
doctest = false
[dependencies]
nu-completion = { version = "0.32.1", path = "../nu-completion" }
nu-command = { version = "0.32.1", path = "../nu-command" }
nu-data = { version = "0.32.1", path = "../nu-data" }
nu-engine = { version = "0.32.1", path = "../nu-engine" }

View File

@ -1,37 +0,0 @@
pub(crate) mod command;
pub(crate) mod engine;
pub(crate) mod flag;
pub(crate) mod matchers;
pub(crate) mod path;
use matchers::Matcher;
use nu_engine::EvaluationContext;
#[derive(Debug, Eq, PartialEq)]
pub struct Suggestion {
pub display: String,
pub replacement: String,
}
pub struct CompletionContext<'a>(&'a EvaluationContext);
impl<'a> CompletionContext<'a> {
pub fn new(a: &'a EvaluationContext) -> CompletionContext<'a> {
CompletionContext(a)
}
}
impl<'a> AsRef<EvaluationContext> for CompletionContext<'a> {
fn as_ref(&self) -> &EvaluationContext {
self.0
}
}
pub trait Completer {
fn complete(
&self,
ctx: &CompletionContext<'_>,
partial: &str,
matcher: &dyn Matcher,
) -> Vec<Suggestion>;
}

View File

@ -11,8 +11,6 @@ extern crate quickcheck_macros;
mod app;
mod cli;
#[cfg(feature = "rustyline-support")]
mod completion;
mod format;
#[cfg(feature = "rustyline-support")]
mod keybinding;

View File

@ -1,7 +1,5 @@
#![allow(clippy::module_inception)]
#[cfg(feature = "rustyline-support")]
pub(crate) mod completer;
#[cfg(feature = "rustyline-support")]
pub(crate) mod helper;

View File

@ -1,6 +1,5 @@
use crate::completion;
use crate::shell::completer::NuCompleter;
use nu_ansi_term::Color;
use nu_completion::NuCompleter;
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
use nu_source::{Tag, Tagged};
use std::borrow::Cow::{self, Owned};
@ -28,18 +27,28 @@ impl Helper {
}
}
impl rustyline::completion::Candidate for completion::Suggestion {
struct CompletionContext<'a>(&'a EvaluationContext);
impl<'a> nu_completion::CompletionContext for CompletionContext<'a> {
fn signature_registry(&self) -> &dyn nu_parser::ParserScope {
&self.0.scope
}
}
pub struct CompletionSuggestion(nu_completion::Suggestion);
impl rustyline::completion::Candidate for CompletionSuggestion {
fn display(&self) -> &str {
&self.display
&self.0.display
}
fn replacement(&self) -> &str {
&self.replacement
&self.0.replacement
}
}
impl rustyline::completion::Completer for Helper {
type Candidate = completion::Suggestion;
type Candidate = CompletionSuggestion;
fn complete(
&self,
@ -47,8 +56,10 @@ impl rustyline::completion::Completer for Helper {
pos: usize,
_ctx: &rustyline::Context<'_>,
) -> Result<(usize, Vec<Self::Candidate>), rustyline::error::ReadlineError> {
let ctx = completion::CompletionContext::new(&self.context);
Ok(self.completer.complete(line, pos, &ctx))
let ctx = CompletionContext(&self.context);
let (position, suggestions) = self.completer.complete(line, pos, &ctx);
let suggestions = suggestions.into_iter().map(CompletionSuggestion).collect();
Ok((position, suggestions))
}
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) {

View File

@ -0,0 +1,29 @@
[package]
authors = ["The Nu Project Contributors"]
description = "Completions for nushell"
edition = "2018"
license = "MIT"
name = "nu-completion"
version = "0.32.1"
[lib]
doctest = false
[dependencies]
nu-data = { version = "0.32.1", path = "../nu-data" }
nu-engine = { version = "0.32.1", path = "../nu-engine" }
nu-errors = { version = "0.32.1", path = "../nu-errors" }
nu-parser = { version = "0.32.1", path = "../nu-parser" }
nu-protocol = { version = "0.32.1", path = "../nu-protocol" }
nu-source = { version = "0.32.1", path = "../nu-source" }
nu-test-support = { version = "0.32.1", path = "../nu-test-support" }
directories-next = { version = "2.0.0", optional = true }
dirs-next = { version = "2.0.0", optional = true }
indexmap = { version = "1.6.1", features = ["serde-1"] }
rustyline = { version = "8.1.0", optional = true }
[features]
rustyline-support = ["rustyline", "nu-engine/rustyline-support"]
dirs = ["dirs-next"]
directories = ["directories-next"]

View File

@ -1,24 +1,22 @@
use nu_test_support::NATIVE_PATH_ENV_VAR;
use std::iter::FromIterator;
use std::path::Path;
use indexmap::set::IndexSet;
use super::matchers::Matcher;
use crate::completion::{Completer, CompletionContext, Suggestion};
use nu_engine::EvaluationContext;
use nu_test_support::NATIVE_PATH_ENV_VAR;
use crate::{Completer, CompletionContext, Suggestion};
pub struct CommandCompleter;
impl Completer for CommandCompleter {
fn complete(
&self,
ctx: &CompletionContext<'_>,
partial: &str,
matcher: &dyn Matcher,
) -> Vec<Suggestion> {
let context: &EvaluationContext = ctx.as_ref();
let mut commands: IndexSet<String> = IndexSet::from_iter(context.scope.get_command_names());
impl<Context> Completer<Context> for CommandCompleter
where
Context: CompletionContext,
{
fn complete(&self, ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion> {
let registry = ctx.signature_registry();
let mut commands: IndexSet<String> = IndexSet::from_iter(registry.get_names());
// Command suggestions can come from three possible sets:
// 1. internal command names,
@ -40,7 +38,7 @@ impl Completer for CommandCompleter {
.collect();
if !partial.is_empty() {
let path_completer = crate::completion::path::PathCompleter;
let path_completer = crate::path::PathCompleter;
let path_results = path_completer.path_suggestions(partial, matcher);
let iter = path_results.into_iter().filter_map(|path_suggestion| {
let path = path_suggestion.path;

View File

@ -1,35 +1,34 @@
use crate::completion::command::CommandCompleter;
use crate::completion::flag::FlagCompleter;
use crate::completion::matchers;
use crate::completion::matchers::Matcher;
use crate::completion::path::{PathCompleter, PathSuggestion};
use crate::completion::{self, Completer, Suggestion};
use nu_engine::EvaluationContext;
use nu_parser::ParserScope;
use nu_source::{Span, Tag};
use std::borrow::Cow;
pub(crate) struct NuCompleter {}
use nu_source::{Span, Tag};
use crate::command::CommandCompleter;
use crate::engine;
use crate::flag::FlagCompleter;
use crate::matchers;
use crate::matchers::Matcher;
use crate::path::{PathCompleter, PathSuggestion};
use crate::{Completer, CompletionContext, Suggestion};
pub struct NuCompleter {}
impl NuCompleter {}
impl NuCompleter {
pub fn complete(
pub fn complete<Context: CompletionContext>(
&self,
line: &str,
pos: usize,
context: &completion::CompletionContext,
context: &Context,
) -> (usize, Vec<Suggestion>) {
use completion::engine::LocationType;
use engine::LocationType;
let nu_context: &EvaluationContext = context.as_ref();
let tokens = nu_parser::lex(line, 0).0;
nu_context.scope.enter_scope();
let (block, _) = nu_parser::parse(line, 0, &nu_context.scope);
nu_context.scope.exit_scope();
let locations = completion::engine::completion_location(line, &block, pos);
let locations = Some(nu_parser::parse_block(tokens).0)
.map(|block| nu_parser::classify_block(&block, context.signature_registry()))
.map(|(block, _)| engine::completion_location(line, &block, pos))
.unwrap_or_default();
let matcher = nu_data::config::config(Tag::unknown())
.ok()
@ -61,7 +60,6 @@ impl NuCompleter {
pos = location.span.start();
}
}
let suggestions = locations
.into_iter()
.flat_map(|location| {
@ -147,7 +145,16 @@ fn select_directory_suggestions(completed_paths: Vec<PathSuggestion>) -> Vec<Pat
}
fn requote(orig_value: String, previously_quoted: bool) -> String {
let value: Cow<str> = rustyline::completion::unescape(&orig_value, Some('\\'));
let value: Cow<str> = {
#[cfg(feature = "rustyline-support")]
{
rustyline::completion::unescape(&orig_value, Some('\\'))
}
#[cfg(not(feature = "rustyline-support"))]
{
orig_value.into()
}
};
let mut quotes = vec!['"', '\''];
let mut should_quote = false;

View File

@ -301,6 +301,10 @@ mod tests {
}
impl ParserScope for VecRegistry {
fn get_names(&self) -> Vec<String> {
self.0.iter().cloned().map(|s| s.name).collect()
}
fn has_signature(&self, name: &str) -> bool {
self.0.iter().any(|v| v.name == name)
}
@ -331,8 +335,6 @@ mod tests {
mod completion_location {
use super::*;
use nu_parser::ParserScope;
fn completion_location(
line: &str,
scope: &dyn ParserScope,

View File

@ -1,22 +1,16 @@
use super::matchers::Matcher;
use crate::completion::{Completer, CompletionContext, Suggestion};
use nu_engine::EvaluationContext;
use crate::{Completer, CompletionContext, Suggestion};
pub struct FlagCompleter {
pub(crate) cmd: String,
}
impl Completer for FlagCompleter {
fn complete(
&self,
ctx: &CompletionContext<'_>,
partial: &str,
matcher: &dyn Matcher,
) -> Vec<Suggestion> {
let context: &EvaluationContext = ctx.as_ref();
if let Some(cmd) = context.scope.get_command(&self.cmd) {
let sig = cmd.signature();
impl<Context> Completer<Context> for FlagCompleter
where
Context: CompletionContext,
{
fn complete(&self, ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion> {
if let Some(sig) = ctx.signature_registry().get_signature(&self.cmd) {
let mut suggestions = Vec::new();
for (name, (named_type, _desc)) in sig.named.iter() {
suggestions.push(format!("--{}", name));

View File

@ -0,0 +1,24 @@
pub(crate) mod command;
pub(crate) mod completer;
pub(crate) mod engine;
pub(crate) mod flag;
pub(crate) mod matchers;
pub(crate) mod path;
use matchers::Matcher;
pub use completer::NuCompleter;
#[derive(Debug, Eq, PartialEq)]
pub struct Suggestion {
pub display: String,
pub replacement: String,
}
pub trait CompletionContext {
fn signature_registry(&self) -> &dyn nu_parser::ParserScope;
}
pub trait Completer<Context: CompletionContext> {
fn complete(&self, ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion>;
}

View File

@ -1,4 +1,5 @@
use crate::completion::matchers;
use crate::matchers;
pub struct Matcher;
impl matchers::Matcher for Matcher {
@ -12,7 +13,7 @@ impl matchers::Matcher for Matcher {
mod tests {
use super::*;
// TODO: check some Unicode matches if this becomes relevant
// TODO: check some unicode matches if this becomes relevant
// FIXME: could work exhaustively through ['-', '--'. ''] in a loop for each test
#[test]

View File

@ -1,4 +1,4 @@
use crate::completion::matchers;
use crate::matchers;
pub struct Matcher;

View File

@ -1,7 +1,7 @@
use std::path::PathBuf;
use super::matchers::Matcher;
use crate::completion::{Completer, CompletionContext, Suggestion};
use crate::{Completer, CompletionContext, Suggestion};
const SEP: char = std::path::MAIN_SEPARATOR;
@ -74,13 +74,11 @@ impl PathCompleter {
}
}
impl Completer for PathCompleter {
fn complete(
&self,
_ctx: &CompletionContext<'_>,
partial: &str,
matcher: &dyn Matcher,
) -> Vec<Suggestion> {
impl<Context> Completer<Context> for PathCompleter
where
Context: CompletionContext,
{
fn complete(&self, _ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion> {
self.path_suggestions(partial, matcher)
.into_iter()
.map(|ps| ps.suggestion)

View File

@ -319,6 +319,10 @@ impl Scope {
}
impl ParserScope for Scope {
fn get_names(&self) -> Vec<String> {
self.get_command_names()
}
fn get_signature(&self, name: &str) -> Option<nu_protocol::Signature> {
self.get_command(name).map(|x| x.signature())
}

View File

@ -3,6 +3,8 @@ use nu_source::Spanned;
use std::{fmt::Debug, sync::Arc};
pub trait ParserScope: Debug {
fn get_names(&self) -> Vec<String>;
fn get_signature(&self, name: &str) -> Option<nu_protocol::Signature>;
fn has_signature(&self, name: &str) -> bool;