From 188d33b3060cc2cf531f16fb88e58a57cc07d073 Mon Sep 17 00:00:00 2001 From: Jason Gedge Date: Sun, 30 Aug 2020 22:28:09 -0400 Subject: [PATCH] Add a custom path completer to nushell. (#2463) Previously, we used rustyline's filename completer. This allowed us to make progress on the completion engine without building all the parts at once. We now need our own filename completer to make progress. The primary driver to having our own filename completer is that it can better integrate with our path constructs. For example, if we have > ls .../ we want to show a list of suggestions that includes all files two directories up from the current working directory. The least jarring experience to a user would be to maintain the three dots. The easiest way for us to do this is by building our own completer and path constructs. --- crates/nu-cli/src/completion/command.rs | 2 +- crates/nu-cli/src/completion/path.rs | 52 +++++++++++++++++-------- crates/nu-cli/src/shell/completer.rs | 4 +- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/crates/nu-cli/src/completion/command.rs b/crates/nu-cli/src/completion/command.rs index 077baee05..66457ac9f 100644 --- a/crates/nu-cli/src/completion/command.rs +++ b/crates/nu-cli/src/completion/command.rs @@ -33,7 +33,7 @@ impl Completer { .collect(); if partial != "" { - let path_completer = crate::completion::path::Completer::new(); + let path_completer = crate::completion::path::Completer; let path_results = path_completer.complete(ctx, partial); suggestions.extend(path_results.into_iter().filter(|suggestion| { let path = Path::new(&suggestion.replacement); diff --git a/crates/nu-cli/src/completion/path.rs b/crates/nu-cli/src/completion/path.rs index 88c3fc4d8..85acc8308 100644 --- a/crates/nu-cli/src/completion/path.rs +++ b/crates/nu-cli/src/completion/path.rs @@ -1,27 +1,47 @@ -use rustyline::completion::FilenameCompleter; +use std::path::Path; use crate::completion::{Context, Suggestion}; -pub struct Completer { - inner: FilenameCompleter, -} +const SEP: char = std::path::MAIN_SEPARATOR; + +pub struct Completer; impl Completer { - pub fn new() -> Completer { - Completer { - inner: FilenameCompleter::new(), - } - } - pub fn complete(&self, _ctx: &Context<'_>, partial: &str) -> Vec { let expanded = nu_parser::expand_ndots(partial); + let expanded = expanded.as_ref(); - if let Ok((_pos, pairs)) = self.inner.complete_path(&expanded, expanded.len()) { - pairs - .into_iter() - .map(|v| Suggestion { - replacement: v.replacement, - display: v.display, + let (base_dir_name, partial) = match expanded.rfind(SEP) { + Some(pos) => expanded.split_at(pos + SEP.len_utf8()), + None => ("", expanded), + }; + + let base_dir = if base_dir_name == "" { + Path::new(".") + } else { + Path::new(base_dir_name) + }; + + if let Ok(result) = base_dir.read_dir() { + result + .filter_map(|entry| { + entry.ok().and_then(|entry| { + let mut file_name = entry.file_name().to_string_lossy().into_owned(); + if file_name.starts_with(partial) { + let mut path = format!("{}{}", base_dir_name, file_name); + if entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) { + path.push(std::path::MAIN_SEPARATOR); + file_name.push(std::path::MAIN_SEPARATOR); + } + + Some(Suggestion { + replacement: path, + display: file_name, + }) + } else { + None + } + }) }) .collect() } else { diff --git a/crates/nu-cli/src/shell/completer.rs b/crates/nu-cli/src/shell/completer.rs index 46fda9447..2e107a0f2 100644 --- a/crates/nu-cli/src/shell/completer.rs +++ b/crates/nu-cli/src/shell/completer.rs @@ -41,12 +41,12 @@ impl NuCompleter { } LocationType::Flag(cmd) => { - let flag_completer = crate::completion::flag::Completer {}; + let flag_completer = crate::completion::flag::Completer; flag_completer.complete(context, cmd, partial) } LocationType::Argument(cmd, _arg_name) => { - let path_completer = crate::completion::path::Completer::new(); + let path_completer = crate::completion::path::Completer; let completed_paths = path_completer.complete(context, partial); match cmd.as_deref().unwrap_or("") { "cd" => select_directory_suggestions(completed_paths),