fix(completion): dotnu_completion dir with space, expand tilde (#14983)

# Description

This PR closes #14956, only one known issue on that list remains.

# User-Facing Changes
# Tests + Formatting
new cases added
# After Submitting
This commit is contained in:
zc he 2025-02-02 21:54:09 +08:00 committed by GitHub
parent 63fa6a6df7
commit 5291f978c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 52 additions and 20 deletions

View File

@ -1,4 +1,5 @@
use crate::completions::{file_path_completion, Completer, CompletionOptions}; use crate::completions::{file_path_completion, Completer, CompletionOptions};
use nu_path::expand_tilde;
use nu_protocol::{ use nu_protocol::{
engine::{Stack, StateWorkingSet}, engine::{Stack, StateWorkingSet},
Span, Span,
@ -28,7 +29,10 @@ impl Completer for DotNuCompletion {
_pos: usize, _pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(prefix).replace('`', ""); let prefix_str = String::from_utf8_lossy(prefix);
let start_with_backquote = prefix_str.starts_with('`');
let end_with_backquote = prefix_str.ends_with('`');
let prefix_str = prefix_str.replace('`', "");
let mut search_dirs: Vec<PathBuf> = vec![]; let mut search_dirs: Vec<PathBuf> = vec![];
// If prefix_str is only a word we want to search in the current dir // If prefix_str is only a word we want to search in the current dir
@ -46,12 +50,8 @@ impl Completer for DotNuCompletion {
lib_dirs lib_dirs
.as_list() .as_list()
.into_iter() .into_iter()
.flat_map(|it| { .flat_map(|it| it.iter().filter_map(|x| x.to_path().ok()))
it.iter().map(|x| { .map(expand_tilde)
x.to_path()
.expect("internal error: failed to convert lib path")
})
})
.collect() .collect()
}) })
.unwrap_or_default(); .unwrap_or_default();
@ -78,13 +78,12 @@ impl Completer for DotNuCompletion {
// Fetch the files filtering the ones that ends with .nu // Fetch the files filtering the ones that ends with .nu
// and transform them into suggestions // and transform them into suggestions
let completions = file_path_completion( let completions = file_path_completion(
span, span,
partial, partial,
&search_dirs &search_dirs
.iter() .iter()
.map(|d| d.to_str().unwrap_or_default()) .filter_map(|d| d.to_str())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
options, options,
working_set.permanent_state, working_set.permanent_state,
@ -93,20 +92,35 @@ impl Completer for DotNuCompletion {
completions completions
.into_iter() .into_iter()
// Different base dir, so we list the .nu files or folders // Different base dir, so we list the .nu files or folders
.filter(|it| it.path.ends_with(".nu") || it.path.ends_with(SEP)) .filter(|it| {
// for paths with spaces in them
let path = it.path.trim_end_matches('`');
path.ends_with(".nu") || path.ends_with(SEP)
})
.map(|x| { .map(|x| {
let append_whitespace = x.path.ends_with(".nu"); let append_whitespace =
x.path.ends_with(".nu") && (!start_with_backquote || end_with_backquote);
// Re-calculate the span to replace // Re-calculate the span to replace
let span_offset = if base_dir == "." { let mut span_offset = 0;
0 let mut value = x.path.to_string();
} else { // Complete only the last path component
base_dir.len() + 1 if base_dir != "." {
} + prefix.iter().take_while(|c| **c == b'`').count(); span_offset = base_dir.len() + 1
}
// Retain only one '`'
if start_with_backquote {
value = value.trim_start_matches('`').to_string();
span_offset += 1;
}
// Add the backquote back
if end_with_backquote && !value.ends_with('`') {
value.push('`');
}
let end = x.span.end - offset; let end = x.span.end - offset;
let start = std::cmp::min(end, x.span.start - offset + span_offset); let start = std::cmp::min(end, x.span.start - offset + span_offset);
SemanticSuggestion { SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: x.path, value,
style: x.style, style: x.style,
span: reedline::Span { start, end }, span: reedline::Span { start, end },
append_whitespace, append_whitespace,

View File

@ -275,12 +275,30 @@ fn dotnu_completions() {
// Test nested nu script // Test nested nu script
#[cfg(windows)] #[cfg(windows)]
let completion_str = "use .\\dir_module\\sub_module\\".to_string(); let completion_str = "use `.\\dir_module\\".to_string();
#[cfg(not(windows))] #[cfg(not(windows))]
let completion_str = "use ./dir_module/sub_module/".to_string(); let completion_str = "use `./dir_module/".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
match_suggestions(&vec!["sub.nu".into()], &suggestions); match_suggestions(
&vec![
"mod.nu".into(),
#[cfg(windows)]
"sub module\\`".into(),
#[cfg(not(windows))]
"sub module/`".into(),
],
&suggestions,
);
// Test nested nu script, with ending '`'
#[cfg(windows)]
let completion_str = "use `.\\dir_module\\sub module\\`".to_string();
#[cfg(not(windows))]
let completion_str = "use `./dir_module/sub module/`".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
match_suggestions(&vec!["sub.nu`".into()], &suggestions);
let expected = vec![ let expected = vec![
"asdf.nu".into(), "asdf.nu".into(),