fix(completion): more quoting for file_completion/directory_completion (#15299)

# Description

Found inconsistent behaviors of `directory_completion` and
`file_completion`, https://github.com/nushell/nushell/issues/13951

https://github.com/nushell/reedline/pull/886

Also there're failing cases with such file names/dir names `foo(`,
`foo{`, `foo[`.
I think it doesn't harm to be more conservative at adding quotes, even
if it might be unnecessary for paired names like `foo{}`.

# User-Facing Changes

# Tests + Formatting

Adjusted

# After Submitting
This commit is contained in:
zc he 2025-03-15 22:17:59 +08:00 committed by GitHub
parent 8f634f4140
commit 74f62305b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 43 additions and 19 deletions

View File

@ -276,7 +276,7 @@ pub fn complete_item(
}); });
FileSuggestion { FileSuggestion {
span, span,
path: escape_path(path, want_directory), path: escape_path(path),
style, style,
is_dir, is_dir,
} }
@ -285,32 +285,32 @@ pub fn complete_item(
} }
// Fix files or folders with quotes or hashes // Fix files or folders with quotes or hashes
pub fn escape_path(path: String, dir: bool) -> String { pub fn escape_path(path: String) -> String {
// make glob pattern have the highest priority. // make glob pattern have the highest priority.
if nu_glob::is_glob(path.as_str()) { if nu_glob::is_glob(path.as_str()) || path.contains('`') {
// expand home `~` for https://github.com/nushell/nushell/issues/13905
let pathbuf = nu_path::expand_tilde(path); let pathbuf = nu_path::expand_tilde(path);
let path = pathbuf.to_string_lossy(); let path = pathbuf.to_string_lossy();
return if path.contains('\'') { if path.contains('\'') {
// decide to use double quote, also need to escape `"` in path // decide to use double quotes
// or else users can't do anything with completed path either. // Path as Debug will do the escaping for `"`, `\`
format!("\"{}\"", path.replace('"', r#"\""#)) format!("{:?}", path)
} else { } else {
format!("'{path}'") format!("'{path}'")
};
} }
} else {
let filename_contaminated = !dir && path.contains(['\'', '"', ' ', '#', '(', ')']); let contaminated =
let dirname_contaminated = dir && path.contains(['\'', '"', ' ', '#']); path.contains(['\'', '"', ' ', '#', '(', ')', '{', '}', '[', ']', '|', ';']);
let maybe_flag = path.starts_with('-'); let maybe_flag = path.starts_with('-');
let maybe_variable = path.starts_with('$'); let maybe_variable = path.starts_with('$');
let maybe_number = path.parse::<f64>().is_ok(); let maybe_number = path.parse::<f64>().is_ok();
if filename_contaminated || dirname_contaminated || maybe_flag || maybe_variable || maybe_number if contaminated || maybe_flag || maybe_variable || maybe_number {
{
format!("`{path}`") format!("`{path}`")
} else { } else {
path path
} }
} }
}
pub struct AdjustView { pub struct AdjustView {
pub prefix: String, pub prefix: String,

View File

@ -896,6 +896,7 @@ fn partial_completions() {
folder(dir.join("partial-a")), folder(dir.join("partial-a")),
folder(dir.join("partial-b")), folder(dir.join("partial-b")),
folder(dir.join("partial-c")), folder(dir.join("partial-c")),
format!("`{}`", folder(dir.join("partial-d("))),
]; ];
// Match the results // Match the results
@ -938,6 +939,7 @@ fn partial_completions() {
file(dir.join("partial-b").join("hello_b")), file(dir.join("partial-b").join("hello_b")),
file(dir.join("partial-b").join("hi_b")), file(dir.join("partial-b").join("hi_b")),
file(dir.join("partial-c").join("hello_c")), file(dir.join("partial-c").join("hello_c")),
format!("`{}`", file(dir.join("partial-d(").join(".gitkeep"))),
]; ];
// Match the results // Match the results
@ -985,6 +987,15 @@ fn partial_completions() {
.join("final_partial") .join("final_partial")
.join("somefile"), .join("somefile"),
), ),
format!(
"`{}`",
file(
dir.join("partial-d(")
.join("..")
.join("final_partial")
.join("somefile"),
)
),
]; ];
// Match the results // Match the results
@ -1065,6 +1076,16 @@ fn partial_completion_with_dot_expansions() {
.join("final_partial") .join("final_partial")
.join("somefile"), .join("somefile"),
), ),
format!(
"`{}`",
file(
dir.join("partial-d(")
.join("...")
.join("partial_completions")
.join("final_partial")
.join("somefile"),
)
),
]; ];
// Match the results // Match the results
@ -1389,6 +1410,9 @@ fn file_completion_quoted() {
"`-inf`", "`-inf`",
"`4.2`", "`4.2`",
"\'[a] bc.txt\'", "\'[a] bc.txt\'",
"`curly-bracket_{.txt`",
"`semicolon_;.txt`",
"'square-bracket_[.txt'",
"`te st.txt`", "`te st.txt`",
"`te#st.txt`", "`te#st.txt`",
"`te'st.txt`", "`te'st.txt`",

View File

View File

View File

View File