Skip to content

Commit 588ada5

Browse files
authored
Improve the command for printing completion scripts (urfave#1998)
1 parent 0a1c772 commit 588ada5

File tree

7 files changed

+62
-53
lines changed

7 files changed

+62
-53
lines changed

autocomplete/bash_autocomplete

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
#! /bin/bash
1+
#!/bin/bash
22

3-
: ${PROG:=$(basename ${BASH_SOURCE})}
3+
# This is a shell completion script auto-generated by https://github.com/urfave/cli for bash.
44

55
# Macs have bash3 for which the bash-completion package doesn't include
66
# _init_completion. This is a minimal version of that function.
7-
_cli_init_completion() {
7+
__%[1]s_init_completion() {
88
COMPREPLY=()
99
_get_comp_words_by_ref "$@" cur prev words cword
1010
}
1111

12-
_cli_bash_autocomplete() {
12+
__%[1]s_bash_autocomplete() {
1313
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
1414
local cur opts base words
1515
COMPREPLY=()
1616
cur="${COMP_WORDS[COMP_CWORD]}"
1717
if declare -F _init_completion >/dev/null 2>&1; then
1818
_init_completion -n "=:" || return
1919
else
20-
_cli_init_completion -n "=:" || return
20+
__%[1]s_init_completion -n "=:" || return
2121
fi
2222
words=("${words[@]:0:$cword}")
2323
if [[ "$cur" == "-"* ]]; then
@@ -31,5 +31,4 @@ _cli_bash_autocomplete() {
3131
fi
3232
}
3333

34-
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG
35-
unset PROG
34+
complete -o bashdefault -o default -o nospace -F __%[1]s_bash_autocomplete %[1]s

autocomplete/powershell_autocomplete.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock {
66
Invoke-Expression $other | ForEach-Object {
77
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
88
}
9-
}
9+
}

autocomplete/zsh_autocomplete

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
1-
#compdef program
2-
compdef _program program
1+
#compdef %[1]s
2+
compdef _%[1]s %[1]s
33

4-
# Replace all occurrences of "program" in this file with the actual name of your
5-
# CLI program. We recommend using Find+Replace feature of your editor. Let's say
6-
# your CLI program is called "acme", then replace like so:
7-
# * program => acme
8-
# * _program => _acme
4+
# This is a shell completion script auto-generated by https://github.com/urfave/cli for zsh.
95

10-
_program() {
11-
local -a opts
12-
local cur
13-
cur=${words[-1]}
14-
if [[ "$cur" == "-"* ]]; then
15-
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-shell-completion)}")
6+
_%[1]s() {
7+
local -a opts # Declare a local array
8+
local current
9+
current=${words[-1]} # -1 means "the last element"
10+
if [[ "$current" == "-"* ]]; then
11+
# Current word starts with a hyphen, so complete flags/options
12+
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${current} --generate-shell-completion)}")
1613
else
14+
# Current word does not start with a hyphen, so complete subcommands
1715
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-shell-completion)}")
1816
fi
1917

@@ -24,7 +22,8 @@ _program() {
2422
fi
2523
}
2624

27-
# don't run the completion function when being source-ed or eval-ed
28-
if [ "$funcstack[1]" = "_program" ]; then
29-
_program
25+
# Don't run the completion function when being source-ed or eval-ed.
26+
# See https://github.com/urfave/cli/issues/1874 for discussion.
27+
if [ "$funcstack[1]" = "_%[1]s" ]; then
28+
_%[1]s
3029
fi

command.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ func (cmd *Command) setupDefaults(osArgs []string) {
257257
}
258258

