podman

Форк
0
/
bash_completionsV2.go 
396 строк · 13.2 Кб
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
package cobra
16

17
import (
18
	"bytes"
19
	"fmt"
20
	"io"
21
	"os"
22
)
23

24
func (c *Command) genBashCompletion(w io.Writer, includeDesc bool) error {
25
	buf := new(bytes.Buffer)
26
	genBashComp(buf, c.Name(), includeDesc)
27
	_, err := buf.WriteTo(w)
28
	return err
29
}
30

31
func genBashComp(buf io.StringWriter, name string, includeDesc bool) {
32
	compCmd := ShellCompRequestCmd
33
	if !includeDesc {
34
		compCmd = ShellCompNoDescRequestCmd
35
	}
36

37
	WriteStringAndCheck(buf, fmt.Sprintf(`# bash completion V2 for %-36[1]s -*- shell-script -*-
38

39
__%[1]s_debug()
40
{
41
    if [[ -n ${BASH_COMP_DEBUG_FILE-} ]]; then
42
        echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
43
    fi
44
}
45

46
# Macs have bash3 for which the bash-completion package doesn't include
47
# _init_completion. This is a minimal version of that function.
48
__%[1]s_init_completion()
49
{
50
    COMPREPLY=()
51
    _get_comp_words_by_ref "$@" cur prev words cword
52
}
53

54
# This function calls the %[1]s program to obtain the completion
55
# results and the directive.  It fills the 'out' and 'directive' vars.
56
__%[1]s_get_completion_results() {
57
    local requestComp lastParam lastChar args
58

59
    # Prepare the command to request completions for the program.
60
    # Calling ${words[0]} instead of directly %[1]s allows handling aliases
61
    args=("${words[@]:1}")
62
    requestComp="${words[0]} %[2]s ${args[*]}"
63

64
    lastParam=${words[$((${#words[@]}-1))]}
65
    lastChar=${lastParam:$((${#lastParam}-1)):1}
66
    __%[1]s_debug "lastParam ${lastParam}, lastChar ${lastChar}"
67

68
    if [[ -z ${cur} && ${lastChar} != = ]]; then
69
        # If the last parameter is complete (there is a space following it)
70
        # We add an extra empty parameter so we can indicate this to the go method.
71
        __%[1]s_debug "Adding extra empty parameter"
72
        requestComp="${requestComp} ''"
73
    fi
74

75
    # When completing a flag with an = (e.g., %[1]s -n=<TAB>)
76
    # bash focuses on the part after the =, so we need to remove
77
    # the flag part from $cur
78
    if [[ ${cur} == -*=* ]]; then
79
        cur="${cur#*=}"
80
    fi
81

82
    __%[1]s_debug "Calling ${requestComp}"
83
    # Use eval to handle any environment variables and such
84
    out=$(eval "${requestComp}" 2>/dev/null)
85

86
    # Extract the directive integer at the very end of the output following a colon (:)
87
    directive=${out##*:}
88
    # Remove the directive
89
    out=${out%%:*}
90
    if [[ ${directive} == "${out}" ]]; then
91
        # There is not directive specified
92
        directive=0
93
    fi
94
    __%[1]s_debug "The completion directive is: ${directive}"
95
    __%[1]s_debug "The completions are: ${out}"
96
}
97

98
__%[1]s_process_completion_results() {
99
    local shellCompDirectiveError=%[3]d
100
    local shellCompDirectiveNoSpace=%[4]d
101
    local shellCompDirectiveNoFileComp=%[5]d
102
    local shellCompDirectiveFilterFileExt=%[6]d
103
    local shellCompDirectiveFilterDirs=%[7]d
104
    local shellCompDirectiveKeepOrder=%[8]d
105

106
    if (((directive & shellCompDirectiveError) != 0)); then
107
        # Error code.  No completion.
108
        __%[1]s_debug "Received error from custom completion go code"
109
        return
110
    else
111
        if (((directive & shellCompDirectiveNoSpace) != 0)); then
112
            if [[ $(type -t compopt) == builtin ]]; then
113
                __%[1]s_debug "Activating no space"
114
                compopt -o nospace
115
            else
116
                __%[1]s_debug "No space directive not supported in this version of bash"
117
            fi
118
        fi
119
        if (((directive & shellCompDirectiveKeepOrder) != 0)); then
120
            if [[ $(type -t compopt) == builtin ]]; then
121
                # no sort isn't supported for bash less than < 4.4
122
                if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then
123
                    __%[1]s_debug "No sort directive not supported in this version of bash"
124
                else
125
                    __%[1]s_debug "Activating keep order"
126
                    compopt -o nosort
127
                fi
128
            else
129
                __%[1]s_debug "No sort directive not supported in this version of bash"
130
            fi
131
        fi
132
        if (((directive & shellCompDirectiveNoFileComp) != 0)); then
133
            if [[ $(type -t compopt) == builtin ]]; then
134
                __%[1]s_debug "Activating no file completion"
135
                compopt +o default
136
            else
137
                __%[1]s_debug "No file completion directive not supported in this version of bash"
138
            fi
139
        fi
140
    fi
141

142
    # Separate activeHelp from normal completions
143
    local completions=()
144
    local activeHelp=()
145
    __%[1]s_extract_activeHelp
146

147
    if (((directive & shellCompDirectiveFilterFileExt) != 0)); then
148
        # File extension filtering
149
        local fullFilter filter filteringCmd
150

151
        # Do not use quotes around the $completions variable or else newline
152
        # characters will be kept.
153
        for filter in ${completions[*]}; do
154
            fullFilter+="$filter|"
155
        done
156

157
        filteringCmd="_filedir $fullFilter"
158
        __%[1]s_debug "File filtering command: $filteringCmd"
159
        $filteringCmd
160
    elif (((directive & shellCompDirectiveFilterDirs) != 0)); then
161
        # File completion for directories only
162

163
        local subdir
164
        subdir=${completions[0]}
165
        if [[ -n $subdir ]]; then
166
            __%[1]s_debug "Listing directories in $subdir"
167
            pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return
168
        else
169
            __%[1]s_debug "Listing directories in ."
170
            _filedir -d
171
        fi
172
    else
173
        __%[1]s_handle_completion_types
174
    fi
175

176
    __%[1]s_handle_special_char "$cur" :
177
    __%[1]s_handle_special_char "$cur" =
178

179
    # Print the activeHelp statements before we finish
180
    if ((${#activeHelp[*]} != 0)); then
181
        printf "\n";
182
        printf "%%s\n" "${activeHelp[@]}"
183
        printf "\n"
184

185
        # The prompt format is only available from bash 4.4.
186
        # We test if it is available before using it.
187
        if (x=${PS1@P}) 2> /dev/null; then
188
            printf "%%s" "${PS1@P}${COMP_LINE[@]}"
189
        else
190
            # Can't print the prompt.  Just print the
191
            # text the user had typed, it is workable enough.
192
            printf "%%s" "${COMP_LINE[@]}"
193
        fi
194
    fi
195
}
196

197
# Separate activeHelp lines from real completions.
198
# Fills the $activeHelp and $completions arrays.
199
__%[1]s_extract_activeHelp() {
200
    local activeHelpMarker="%[9]s"
201
    local endIndex=${#activeHelpMarker}
202

203
    while IFS='' read -r comp; do
204
        if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then
205
            comp=${comp:endIndex}
206
            __%[1]s_debug "ActiveHelp found: $comp"
207
            if [[ -n $comp ]]; then
208
                activeHelp+=("$comp")
209
            fi
210
        else
211
            # Not an activeHelp line but a normal completion
212
            completions+=("$comp")
213
        fi
214
    done <<<"${out}"
215
}
216

217
__%[1]s_handle_completion_types() {
218
    __%[1]s_debug "__%[1]s_handle_completion_types: COMP_TYPE is $COMP_TYPE"
219

220
    case $COMP_TYPE in
221
    37|42)
222
        # Type: menu-complete/menu-complete-backward and insert-completions
223
        # If the user requested inserting one completion at a time, or all
224
        # completions at once on the command-line we must remove the descriptions.
225
        # https://github.com/spf13/cobra/issues/1508
226
        local tab=$'\t' comp
227
        while IFS='' read -r comp; do
228
            [[ -z $comp ]] && continue
229
            # Strip any description
230
            comp=${comp%%%%$tab*}
231
            # Only consider the completions that match
232
            if [[ $comp == "$cur"* ]]; then
233
                COMPREPLY+=("$comp")
234
            fi
235
        done < <(printf "%%s\n" "${completions[@]}")
236
        ;;
237

238
    *)
239
        # Type: complete (normal completion)
240
        __%[1]s_handle_standard_completion_case
241
        ;;
242
    esac
243
}
244

245
__%[1]s_handle_standard_completion_case() {
246
    local tab=$'\t' comp
247

248
    # Short circuit to optimize if we don't have descriptions
249
    if [[ "${completions[*]}" != *$tab* ]]; then
250
        IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur")
251
        return 0
252
    fi
253

254
    local longest=0
255
    local compline
256
    # Look for the longest completion so that we can format things nicely
257
    while IFS='' read -r compline; do
258
        [[ -z $compline ]] && continue
259
        # Strip any description before checking the length
260
        comp=${compline%%%%$tab*}
261
        # Only consider the completions that match
262
        [[ $comp == "$cur"* ]] || continue
263
        COMPREPLY+=("$compline")
264
        if ((${#comp}>longest)); then
265
            longest=${#comp}
266
        fi
267
    done < <(printf "%%s\n" "${completions[@]}")
268

269
    # If there is a single completion left, remove the description text
270
    if ((${#COMPREPLY[*]} == 1)); then
271
        __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
272
        comp="${COMPREPLY[0]%%%%$tab*}"
273
        __%[1]s_debug "Removed description from single completion, which is now: ${comp}"
274
        COMPREPLY[0]=$comp
275
    else # Format the descriptions
276
        __%[1]s_format_comp_descriptions $longest
277
    fi
278
}
279

280
__%[1]s_handle_special_char()
281
{
282
    local comp="$1"
283
    local char=$2
284
    if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then
285
        local word=${comp%%"${comp##*${char}}"}
286
        local idx=${#COMPREPLY[*]}
287
        while ((--idx >= 0)); do
288
            COMPREPLY[idx]=${COMPREPLY[idx]#"$word"}
289
        done
290
    fi
291
}
292

293
__%[1]s_format_comp_descriptions()
294
{
295
    local tab=$'\t'
296
    local comp desc maxdesclength
297
    local longest=$1
298

299
    local i ci
300
    for ci in ${!COMPREPLY[*]}; do
301
        comp=${COMPREPLY[ci]}
302
        # Properly format the description string which follows a tab character if there is one
303
        if [[ "$comp" == *$tab* ]]; then
304
            __%[1]s_debug "Original comp: $comp"
305
            desc=${comp#*$tab}
306
            comp=${comp%%%%$tab*}
307

308
            # $COLUMNS stores the current shell width.
309
            # Remove an extra 4 because we add 2 spaces and 2 parentheses.
310
            maxdesclength=$(( COLUMNS - longest - 4 ))
311

312
            # Make sure we can fit a description of at least 8 characters
313
            # if we are to align the descriptions.
314
            if ((maxdesclength > 8)); then
315
                # Add the proper number of spaces to align the descriptions
316
                for ((i = ${#comp} ; i < longest ; i++)); do
317
                    comp+=" "
318
                done
319
            else
320
                # Don't pad the descriptions so we can fit more text after the completion
321
                maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
322
            fi
323

324
            # If there is enough space for any description text,
325
            # truncate the descriptions that are too long for the shell width
326
            if ((maxdesclength > 0)); then
327
                if ((${#desc} > maxdesclength)); then
328
                    desc=${desc:0:$(( maxdesclength - 1 ))}
329
                    desc+="…"
330
                fi
331
                comp+="  ($desc)"
332
            fi
333
            COMPREPLY[ci]=$comp
334
            __%[1]s_debug "Final comp: $comp"
335
        fi
336
    done
337
}
338

339
__start_%[1]s()
340
{
341
    local cur prev words cword split
342

343
    COMPREPLY=()
344

345
    # Call _init_completion from the bash-completion package
346
    # to prepare the arguments properly
347
    if declare -F _init_completion >/dev/null 2>&1; then
348
        _init_completion -n =: || return
349
    else
350
        __%[1]s_init_completion -n =: || return
351
    fi
352

353
    __%[1]s_debug
354
    __%[1]s_debug "========= starting completion logic =========="
355
    __%[1]s_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword"
356

357
    # The user could have moved the cursor backwards on the command-line.
358
    # We need to trigger completion from the $cword location, so we need
359
    # to truncate the command-line ($words) up to the $cword location.
360
    words=("${words[@]:0:$cword+1}")
361
    __%[1]s_debug "Truncated words[*]: ${words[*]},"
362

363
    local out directive
364
    __%[1]s_get_completion_results
365
    __%[1]s_process_completion_results
366
}
367

368
if [[ $(type -t compopt) = "builtin" ]]; then
369
    complete -o default -F __start_%[1]s %[1]s
370
else
371
    complete -o default -o nospace -F __start_%[1]s %[1]s
372
fi
373

374
# ex: ts=4 sw=4 et filetype=sh
375
`, name, compCmd,
376
		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
377
		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder,
378
		activeHelpMarker))
379
}
380

381
// GenBashCompletionFileV2 generates Bash completion version 2.
382
func (c *Command) GenBashCompletionFileV2(filename string, includeDesc bool) error {
383
	outFile, err := os.Create(filename)
384
	if err != nil {
385
		return err
386
	}
387
	defer outFile.Close()
388

389
	return c.GenBashCompletionV2(outFile, includeDesc)
390
}
391

392
// GenBashCompletionV2 generates Bash completion file version 2
393
// and writes it to the passed writer.
394
func (c *Command) GenBashCompletionV2(w io.Writer, includeDesc bool) error {
395
	return c.genBashCompletion(w, includeDesc)
396
}
397

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.