From 4af0cd65f109929b37c491428afe09ec496fe552 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 2 Oct 2024 09:54:31 -0500 Subject: [PATCH 1/3] fix(complete): Loosen requirements on Command factory --- clap_complete/src/env/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clap_complete/src/env/mod.rs b/clap_complete/src/env/mod.rs index 3e4524ad38b..ba67d55e19a 100644 --- a/clap_complete/src/env/mod.rs +++ b/clap_complete/src/env/mod.rs @@ -96,7 +96,7 @@ pub struct CompleteEnv<'s, F> { shells: Shells<'s>, } -impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { +impl<'s, F: Fn() -> clap::Command> CompleteEnv<'s, F> { /// Complete a [`clap::Command`] /// /// # Example @@ -174,7 +174,7 @@ impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { } } -impl<'s, F: FnOnce() -> clap::Command> CompleteEnv<'s, F> { +impl<'s, F: Fn() -> clap::Command> CompleteEnv<'s, F> { /// Process the completion request and exit /// /// **Warning:** `stdout` should not be written to before this has had a From 95e99ef1187733f5befb692a141f51669844021b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 2 Oct 2024 09:55:33 -0500 Subject: [PATCH 2/3] refactor(complete): Pull out shell lookup --- clap_complete/src/env/mod.rs | 47 ++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/clap_complete/src/env/mod.rs b/clap_complete/src/env/mod.rs index ba67d55e19a..6562dd38ba0 100644 --- a/clap_complete/src/env/mod.rs +++ b/clap_complete/src/env/mod.rs @@ -217,27 +217,7 @@ impl<'s, F: Fn() -> clap::Command> CompleteEnv<'s, F> { // completion logic. std::env::remove_var(self.var); - // Strip off the parent dir in case `$SHELL` was used - let name = std::path::Path::new(&name).file_stem().unwrap_or(&name); - // lossy won't match but this will delegate to unknown - // error - let name = name.to_string_lossy(); - - let shell = self.shells.completer(&name).ok_or_else(|| { - let shells = self - .shells - .names() - .enumerate() - .map(|(i, name)| { - let prefix = if i == 0 { "" } else { ", " }; - format!("{prefix}`{name}`") - }) - .collect::(); - std::io::Error::new( - std::io::ErrorKind::Other, - format!("unknown shell `{name}`, expected one of {shells}"), - ) - })?; + let shell = self.shell(std::path::Path::new(&name))?; let mut cmd = (self.factory)(); cmd.build(); @@ -279,6 +259,31 @@ impl<'s, F: Fn() -> clap::Command> CompleteEnv<'s, F> { Ok(true) } + + fn shell(&self, name: &std::path::Path) -> Result<&dyn EnvCompleter, std::io::Error> { + // Strip off the parent dir in case `$SHELL` was used + let name = name.file_stem().unwrap_or(name.as_os_str()); + // lossy won't match but this will delegate to unknown + // error + let name = name.to_string_lossy(); + + let shell = self.shells.completer(&name).ok_or_else(|| { + let shells = self + .shells + .names() + .enumerate() + .map(|(i, name)| { + let prefix = if i == 0 { "" } else { ", " }; + format!("{prefix}`{name}`") + }) + .collect::(); + std::io::Error::new( + std::io::ErrorKind::Other, + format!("unknown shell `{name}`, expected one of {shells}"), + ) + })?; + Ok(shell) + } } /// Collection of shell-specific completers From e90b2daf5ecce520ad3c8c565d5d1daa7e7d0738 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 2 Oct 2024 10:10:58 -0500 Subject: [PATCH 3/3] refactor(complete): Pull our registration writing --- clap_complete/src/env/mod.rs | 51 ++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/clap_complete/src/env/mod.rs b/clap_complete/src/env/mod.rs index 6562dd38ba0..ea33d8d68a3 100644 --- a/clap_complete/src/env/mod.rs +++ b/clap_complete/src/env/mod.rs @@ -230,26 +230,8 @@ impl<'s, F: Fn() -> clap::Command> CompleteEnv<'s, F> { .unwrap_or(args.len()); args.drain(0..escape_index); if args.is_empty() { - let name = cmd.get_name(); - let bin = self - .bin - .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 { - 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)?; + self.write_registration(&cmd, current_dir, shell, completer, &mut buf)?; std::io::stdout().write_all(&buf)?; } else { let mut buf = Vec::new(); @@ -284,6 +266,37 @@ impl<'s, F: Fn() -> clap::Command> CompleteEnv<'s, F> { })?; Ok(shell) } + + fn write_registration( + &self, + cmd: &clap::Command, + current_dir: Option<&std::path::Path>, + shell: &dyn EnvCompleter, + completer: OsString, + buf: &mut dyn std::io::Write, + ) -> Result<(), std::io::Error> { + let name = cmd.get_name(); + let bin = self + .bin + .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 { + if 1 < completer.components().count() { + completer = current_dir.join(completer); + } + } + completer.to_string_lossy().into_owned() + }; + + shell.write_registration(self.var, name, bin, &completer, buf)?; + + Ok(()) + } } /// Collection of shell-specific completers