diff --git a/src/bin/argc/main.rs b/src/bin/argc/main.rs index 04abc255..5117227b 100644 --- a/src/bin/argc/main.rs +++ b/src/bin/argc/main.rs @@ -160,11 +160,13 @@ fn run_compgen(mut args: Vec) -> Option<()> { let shell: Shell = args.get(2).and_then(|v| v.parse().ok())?; if args[3].is_empty() { if args[4] == "argc" { - if let Some((_, script_file_)) = get_script_path(true) { - args[3] = script_file_.to_string_lossy().to_string(); + if let Some((_, script_file)) = get_script_path(true) { + args[3] = script_file.to_string_lossy().to_string(); } - } else if let Ok(script_file_) = which(&args[4]) { - args[3] = script_file_.to_string_lossy().to_string(); + } else if let Some(script_file) = search_completion_script(&mut args) { + args[3] = script_file.to_string_lossy().to_string(); + } else if let Ok(script_file) = which(&args[4]) { + args[3] = script_file.to_string_lossy().to_string(); } else { return None; } diff --git a/src/bin/argc/utils.rs b/src/bin/argc/utils.rs index d3a05a39..7514611f 100644 --- a/src/bin/argc/utils.rs +++ b/src/bin/argc/utils.rs @@ -85,3 +85,67 @@ pub fn candidate_script_names() -> Vec { names.extend(ARGC_SCRIPT_NAMES.into_iter().map(|v| v.to_string())); names } + +pub fn search_completion_script(args: &mut Vec) -> Option { + let search_paths = std::env::var("ARGC_COMPLETIONS_PATH").ok()?; + let cmd = Path::new(&args[4]) + .file_stem() + .and_then(|v| v.to_str())? + .to_string(); + let subcmd = if args.len() >= 7 + && args[5] + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-')) + { + Some(args[5].clone()) + } else { + None + }; + let mut handlers = vec![]; + for path in search_paths + .split(':') + .filter(|v| !v.is_empty() && *v != ":") + { + let path = path.to_string(); + let cmd = cmd.to_string(); + let subcmd = subcmd.clone(); + let handler = std::thread::spawn(move || { + if let Some(subcmd) = subcmd { + let mut search_path = PathBuf::from(path.clone()); + search_path.push(cmd.clone()); + search_path.push(format!("{subcmd}.sh")); + if search_path.exists() { + return Some((search_path, true)); + } + } + let mut search_path = PathBuf::from(path); + search_path.push(format!("{cmd}.sh")); + if search_path.exists() { + return Some((search_path, false)); + } + None + }); + handlers.push(handler); + } + let mut subcmd_script_path = None; + let mut cmd_script_path = None; + for handler in handlers { + if let Ok(Some((path, is_subcmd))) = handler.join() { + if is_subcmd { + subcmd_script_path = Some(path); + break; + } else if cmd_script_path.is_none() { + cmd_script_path = Some(path); + } + } + } + if let (Some(path), Some(subcmd)) = (subcmd_script_path, subcmd) { + args.remove(4); + args[4] = format!("{cmd}-{subcmd}"); + return Some(path); + } + if let Some(path) = cmd_script_path { + return Some(path); + } + None +}