From 09cc27e0a3f5b83fab0c3342e471353c0b7cbc79 Mon Sep 17 00:00:00 2001 From: Bittrance Date: Mon, 5 May 2025 21:48:48 +0200 Subject: [PATCH 1/2] Fish completion no longer confuses completions for subcommands with identical names. Previously, completions for [foo bar --] and [baz bar --] would include flags from both subcommands because they would only match on "bar". This is now fixed by including the full ancestry. --- fish.go | 28 ++++++++++++++++++++++------ testdata/expected-fish-full.fish | 14 +++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/fish.go b/fish.go index 481b665e85..0441d092eb 100644 --- a/fish.go +++ b/fish.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "slices" "strings" "text/template" ) @@ -151,9 +152,10 @@ func fishSubcommandHelper(binary string, command *Command, siblings []*Command) for _, sibling := range siblings { siblingNames = append(siblingNames, sibling.Names()...) } + ancestry := commandAncestry(command) fishHelper = fmt.Sprintf( - "__fish_seen_subcommand_from %s; and not __fish_seen_subcommand_from %s", - strings.Join(command.Names(), " "), + "%s; and not __fish_seen_subcommand_from %s", + ancestry, strings.Join(siblingNames, " "), ) } @@ -163,14 +165,28 @@ func fishSubcommandHelper(binary string, command *Command, siblings []*Command) func fishFlagHelper(binary string, command *Command) string { fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", binary) if len(command.Lineage()) > 1 { - fishHelper = fmt.Sprintf( - "__fish_seen_subcommand_from %s", - strings.Join(command.Names(), " "), - ) + fishHelper = commandAncestry(command) } return fishHelper } +func commandAncestry(command *Command) string { + var ancestry []string + ancestors := command.Lineage() + slices.Reverse(ancestors) + ancestors = ancestors[1:] + for _, ancestor := range ancestors { + ancestry = append( + ancestry, + fmt.Sprintf( + "__fish_seen_subcommand_from %s", + strings.Join(ancestor.Names(), " "), + ), + ) + } + return strings.Join(ancestry, "; and ") +} + func escapeSingleQuotes(input string) string { return strings.ReplaceAll(input, `'`, `\'`) } diff --git a/testdata/expected-fish-full.fish b/testdata/expected-fish-full.fish index e02a659efb..b69e8e51d5 100644 --- a/testdata/expected-fish-full.fish +++ b/testdata/expected-fish-full.fish @@ -19,10 +19,10 @@ complete -c greet -n '__fish_seen_subcommand_from config c' -l flag -s fl -s f - complete -c greet -n '__fish_seen_subcommand_from config c' -f -l another-flag -s b -d 'another usage text' complete -c greet -n '__fish_seen_subcommand_from config c' -f -l help -s h -d 'show help' complete -x -c greet -n '__fish_seen_subcommand_from config c; and not __fish_seen_subcommand_from sub-config s ss help h' -a 'sub-config' -d 'another usage test' -complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l sub-flag -s sub-fl -s s -r -complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l sub-command-flag -s s -d 'some usage text' -complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l help -s h -d 'show help' -complete -x -c greet -n '__fish_seen_subcommand_from sub-config s ss; and not __fish_seen_subcommand_from help h' -a 'help' -d 'Shows a list of commands or help for one command' +complete -c greet -n '__fish_seen_subcommand_from config c; and __fish_seen_subcommand_from sub-config s ss' -f -l sub-flag -s sub-fl -s s -r +complete -c greet -n '__fish_seen_subcommand_from config c; and __fish_seen_subcommand_from sub-config s ss' -f -l sub-command-flag -s s -d 'some usage text' +complete -c greet -n '__fish_seen_subcommand_from config c; and __fish_seen_subcommand_from sub-config s ss' -f -l help -s h -d 'show help' +complete -x -c greet -n '__fish_seen_subcommand_from config c; and __fish_seen_subcommand_from sub-config s ss; 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_seen_subcommand_from config c; and not __fish_seen_subcommand_from sub-config s ss 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 'info' -d 'retrieve generic information' complete -c greet -n '__fish_seen_subcommand_from info i in' -f -l help -s h -d 'show help' @@ -38,7 +38,7 @@ complete -c greet -n '__fish_seen_subcommand_from usage u' -l flag -s fl -s f -r complete -c greet -n '__fish_seen_subcommand_from usage u' -f -l another-flag -s b -d 'another usage text' complete -c greet -n '__fish_seen_subcommand_from usage u' -f -l help -s h -d 'show help' complete -x -c greet -n '__fish_seen_subcommand_from usage u; and not __fish_seen_subcommand_from sub-usage su help h' -a 'sub-usage' -d 'standard usage text' -complete -c greet -n '__fish_seen_subcommand_from sub-usage su' -f -l sub-command-flag -s s -d 'some usage text' -complete -c greet -n '__fish_seen_subcommand_from sub-usage su' -f -l help -s h -d 'show help' -complete -x -c greet -n '__fish_seen_subcommand_from sub-usage su; and not __fish_seen_subcommand_from help h' -a 'help' -d 'Shows a list of commands or help for one command' +complete -c greet -n '__fish_seen_subcommand_from usage u; and __fish_seen_subcommand_from sub-usage su' -f -l sub-command-flag -s s -d 'some usage text' +complete -c greet -n '__fish_seen_subcommand_from usage u; and __fish_seen_subcommand_from sub-usage su' -f -l help -s h -d 'show help' +complete -x -c greet -n '__fish_seen_subcommand_from usage u; and __fish_seen_subcommand_from sub-usage su; 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_seen_subcommand_from usage u; and not __fish_seen_subcommand_from sub-usage su help h' -a 'help' -d 'Shows a list of commands or help for one command' From 0edd0388b2c747e30b38d7f01558b87e64589a70 Mon Sep 17 00:00:00 2001 From: Bittrance Date: Wed, 21 May 2025 23:18:55 +0200 Subject: [PATCH 2/2] Simplify fish subcommand chain generation. Co-authored-by: Eng Zer Jun --- fish.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fish.go b/fish.go index 0441d092eb..320dfc6c69 100644 --- a/fish.go +++ b/fish.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "slices" "strings" "text/template" ) @@ -173,14 +172,12 @@ func fishFlagHelper(binary string, command *Command) string { func commandAncestry(command *Command) string { var ancestry []string ancestors := command.Lineage() - slices.Reverse(ancestors) - ancestors = ancestors[1:] - for _, ancestor := range ancestors { + for i := len(ancestors) - 2; i >= 0; i-- { ancestry = append( ancestry, fmt.Sprintf( "__fish_seen_subcommand_from %s", - strings.Join(ancestor.Names(), " "), + strings.Join(ancestors[i].Names(), " "), ), ) }