From 275dba82d599c89726466fcb231c05820ad60933 Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee Date: Tue, 31 Oct 2023 18:09:14 +0530 Subject: [PATCH] fix: preserve path when completing intermediate directory (#10831) - fixes #10766 # Description If the partial supplied to the completion function is shorter than the span, the cursor is in between the path, we are trying to complete an intermediate directory. In such a case we: - only suggest directory names - don't append the slash since it is already present - only complete the path till the component the cursor is on # User-Facing Changes Intermediate directories can be completed without erasing the rest of the path. # Tests + Formatting # After Submitting --- .../src/completions/completion_common.rs | 34 +++++++++++++++++++ .../src/completions/directory_completions.rs | 11 +++--- .../src/completions/file_completions.rs | 17 +++++++--- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/crates/nu-cli/src/completions/completion_common.rs b/crates/nu-cli/src/completions/completion_common.rs index 93629d74b1..ec1300dadb 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 7c936b5bb6..eca4344af1 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 3f4176b427..a3eadf2938 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(),