diff --git a/fish.go b/fish.go index 1607f55b13..8002a33ca1 100644 --- a/fish.go +++ b/fish.go @@ -34,6 +34,18 @@ func (cmd *Command) writeFishCompletionTemplate(w io.Writer) error { // Add global flags completions := prepareFishFlags(cmd.Name, cmd) + if cmd.ShellComplete != nil { + var completion strings.Builder + fmt.Fprintf(&completion, + "complete -c %s -n '%s' -xa '(%s %s 2>/dev/null)'", + cmd.Name, + fishFlagHelper(cmd.Name, cmd), + cmd.Name, + completionFlag, + ) + completions = append(completions, completion.String()) + } + // Add commands and their flags completions = append( completions, @@ -72,6 +84,26 @@ func prepareFishCommands(binary string, parent *Command) []string { } completions = append(completions, completion.String()) } + + if command.ShellComplete != nil { + var completion strings.Builder + var path []string + lineage := command.Lineage() + for i := len(lineage) - 2; i >= 0; i-- { + path = append(path, lineage[i].Name) + } + + fmt.Fprintf(&completion, + "complete -c %s -n '%s' -xa '(%s %s %s 2>/dev/null)'", + binary, + fishFlagHelper(binary, command), + binary, + strings.Join(path, " "), + completionFlag, + ) + completions = append(completions, completion.String()) + } + completions = append( completions, prepareFishFlags(binary, command)..., diff --git a/fish_test.go b/fish_test.go index 492b516b75..1b3908f1ba 100644 --- a/fish_test.go +++ b/fish_test.go @@ -1,6 +1,8 @@ package cli import ( + "context" + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -38,3 +40,23 @@ func TestFishCompletion(t *testing.T) { require.NoError(t, err) expectFileContent(t, "testdata/expected-fish-full.fish", res) } + +func TestFishCompletionShellComplete(t *testing.T) { + cmd := buildExtendedTestCommand() + cmd.ShellComplete = func(context.Context, *Command) {} + + configCmd := cmd.Command("config") + configCmd.ShellComplete = func(context.Context, *Command) {} + + subConfigCmd := configCmd.Command("sub-config") + subConfigCmd.ShellComplete = func(context.Context, *Command) {} + + cmd.setupCommandGraph() + + res, err := cmd.ToFishCompletion() + require.NoError(t, err) + + assert.Contains(t, res, fmt.Sprintf("complete -c greet -n '__fish_greet_no_subcommand' -xa '(greet %s 2>/dev/null)'", completionFlag)) + assert.Contains(t, res, fmt.Sprintf("complete -c greet -n '__fish_seen_subcommand_from config c' -xa '(greet config %s 2>/dev/null)'", completionFlag)) + assert.Contains(t, res, fmt.Sprintf("complete -c greet -n '__fish_seen_subcommand_from config c; and __fish_seen_subcommand_from sub-config s ss' -xa '(greet config sub-config %s 2>/dev/null)'", completionFlag)) +}