podman
325 строк · 12.3 Кб
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
18package cobra
19
20import (
21"bytes"
22"fmt"
23"io"
24"os"
25"strings"
26)
27
28func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
29// Variables should not contain a '-' or ':' character
30nameForVar := name
31nameForVar = strings.Replace(nameForVar, "-", "_", -1)
32nameForVar = strings.Replace(nameForVar, ":", "_", -1)
33
34compCmd := ShellCompRequestCmd
35if !includeDesc {
36compCmd = ShellCompNoDescRequestCmd
37}
38WriteStringAndCheck(buf, fmt.Sprintf(`# powershell completion for %-36[1]s -*- shell-script -*-
39
40function __%[1]s_debug {
41if ($env:BASH_COMP_DEBUG_FILE) {
42"$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE"
43}
44}
45
46filter __%[1]s_escapeStringWithSpecialChars {
47`+" $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+`
48}
49
50[scriptblock]${__%[2]sCompleterBlock} = {
51param(
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.
70if ($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
92if ($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 "--*=*" )
100if ( $IsEqualFlag ) {
101__%[1]s_debug "Completing equal sign flag"
102# Remove the flag part
103$Flag,$WordToComplete = $WordToComplete.Split("=",2)
104}
105
106if ( $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!!!"+`
113if ($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
129Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
130
131# get directive from last line
132[int]$Directive = $Out[-1].TrimStart(':')
133if ($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
143if (($Directive -band $ShellCompDirectiveError) -ne 0 ) {
144# Error code. No completion.
145__%[1]s_debug "Received error from custom completion go code"
146return
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
156if ($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
162if (-Not $Description) {
163$Description = " "
164}
165@{Name="$Name";Description="$Description"}
166}
167
168
169$Space = " "
170if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) {
171# remove the space here
172__%[1]s_debug "ShellCompDirectiveNoSpace is called"
173$Space = ""
174}
175
176if ((($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
181return
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
189if ( $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
196if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) {
197$Values = $Values | Sort-Object -Property Name
198}
199
200if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
201__%[1]s_debug "ShellCompDirectiveNoFileComp is called"
202
203if ($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""
209return
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
234switch ($Mode) {
235
236# bash like
237"Complete" {
238
239if ($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
247while($comp.Name.Length -lt $Longest) {
248$comp.Name = $comp.Name + " "
249}
250
251# Check for empty description and only add parentheses if needed
252if ($($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
271Default {
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
282Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock ${__%[2]sCompleterBlock}
283`, name, nameForVar, compCmd,
284ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
285ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name)))
286}
287
288func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error {
289buf := new(bytes.Buffer)
290genPowerShellComp(buf, c.Name(), includeDesc)
291_, err := buf.WriteTo(w)
292return err
293}
294
295func (c *Command) genPowerShellCompletionFile(filename string, includeDesc bool) error {
296outFile, err := os.Create(filename)
297if err != nil {
298return err
299}
300defer outFile.Close()
301
302return c.genPowerShellCompletion(outFile, includeDesc)
303}
304
305// GenPowerShellCompletionFile generates powershell completion file without descriptions.
306func (c *Command) GenPowerShellCompletionFile(filename string) error {
307return c.genPowerShellCompletionFile(filename, false)
308}
309
310// GenPowerShellCompletion generates powershell completion file without descriptions
311// and writes it to the passed writer.
312func (c *Command) GenPowerShellCompletion(w io.Writer) error {
313return c.genPowerShellCompletion(w, false)
314}
315
316// GenPowerShellCompletionFileWithDesc generates powershell completion file with descriptions.
317func (c *Command) GenPowerShellCompletionFileWithDesc(filename string) error {
318return c.genPowerShellCompletionFile(filename, true)
319}
320
321// GenPowerShellCompletionWithDesc generates powershell completion file with descriptions
322// and writes it to the passed writer.
323func (c *Command) GenPowerShellCompletionWithDesc(w io.Writer) error {
324return c.genPowerShellCompletion(w, true)
325}
326