forked from extern/nushell
Refactor completion trait (#2555)
* Remove completion code from help/value shells * Tweak the `Completer` trait for nushell. Previously, this trait was built around rustyline's completion traits, and for `Shell` instances. Now it is built for individual completers inside of nushell that will complete a specific location based on a partial string. For example, for completing a partially typed command in the command position.
This commit is contained in:
parent
d19a5f4c2f
commit
f5fad393d0
@ -3,13 +3,13 @@ use std::path::Path;
|
||||
|
||||
use indexmap::set::IndexSet;
|
||||
|
||||
use crate::completion::{Context, Suggestion};
|
||||
use crate::completion::{Completer, Context, Suggestion};
|
||||
use crate::context;
|
||||
|
||||
pub struct Completer;
|
||||
pub struct CommandCompleter;
|
||||
|
||||
impl Completer {
|
||||
pub fn complete(&self, ctx: &Context<'_>, partial: &str) -> Vec<Suggestion> {
|
||||
impl Completer for CommandCompleter {
|
||||
fn complete(&self, ctx: &Context<'_>, partial: &str) -> Vec<Suggestion> {
|
||||
let context: &context::Context = ctx.as_ref();
|
||||
let mut commands: IndexSet<String> = IndexSet::from_iter(context.registry.names());
|
||||
|
||||
@ -33,8 +33,8 @@ impl Completer {
|
||||
.collect();
|
||||
|
||||
if partial != "" {
|
||||
let path_completer = crate::completion::path::Completer;
|
||||
let path_results = path_completer.path_suggestions(ctx, partial);
|
||||
let path_completer = crate::completion::path::PathCompleter;
|
||||
let path_results = path_completer.path_suggestions(partial);
|
||||
let iter = path_results.into_iter().filter_map(|path_suggestion| {
|
||||
let path = path_suggestion.path;
|
||||
if path.is_dir() || is_executable(&path) {
|
||||
|
@ -1,13 +1,15 @@
|
||||
use crate::completion::{Context, Suggestion};
|
||||
use crate::completion::{Completer, Context, Suggestion};
|
||||
use crate::context;
|
||||
|
||||
pub struct Completer;
|
||||
pub struct FlagCompleter {
|
||||
pub(crate) cmd: String,
|
||||
}
|
||||
|
||||
impl Completer {
|
||||
pub fn complete(&self, ctx: &Context<'_>, cmd: String, partial: &str) -> Vec<Suggestion> {
|
||||
impl Completer for FlagCompleter {
|
||||
fn complete(&self, ctx: &Context<'_>, partial: &str) -> Vec<Suggestion> {
|
||||
let context: &context::Context = ctx.as_ref();
|
||||
|
||||
if let Some(cmd) = context.registry.get_command(&cmd) {
|
||||
if let Some(cmd) = context.registry.get_command(&self.cmd) {
|
||||
let sig = cmd.signature();
|
||||
let mut suggestions = Vec::new();
|
||||
for (name, (named_type, _desc)) in sig.named.iter() {
|
||||
|
@ -3,8 +3,6 @@ pub(crate) mod engine;
|
||||
pub(crate) mod flag;
|
||||
pub(crate) mod path;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
|
||||
use crate::context;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
@ -28,10 +26,5 @@ impl<'a> AsRef<context::Context> for Context<'a> {
|
||||
}
|
||||
|
||||
pub trait Completer {
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
ctx: &Context<'_>,
|
||||
) -> Result<(usize, Vec<Suggestion>), ShellError>;
|
||||
fn complete(&self, ctx: &Context<'_>, partial: &str) -> Vec<Suggestion>;
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::completion::{Context, Suggestion};
|
||||
use crate::completion::{Completer, Context, Suggestion};
|
||||
|
||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
||||
|
||||
pub struct Completer;
|
||||
pub struct PathCompleter;
|
||||
|
||||
pub struct PathSuggestion {
|
||||
pub(crate) path: PathBuf,
|
||||
pub(crate) suggestion: Suggestion,
|
||||
}
|
||||
|
||||
impl Completer {
|
||||
pub fn path_suggestions(&self, _ctx: &Context<'_>, partial: &str) -> Vec<PathSuggestion> {
|
||||
impl PathCompleter {
|
||||
pub fn path_suggestions(&self, partial: &str) -> Vec<PathSuggestion> {
|
||||
let expanded = nu_parser::expand_ndots(partial);
|
||||
let expanded = expanded.as_ref();
|
||||
|
||||
@ -71,3 +71,12 @@ impl Completer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for PathCompleter {
|
||||
fn complete(&self, _ctx: &Context<'_>, partial: &str) -> Vec<Suggestion> {
|
||||
self.path_suggestions(partial)
|
||||
.into_iter()
|
||||
.map(|ps| ps.suggestion)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::completion::path::PathSuggestion;
|
||||
use crate::completion::{self, Suggestion};
|
||||
use crate::completion::command::CommandCompleter;
|
||||
use crate::completion::flag::FlagCompleter;
|
||||
use crate::completion::path::{PathCompleter, PathSuggestion};
|
||||
use crate::completion::{self, Completer, Suggestion};
|
||||
use crate::context;
|
||||
|
||||
pub(crate) struct NuCompleter {}
|
||||
@ -13,7 +15,7 @@ impl NuCompleter {
|
||||
pos: usize,
|
||||
context: &completion::Context,
|
||||
) -> (usize, Vec<Suggestion>) {
|
||||
use crate::completion::engine::LocationType;
|
||||
use completion::engine::LocationType;
|
||||
|
||||
let nu_context: &context::Context = context.as_ref();
|
||||
let lite_block = match nu_parser::lite_parse(line, 0) {
|
||||
@ -23,7 +25,7 @@ impl NuCompleter {
|
||||
|
||||
let locations = lite_block
|
||||
.map(|block| nu_parser::classify_block(&block, &nu_context.registry))
|
||||
.map(|block| crate::completion::engine::completion_location(line, &block.block, pos))
|
||||
.map(|block| completion::engine::completion_location(line, &block.block, pos))
|
||||
.unwrap_or_default();
|
||||
|
||||
if locations.is_empty() {
|
||||
@ -36,17 +38,17 @@ impl NuCompleter {
|
||||
let partial = location.span.slice(line);
|
||||
match location.item {
|
||||
LocationType::Command => {
|
||||
let command_completer = crate::completion::command::Completer;
|
||||
let command_completer = CommandCompleter;
|
||||
command_completer.complete(context, partial)
|
||||
}
|
||||
|
||||
LocationType::Flag(cmd) => {
|
||||
let flag_completer = crate::completion::flag::Completer;
|
||||
flag_completer.complete(context, cmd, partial)
|
||||
let flag_completer = FlagCompleter { cmd };
|
||||
flag_completer.complete(context, partial)
|
||||
}
|
||||
|
||||
LocationType::Argument(cmd, _arg_name) => {
|
||||
let path_completer = crate::completion::path::Completer;
|
||||
let path_completer = PathCompleter;
|
||||
|
||||
const QUOTE_CHARS: &[char] = &['\'', '"', '`'];
|
||||
|
||||
@ -71,7 +73,7 @@ impl NuCompleter {
|
||||
partial
|
||||
};
|
||||
|
||||
let completed_paths = path_completer.path_suggestions(context, partial);
|
||||
let completed_paths = path_completer.path_suggestions(partial);
|
||||
match cmd.as_deref().unwrap_or("") {
|
||||
"cd" => select_directory_suggestions(completed_paths),
|
||||
_ => completed_paths,
|
||||
|
@ -6,7 +6,6 @@ use crate::commands::ls::LsArgs;
|
||||
use crate::commands::mkdir::MkdirArgs;
|
||||
use crate::commands::move_::mv::Arguments as MvArgs;
|
||||
use crate::commands::rm::RemoveArgs;
|
||||
use crate::completion;
|
||||
use crate::prelude::*;
|
||||
use crate::shell::shell::Shell;
|
||||
|
||||
@ -230,56 +229,3 @@ impl Shell for HelpShell {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl completion::Completer for HelpShell {
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
_ctx: &completion::Context<'_>,
|
||||
) -> Result<(usize, Vec<completion::Suggestion>), ShellError> {
|
||||
let mut possible_completion = vec![];
|
||||
let commands = self.commands();
|
||||
for cmd in commands {
|
||||
let Value { value, .. } = cmd;
|
||||
for desc in value.data_descriptors() {
|
||||
possible_completion.push(desc);
|
||||
}
|
||||
}
|
||||
|
||||
let line_chars: Vec<_> = line.chars().collect();
|
||||
let mut replace_pos = pos;
|
||||
while replace_pos > 0 {
|
||||
if line_chars[replace_pos - 1] == ' ' {
|
||||
break;
|
||||
}
|
||||
replace_pos -= 1;
|
||||
}
|
||||
|
||||
let mut completions = vec![];
|
||||
for command in possible_completion.iter() {
|
||||
let mut pos = replace_pos;
|
||||
let mut matched = true;
|
||||
if pos < line_chars.len() {
|
||||
for chr in command.chars() {
|
||||
if line_chars[pos] != chr {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
pos += 1;
|
||||
if pos == line_chars.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matched {
|
||||
completions.push(completion::Suggestion {
|
||||
display: command.to_string(),
|
||||
replacement: command.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok((replace_pos, completions))
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ use crate::commands::ls::LsArgs;
|
||||
use crate::commands::mkdir::MkdirArgs;
|
||||
use crate::commands::move_::mv::Arguments as MvArgs;
|
||||
use crate::commands::rm::RemoveArgs;
|
||||
use crate::completion;
|
||||
use crate::prelude::*;
|
||||
use crate::shell::shell::Shell;
|
||||
use crate::utils::ValueStructure;
|
||||
@ -76,6 +75,8 @@ impl ValueShell {
|
||||
shell_entries
|
||||
}
|
||||
|
||||
// TODO make use of this in the new completion engine
|
||||
#[allow(dead_code)]
|
||||
fn members(&self) -> VecDeque<Value> {
|
||||
self.members_under(Path::new("."))
|
||||
}
|
||||
@ -255,56 +256,3 @@ impl Shell for ValueShell {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl completion::Completer for ValueShell {
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
_ctx: &completion::Context<'_>,
|
||||
) -> Result<(usize, Vec<completion::Suggestion>), ShellError> {
|
||||
let mut possible_completion = vec![];
|
||||
let members = self.members();
|
||||
for member in members {
|
||||
let Value { value, .. } = member;
|
||||
for desc in value.data_descriptors() {
|
||||
possible_completion.push(desc);
|
||||
}
|
||||
}
|
||||
|
||||
let line_chars: Vec<_> = line.chars().collect();
|
||||
let mut replace_pos = pos;
|
||||
while replace_pos > 0 {
|
||||
if line_chars[replace_pos - 1] == ' ' {
|
||||
break;
|
||||
}
|
||||
replace_pos -= 1;
|
||||
}
|
||||
|
||||
let mut completions = vec![];
|
||||
for command in possible_completion.iter() {
|
||||
let mut pos = replace_pos;
|
||||
let mut matched = true;
|
||||
if pos < line_chars.len() {
|
||||
for chr in command.chars() {
|
||||
if line_chars[pos] != chr {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
pos += 1;
|
||||
if pos == line_chars.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matched {
|
||||
completions.push(completion::Suggestion {
|
||||
display: command.to_string(),
|
||||
replacement: command.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok((replace_pos, completions))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user