fix(complete): Preserve absolute paths

This commit is contained in:
Ed Page 2024-08-21 11:58:11 -05:00
parent ebb1302e90
commit 78157e6ce6
3 changed files with 24 additions and 22 deletions

1
Cargo.lock generated
View file

@ -482,7 +482,6 @@ dependencies = [
"completest", "completest",
"completest-pty", "completest-pty",
"is_executable", "is_executable",
"pathdiff",
"shlex", "shlex",
"snapbox", "snapbox",
"trycmd", "trycmd",

View file

@ -37,7 +37,6 @@ bench = false
clap = { path = "../", version = "4.5.15", default-features = false, features = ["std"] } clap = { path = "../", version = "4.5.15", default-features = false, features = ["std"] }
clap_lex = { path = "../clap_lex", version = "0.7.0", optional = true } clap_lex = { path = "../clap_lex", version = "0.7.0", optional = true }
is_executable = { version = "1.0.1", optional = true } is_executable = { version = "1.0.1", optional = true }
pathdiff = { version = "0.2.1", optional = true }
shlex = { version = "1.1.0", optional = true } shlex = { version = "1.1.0", optional = true }
unicode-xid = { version = "0.2.2", optional = true } unicode-xid = { version = "0.2.2", optional = true }
@ -57,8 +56,8 @@ required-features = ["unstable-dynamic", "unstable-command"]
[features] [features]
default = [] default = []
unstable-doc = ["unstable-dynamic", "unstable-command"] # for docs.rs unstable-doc = ["unstable-dynamic", "unstable-command"] # for docs.rs
unstable-dynamic = ["dep:clap_lex", "dep:shlex", "dep:is_executable", "dep:pathdiff", "clap/unstable-ext"] unstable-dynamic = ["dep:clap_lex", "dep:shlex", "dep:is_executable", "clap/unstable-ext"]
unstable-command = ["unstable-dynamic", "dep:unicode-xid", "clap/derive", "dep:is_executable", "dep:pathdiff", "clap/unstable-ext"] unstable-command = ["unstable-dynamic", "dep:unicode-xid", "clap/derive", "dep:is_executable", "clap/unstable-ext"]
debug = ["clap/debug"] debug = ["clap/debug"]
[lints] [lints]

View file

@ -348,6 +348,12 @@ fn complete_path(
) -> Vec<CompletionCandidate> { ) -> Vec<CompletionCandidate> {
let mut completions = Vec::new(); let mut completions = Vec::new();
let value_path = std::path::Path::new(value_os);
let (prefix, current) = split_file_name(value_path);
let current = current.to_string_lossy();
let search_root = if prefix.is_absolute() {
prefix.to_owned()
} else {
let current_dir = match current_dir { let current_dir = match current_dir {
Some(current_dir) => current_dir, Some(current_dir) => current_dir,
None => { None => {
@ -355,31 +361,29 @@ fn complete_path(
return Vec::new(); return Vec::new();
} }
}; };
let absolute = current_dir.join(value_os); current_dir.join(prefix)
let (root, prefix) = split_file_name(&absolute); };
let prefix = prefix.to_string_lossy(); debug!("complete_path: search_root={search_root:?}, prefix={prefix:?}");
debug!("complete_path: root={root:?}, prefix={prefix:?}");
for entry in std::fs::read_dir(&root) for entry in std::fs::read_dir(&search_root)
.ok() .ok()
.into_iter() .into_iter()
.flatten() .flatten()
.filter_map(Result::ok) .filter_map(Result::ok)
{ {
let raw_file_name = entry.file_name(); let raw_file_name = entry.file_name();
if !raw_file_name.starts_with(&prefix) { if !raw_file_name.starts_with(&current) {
continue; continue;
} }
if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) { if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) {
let path = entry.path(); let mut suggestion = prefix.join(raw_file_name);
let mut suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
suggestion.push(""); // Ensure trailing `/` suggestion.push(""); // Ensure trailing `/`
completions.push(CompletionCandidate::new(suggestion.as_os_str().to_owned())); completions.push(CompletionCandidate::new(suggestion.as_os_str().to_owned()));
} else { } else {
let path = entry.path(); let path = entry.path();
if is_wanted(&path) { if is_wanted(&path) {
let suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path); let suggestion = prefix.join(raw_file_name);
completions.push(CompletionCandidate::new(suggestion.as_os_str().to_owned())); completions.push(CompletionCandidate::new(suggestion.as_os_str().to_owned()));
} }
} }
@ -401,12 +405,12 @@ fn split_file_name(path: &std::path::Path) -> (&std::path::Path, &OsStr) {
} }
fn path_has_name(path: &std::path::Path) -> bool { fn path_has_name(path: &std::path::Path) -> bool {
let path = path.as_os_str().as_encoded_bytes(); let path_bytes = path.as_os_str().as_encoded_bytes();
let Some(trailing) = path.last() else { let Some(trailing) = path_bytes.last() else {
return false; return false;
}; };
let trailing = *trailing as char; let trailing = *trailing as char;
!std::path::is_separator(trailing) !std::path::is_separator(trailing) && path.file_name().is_some()
} }
fn complete_custom_arg_value( fn complete_custom_arg_value(