From 3064887ba769f9a62c0383ca86c99a9fe8bc0f73 Mon Sep 17 00:00:00 2001 From: Bittrance Date: Fri, 23 May 2025 23:08:56 +0200 Subject: [PATCH] Fish completion now supports dynamic completions. --- command_setup.go | 5 ----- command_test.go | 34 ++++++++++++++++++++++++++++++++ fish.go | 15 +++++++++++++- help.go | 3 +++ testdata/expected-fish-full.fish | 4 +++- 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/command_setup.go b/command_setup.go index 09df4a3084..b8b32184eb 100644 --- a/command_setup.go +++ b/command_setup.go @@ -19,11 +19,6 @@ func (cmd *Command) setupDefaults(osArgs []string) { isRoot := cmd.parent == nil tracef("isRoot? %[1]v (cmd=%[2]q)", isRoot, cmd.Name) - if cmd.ShellComplete == nil { - tracef("setting default ShellComplete (cmd=%[1]q)", cmd.Name) - cmd.ShellComplete = DefaultCompleteWithFlags - } - if cmd.Name == "" && isRoot { name := filepath.Base(osArgs[0]) tracef("setting cmd.Name from first arg basename (cmd=%[1]q)", name) diff --git a/command_test.go b/command_test.go index f88fea9d05..7aa37a1cc0 100644 --- a/command_test.go +++ b/command_test.go @@ -95,6 +95,10 @@ func buildExtendedTestCommand() *Command { Usage: "retrieve generic information", }, { Name: "some-command", + }, { + Name: "custom", + ShellComplete: func(ctx context.Context, c *Command) {}, + HideHelp: true, }, { Name: "hidden-command", Hidden: true, @@ -4870,6 +4874,36 @@ func TestJSONExportCommand(t *testing.T) { "arguments": null, "readArgsFromStdin": false }, + { + "name": "custom", + "aliases": null, + "usage": "", + "usageText": "", + "argsUsage": "", + "version": "", + "description": "", + "defaultCommand": "", + "category": "", + "commands": null, + "flags": null, + "hideHelp": true, + "hideHelpCommand": false, + "hideVersion": false, + "hidden": false, + "authors": null, + "copyright": "", + "metadata": null, + "sliceFlagSeparator": "", + "disableSliceFlagSeparator": false, + "useShortOptionHandling": false, + "suggest": false, + "allowExtFlags": false, + "skipFlagParsing": false, + "prefixMatchCommands": false, + "mutuallyExclusiveFlags": null, + "arguments": null, + "readArgsFromStdin": false + }, { "name": "hidden-command", "aliases": null, diff --git a/fish.go b/fish.go index 320dfc6c69..b731b28456 100644 --- a/fish.go +++ b/fish.go @@ -53,8 +53,21 @@ func (cmd *Command) writeFishCompletionTemplate(w io.Writer) error { } func prepareFishCommands(binary string, parent *Command) []string { - commands := parent.Commands completions := []string{} + if parent.ShellComplete != nil { + var completion strings.Builder + fmt.Fprintf(&completion, + "complete -x -c %s -n '%s' -a '%s'", + binary, + commandAncestry(parent), + "(eval command (commandline -pc) "+completionFlag+")", + ) + completions = append( + completions, + completion.String(), + ) + } + commands := parent.Commands for _, command := range commands { if !command.Hidden { var completion strings.Builder diff --git a/help.go b/help.go index c039a5d02b..2ffe83b422 100644 --- a/help.go +++ b/help.go @@ -502,6 +502,9 @@ func checkCompletions(ctx context.Context, cmd *Command) bool { if cmd.ShellComplete != nil { tracef("running shell completion func for command %[1]q", cmd.Name) cmd.ShellComplete(ctx, cmd) + } else { + tracef("running default shell completion func for command %[1]q", cmd.Name) + DefaultCompleteWithFlags(ctx, cmd) } return true diff --git a/testdata/expected-fish-full.fish b/testdata/expected-fish-full.fish index b69e8e51d5..cff2ffceac 100644 --- a/testdata/expected-fish-full.fish +++ b/testdata/expected-fish-full.fish @@ -2,7 +2,7 @@ function __fish_greet_no_subcommand --description 'Test if there has been any subcommand yet' for i in (commandline -opc) - if contains -- $i config c info i in some-command hidden-command usage u + if contains -- $i config c info i in some-command custom hidden-command usage u return 1 end end @@ -30,6 +30,8 @@ complete -x -c greet -n '__fish_seen_subcommand_from info i in; and not __fish_s complete -x -c greet -n '__fish_greet_no_subcommand' -a 'some-command' complete -c greet -n '__fish_seen_subcommand_from some-command' -f -l help -s h -d 'show help' complete -x -c greet -n '__fish_seen_subcommand_from some-command; and not __fish_seen_subcommand_from help h' -a 'help' -d 'Shows a list of commands or help for one command' +complete -x -c greet -n '__fish_greet_no_subcommand' -a 'custom' +complete -x -c greet -n '__fish_seen_subcommand_from custom' -a '(eval command (commandline -pc) --generate-shell-completion)' complete -c greet -n '__fish_seen_subcommand_from hidden-command' -f -l completable complete -c greet -n '__fish_seen_subcommand_from hidden-command' -f -l help -s h -d 'show help' complete -x -c greet -n '__fish_seen_subcommand_from hidden-command; and not __fish_seen_subcommand_from help h' -a 'help' -d 'Shows a list of commands or help for one command'