From 6b31a006b8d8d95619fcf542c341e4001c0fd5ca Mon Sep 17 00:00:00 2001 From: Jason Gedge Date: Fri, 24 Jul 2020 19:39:12 -0400 Subject: [PATCH] Refactor all completion logic into `NuCompleter` (#2252) * Refactor all completion logic into `NuCompleter` This is the next step to improving completions. Previously, completion logic was scattered about (`FilesystemShell`, `NuCompleter`, `Helper`, and `ShellManager`). By unifying the core logic into a central location, it will be easier to take the next steps in improving completion. * Update context.rs Co-authored-by: Jonathan Turner --- crates/nu-cli/src/cli.rs | 6 +- .../src/commands/classified/internal.rs | 5 +- crates/nu-cli/src/completion.rs | 18 +- crates/nu-cli/src/context.rs | 8 +- crates/nu-cli/src/shell/completer.rs | 389 +++++++++++------- crates/nu-cli/src/shell/filesystem_shell.rs | 109 +---- crates/nu-cli/src/shell/helper.rs | 21 +- crates/nu-cli/src/shell/shell.rs | 3 +- crates/nu-cli/src/shell/shell_manager.rs | 28 +- 9 files changed, 279 insertions(+), 308 deletions(-) diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 7cc4abe094..d8606c9817 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -7,6 +7,7 @@ use crate::context::Context; use crate::git::current_branch; use crate::path::canonicalize; use crate::prelude::*; +use crate::shell::completer::NuCompleter; use crate::shell::Helper; use crate::EnvironmentSyncer; use futures_codec::FramedRead; @@ -778,7 +779,10 @@ pub async fn cli( let cwd = context.shell_manager.path(); - rl.set_helper(Some(crate::shell::Helper::new(context.clone()))); + rl.set_helper(Some(crate::shell::Helper::new( + Box::new(::default()), + context.clone(), + ))); let colored_prompt = { if use_starship { diff --git a/crates/nu-cli/src/commands/classified/internal.rs b/crates/nu-cli/src/commands/classified/internal.rs index 4fdf56aa70..52ecb167c3 100644 --- a/crates/nu-cli/src/commands/classified/internal.rs +++ b/crates/nu-cli/src/commands/classified/internal.rs @@ -182,10 +182,7 @@ pub(crate) async fn run_internal_command( } CommandAction::EnterShell(location) => { context.shell_manager.insert_at_current(Box::new( - match FilesystemShell::with_location( - location, - context.registry().clone(), - ) { + match FilesystemShell::with_location(location) { Ok(v) => v, Err(err) => { return InputStream::one( diff --git a/crates/nu-cli/src/completion.rs b/crates/nu-cli/src/completion.rs index bde18576b0..0c5b279f41 100644 --- a/crates/nu-cli/src/completion.rs +++ b/crates/nu-cli/src/completion.rs @@ -1,16 +1,30 @@ use nu_errors::ShellError; +use crate::context; + #[derive(Debug, Eq, PartialEq)] pub struct Suggestion { pub display: String, pub replacement: String, } -pub struct Context<'a>(pub &'a rustyline::Context<'a>); +pub struct Context<'a>(&'a context::Context, &'a rustyline::Context<'a>); + +impl<'a> Context<'a> { + pub fn new(a: &'a context::Context, b: &'a rustyline::Context<'a>) -> Context<'a> { + Context(a, b) + } +} + +impl<'a> AsRef for Context<'a> { + fn as_ref(&self) -> &context::Context { + self.0 + } +} impl<'a> AsRef> for Context<'a> { fn as_ref(&self) -> &rustyline::Context<'a> { - self.0 + self.1 } } diff --git a/crates/nu-cli/src/context.rs b/crates/nu-cli/src/context.rs index 09c0741658..f7a51f623b 100644 --- a/crates/nu-cli/src/context.rs +++ b/crates/nu-cli/src/context.rs @@ -150,14 +150,14 @@ impl Context { #[cfg(windows)] { Ok(Context { - registry: registry.clone(), + registry, host: Arc::new(parking_lot::Mutex::new(Box::new( crate::env::host::BasicHost, ))), current_errors: Arc::new(Mutex::new(vec![])), ctrl_c: Arc::new(AtomicBool::new(false)), user_recently_used_autoenv_untrust: false, - shell_manager: ShellManager::basic(registry)?, + shell_manager: ShellManager::basic()?, windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())), raw_input: String::default(), }) @@ -166,14 +166,14 @@ impl Context { #[cfg(not(windows))] { Ok(Context { - registry: registry.clone(), + registry, host: Arc::new(parking_lot::Mutex::new(Box::new( crate::env::host::BasicHost, ))), current_errors: Arc::new(Mutex::new(vec![])), ctrl_c: Arc::new(AtomicBool::new(false)), user_recently_used_autoenv_untrust: false, - shell_manager: ShellManager::basic(registry)?, + shell_manager: ShellManager::basic()?, raw_input: String::default(), }) } diff --git a/crates/nu-cli/src/shell/completer.rs b/crates/nu-cli/src/shell/completer.rs index 20dccfb412..f17b7e1132 100644 --- a/crates/nu-cli/src/shell/completer.rs +++ b/crates/nu-cli/src/shell/completer.rs @@ -1,24 +1,24 @@ -use crate::context::CommandRegistry; - -use crate::data::config; -use crate::prelude::*; -use derive_new::new; -#[cfg(all(windows, feature = "ichwh"))] -use ichwh::IchwhError; -#[cfg(all(windows, feature = "ichwh"))] -use ichwh::IchwhResult; -use indexmap::set::IndexSet; -use rustyline::completion::{Completer, FilenameCompleter}; use std::fs::{read_dir, DirEntry}; + #[cfg(unix)] use std::os::unix::fs::PermissionsExt; -use std::path::PathBuf; -#[derive(new)] +use indexmap::set::IndexSet; +use nu_errors::ShellError; +use rustyline::completion::{Completer as _, FilenameCompleter}; +use rustyline::hint::{Hinter as _, HistoryHinter}; + +#[cfg(all(windows, feature = "ichwh"))] +use ichwh::{IchwhError, IchwhResult}; + +use crate::completion::{self, Completer}; +use crate::context; +use crate::data::config; +use crate::prelude::*; + pub(crate) struct NuCompleter { - pub file_completer: FilenameCompleter, - pub commands: CommandRegistry, - pub homedir: Option, + file_completer: FilenameCompleter, + hinter: HistoryHinter, } #[derive(PartialEq, Eq, Debug)] @@ -28,30 +28,33 @@ enum ReplacementLocation { } impl NuCompleter { - pub fn complete( + fn complete_internal( &self, line: &str, pos: usize, - context: &rustyline::Context, + context: &completion::Context, ) -> rustyline::Result<(usize, Vec)> { - let commands: Vec = self.commands.names(); - let line_chars: Vec<_> = line[..pos].chars().collect(); - let (replace_pos, replace_loc) = self.get_replace_pos(line, pos); - - let mut completions; + let (replace_pos, replace_loc) = get_replace_pos(line, pos); // See if we're a flag + let mut completions; if pos > 0 && replace_pos < line_chars.len() && line_chars[replace_pos] == '-' { if let Ok(lite_block) = nu_parser::lite_parse(line, 0) { - completions = - self.get_matching_arguments(&lite_block, &line_chars, line, replace_pos, pos); + completions = get_matching_arguments( + context.as_ref(), + &lite_block, + &line_chars, + line, + replace_pos, + pos, + ); } else { - completions = self.file_completer.complete(line, pos, context)?.1; + completions = self.file_completer.complete(line, pos, context.as_ref())?.1; } } else { - completions = self.file_completer.complete(line, pos, context)?.1; + completions = self.file_completer.complete(line, pos, context.as_ref())?.1; for completion in &mut completions { if completion.replacement.contains("\\ ") { @@ -70,7 +73,7 @@ impl NuCompleter { } } } - }; + } let complete_from_path = match config::config(Tag::unknown()) { Ok(conf) => match conf.get("complete_from_path") { @@ -83,9 +86,11 @@ impl NuCompleter { // Only complete executables or commands if the thing we're completing // is syntactically a command if replace_loc == ReplacementLocation::Command { + let context: &context::Context = context.as_ref(); + let commands: Vec = context.registry.names(); let mut all_executables: IndexSet<_> = commands.iter().map(|x| x.to_string()).collect(); if complete_from_path { - let path_executables = self.find_path_executables().unwrap_or_default(); + let path_executables = find_path_executables().unwrap_or_default(); for path_exe in path_executables { all_executables.insert(path_exe); } @@ -126,168 +131,248 @@ impl NuCompleter { Ok((replace_pos, completions)) } +} - fn get_replace_pos(&self, line: &str, pos: usize) -> (usize, ReplacementLocation) { - let line_chars: Vec<_> = line[..pos].chars().collect(); - let mut replace_pos = line_chars.len(); - let mut parsed_pos = false; - let mut loc = ReplacementLocation::Other; - if let Ok(lite_block) = nu_parser::lite_parse(line, 0) { - 'outer: for pipeline in lite_block.block.iter() { - for command in pipeline.commands.iter() { - let name_span = command.name.span; - if name_span.start() <= pos && name_span.end() >= pos { - replace_pos = name_span.start(); - parsed_pos = true; - loc = ReplacementLocation::Command; - break 'outer; - } +impl Completer for NuCompleter { + fn complete( + &self, + line: &str, + pos: usize, + context: &completion::Context, + ) -> Result<(usize, Vec), ShellError> { + let expanded = nu_parser::expand_ndots(&line); - for arg in command.args.iter() { - if arg.span.start() <= pos && arg.span.end() >= pos { - replace_pos = arg.span.start(); - parsed_pos = true; - break 'outer; - } - } - } + // Find the first not-matching char position, if there is one + let differ_pos = line + .chars() + .zip(expanded.chars()) + .enumerate() + .find(|(_index, (a, b))| a != b) + .map(|(differ_pos, _)| differ_pos); + + let pos = if let Some(differ_pos) = differ_pos { + if differ_pos < pos { + pos + (expanded.len() - line.len()) + } else { + pos } - } + } else { + pos + }; - if !parsed_pos { - // If the command won't parse, naively detect the completion start point - while replace_pos > 0 { - if line_chars[replace_pos - 1] == ' ' { - break; - } - replace_pos -= 1; - } - } - - (replace_pos, loc) + self.complete_internal(&expanded, pos, context) + .map_err(|e| ShellError::untagged_runtime_error(format!("{}", e))) + .map(requote) + .map(|(pos, completions)| { + ( + pos, + completions + .into_iter() + .map(|pair| completion::Suggestion { + display: pair.display, + replacement: pair.replacement, + }) + .collect(), + ) + }) } - fn get_matching_arguments( - &self, - lite_block: &nu_parser::LiteBlock, - line_chars: &[char], - line: &str, - replace_pos: usize, - pos: usize, - ) -> Vec { - let mut matching_arguments = vec![]; + fn hint(&self, line: &str, pos: usize, ctx: &completion::Context<'_>) -> Option { + self.hinter.hint(line, pos, &ctx.as_ref()) + } +} - let mut line_copy = line.to_string(); - let substring = line_chars[replace_pos..pos].iter().collect::(); - let replace_string = (replace_pos..pos).map(|_| " ").collect::(); - line_copy.replace_range(replace_pos..pos, &replace_string); +impl Default for NuCompleter { + fn default() -> NuCompleter { + NuCompleter { + file_completer: FilenameCompleter::new(), + hinter: HistoryHinter {}, + } + } +} - let result = nu_parser::classify_block(&lite_block, &self.commands); +fn get_matching_arguments( + context: &context::Context, + lite_block: &nu_parser::LiteBlock, + line_chars: &[char], + line: &str, + replace_pos: usize, + pos: usize, +) -> Vec { + let mut matching_arguments = vec![]; - for pipeline in &result.block.block { - for command in &pipeline.list { - if let nu_protocol::hir::ClassifiedCommand::Internal( - nu_protocol::hir::InternalCommand { args, .. }, - ) = command - { - if replace_pos >= args.span.start() && replace_pos <= args.span.end() { - if let Some(named) = &args.named { - for (name, _) in named.iter() { - let full_flag = format!("--{}", name); + let mut line_copy = line.to_string(); + let substring = line_chars[replace_pos..pos].iter().collect::(); + let replace_string = (replace_pos..pos).map(|_| " ").collect::(); + line_copy.replace_range(replace_pos..pos, &replace_string); - if full_flag.starts_with(&substring) { - matching_arguments.push(rustyline::completion::Pair { - display: full_flag.clone(), - replacement: full_flag, - }); - } + let result = nu_parser::classify_block(&lite_block, &context.registry); + + for pipeline in &result.block.block { + for command in &pipeline.list { + if let nu_protocol::hir::ClassifiedCommand::Internal( + nu_protocol::hir::InternalCommand { args, .. }, + ) = command + { + if replace_pos >= args.span.start() && replace_pos <= args.span.end() { + if let Some(named) = &args.named { + for (name, _) in named.iter() { + let full_flag = format!("--{}", name); + + if full_flag.starts_with(&substring) { + matching_arguments.push(rustyline::completion::Pair { + display: full_flag.clone(), + replacement: full_flag, + }); } } } } } } - - matching_arguments } - // These is_executable/pathext implementations are copied from ichwh and modified - // to not be async + matching_arguments +} - #[cfg(windows)] - fn pathext(&self) -> IchwhResult> { - Ok(std::env::var_os("PATHEXT") - .ok_or(IchwhError::PathextNotDefined)? - .to_string_lossy() - .split(';') - // Cut off the leading '.' character - .map(|ext| ext[1..].to_string()) - .collect::>()) - } +// These is_executable/pathext implementations are copied from ichwh and modified +// to not be async - #[cfg(windows)] - fn is_executable(&self, file: &DirEntry) -> bool { - if let Ok(metadata) = file.metadata() { - let file_type = metadata.file_type(); +#[cfg(windows)] +fn pathext() -> IchwhResult> { + Ok(std::env::var_os("PATHEXT") + .ok_or(IchwhError::PathextNotDefined)? + .to_string_lossy() + .split(';') + // Cut off the leading '.' character + .map(|ext| ext[1..].to_string()) + .collect::>()) +} - // If the entry isn't a file, it cannot be executable - if !(file_type.is_file() || file_type.is_symlink()) { - return false; - } +#[cfg(windows)] +fn is_executable(file: &DirEntry) -> bool { + if let Ok(metadata) = file.metadata() { + let file_type = metadata.file_type(); - if let Some(extension) = file.path().extension() { - if let Ok(exts) = self.pathext() { - exts.iter() - .any(|ext| extension.to_string_lossy().eq_ignore_ascii_case(ext)) - } else { - false - } + // If the entry isn't a file, it cannot be executable + if !(file_type.is_file() || file_type.is_symlink()) { + return false; + } + + if let Some(extension) = file.path().extension() { + if let Ok(exts) = pathext() { + exts.iter() + .any(|ext| extension.to_string_lossy().eq_ignore_ascii_case(ext)) } else { false } } else { false } - } - - #[cfg(target_arch = "wasm32")] - fn is_executable(&self, file: &DirEntry) -> bool { + } else { false } +} - #[cfg(unix)] - fn is_executable(&self, file: &DirEntry) -> bool { - let metadata = file.metadata(); +#[cfg(target_arch = "wasm32")] +fn is_executable(file: &DirEntry) -> bool { + false +} - if let Ok(metadata) = metadata { - let filetype = metadata.file_type(); - let permissions = metadata.permissions(); +#[cfg(unix)] +fn is_executable(file: &DirEntry) -> bool { + let metadata = file.metadata(); - // The file is executable if it is a directory or a symlink and the permissions are set for - // owner, group, or other - (filetype.is_file() || filetype.is_symlink()) && (permissions.mode() & 0o111 != 0) - } else { - false - } + if let Ok(metadata) = metadata { + let filetype = metadata.file_type(); + let permissions = metadata.permissions(); + + // The file is executable if it is a directory or a symlink and the permissions are set for + // owner, group, or other + (filetype.is_file() || filetype.is_symlink()) && (permissions.mode() & 0o111 != 0) + } else { + false } +} - fn find_path_executables(&self) -> Option> { - let path_var = std::env::var_os("PATH")?; - let paths: Vec<_> = std::env::split_paths(&path_var).collect(); +fn find_path_executables() -> Option> { + let path_var = std::env::var_os("PATH")?; + let paths: Vec<_> = std::env::split_paths(&path_var).collect(); - let mut executables: IndexSet = IndexSet::new(); - for path in paths { - if let Ok(mut contents) = read_dir(path) { - while let Some(Ok(item)) = contents.next() { - if self.is_executable(&item) { - if let Ok(name) = item.file_name().into_string() { - executables.insert(name); - } + let mut executables: IndexSet = IndexSet::new(); + for path in paths { + if let Ok(mut contents) = read_dir(path) { + while let Some(Ok(item)) = contents.next() { + if is_executable(&item) { + if let Ok(name) = item.file_name().into_string() { + executables.insert(name); } } } } - - Some(executables) } + + Some(executables) +} + +fn get_replace_pos(line: &str, pos: usize) -> (usize, ReplacementLocation) { + let line_chars: Vec<_> = line[..pos].chars().collect(); + let mut replace_pos = line_chars.len(); + let mut parsed_pos = false; + let mut loc = ReplacementLocation::Other; + if let Ok(lite_block) = nu_parser::lite_parse(line, 0) { + 'outer: for pipeline in lite_block.block.iter() { + for command in pipeline.commands.iter() { + let name_span = command.name.span; + if name_span.start() <= pos && name_span.end() >= pos { + replace_pos = name_span.start(); + parsed_pos = true; + loc = ReplacementLocation::Command; + break 'outer; + } + + for arg in command.args.iter() { + if arg.span.start() <= pos && arg.span.end() >= pos { + replace_pos = arg.span.start(); + parsed_pos = true; + break 'outer; + } + } + } + } + } + + if !parsed_pos { + // If the command won't parse, naively detect the completion start point + while replace_pos > 0 { + if line_chars[replace_pos - 1] == ' ' { + break; + } + replace_pos -= 1; + } + } + + (replace_pos, loc) +} + +fn requote( + items: (usize, Vec), +) -> (usize, Vec) { + let mut new_items = Vec::with_capacity(items.1.len()); + + for item in items.1 { + let unescaped = rustyline::completion::unescape(&item.replacement, Some('\\')); + let maybe_quote = if unescaped != item.replacement { + "\"" + } else { + "" + }; + + new_items.push(rustyline::completion::Pair { + display: item.display, + replacement: format!("{}{}{}", maybe_quote, unescaped, maybe_quote), + }); + } + + (items.0, new_items) } diff --git a/crates/nu-cli/src/shell/filesystem_shell.rs b/crates/nu-cli/src/shell/filesystem_shell.rs index 59f1e1a7d3..6b3e6bd06a 100644 --- a/crates/nu-cli/src/shell/filesystem_shell.rs +++ b/crates/nu-cli/src/shell/filesystem_shell.rs @@ -5,16 +5,12 @@ 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::data::dir_entry_dict; use crate::path::canonicalize; use crate::prelude::*; -use crate::shell::completer::NuCompleter; use crate::shell::shell::Shell; use crate::utils::FileStructure; -use rustyline::completion::FilenameCompleter; -use rustyline::hint::{Hinter, HistoryHinter}; use std::collections::HashMap; use std::io::{Error, ErrorKind}; use std::path::{Path, PathBuf}; @@ -28,15 +24,12 @@ use futures_util::TryStreamExt; use std::os::unix::fs::PermissionsExt; use nu_errors::ShellError; -use nu_parser::expand_ndots; use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue}; use nu_source::Tagged; pub struct FilesystemShell { pub(crate) path: String, pub(crate) last_path: String, - completer: NuCompleter, - hinter: HistoryHinter, } impl std::fmt::Debug for FilesystemShell { @@ -50,18 +43,12 @@ impl Clone for FilesystemShell { FilesystemShell { path: self.path.clone(), last_path: self.path.clone(), - completer: NuCompleter { - file_completer: FilenameCompleter::new(), - commands: self.completer.commands.clone(), - homedir: self.homedir(), - }, - hinter: HistoryHinter {}, } } } impl FilesystemShell { - pub fn basic(commands: CommandRegistry) -> Result { + pub fn basic() -> Result { let path = match std::env::current_dir() { Ok(path) => path, Err(_) => PathBuf::from("/"), @@ -70,33 +57,15 @@ impl FilesystemShell { Ok(FilesystemShell { path: path.to_string_lossy().to_string(), last_path: path.to_string_lossy().to_string(), - completer: NuCompleter { - file_completer: FilenameCompleter::new(), - commands, - homedir: homedir_if_possible(), - }, - hinter: HistoryHinter {}, }) } - pub fn with_location( - path: String, - commands: CommandRegistry, - ) -> Result { + pub fn with_location(path: String) -> Result { let path = canonicalize(std::env::current_dir()?, &path)?; let path = path.display().to_string(); let last_path = path.clone(); - Ok(FilesystemShell { - path, - last_path, - completer: NuCompleter { - file_completer: FilenameCompleter::new(), - commands, - homedir: homedir_if_possible(), - }, - hinter: HistoryHinter {}, - }) + Ok(FilesystemShell { path, last_path }) } } @@ -740,56 +709,6 @@ impl Shell for FilesystemShell { } } -impl completion::Completer for FilesystemShell { - fn complete( - &self, - line: &str, - pos: usize, - ctx: &completion::Context<'_>, - ) -> Result<(usize, Vec), ShellError> { - let expanded = expand_ndots(&line); - - // Find the first not-matching char position, if there is one - let differ_pos = line - .chars() - .zip(expanded.chars()) - .enumerate() - .find(|(_index, (a, b))| a != b) - .map(|(differ_pos, _)| differ_pos); - - let pos = if let Some(differ_pos) = differ_pos { - if differ_pos < pos { - pos + (expanded.len() - line.len()) - } else { - pos - } - } else { - pos - }; - - self.completer - .complete(&expanded, pos, ctx.as_ref()) - .map_err(|e| ShellError::untagged_runtime_error(format!("{}", e))) - .map(requote) - .map(|(pos, completions)| { - ( - pos, - completions - .into_iter() - .map(|pair| completion::Suggestion { - display: pair.display, - replacement: pair.replacement, - }) - .collect(), - ) - }) - } - - fn hint(&self, line: &str, pos: usize, ctx: &completion::Context<'_>) -> Option { - self.hinter.hint(line, pos, ctx.as_ref()) - } -} - struct TaggedPathBuf<'a>(&'a PathBuf, &'a Tag); fn move_file(from: TaggedPathBuf, to: TaggedPathBuf) -> Result<(), ShellError> { @@ -876,25 +795,3 @@ fn is_hidden_dir(dir: impl AsRef) -> bool { .unwrap_or(false) } } - -fn requote( - items: (usize, Vec), -) -> (usize, Vec) { - let mut new_items = Vec::with_capacity(items.1.len()); - - for item in items.1 { - let unescaped = rustyline::completion::unescape(&item.replacement, Some('\\')); - let maybe_quote = if unescaped != item.replacement { - "\"" - } else { - "" - }; - - new_items.push(rustyline::completion::Pair { - display: item.display, - replacement: format!("{}{}{}", maybe_quote, unescaped, maybe_quote), - }); - } - - (items.0, new_items) -} diff --git a/crates/nu-cli/src/shell/helper.rs b/crates/nu-cli/src/shell/helper.rs index a2fc7a3842..d1544894d6 100644 --- a/crates/nu-cli/src/shell/helper.rs +++ b/crates/nu-cli/src/shell/helper.rs @@ -1,4 +1,4 @@ -use crate::completion::{self, Completer as _}; +use crate::completion::{self, Completer}; use crate::context::Context; use crate::shell::palette::{DefaultPalette, Palette}; @@ -6,19 +6,19 @@ use ansi_term::{Color, Style}; use nu_parser::SignatureRegistry; use nu_protocol::hir::FlatShape; use nu_source::{Spanned, Tag, Tagged}; -use rustyline::completion::Completer; -use rustyline::highlight::Highlighter; use rustyline::hint::Hinter; use std::borrow::Cow::{self, Owned}; pub struct Helper { + completer: Box, context: Context, pub colored_prompt: String, } impl Helper { - pub(crate) fn new(context: Context) -> Helper { + pub(crate) fn new(completer: Box, context: Context) -> Helper { Helper { + completer, context, colored_prompt: String::new(), } @@ -35,7 +35,7 @@ impl rustyline::completion::Candidate for completion::Suggestion { } } -impl Completer for Helper { +impl rustyline::completion::Completer for Helper { type Candidate = completion::Suggestion; fn complete( @@ -44,9 +44,8 @@ impl Completer for Helper { pos: usize, ctx: &rustyline::Context<'_>, ) -> Result<(usize, Vec), rustyline::error::ReadlineError> { - let ctx = completion::Context(ctx); - self.context - .shell_manager + let ctx = completion::Context::new(&self.context, ctx); + self.completer .complete(line, pos, &ctx) .map_err(|_| rustyline::error::ReadlineError::Eof) } @@ -54,12 +53,12 @@ impl Completer for Helper { impl Hinter for Helper { fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { - let ctx = completion::Context(ctx); - self.context.shell_manager.hint(line, pos, &ctx) + let ctx = completion::Context::new(&self.context, ctx); + self.completer.hint(line, pos, &ctx) } } -impl Highlighter for Helper { +impl rustyline::highlight::Highlighter for Helper { fn highlight_prompt<'b, 's: 'b, 'p: 'b>( &'s self, prompt: &'p str, diff --git a/crates/nu-cli/src/shell/shell.rs b/crates/nu-cli/src/shell/shell.rs index 82dc3d6780..035be9e531 100644 --- a/crates/nu-cli/src/shell/shell.rs +++ b/crates/nu-cli/src/shell/shell.rs @@ -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::stream::OutputStream; @@ -14,7 +13,7 @@ use encoding_rs::Encoding; use nu_errors::ShellError; use std::path::PathBuf; -pub trait Shell: completion::Completer + std::fmt::Debug { +pub trait Shell: std::fmt::Debug { fn name(&self) -> String; fn homedir(&self) -> Option; diff --git a/crates/nu-cli/src/shell/shell_manager.rs b/crates/nu-cli/src/shell/shell_manager.rs index c5c29bb852..e3925d0970 100644 --- a/crates/nu-cli/src/shell/shell_manager.rs +++ b/crates/nu-cli/src/shell/shell_manager.rs @@ -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::{self, Completer}; use crate::prelude::*; use crate::shell::filesystem_shell::FilesystemShell; use crate::shell::shell::Shell; @@ -27,12 +26,10 @@ pub struct ShellManager { } impl ShellManager { - pub fn basic(commands: CommandRegistry) -> Result> { + pub fn basic() -> Result> { Ok(ShellManager { current_shell: Arc::new(AtomicUsize::new(0)), - shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic( - commands, - )?)])), + shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic()?)])), }) } @@ -181,24 +178,3 @@ impl ShellManager { shells[self.current_shell()].mv(args, name, &path) } } - -impl Completer for ShellManager { - fn complete( - &self, - line: &str, - pos: usize, - ctx: &completion::Context<'_>, - ) -> Result<(usize, Vec), ShellError> { - self.shells.lock()[self.current_shell()].complete(line, pos, ctx) - } - - fn hint( - &self, - line: &str, - pos: usize, - ctx: &completion::Context<'_>, - //context: ExpandContext, - ) -> Option { - self.shells.lock()[self.current_shell()].hint(line, pos, ctx) - } -}