Path: blob/main/vendor/github.com/spf13/cobra/powershell_completions.go
2875 views
// Copyright 2013-2023 The Cobra Authors1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314// The generated scripts require PowerShell v5.0+ (which comes Windows 10, but15// can be downloaded separately for windows 7 or 8.1).1617package cobra1819import (20"bytes"21"fmt"22"io"23"os"24"strings"25)2627func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {28// Variables should not contain a '-' or ':' character29nameForVar := name30nameForVar = strings.ReplaceAll(nameForVar, "-", "_")31nameForVar = strings.ReplaceAll(nameForVar, ":", "_")3233compCmd := ShellCompRequestCmd34if !includeDesc {35compCmd = ShellCompNoDescRequestCmd36}37WriteStringAndCheck(buf, fmt.Sprintf(`# powershell completion for %-36[1]s -*- shell-script -*-3839function __%[1]s_debug {40if ($env:BASH_COMP_DEBUG_FILE) {41"$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE"42}43}4445filter __%[1]s_escapeStringWithSpecialChars {46`+" $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+`47}4849[scriptblock]${__%[2]sCompleterBlock} = {50param(51$WordToComplete,52$CommandAst,53$CursorPosition54)5556# Get the current command line and convert into a string57$Command = $CommandAst.CommandElements58$Command = "$Command"5960__%[1]s_debug ""61__%[1]s_debug "========= starting completion logic =========="62__%[1]s_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition"6364# The user could have moved the cursor backwards on the command-line.65# We need to trigger completion from the $CursorPosition location, so we need66# to truncate the command-line ($Command) up to the $CursorPosition location.67# Make sure the $Command is longer then the $CursorPosition before we truncate.68# This happens because the $Command does not include the last space.69if ($Command.Length -gt $CursorPosition) {70$Command=$Command.Substring(0,$CursorPosition)71}72__%[1]s_debug "Truncated command: $Command"7374$ShellCompDirectiveError=%[4]d75$ShellCompDirectiveNoSpace=%[5]d76$ShellCompDirectiveNoFileComp=%[6]d77$ShellCompDirectiveFilterFileExt=%[7]d78$ShellCompDirectiveFilterDirs=%[8]d79$ShellCompDirectiveKeepOrder=%[9]d8081# Prepare the command to request completions for the program.82# Split the command at the first space to separate the program and arguments.83$Program,$Arguments = $Command.Split(" ",2)8485$RequestComp="$Program %[3]s $Arguments"86__%[1]s_debug "RequestComp: $RequestComp"8788# we cannot use $WordToComplete because it89# has the wrong values if the cursor was moved90# so use the last argument91if ($WordToComplete -ne "" ) {92$WordToComplete = $Arguments.Split(" ")[-1]93}94__%[1]s_debug "New WordToComplete: $WordToComplete"959697# Check for flag with equal sign98$IsEqualFlag = ($WordToComplete -Like "--*=*" )99if ( $IsEqualFlag ) {100__%[1]s_debug "Completing equal sign flag"101# Remove the flag part102$Flag,$WordToComplete = $WordToComplete.Split("=",2)103}104105if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) {106# If the last parameter is complete (there is a space following it)107# We add an extra empty parameter so we can indicate this to the go method.108__%[1]s_debug "Adding extra empty parameter"109# PowerShell 7.2+ changed the way how the arguments are passed to executables,110# so for pre-7.2 or when Legacy argument passing is enabled we need to use111`+" # `\"`\" to pass an empty argument, a \"\" or '' does not work!!!"+`112if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or113($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or114(($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and115$PSNativeCommandArgumentPassing -eq 'Legacy')) {116`+" $RequestComp=\"$RequestComp\" + ' `\"`\"'"+`117} else {118$RequestComp="$RequestComp" + ' ""'119}120}121122__%[1]s_debug "Calling $RequestComp"123# First disable ActiveHelp which is not supported for Powershell124${env:%[10]s}=0125126#call the command store the output in $out and redirect stderr and stdout to null127# $Out is an array contains each line per element128Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null129130# get directive from last line131[int]$Directive = $Out[-1].TrimStart(':')132if ($Directive -eq "") {133# There is no directive specified134$Directive = 0135}136__%[1]s_debug "The completion directive is: $Directive"137138# remove directive (last element) from out139$Out = $Out | Where-Object { $_ -ne $Out[-1] }140__%[1]s_debug "The completions are: $Out"141142if (($Directive -band $ShellCompDirectiveError) -ne 0 ) {143# Error code. No completion.144__%[1]s_debug "Received error from custom completion go code"145return146}147148$Longest = 0149[Array]$Values = $Out | ForEach-Object {150#Split the output in name and description151`+" $Name, $Description = $_.Split(\"`t\",2)"+`152__%[1]s_debug "Name: $Name Description: $Description"153154# Look for the longest completion so that we can format things nicely155if ($Longest -lt $Name.Length) {156$Longest = $Name.Length157}158159# Set the description to a one space string if there is none set.160# This is needed because the CompletionResult does not accept an empty string as argument161if (-Not $Description) {162$Description = " "163}164New-Object -TypeName PSCustomObject -Property @{165Name = "$Name"166Description = "$Description"167}168}169170171$Space = " "172if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) {173# remove the space here174__%[1]s_debug "ShellCompDirectiveNoSpace is called"175$Space = ""176}177178if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or179(($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) {180__%[1]s_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported"181182# return here to prevent the completion of the extensions183return184}185186$Values = $Values | Where-Object {187# filter the result188$_.Name -like "$WordToComplete*"189190# Join the flag back if we have an equal sign flag191if ( $IsEqualFlag ) {192__%[1]s_debug "Join the equal sign flag back to the completion value"193$_.Name = $Flag + "=" + $_.Name194}195}196197# we sort the values in ascending order by name if keep order isn't passed198if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) {199$Values = $Values | Sort-Object -Property Name200}201202if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {203__%[1]s_debug "ShellCompDirectiveNoFileComp is called"204205if ($Values.Length -eq 0) {206# Just print an empty string here so the207# shell does not start to complete paths.208# We cannot use CompletionResult here because209# it does not accept an empty string as argument.210""211return212}213}214215# Get the current mode216$Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function217__%[1]s_debug "Mode: $Mode"218219$Values | ForEach-Object {220221# store temporary because switch will overwrite $_222$comp = $_223224# PowerShell supports three different completion modes225# - TabCompleteNext (default windows style - on each key press the next option is displayed)226# - Complete (works like bash)227# - MenuComplete (works like zsh)228# You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function <mode>229230# CompletionResult Arguments:231# 1) CompletionText text to be used as the auto completion result232# 2) ListItemText text to be displayed in the suggestion list233# 3) ResultType type of completion result234# 4) ToolTip text for the tooltip with details about the object235236switch ($Mode) {237238# bash like239"Complete" {240241if ($Values.Length -eq 1) {242__%[1]s_debug "Only one completion left"243244# insert space after value245$CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space246if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){247[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")248} else {249$CompletionText250}251252} else {253# Add the proper number of spaces to align the descriptions254while($comp.Name.Length -lt $Longest) {255$comp.Name = $comp.Name + " "256}257258# Check for empty description and only add parentheses if needed259if ($($comp.Description) -eq " " ) {260$Description = ""261} else {262$Description = " ($($comp.Description))"263}264265$CompletionText = "$($comp.Name)$Description"266if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){267[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")268} else {269$CompletionText270}271}272}273274# zsh like275"MenuComplete" {276# insert space after value277# MenuComplete will automatically show the ToolTip of278# the highlighted value at the bottom of the suggestions.279280$CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space281if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){282[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")283} else {284$CompletionText285}286}287288# TabCompleteNext and in case we get something unknown289Default {290# Like MenuComplete but we don't want to add a space here because291# the user need to press space anyway to get the completion.292# Description will not be shown because that's not possible with TabCompleteNext293294$CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars)295if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){296[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")297} else {298$CompletionText299}300}301}302303}304}305306Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock ${__%[2]sCompleterBlock}307`, name, nameForVar, compCmd,308ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,309ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name)))310}311312func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error {313buf := new(bytes.Buffer)314genPowerShellComp(buf, c.Name(), includeDesc)315_, err := buf.WriteTo(w)316return err317}318319func (c *Command) genPowerShellCompletionFile(filename string, includeDesc bool) error {320outFile, err := os.Create(filename)321if err != nil {322return err323}324defer outFile.Close()325326return c.genPowerShellCompletion(outFile, includeDesc)327}328329// GenPowerShellCompletionFile generates powershell completion file without descriptions.330func (c *Command) GenPowerShellCompletionFile(filename string) error {331return c.genPowerShellCompletionFile(filename, false)332}333334// GenPowerShellCompletion generates powershell completion file without descriptions335// and writes it to the passed writer.336func (c *Command) GenPowerShellCompletion(w io.Writer) error {337return c.genPowerShellCompletion(w, false)338}339340// GenPowerShellCompletionFileWithDesc generates powershell completion file with descriptions.341func (c *Command) GenPowerShellCompletionFileWithDesc(filename string) error {342return c.genPowerShellCompletionFile(filename, true)343}344345// GenPowerShellCompletionWithDesc generates powershell completion file with descriptions346// and writes it to the passed writer.347func (c *Command) GenPowerShellCompletionWithDesc(w io.Writer) error {348return c.genPowerShellCompletion(w, true)349}350351352