From fb3f35b059c3bf440e9d84ddca1fe7d77c95ffd7 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 20 Apr 2025 11:14:50 +0900 Subject: [PATCH 01/11] feat: publish buildCompletionCommand This allows you to customize completion command. --- command_setup.go | 2 +- completion.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command_setup.go b/command_setup.go index 69b3ca198c..c4f901700c 100644 --- a/command_setup.go +++ b/command_setup.go @@ -89,7 +89,7 @@ func (cmd *Command) setupDefaults(osArgs []string) { } if cmd.EnableShellCompletion || cmd.Root().shellCompletion { - completionCommand := buildCompletionCommand(cmd.Name) + completionCommand := BuildCompletionCommand(cmd.Name) if cmd.ShellCompletionCommandName != "" { tracef( diff --git a/completion.go b/completion.go index d778380474..b4a5a60232 100644 --- a/completion.go +++ b/completion.go @@ -39,7 +39,7 @@ var ( } ) -func buildCompletionCommand(appName string) *Command { +func BuildCompletionCommand(appName string) *Command { return &Command{ Name: completionCommandName, Hidden: true, From dfa12967e2672de20d70b415db69ace838755fce Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 20 Apr 2025 11:39:15 +0900 Subject: [PATCH 02/11] feat: add the default usage and description --- completion.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/completion.go b/completion.go index b4a5a60232..e54588b512 100644 --- a/completion.go +++ b/completion.go @@ -39,10 +39,28 @@ var ( } ) +const completionDescription = `Output shell completion script for bash, zsh, fish, or Powershell. +Source the output to enable completion. + +# .bashrc +source <($COMMAND completion bash) + +# .zshrc +source <($COMMAND completion zsh) + +# fish +$COMMAND completion fish > ~/.config/fish/completions/$COMMAND.fish + +# Powershell +Output the script to path/to/autocomplete/$COMMAND.ps1 an run it. +` + func BuildCompletionCommand(appName string) *Command { return &Command{ - Name: completionCommandName, - Hidden: true, + Name: completionCommandName, + Hidden: true, + Usage: "Output shell completion script for bash, zsh, fish, or Powershell", + Description: completionDescription, Action: func(ctx context.Context, cmd *Command) error { return printShellCompletion(ctx, cmd, appName) }, From 484c29f1b06ee7721f538670e62d6f52fc322f77 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 20 Apr 2025 11:44:08 +0900 Subject: [PATCH 03/11] fix: replace $COMMAND with appName --- completion.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/completion.go b/completion.go index e54588b512..49423adc1f 100644 --- a/completion.go +++ b/completion.go @@ -5,6 +5,7 @@ import ( "embed" "fmt" "sort" + "strings" ) const ( @@ -60,7 +61,7 @@ func BuildCompletionCommand(appName string) *Command { Name: completionCommandName, Hidden: true, Usage: "Output shell completion script for bash, zsh, fish, or Powershell", - Description: completionDescription, + Description: strings.ReplaceAll(completionDescription, "$COMMAND", appName), Action: func(ctx context.Context, cmd *Command) error { return printShellCompletion(ctx, cmd, appName) }, From 91dfca6da1c0d0aae6c5a9b46eb77537dc57d720 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 20 Apr 2025 12:59:18 +0900 Subject: [PATCH 04/11] docs: update the document ```sh make generate ``` --- godoc-current.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/godoc-current.txt b/godoc-current.txt index d9448f3cbc..c6b1684c41 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -463,6 +463,8 @@ type Command struct { string slice of arguments such as os.Args. A given Command may contain Flags and sub-commands in Commands. +func BuildCompletionCommand(appName string) *Command + func (cmd *Command) Args() Args Args returns the command line arguments associated with the command. From ce1d77e9513489f6b493ef4cfeb09b314c63dff1 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 20 Apr 2025 13:03:08 +0900 Subject: [PATCH 05/11] docs: update the document ```sh make v3approve ``` --- testdata/godoc-v3.x.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index d9448f3cbc..c6b1684c41 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -463,6 +463,8 @@ type Command struct { string slice of arguments such as os.Args. A given Command may contain Flags and sub-commands in Commands. +func BuildCompletionCommand(appName string) *Command + func (cmd *Command) Args() Args Args returns the command line arguments associated with the command. From 23f8f563b32adb2f6000b8d17f91883ff3aa01c2 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 20 Apr 2025 20:18:50 +0900 Subject: [PATCH 06/11] docs: update the shell completion guide --- .../examples/completions/shell-completions.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/v3/examples/completions/shell-completions.md b/docs/v3/examples/completions/shell-completions.md index 901171a716..3c5a36bfb5 100644 --- a/docs/v3/examples/completions/shell-completions.md +++ b/docs/v3/examples/completions/shell-completions.md @@ -233,6 +233,48 @@ func main() { ``` ![](../../images/custom-bash-autocomplete.gif) +#### Make a completion command public + +By default, a completion command is hidden, meaning the command isn't included in the help message. +You can make it public by creating a completion command by `BuildCompletionCommand`, customizing the command, and appending it to the root command's `Commands`. + +```go +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Name: "greet", + // EnableShellCompletion is unnecessary + Commands: []*cli.Command{ + { + Name: "hello", + Usage: "Say hello", + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Println("Hello") + return nil + }, + }, + }, + } + completion := cli.BuildCompletionCommand(cmd.Name) // Create a completion command + completion.Hidden = false // Make completion command public + cmd.Commands = append(cmd.Commands, completion) // Append the completion command + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + #### Customization The default shell completion flag (`--generate-shell-completion`) is defined as From ec155d9cb10d3800420d26fcbf0cadf1a0a79556 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Thu, 24 Apr 2025 11:35:44 +0900 Subject: [PATCH 07/11] feat: add ConfigureShellCompletionCommand --- command.go | 2 ++ command_setup.go | 7 +++++-- completion.go | 2 +- funcs.go | 3 +++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/command.go b/command.go index 2783cb6b2f..30efa9d3e5 100644 --- a/command.go +++ b/command.go @@ -56,6 +56,8 @@ type Command struct { ShellCompletionCommandName string `json:"-"` // The function to call when checking for shell command completions ShellComplete ShellCompleteFunc `json:"-"` + // The function to configure a shell completion command + ConfigureShellCompletionCommand ConfigureShellCompletionCommand // An action to execute before any subcommands are run, but after the context is ready // If a non-nil error is returned, no subcommands are run Before BeforeFunc `json:"-"` diff --git a/command_setup.go b/command_setup.go index 9c6d8b0f4f..09df4a3084 100644 --- a/command_setup.go +++ b/command_setup.go @@ -88,8 +88,8 @@ func (cmd *Command) setupDefaults(osArgs []string) { cmd.SuggestCommandFunc = suggestCommand } - if isRoot && cmd.EnableShellCompletion { - completionCommand := BuildCompletionCommand(cmd.Name) + if isRoot && cmd.EnableShellCompletion || cmd.ConfigureShellCompletionCommand != nil { + completionCommand := buildCompletionCommand(cmd.Name) if cmd.ShellCompletionCommandName != "" { tracef( @@ -102,6 +102,9 @@ func (cmd *Command) setupDefaults(osArgs []string) { tracef("appending completionCommand (cmd=%[1]q)", cmd.Name) cmd.appendCommand(completionCommand) + if cmd.ConfigureShellCompletionCommand != nil { + cmd.ConfigureShellCompletionCommand(completionCommand) + } } tracef("setting command categories (cmd=%[1]q)", cmd.Name) diff --git a/completion.go b/completion.go index 1be06d1a08..d97ade6e49 100644 --- a/completion.go +++ b/completion.go @@ -56,7 +56,7 @@ $COMMAND completion fish > ~/.config/fish/completions/$COMMAND.fish Output the script to path/to/autocomplete/$COMMAND.ps1 an run it. ` -func BuildCompletionCommand(appName string) *Command { +func buildCompletionCommand(appName string) *Command { return &Command{ Name: completionCommandName, Hidden: true, diff --git a/funcs.go b/funcs.go index e0d1e19c2b..fe1224c44e 100644 --- a/funcs.go +++ b/funcs.go @@ -20,6 +20,9 @@ type ActionFunc func(context.Context, *Command) error // CommandNotFoundFunc is executed if the proper command cannot be found type CommandNotFoundFunc func(context.Context, *Command, string) +// ConfigureShellCompletionCommand is a function to configure a shell completion command +type ConfigureShellCompletionCommand func(*Command) + // OnUsageErrorFunc is executed if a usage error occurs. This is useful for displaying // customized usage error messages. This function is able to replace the // original error messages. If this function is not set, the "Incorrect usage" From ce756b4a723d335daee7a2c7b0d83a794c07d039 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 27 Apr 2025 07:51:28 +0900 Subject: [PATCH 08/11] docs: update godoc and the document --- docs/v3/examples/completions/shell-completions.md | 12 +++++++----- godoc-current.txt | 2 -- testdata/godoc-v3.x.txt | 2 -- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/v3/examples/completions/shell-completions.md b/docs/v3/examples/completions/shell-completions.md index 3c5a36bfb5..29b55daab3 100644 --- a/docs/v3/examples/completions/shell-completions.md +++ b/docs/v3/examples/completions/shell-completions.md @@ -233,10 +233,10 @@ func main() { ``` ![](../../images/custom-bash-autocomplete.gif) -#### Make a completion command public +#### Customize a completion command By default, a completion command is hidden, meaning the command isn't included in the help message. -You can make it public by creating a completion command by `BuildCompletionCommand`, customizing the command, and appending it to the root command's `Commands`. +You can customize it by setting root Command's `ConfigureShellCompletionCommand`. ```go package main @@ -254,6 +254,11 @@ func main() { cmd := &cli.Command{ Name: "greet", // EnableShellCompletion is unnecessary + ConfigureShellCompletionCommand: func(cmd *cli.Command) { // cmd is a completion command + cmd.Hidden = false // Make a completion command public + cmd.Usage = "..." // Customize Usage + cmd.Description = "..." // Customize Description + }, Commands: []*cli.Command{ { Name: "hello", @@ -265,9 +270,6 @@ func main() { }, }, } - completion := cli.BuildCompletionCommand(cmd.Name) // Create a completion command - completion.Hidden = false // Make completion command public - cmd.Commands = append(cmd.Commands, completion) // Append the completion command if err := cmd.Run(context.Background(), os.Args); err != nil { log.Fatal(err) diff --git a/godoc-current.txt b/godoc-current.txt index f7892fb16e..4a55270015 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -509,8 +509,6 @@ type Command struct { string slice of arguments such as os.Args. A given Command may contain Flags and sub-commands in Commands. -func BuildCompletionCommand(appName string) *Command - func (cmd *Command) Args() Args Args returns the command line arguments associated with the command. diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index f7892fb16e..4a55270015 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -509,8 +509,6 @@ type Command struct { string slice of arguments such as os.Args. A given Command may contain Flags and sub-commands in Commands. -func BuildCompletionCommand(appName string) *Command - func (cmd *Command) Args() Args Args returns the command line arguments associated with the command. From e905d7a9d9900c4f3e1b50f1327149e2619e5f08 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 27 Apr 2025 07:55:13 +0900 Subject: [PATCH 09/11] fix: fix a lint error --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index 30efa9d3e5..541081a59c 100644 --- a/command.go +++ b/command.go @@ -57,7 +57,7 @@ type Command struct { // The function to call when checking for shell command completions ShellComplete ShellCompleteFunc `json:"-"` // The function to configure a shell completion command - ConfigureShellCompletionCommand ConfigureShellCompletionCommand + ConfigureShellCompletionCommand ConfigureShellCompletionCommand `json:"-"` // An action to execute before any subcommands are run, but after the context is ready // If a non-nil error is returned, no subcommands are run Before BeforeFunc `json:"-"` From 38045e57c56a6fffbd84cd67d61da0f07cdc6b14 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 27 Apr 2025 07:58:25 +0900 Subject: [PATCH 10/11] docs: make generate --- godoc-current.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/godoc-current.txt b/godoc-current.txt index 4a55270015..34ef5b6158 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -433,6 +433,8 @@ type Command struct { ShellCompletionCommandName string `json:"-"` // The function to call when checking for shell command completions ShellComplete ShellCompleteFunc `json:"-"` + // The function to configure a shell completion command + ConfigureShellCompletionCommand ConfigureShellCompletionCommand `json:"-"` // An action to execute before any subcommands are run, but after the context is ready // If a non-nil error is returned, no subcommands are run Before BeforeFunc `json:"-"` @@ -751,6 +753,10 @@ type CommandCategory interface { type CommandNotFoundFunc func(context.Context, *Command, string) CommandNotFoundFunc is executed if the proper command cannot be found +type ConfigureShellCompletionCommand func(*Command) + ConfigureShellCompletionCommand is a function to configure a shell + completion command + type Countable interface { Count() int } From d82d555e514c1e8c20891fc11b671ac3cf60dd8c Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 27 Apr 2025 08:04:17 +0900 Subject: [PATCH 11/11] docs: make v3approve --- testdata/godoc-v3.x.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index 4a55270015..34ef5b6158 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -433,6 +433,8 @@ type Command struct { ShellCompletionCommandName string `json:"-"` // The function to call when checking for shell command completions ShellComplete ShellCompleteFunc `json:"-"` + // The function to configure a shell completion command + ConfigureShellCompletionCommand ConfigureShellCompletionCommand `json:"-"` // An action to execute before any subcommands are run, but after the context is ready // If a non-nil error is returned, no subcommands are run Before BeforeFunc `json:"-"` @@ -751,6 +753,10 @@ type CommandCategory interface { type CommandNotFoundFunc func(context.Context, *Command, string) CommandNotFoundFunc is executed if the proper command cannot be found +type ConfigureShellCompletionCommand func(*Command) + ConfigureShellCompletionCommand is a function to configure a shell + completion command + type Countable interface { Count() int }