Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add powershell autocompletion script generation #420

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -801,10 +801,10 @@ French commands:
au-revoir Say goodbye
```

## Bash, Zsh, and Fish Completions
## Bash, Zsh, Fish, and Pwsh Completions

`optparse-applicative` has built-in support for the completion of
command line options and arguments in bash, zsh, and fish shells.
command line options and arguments in bash, zsh, fish, and pwsh shells.
Any parser, when run using the `execParser` family of functions,
is automatically extended with a few (hidden) options for the
completion system:
Expand All @@ -827,13 +827,29 @@ completion system:

- `--fish-completion-script`: which is analogous for fish shell;

- `--pwsh-completion-script`: which is analogous for powershell/pwsh.
You might want to generate a script and then dot-source it as

```console
PS> foo --pwsh-completion-script (Get-Command foo).Source >> _foo.ps1
PS> . _foo.ps1
```
You might want to add `Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete`
to your powershell/pwsh profile in order to get better experience
with completion tooltips.

Note for windows users - this will generate completion script for `foo.exe`,
which is not equivalent to `foo` from tab-completion engine's point of view,
even though from usage point view they are equivalent. You might want to edit
the file `_foo.ps1` if you prefer to use `foo` and not `foo.exe`.

- `--bash-completion-index`, `--bash-completion-word`: internal options used
by the completion script to obtain a list of possible completions for a
given command line;

- `--bash-completion-enriched`: a flag to tell the completion system to emit
descriptions along with possible completions. This is used to provide help
along with the completion for `zsh` and `fish`.
along with the completion for `zsh`, `fish`, and `pwsh`.

### Actions and completers

Expand Down
40 changes: 39 additions & 1 deletion src/Options/Applicative/BashCompletion.hs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ bashCompletionParser pinfo pprefs = complParser
strOption (long "fish-completion-script" `mappend` internal)
, scriptRequest . zshCompletionScript <$>
strOption (long "zsh-completion-script" `mappend` internal)
, scriptRequest . pwshCompletionScript <$>
strOption (long "pwsh-completion-script" `mappend` internal)
]

bashCompletionQuery :: ParserInfo a -> ParserPrefs -> Richness -> [String] -> Int -> String -> IO [String]
Expand Down Expand Up @@ -202,7 +204,7 @@ words.
Tab characters separate items from descriptions.
-}

-- | Generated fish shell completion script
-- | Generated fish shell completion script
fishCompletionScript :: String -> String -> String
fishCompletionScript prog progn = unlines
[ " function _" ++ progn
Expand Down Expand Up @@ -262,3 +264,39 @@ zshCompletionScript prog progn = unlines
, " fi"
, "done"
]

pwshCompletionScript :: String -> String -> String
pwshCompletionScript prog progn = unlines
[ "using namespace System.Management.Automation"
, "using namespace System.Management.Automation.Language"
, "Register-ArgumentCompleter -Native -CommandName '" ++ progn ++ "' -ScriptBlock {"
, " param($wordToComplete, $commandAst)"
, " [string[]]$localCommand = @('\"--bash-completion-enriched\"')"
, " $hay = [System.Collections.Generic.List[string]]::new()"
, " foreach ($item in $commandAst.CommandElements) {"
, " $localCommand += '\"--bash-completion-word\"'"
, " $localCommand += \"\"\"$item\"\"\""
, " $hay.Add($item.ToString())"
, " }"
, ""
, ""
, " $localCommand += '\"--bash-completion-index\"'"
, " if ($wordToComplete.Equals(\"\")) {"
, " $localCommand += '\"' + $commandAst.CommandElements.Count +'\"'"
, " }"
, " else {"
, " $localCommand += '\"' + $hay.IndexOf($wordToComplete) + '\"'"
, " }"
, " $inp = & '" ++ prog ++ "' @localCommand"
, " [CompletionResult[]]$out = @()"
, " [string]$suffix = if ($inp.Count -eq 1) {' '} else {\"\"}"
, " foreach ($item in $inp) {"
, " $spl = $item.Split(\"`t\")"
, " $show = $spl[0]"
, " $tooltip = if ($spl.Length -eq 1) { $spl[0] } else { $spl[1] }"
, " $crt = if ($show.StartsWith('-')) { [CompletionResultType]::ParameterName } else { [CompletionResultType]::ParameterValue }"
, " $out += [CompletionResult]::new($show + $suffix, $show, $crt, $tooltip)"
, " }"
, " $out"
, "}"
]