yargs / yargs

yargs the modern, pirate-themed successor to optimist.
https://yargs.js.org/
MIT License
11.1k stars 992 forks source link

PowerShell completions #1290

Open cspotcode opened 5 years ago

cspotcode commented 5 years ago

Related to #1210.

I posted something similar over on tabtab: https://github.com/mklabs/tabtab/issues/26#issuecomment-462102606

Here's some info about how this could be implemented with very minimal PowerShell syntax, keeping almost everything in familiar JS.

PowerShell Core includes Register-ArgumentCompleter to register completions for external binaries. https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/register-argumentcompleter?view=powershell-6

It can return an array of CompletionResult instances, which describe the completion text, label text, description, etc.

https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.completionresult?view=powershellsdk-1.1.0

PowerShell can natively emit and parse JSON, so it should be easy to pass all the necessary data to and from yargs without the need to write much PowerShell syntax. yargs can do all the work in JS and write JSON to stdout.

Register-ArgumentCompleter -Native -CommandName tabtab-test -ScriptBlock {
    $argsEncoded = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($args | convertto-json)))
    $results = yargs-command completion -- $argsEncoded | convertfrom-json
    foreach($result in $results) {
        [Management.Automation.CompletionResult]::new($result[0], $result[1], $result[2], $result[3])
    }
}
LucaSalzani commented 2 years ago

Hi. Partly thanks to your input I got code completion running quite quickly for our CLI.

This is our Profile. I just created a new program in the cli to take the position and current commandAst in as arguments and calculate the options

Register-ArgumentCompleter -Native -CommandName mycommand -ScriptBlock {
  param($commandName, $commandAst, $cursorPosition)
  mycommand complete --position $cursorPosition "$commandAst" | ConvertFrom-Json | ForEach-Object {
    [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
  }
}

mycommand complete looks like this.

  1. split into finished words and started words ("mycommand run te" would split into "mycommand run" and "te")
  2. call --get-yargs-completions (mycommand run --get-yargs-completions) to get all the possibilities from there (returns for example "test", "build", "lint")
  3. filter the output of the call for words that start with the started word ("te" -> returns "test")
bcoe commented 2 years ago

@LucaSalzani I would happily take a PR for adding PowerShell support, if thios is something that can be made generic.