podman
308 строк · 10.8 Кб
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
15package cobra
16
17import (
18"bytes"
19"fmt"
20"io"
21"os"
22)
23
24// GenZshCompletionFile generates zsh completion file including descriptions.
25func (c *Command) GenZshCompletionFile(filename string) error {
26return c.genZshCompletionFile(filename, true)
27}
28
29// GenZshCompletion generates zsh completion file including descriptions
30// and writes it to the passed writer.
31func (c *Command) GenZshCompletion(w io.Writer) error {
32return c.genZshCompletion(w, true)
33}
34
35// GenZshCompletionFileNoDesc generates zsh completion file without descriptions.
36func (c *Command) GenZshCompletionFileNoDesc(filename string) error {
37return c.genZshCompletionFile(filename, false)
38}
39
40// GenZshCompletionNoDesc generates zsh completion file without descriptions
41// and writes it to the passed writer.
42func (c *Command) GenZshCompletionNoDesc(w io.Writer) error {
43return c.genZshCompletion(w, false)
44}
45
46// MarkZshCompPositionalArgumentFile only worked for zsh and its behavior was
47// not consistent with Bash completion. It has therefore been disabled.
48// Instead, when no other completion is specified, file completion is done by
49// default for every argument. One can disable file completion on a per-argument
50// basis by using ValidArgsFunction and ShellCompDirectiveNoFileComp.
51// To achieve file extension filtering, one can use ValidArgsFunction and
52// ShellCompDirectiveFilterFileExt.
53//
54// Deprecated
55func (c *Command) MarkZshCompPositionalArgumentFile(argPosition int, patterns ...string) error {
56return nil
57}
58
59// MarkZshCompPositionalArgumentWords only worked for zsh. It has therefore
60// been disabled.
61// To achieve the same behavior across all shells, one can use
62// ValidArgs (for the first argument only) or ValidArgsFunction for
63// any argument (can include the first one also).
64//
65// Deprecated
66func (c *Command) MarkZshCompPositionalArgumentWords(argPosition int, words ...string) error {
67return nil
68}
69
70func (c *Command) genZshCompletionFile(filename string, includeDesc bool) error {
71outFile, err := os.Create(filename)
72if err != nil {
73return err
74}
75defer outFile.Close()
76
77return c.genZshCompletion(outFile, includeDesc)
78}
79
80func (c *Command) genZshCompletion(w io.Writer, includeDesc bool) error {
81buf := new(bytes.Buffer)
82genZshComp(buf, c.Name(), includeDesc)
83_, err := buf.WriteTo(w)
84return err
85}
86
87func genZshComp(buf io.StringWriter, name string, includeDesc bool) {
88compCmd := ShellCompRequestCmd
89if !includeDesc {
90compCmd = ShellCompNoDescRequestCmd
91}
92WriteStringAndCheck(buf, fmt.Sprintf(`#compdef %[1]s
93compdef _%[1]s %[1]s
94
95# zsh completion for %-36[1]s -*- shell-script -*-
96
97__%[1]s_debug()
98{
99local file="$BASH_COMP_DEBUG_FILE"
100if [[ -n ${file} ]]; then
101echo "$*" >> "${file}"
102fi
103}
104
105_%[1]s()
106{
107local shellCompDirectiveError=%[3]d
108local shellCompDirectiveNoSpace=%[4]d
109local shellCompDirectiveNoFileComp=%[5]d
110local shellCompDirectiveFilterFileExt=%[6]d
111local shellCompDirectiveFilterDirs=%[7]d
112local shellCompDirectiveKeepOrder=%[8]d
113
114local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
115local -a completions
116
117__%[1]s_debug "\n========= starting completion logic =========="
118__%[1]s_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
119
120# The user could have moved the cursor backwards on the command-line.
121# We need to trigger completion from the $CURRENT location, so we need
122# to truncate the command-line ($words) up to the $CURRENT location.
123# (We cannot use $CURSOR as its value does not work when a command is an alias.)
124words=("${=words[1,CURRENT]}")
125__%[1]s_debug "Truncated words[*]: ${words[*]},"
126
127lastParam=${words[-1]}
128lastChar=${lastParam[-1]}
129__%[1]s_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
130
131# For zsh, when completing a flag with an = (e.g., %[1]s -n=<TAB>)
132# completions must be prefixed with the flag
133setopt local_options BASH_REMATCH
134if [[ "${lastParam}" =~ '-.*=' ]]; then
135# We are dealing with a flag with an =
136flagPrefix="-P ${BASH_REMATCH}"
137fi
138
139# Prepare the command to obtain completions
140requestComp="${words[1]} %[2]s ${words[2,-1]}"
141if [ "${lastChar}" = "" ]; then
142# If the last parameter is complete (there is a space following it)
143# We add an extra empty parameter so we can indicate this to the go completion code.
144__%[1]s_debug "Adding extra empty parameter"
145requestComp="${requestComp} \"\""
146fi
147
148__%[1]s_debug "About to call: eval ${requestComp}"
149
150# Use eval to handle any environment variables and such
151out=$(eval ${requestComp} 2>/dev/null)
152__%[1]s_debug "completion output: ${out}"
153
154# Extract the directive integer following a : from the last line
155local lastLine
156while IFS='\n' read -r line; do
157lastLine=${line}
158done < <(printf "%%s\n" "${out[@]}")
159__%[1]s_debug "last line: ${lastLine}"
160
161if [ "${lastLine[1]}" = : ]; then
162directive=${lastLine[2,-1]}
163# Remove the directive including the : and the newline
164local suffix
165(( suffix=${#lastLine}+2))
166out=${out[1,-$suffix]}
167else
168# There is no directive specified. Leave $out as is.
169__%[1]s_debug "No directive found. Setting do default"
170directive=0
171fi
172
173__%[1]s_debug "directive: ${directive}"
174__%[1]s_debug "completions: ${out}"
175__%[1]s_debug "flagPrefix: ${flagPrefix}"
176
177if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
178__%[1]s_debug "Completion received error. Ignoring completions."
179return
180fi
181
182local activeHelpMarker="%[9]s"
183local endIndex=${#activeHelpMarker}
184local startIndex=$((${#activeHelpMarker}+1))
185local hasActiveHelp=0
186while IFS='\n' read -r comp; do
187# Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
188if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
189__%[1]s_debug "ActiveHelp found: $comp"
190comp="${comp[$startIndex,-1]}"
191if [ -n "$comp" ]; then
192compadd -x "${comp}"
193__%[1]s_debug "ActiveHelp will need delimiter"
194hasActiveHelp=1
195fi
196
197continue
198fi
199
200if [ -n "$comp" ]; then
201# If requested, completions are returned with a description.
202# The description is preceded by a TAB character.
203# For zsh's _describe, we need to use a : instead of a TAB.
204# We first need to escape any : as part of the completion itself.
205comp=${comp//:/\\:}
206
207local tab="$(printf '\t')"
208comp=${comp//$tab/:}
209
210__%[1]s_debug "Adding completion: ${comp}"
211completions+=${comp}
212lastComp=$comp
213fi
214done < <(printf "%%s\n" "${out[@]}")
215
216# Add a delimiter after the activeHelp statements, but only if:
217# - there are completions following the activeHelp statements, or
218# - file completion will be performed (so there will be choices after the activeHelp)
219if [ $hasActiveHelp -eq 1 ]; then
220if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
221__%[1]s_debug "Adding activeHelp delimiter"
222compadd -x "--"
223hasActiveHelp=0
224fi
225fi
226
227if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
228__%[1]s_debug "Activating nospace."
229noSpace="-S ''"
230fi
231
232if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
233__%[1]s_debug "Activating keep order."
234keepOrder="-V"
235fi
236
237if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
238# File extension filtering
239local filteringCmd
240filteringCmd='_files'
241for filter in ${completions[@]}; do
242if [ ${filter[1]} != '*' ]; then
243# zsh requires a glob pattern to do file filtering
244filter="\*.$filter"
245fi
246filteringCmd+=" -g $filter"
247done
248filteringCmd+=" ${flagPrefix}"
249
250__%[1]s_debug "File filtering command: $filteringCmd"
251_arguments '*:filename:'"$filteringCmd"
252elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
253# File completion for directories only
254local subdir
255subdir="${completions[1]}"
256if [ -n "$subdir" ]; then
257__%[1]s_debug "Listing directories in $subdir"
258pushd "${subdir}" >/dev/null 2>&1
259else
260__%[1]s_debug "Listing directories in ."
261fi
262
263local result
264_arguments '*:dirname:_files -/'" ${flagPrefix}"
265result=$?
266if [ -n "$subdir" ]; then
267popd >/dev/null 2>&1
268fi
269return $result
270else
271__%[1]s_debug "Calling _describe"
272if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
273__%[1]s_debug "_describe found some completions"
274
275# Return the success of having called _describe
276return 0
277else
278__%[1]s_debug "_describe did not find completions."
279__%[1]s_debug "Checking if we should do file completion."
280if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
281__%[1]s_debug "deactivating file completion"
282
283# We must return an error code here to let zsh know that there were no
284# completions found by _describe; this is what will trigger other
285# matching algorithms to attempt to find completions.
286# For example zsh can match letters in the middle of words.
287return 1
288else
289# Perform file completion
290__%[1]s_debug "Activating file completion"
291
292# We must return the result of this command, so it must be the
293# last command, or else we must store its result to return it.
294_arguments '*:filename:_files'" ${flagPrefix}"
295fi
296fi
297fi
298}
299
300# don't run the completion function when being source-ed or eval-ed
301if [ "$funcstack[1]" = "_%[1]s" ]; then
302_%[1]s
303fi
304`, name, compCmd,
305ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
306ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder,
307activeHelpMarker))
308}
309