From b2c8e445aaade1435a5ec576d4e184b6b3e68cbb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 17 Sep 2024 15:43:53 -0500 Subject: [PATCH 1/2] feat(complete): Allow user to override bin/completer --- clap_complete/src/env/mod.rs | 37 ++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/clap_complete/src/env/mod.rs b/clap_complete/src/env/mod.rs index aa5a7964a77..47d7de7dab5 100644 --- a/clap_complete/src/env/mod.rs +++ b/clap_complete/src/env/mod.rs @@ -91,6 +91,8 @@ pub use shells::*; pub struct CompleteEnv<'s, F> { factory: F, var: &'static str, + bin: Option, + completer: Option, shells: Shells<'s>, } @@ -137,6 +139,8 @@ impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { Self { factory, var: "COMPLETE", + bin: None, + completer: None, shells: Shells::builtins(), } } @@ -147,6 +151,22 @@ impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { self } + /// Override the name of the binary to complete + /// + /// Default: `Command::get_bin_name` + pub fn bin(mut self, bin: impl Into) -> Self { + self.bin = Some(bin.into()); + self + } + + /// Override the binary to call to get completions + /// + /// Default: `Command::get_bin_name` + pub fn completer(mut self, completer: impl Into) -> Self { + self.completer = Some(completer.into()); + self + } + /// Override the shells supported for completions pub fn shells(mut self, shells: Shells<'s>) -> Self { self.shells = shells; @@ -230,10 +250,19 @@ impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { args.drain(0..escape_index); if args.is_empty() { let name = cmd.get_name(); - let bin = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name()); + let bin = self + .bin + .as_deref() + .or_else(|| cmd.get_bin_name()) + .unwrap_or_else(|| cmd.get_name()); + let completer = self + .completer + .as_deref() + .or_else(|| cmd.get_bin_name()) + .unwrap_or_else(|| cmd.get_name()); let mut buf = Vec::new(); - shell.write_registration(self.var, name, bin, bin, &mut buf)?; + shell.write_registration(self.var, name, bin, completer, &mut buf)?; std::io::stdout().write_all(&buf)?; } else { let mut buf = Vec::new(); @@ -297,8 +326,8 @@ pub trait EnvCompleter { /// /// - `var`: see [`CompleteEnv::var`] /// - `name`: an identifier to use in the script - /// - `bin`: the binary being completed - /// - `completer`: the command to run to generate completions + /// - `bin`: see [`CompleteEnv::bin`] + /// - `completer`: see [`CompleteEnv::completer`] /// /// **WARNING:** There are no stability guarantees between the call to /// [`EnvCompleter::write_complete`] that this generates and actually calling [`EnvCompleter::write_complete`]. From a7a8f93d6b2b5f4af8e2703b69bc41ca85a7e840 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 17 Sep 2024 15:47:46 -0500 Subject: [PATCH 2/2] fix(complete): Strip wrappers for running completer --- clap_complete/examples/exhaustive.rs | 5 ++++- clap_complete/src/env/mod.rs | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/clap_complete/examples/exhaustive.rs b/clap_complete/examples/exhaustive.rs index 7079ed14233..61ca89ff2ec 100644 --- a/clap_complete/examples/exhaustive.rs +++ b/clap_complete/examples/exhaustive.rs @@ -3,7 +3,10 @@ use clap_complete::{generate, Generator, Shell}; fn main() { #[cfg(feature = "unstable-dynamic")] - clap_complete::CompleteEnv::with_factory(cli).complete(); + clap_complete::CompleteEnv::with_factory(cli) + // Avoid tests snapshotting a path into `target/` + .completer("exhaustive") + .complete(); let matches = cli().get_matches(); if let Some(generator) = matches.get_one::("generate") { diff --git a/clap_complete/src/env/mod.rs b/clap_complete/src/env/mod.rs index 47d7de7dab5..23b5123b5fd 100644 --- a/clap_complete/src/env/mod.rs +++ b/clap_complete/src/env/mod.rs @@ -161,7 +161,7 @@ impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { /// Override the binary to call to get completions /// - /// Default: `Command::get_bin_name` + /// Default: `args_os()[0]` pub fn completer(mut self, completer: impl Into) -> Self { self.completer = Some(completer.into()); self @@ -242,6 +242,7 @@ impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { let mut cmd = (self.factory)(); cmd.build(); + let completer = args.remove(0); let escape_index = args .iter() .position(|a| *a == "--") @@ -255,14 +256,20 @@ impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { .as_deref() .or_else(|| cmd.get_bin_name()) .unwrap_or_else(|| cmd.get_name()); - let completer = self - .completer - .as_deref() - .or_else(|| cmd.get_bin_name()) - .unwrap_or_else(|| cmd.get_name()); + let completer = if let Some(completer) = self.completer.as_deref() { + completer.to_owned() + } else { + let mut completer = std::path::PathBuf::from(completer); + if let Some(current_dir) = current_dir.as_deref() { + if 1 < completer.components().count() { + completer = current_dir.join(completer); + } + } + completer.to_string_lossy().into_owned() + }; let mut buf = Vec::new(); - shell.write_registration(self.var, name, bin, completer, &mut buf)?; + shell.write_registration(self.var, name, bin, &completer, &mut buf)?; std::io::stdout().write_all(&buf)?; } else { let mut buf = Vec::new();