gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

powershell_completions.go (12609B)


      1 // Copyright 2013-2023 The Cobra Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // The generated scripts require PowerShell v5.0+ (which comes Windows 10, but
     16 // can be downloaded separately for windows 7 or 8.1).
     17 
     18 package cobra
     19 
     20 import (
     21 	"bytes"
     22 	"fmt"
     23 	"io"
     24 	"os"
     25 	"strings"
     26 )
     27 
     28 func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
     29 	// Variables should not contain a '-' or ':' character
     30 	nameForVar := name
     31 	nameForVar = strings.Replace(nameForVar, "-", "_", -1)
     32 	nameForVar = strings.Replace(nameForVar, ":", "_", -1)
     33 
     34 	compCmd := ShellCompRequestCmd
     35 	if !includeDesc {
     36 		compCmd = ShellCompNoDescRequestCmd
     37 	}
     38 	WriteStringAndCheck(buf, fmt.Sprintf(`# powershell completion for %-36[1]s -*- shell-script -*-
     39 
     40 function __%[1]s_debug {
     41     if ($env:BASH_COMP_DEBUG_FILE) {
     42         "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE"
     43     }
     44 }
     45 
     46 filter __%[1]s_escapeStringWithSpecialChars {
     47 `+"    $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+`
     48 }
     49 
     50 [scriptblock]$__%[2]sCompleterBlock = {
     51     param(
     52             $WordToComplete,
     53             $CommandAst,
     54             $CursorPosition
     55         )
     56 
     57     # Get the current command line and convert into a string
     58     $Command = $CommandAst.CommandElements
     59     $Command = "$Command"
     60 
     61     __%[1]s_debug ""
     62     __%[1]s_debug "========= starting completion logic =========="
     63     __%[1]s_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition"
     64 
     65     # The user could have moved the cursor backwards on the command-line.
     66     # We need to trigger completion from the $CursorPosition location, so we need
     67     # to truncate the command-line ($Command) up to the $CursorPosition location.
     68     # Make sure the $Command is longer then the $CursorPosition before we truncate.
     69     # This happens because the $Command does not include the last space.
     70     if ($Command.Length -gt $CursorPosition) {
     71         $Command=$Command.Substring(0,$CursorPosition)
     72     }
     73     __%[1]s_debug "Truncated command: $Command"
     74 
     75     $ShellCompDirectiveError=%[4]d
     76     $ShellCompDirectiveNoSpace=%[5]d
     77     $ShellCompDirectiveNoFileComp=%[6]d
     78     $ShellCompDirectiveFilterFileExt=%[7]d
     79     $ShellCompDirectiveFilterDirs=%[8]d
     80     $ShellCompDirectiveKeepOrder=%[9]d
     81 
     82     # Prepare the command to request completions for the program.
     83     # Split the command at the first space to separate the program and arguments.
     84     $Program,$Arguments = $Command.Split(" ",2)
     85 
     86     $RequestComp="$Program %[3]s $Arguments"
     87     __%[1]s_debug "RequestComp: $RequestComp"
     88 
     89     # we cannot use $WordToComplete because it
     90     # has the wrong values if the cursor was moved
     91     # so use the last argument
     92     if ($WordToComplete -ne "" ) {
     93         $WordToComplete = $Arguments.Split(" ")[-1]
     94     }
     95     __%[1]s_debug "New WordToComplete: $WordToComplete"
     96 
     97 
     98     # Check for flag with equal sign
     99     $IsEqualFlag = ($WordToComplete -Like "--*=*" )
    100     if ( $IsEqualFlag ) {
    101         __%[1]s_debug "Completing equal sign flag"
    102         # Remove the flag part
    103         $Flag,$WordToComplete = $WordToComplete.Split("=",2)
    104     }
    105 
    106     if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) {
    107         # If the last parameter is complete (there is a space following it)
    108         # We add an extra empty parameter so we can indicate this to the go method.
    109         __%[1]s_debug "Adding extra empty parameter"
    110         # PowerShell 7.2+ changed the way how the arguments are passed to executables,
    111         # so for pre-7.2 or when Legacy argument passing is enabled we need to use
    112 `+"        # `\"`\" to pass an empty argument, a \"\" or '' does not work!!!"+`
    113         if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or
    114             ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or
    115             (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and
    116               $PSNativeCommandArgumentPassing -eq 'Legacy')) {
    117 `+"             $RequestComp=\"$RequestComp\" + ' `\"`\"'"+`
    118         } else {
    119              $RequestComp="$RequestComp" + ' ""'
    120         }
    121     }
    122 
    123     __%[1]s_debug "Calling $RequestComp"
    124     # First disable ActiveHelp which is not supported for Powershell
    125     $env:%[10]s=0
    126 
    127     #call the command store the output in $out and redirect stderr and stdout to null
    128     # $Out is an array contains each line per element
    129     Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
    130 
    131     # get directive from last line
    132     [int]$Directive = $Out[-1].TrimStart(':')
    133     if ($Directive -eq "") {
    134         # There is no directive specified
    135         $Directive = 0
    136     }
    137     __%[1]s_debug "The completion directive is: $Directive"
    138 
    139     # remove directive (last element) from out
    140     $Out = $Out | Where-Object { $_ -ne $Out[-1] }
    141     __%[1]s_debug "The completions are: $Out"
    142 
    143     if (($Directive -band $ShellCompDirectiveError) -ne 0 ) {
    144         # Error code.  No completion.
    145         __%[1]s_debug "Received error from custom completion go code"
    146         return
    147     }
    148 
    149     $Longest = 0
    150     [Array]$Values = $Out | ForEach-Object {
    151         #Split the output in name and description
    152 `+"        $Name, $Description = $_.Split(\"`t\",2)"+`
    153         __%[1]s_debug "Name: $Name Description: $Description"
    154 
    155         # Look for the longest completion so that we can format things nicely
    156         if ($Longest -lt $Name.Length) {
    157             $Longest = $Name.Length
    158         }
    159 
    160         # Set the description to a one space string if there is none set.
    161         # This is needed because the CompletionResult does not accept an empty string as argument
    162         if (-Not $Description) {
    163             $Description = " "
    164         }
    165         @{Name="$Name";Description="$Description"}
    166     }
    167 
    168 
    169     $Space = " "
    170     if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) {
    171         # remove the space here
    172         __%[1]s_debug "ShellCompDirectiveNoSpace is called"
    173         $Space = ""
    174     }
    175 
    176     if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or
    177        (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 ))  {
    178         __%[1]s_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported"
    179 
    180         # return here to prevent the completion of the extensions
    181         return
    182     }
    183 
    184     $Values = $Values | Where-Object {
    185         # filter the result
    186         $_.Name -like "$WordToComplete*"
    187 
    188         # Join the flag back if we have an equal sign flag
    189         if ( $IsEqualFlag ) {
    190             __%[1]s_debug "Join the equal sign flag back to the completion value"
    191             $_.Name = $Flag + "=" + $_.Name
    192         }
    193     }
    194 
    195     # we sort the values in ascending order by name if keep order isn't passed
    196     if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) {
    197         $Values = $Values | Sort-Object -Property Name
    198     }
    199 
    200     if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
    201         __%[1]s_debug "ShellCompDirectiveNoFileComp is called"
    202 
    203         if ($Values.Length -eq 0) {
    204             # Just print an empty string here so the
    205             # shell does not start to complete paths.
    206             # We cannot use CompletionResult here because
    207             # it does not accept an empty string as argument.
    208             ""
    209             return
    210         }
    211     }
    212 
    213     # Get the current mode
    214     $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function
    215     __%[1]s_debug "Mode: $Mode"
    216 
    217     $Values | ForEach-Object {
    218 
    219         # store temporary because switch will overwrite $_
    220         $comp = $_
    221 
    222         # PowerShell supports three different completion modes
    223         # - TabCompleteNext (default windows style - on each key press the next option is displayed)
    224         # - Complete (works like bash)
    225         # - MenuComplete (works like zsh)
    226         # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function <mode>
    227 
    228         # CompletionResult Arguments:
    229         # 1) CompletionText text to be used as the auto completion result
    230         # 2) ListItemText   text to be displayed in the suggestion list
    231         # 3) ResultType     type of completion result
    232         # 4) ToolTip        text for the tooltip with details about the object
    233 
    234         switch ($Mode) {
    235 
    236             # bash like
    237             "Complete" {
    238 
    239                 if ($Values.Length -eq 1) {
    240                     __%[1]s_debug "Only one completion left"
    241 
    242                     # insert space after value
    243                     [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
    244 
    245                 } else {
    246                     # Add the proper number of spaces to align the descriptions
    247                     while($comp.Name.Length -lt $Longest) {
    248                         $comp.Name = $comp.Name + " "
    249                     }
    250 
    251                     # Check for empty description and only add parentheses if needed
    252                     if ($($comp.Description) -eq " " ) {
    253                         $Description = ""
    254                     } else {
    255                         $Description = "  ($($comp.Description))"
    256                     }
    257 
    258                     [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
    259                 }
    260              }
    261 
    262             # zsh like
    263             "MenuComplete" {
    264                 # insert space after value
    265                 # MenuComplete will automatically show the ToolTip of
    266                 # the highlighted value at the bottom of the suggestions.
    267                 [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
    268             }
    269 
    270             # TabCompleteNext and in case we get something unknown
    271             Default {
    272                 # Like MenuComplete but we don't want to add a space here because
    273                 # the user need to press space anyway to get the completion.
    274                 # Description will not be shown because that's not possible with TabCompleteNext
    275                 [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
    276             }
    277         }
    278 
    279     }
    280 }
    281 
    282 Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock $__%[2]sCompleterBlock
    283 `, name, nameForVar, compCmd,
    284 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
    285 		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name)))
    286 }
    287 
    288 func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error {
    289 	buf := new(bytes.Buffer)
    290 	genPowerShellComp(buf, c.Name(), includeDesc)
    291 	_, err := buf.WriteTo(w)
    292 	return err
    293 }
    294 
    295 func (c *Command) genPowerShellCompletionFile(filename string, includeDesc bool) error {
    296 	outFile, err := os.Create(filename)
    297 	if err != nil {
    298 		return err
    299 	}
    300 	defer outFile.Close()
    301 
    302 	return c.genPowerShellCompletion(outFile, includeDesc)
    303 }
    304 
    305 // GenPowerShellCompletionFile generates powershell completion file without descriptions.
    306 func (c *Command) GenPowerShellCompletionFile(filename string) error {
    307 	return c.genPowerShellCompletionFile(filename, false)
    308 }
    309 
    310 // GenPowerShellCompletion generates powershell completion file without descriptions
    311 // and writes it to the passed writer.
    312 func (c *Command) GenPowerShellCompletion(w io.Writer) error {
    313 	return c.genPowerShellCompletion(w, false)
    314 }
    315 
    316 // GenPowerShellCompletionFileWithDesc generates powershell completion file with descriptions.
    317 func (c *Command) GenPowerShellCompletionFileWithDesc(filename string) error {
    318 	return c.genPowerShellCompletionFile(filename, true)
    319 }
    320 
    321 // GenPowerShellCompletionWithDesc generates powershell completion file with descriptions
    322 // and writes it to the passed writer.
    323 func (c *Command) GenPowerShellCompletionWithDesc(w io.Writer) error {
    324 	return c.genPowerShellCompletion(w, true)
    325 }