diff --git a/clap_complete/src/dynamic/shells/mod.rs b/clap_complete/src/dynamic/shells/mod.rs index 6cfed97ed5e..ecac339d228 100644 --- a/clap_complete/src/dynamic/shells/mod.rs +++ b/clap_complete/src/dynamic/shells/mod.rs @@ -3,10 +3,12 @@ mod bash; mod fish; mod shell; +mod powershell; pub use bash::*; pub use fish::*; pub use shell::*; +pub use powershell::*; use std::ffi::OsString; use std::io::Write as _; diff --git a/clap_complete/src/dynamic/shells/powershell.rs b/clap_complete/src/dynamic/shells/powershell.rs new file mode 100644 index 00000000000..1d8c9dfb03e --- /dev/null +++ b/clap_complete/src/dynamic/shells/powershell.rs @@ -0,0 +1,68 @@ +/// Completion support for Powershell +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Powershell; + +impl crate::dynamic::Completer for Powershell { + fn file_name(&self, name: &str) -> String { + format!("{name}.ps1") + } + + fn write_registration( + &self, + name: &str, + bin: &str, + completer: &str, + buf: &mut dyn std::io::Write, + ) -> Result<(), std::io::Error> { + let bin = shlex::quote(bin); + let completer = shlex::quote(completer); + + writeln!( + buf, + r#" +Register-ArgumentCompleter -Native -CommandName {bin} -ScriptBlock {{ + param($wordToComplete, $commandAst, $cursorPosition) + + $results = Invoke-Expression "&{bin} complete --shell powershell -- $($commandAst.ToString())"; + $results | ForEach-Object {{ + $split = $_.Split("`t"); + $cmd = $split[0]; + + if ($split.Length -eq 2) {{ + $help = $split[1]; + }} + else {{ + $help = $split[0]; + }} + + [System.Management.Automation.CompletionResult]::new($cmd, $cmd, 'ParameterValue', $help) + }} +}}; + "# + ) + } + + fn write_complete( + &self, + cmd: &mut clap::Command, + args: Vec, + current_dir: Option<&std::path::Path>, + buf: &mut dyn std::io::Write, + ) -> Result<(), std::io::Error> { + let index = args.len() - 1; + let completions = crate::dynamic::complete(cmd, args, index, current_dir)?; + + for (completion, help) in completions { + write!(buf, "{}", completion.to_string_lossy())?; + if let Some(help) = help { + write!( + buf, + "\t{}", + help.to_string().lines().next().unwrap_or_default() + )?; + } + writeln!(buf)?; + } + Ok(()) + } +} diff --git a/clap_complete/src/dynamic/shells/shell.rs b/clap_complete/src/dynamic/shells/shell.rs index c36d2a23a41..2cf4700f408 100644 --- a/clap_complete/src/dynamic/shells/shell.rs +++ b/clap_complete/src/dynamic/shells/shell.rs @@ -12,6 +12,8 @@ pub enum Shell { Bash, /// Friendly Interactive SHell (fish) Fish, + /// Powershell + Powershell, } impl Display for Shell { @@ -39,13 +41,14 @@ impl FromStr for Shell { // Hand-rolled so it can work even when `derive` feature is disabled impl ValueEnum for Shell { fn value_variants<'a>() -> &'a [Self] { - &[Shell::Bash, Shell::Fish] + &[Shell::Bash, Shell::Fish, Shell::Powershell] } fn to_possible_value<'a>(&self) -> Option { Some(match self { Shell::Bash => PossibleValue::new("bash"), Shell::Fish => PossibleValue::new("fish"), + Shell::Powershell => PossibleValue::new("powershell"), }) } } @@ -55,6 +58,7 @@ impl Shell { match self { Self::Bash => &super::Bash, Self::Fish => &super::Fish, + Self::Powershell => &super::Powershell, } } }