diff --git a/README.md b/README.md index 34abc1e7..69f3d350 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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 diff --git a/src/Options/Applicative/BashCompletion.hs b/src/Options/Applicative/BashCompletion.hs index e4b6356c..aac06929 100644 --- a/src/Options/Applicative/BashCompletion.hs +++ b/src/Options/Applicative/BashCompletion.hs @@ -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] @@ -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 @@ -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" + , "}" + ]