259259
if cmd.EnableShellCompletion || cmd.Root().shellCompletion {
260-
completionCommand := buildCompletionCommand()
260+
completionCommand := buildCompletionCommand(osArgs[0])
261261

262262
if cmd.ShellCompletionCommandName != "" {
263263
tracef(

completion.go

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,48 @@ import (
88
)
99

1010
const (
11-
completionCommandName = "generate-completion"
12-
completionFlagName = "generate-shell-completion"
13-
completionFlag = "--" + completionFlagName
11+
completionCommandName = "completion"
12+
13+
// This flag is supposed to only be used by the completion script itself to generate completions on the fly.
14+
completionFlag = "--generate-shell-completion"
1415
)
1516

17+
type renderCompletion func(cmd *Command, appName string) (string, error)
18+
1619
var (
1720
//go:embed autocomplete
1821
autoCompleteFS embed.FS
1922

2023
shellCompletions = map[string]renderCompletion{
21-
"bash": getCompletion("autocomplete/bash_autocomplete"),
22-
"ps": getCompletion("autocomplete/powershell_autocomplete.ps1"),
23-
"zsh": getCompletion("autocomplete/zsh_autocomplete"),
24-
"fish": func(c *Command) (string, error) {
24+
"bash": func(c *Command, appName string) (string, error) {
25+
b, err := autoCompleteFS.ReadFile("autocomplete/bash_autocomplete")
26+
return fmt.Sprintf(string(b), appName), err
27+
},
28+
"zsh": func(c *Command, appName string) (string, error) {
29+
b, err := autoCompleteFS.ReadFile("autocomplete/zsh_autocomplete")
30+
return fmt.Sprintf(string(b), appName), err
31+
},
32+
"fish": func(c *Command, appName string) (string, error) {
2533
return c.ToFishCompletion()
2634
},
35+
"pwsh": func(c *Command, appName string) (string, error) {
36+
b, err := autoCompleteFS.ReadFile("autocomplete/powershell_autocomplete.ps1")
37+
return string(b), err
38+
},
2739
}
2840
)
2941

30-
type renderCompletion func(*Command) (string, error)
31-
32-
func getCompletion(s string) renderCompletion {
33-
return func(c *Command) (string, error) {
34-
b, err := autoCompleteFS.ReadFile(s)
35-
return string(b), err
36-
}
37-
}
38-
39-
func buildCompletionCommand() *Command {
42+
func buildCompletionCommand(appName string) *Command {
4043
return &Command{
4144
Name: completionCommandName,
4245
Hidden: true,
43-
Action: completionCommandAction,
46+
Action: func(ctx context.Context, cmd *Command) error {
47+
return printShellCompletion(ctx, cmd, appName)
48+
},
4449
}
4550
}
4651

47-
func completionCommandAction(ctx context.Context, cmd *Command) error {
52+
func printShellCompletion(_ context.Context, cmd *Command, appName string) error {
4853
var shells []string
4954
for k := range shellCompletions {
5055
shells = append(shells, k)
@@ -57,14 +62,20 @@ func completionCommandAction(ctx context.Context, cmd *Command) error {
5762
}
5863
s := cmd.Args().First()
5964

60-
if rc, ok := shellCompletions[s]; !ok {
65+
renderCompletion, ok := shellCompletions[s]
66+
if !ok {
6167
return Exit(fmt.Sprintf("unknown shell %s, available shells are %+v", s, shells), 1)
62-
} else if c, err := rc(cmd); err != nil {
68+
}
69+
70+
completionScript, err := renderCompletion(cmd, appName)
71+
if err != nil {
6372
return Exit(err, 1)
64-
} else {
65-
if _, err = cmd.Writer.Write([]byte(c)); err != nil {
66-
return Exit(err, 1)
67-
}
6873
}
74+
75+
_, err = cmd.Writer.Write([]byte(completionScript))
76+
if err != nil {
77+
return Exit(err, 1)
78+
}
79+
6980
return nil
7081
}

completion_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ func TestCompletionInvalidShell(t *testing.T) {
200200
assert.ErrorContains(t, err, "unknown shell junky-sheell")
201201

202202
enableError := true
203-
shellCompletions[unknownShellName] = func(c *Command) (string, error) {
203+
shellCompletions[unknownShellName] = func(c *Command, appName string) (string, error) {
204204
if enableError {
205205
return "", fmt.Errorf("cant do completion")
206206
}

help.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ func cliArgContains(flagName string, args []string) bool {
190190
}
191191

192192
func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) {
193-
cur := strings.TrimPrefix(lastArg, "-")
194-
cur = strings.TrimPrefix(cur, "-")
193+
// Trim to handle both "-short" and "--long" flags.
194+
cur := strings.TrimLeft(lastArg, "-")
195195
for _, flag := range flags {
196196
if bflag, ok := flag.(*BoolFlag); ok && bflag.Hidden {
197197
continue

0 commit comments

Comments
 (0)