From 5291f978c28e05c165f2af6e8331ed4c24732e8b Mon Sep 17 00:00:00 2001 From: zc he Date: Sun, 2 Feb 2025 21:54:09 +0800 Subject: [PATCH] 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 --- .../src/completions/dotnu_completions.rs | 48 ++++++++++++------- crates/nu-cli/tests/completions/mod.rs | 24 ++++++++-- .../{sub_module => sub module}/sub.nu | 0 3 files changed, 52 insertions(+), 20 deletions(-) rename tests/fixtures/dotnu_completions/dir_module/{sub_module => sub module}/sub.nu (100%) diff --git a/crates/nu-cli/src/completions/dotnu_completions.rs b/crates/nu-cli/src/completions/dotnu_completions.rs index c626e8b1d2..f06b85f5ed 100644 --- a/crates/nu-cli/src/completions/dotnu_completions.rs +++ b/crates/nu-cli/src/completions/dotnu_completions.rs @@ -1,4 +1,5 @@ use crate::completions::{file_path_completion, Completer, CompletionOptions}; +use nu_path::expand_tilde; use nu_protocol::{ engine::{Stack, StateWorkingSet}, Span, @@ -28,7 +29,10 @@ impl Completer for DotNuCompletion { _pos: usize, options: &CompletionOptions, ) -> Vec { - 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 = vec![]; // 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 .as_list() .into_iter() - .flat_map(|it| { - it.iter().map(|x| { - x.to_path() - .expect("internal error: failed to convert lib path") - }) - }) + .flat_map(|it| it.iter().filter_map(|x| x.to_path().ok())) + .map(expand_tilde) .collect() }) .unwrap_or_default(); @@ -78,13 +78,12 @@ impl Completer for DotNuCompletion { // Fetch the files filtering the ones that ends with .nu // and transform them into suggestions - let completions = file_path_completion( span, partial, &search_dirs .iter() - .map(|d| d.to_str().unwrap_or_default()) + .filter_map(|d| d.to_str()) .collect::>(), options, working_set.permanent_state, @@ -93,20 +92,35 @@ impl Completer for DotNuCompletion { completions .into_iter() // 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| { - 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 - let span_offset = if base_dir == "." { - 0 - } else { - base_dir.len() + 1 - } + prefix.iter().take_while(|c| **c == b'`').count(); + let mut span_offset = 0; + let mut value = x.path.to_string(); + // Complete only the last path component + if base_dir != "." { + 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 start = std::cmp::min(end, x.span.start - offset + span_offset); SemanticSuggestion { suggestion: Suggestion { - value: x.path, + value, style: x.style, span: reedline::Span { start, end }, append_whitespace, diff --git a/crates/nu-cli/tests/completions/mod.rs b/crates/nu-cli/tests/completions/mod.rs index d878d312d2..ab9152172e 100644 --- a/crates/nu-cli/tests/completions/mod.rs +++ b/crates/nu-cli/tests/completions/mod.rs @@ -275,12 +275,30 @@ fn dotnu_completions() { // Test nested nu script #[cfg(windows)] - let completion_str = "use .\\dir_module\\sub_module\\".to_string(); + let completion_str = "use `.\\dir_module\\".to_string(); #[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()); - 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![ "asdf.nu".into(), diff --git a/tests/fixtures/dotnu_completions/dir_module/sub_module/sub.nu b/tests/fixtures/dotnu_completions/dir_module/sub module/sub.nu similarity index 100% rename from tests/fixtures/dotnu_completions/dir_module/sub_module/sub.nu rename to tests/fixtures/dotnu_completions/dir_module/sub module/sub.nu