diff --git a/crates/nu-cli/src/completions/completion_common.rs b/crates/nu-cli/src/completions/completion_common.rs index 93629d74b..ec1300dad 100644 --- a/crates/nu-cli/src/completions/completion_common.rs +++ b/crates/nu-cli/src/completions/completion_common.rs @@ -1,5 +1,6 @@ use crate::completions::{matches, CompletionOptions}; use nu_path::home_dir; +use nu_protocol::{engine::StateWorkingSet, Span}; use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP}; fn complete_rec( @@ -160,3 +161,36 @@ pub fn escape_path(path: String, dir: bool) -> String { path } } + +pub struct AdjustView { + pub prefix: String, + pub span: Span, + pub readjusted: bool, +} + +pub fn adjust_if_intermediate( + prefix: &[u8], + working_set: &StateWorkingSet, + mut span: nu_protocol::Span, +) -> AdjustView { + let span_contents = String::from_utf8_lossy(working_set.get_span_contents(span)).to_string(); + let mut prefix = String::from_utf8_lossy(prefix).to_string(); + + // A difference of 1 because of the cursor's unicode code point in between. + // Using .chars().count() because unicode and Windows. + let readjusted = span_contents.chars().count() - prefix.chars().count() > 1; + if readjusted { + let remnant: String = span_contents + .chars() + .skip(prefix.chars().count() + 1) + .take_while(|&c| !is_separator(c)) + .collect(); + prefix.push_str(&remnant); + span = Span::new(span.start, span.start + prefix.chars().count() + 1); + } + AdjustView { + prefix, + span, + readjusted, + } +} diff --git a/crates/nu-cli/src/completions/directory_completions.rs b/crates/nu-cli/src/completions/directory_completions.rs index 7c936b5bb..eca4344af 100644 --- a/crates/nu-cli/src/completions/directory_completions.rs +++ b/crates/nu-cli/src/completions/directory_completions.rs @@ -1,4 +1,7 @@ -use crate::completions::{completion_common::complete_item, Completer, CompletionOptions, SortBy}; +use crate::completions::{ + completion_common::{adjust_if_intermediate, complete_item, AdjustView}, + Completer, CompletionOptions, SortBy, +}; use nu_protocol::{ engine::{EngineState, StateWorkingSet}, levenshtein_distance, Span, @@ -21,19 +24,19 @@ impl DirectoryCompletion { impl Completer for DirectoryCompletion { fn fetch( &mut self, - _: &StateWorkingSet, + working_set: &StateWorkingSet, prefix: Vec, span: Span, offset: usize, _: usize, options: &CompletionOptions, ) -> Vec { - let partial = String::from_utf8_lossy(&prefix).to_string(); + let AdjustView { prefix, span, .. } = adjust_if_intermediate(&prefix, working_set, span); // Filter only the folders let output: Vec<_> = directory_completion( span, - &partial, + &prefix, &self.engine_state.current_work_dir(), options, ) diff --git a/crates/nu-cli/src/completions/file_completions.rs b/crates/nu-cli/src/completions/file_completions.rs index 3f4176b42..a3eadf293 100644 --- a/crates/nu-cli/src/completions/file_completions.rs +++ b/crates/nu-cli/src/completions/file_completions.rs @@ -1,4 +1,7 @@ -use crate::completions::{completion_common::complete_item, Completer, CompletionOptions, SortBy}; +use crate::completions::{ + completion_common::{adjust_if_intermediate, complete_item, AdjustView}, + Completer, CompletionOptions, SortBy, +}; use nu_protocol::{ engine::{EngineState, StateWorkingSet}, levenshtein_distance, Span, @@ -21,15 +24,21 @@ impl FileCompletion { impl Completer for FileCompletion { fn fetch( &mut self, - _: &StateWorkingSet, + working_set: &StateWorkingSet, prefix: Vec, span: Span, offset: usize, _: usize, options: &CompletionOptions, ) -> Vec { - let prefix = String::from_utf8_lossy(&prefix).to_string(); - let output: Vec<_> = file_path_completion( + let AdjustView { + prefix, + span, + readjusted, + } = adjust_if_intermediate(&prefix, working_set, span); + + let output: Vec<_> = complete_item( + readjusted, span, &prefix, &self.engine_state.current_work_dir(),