From 99b6391ee92bfd3356fc8085ee206df97f284e19 Mon Sep 17 00:00:00 2001 From: Stephen Jennings Date: Mon, 16 Dec 2024 15:40:22 -0800 Subject: [PATCH] fix(complete): Fix PowerShell dynamic completion PowerShell does not support inline syntax for assigning environment variables, so we must instead set the value before running the completer and restore it after it exits. The completer will often, if not always, be surrounded by double quotes. To avoid syntax errors, define the argument to Invoke-Expression as a here-string so the quotes don't create a syntax error. Updates the instructions for adding the argument completer to the profile. Piping a native command to Invoke-Expression invokes each line separately. Adding `Out-String` to the pipeline ensures that Invoke-Expression receives the whole script as a single value from the pipeline. Fixes: #5847 --- clap_complete/src/env/mod.rs | 4 +++- clap_complete/src/env/shells.rs | 14 +++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/clap_complete/src/env/mod.rs b/clap_complete/src/env/mod.rs index 9d7f59cf5a1..8902484a57d 100644 --- a/clap_complete/src/env/mod.rs +++ b/clap_complete/src/env/mod.rs @@ -52,7 +52,9 @@ //! //! Powershell //! ```powershell -//! echo "COMPLETE=powershell your_program | Invoke-Expression" >> $PROFILE +//! $env:COMPLETE = "powershell" +//! echo "your_program | Out-String | Invoke-Expression" >> $PROFILE +//! Remove-Item Env:\COMPLETE //! ``` //! //! Zsh diff --git a/clap_complete/src/env/shells.rs b/clap_complete/src/env/shells.rs index 5600927d9c9..427e214917b 100644 --- a/clap_complete/src/env/shells.rs +++ b/clap_complete/src/env/shells.rs @@ -274,13 +274,25 @@ impl EnvCompleter for Powershell { let completer = shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer)); + // `completer` may or may not be surrounded by double quotes, enclosing + // the expression in a here-string ensures the whole thing is + // interpreted as the first argument to the call operator writeln!( buf, r#" Register-ArgumentCompleter -Native -CommandName {bin} -ScriptBlock {{ param($wordToComplete, $commandAst, $cursorPosition) - $results = Invoke-Expression "{var}=powershell &{completer} -- $($commandAst.ToString())"; + $prev = $env:{var}; + $env:{var} = "powershell"; + $results = Invoke-Expression @" +& {completer} -- $commandAst +"@; + if ($null -eq $prev) {{ + Remove-Item Env:\{var}; + }} else {{ + $env:{var} = $prev; + }} $results | ForEach-Object {{ $split = $_.Split("`t"); $cmd = $split[0];