Skip to content

Commit

Permalink
fix(complete): Fix PowerShell dynamic completion
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jennings committed Dec 17, 2024
1 parent ed2360f commit 99b6391
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 2 deletions.
4 changes: 3 additions & 1 deletion clap_complete/src/env/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion clap_complete/src/env/shells.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down

0 comments on commit 99b6391

Please sign in to comment.