From 72b055394ba9268bf7f7a773b5d29262d2ee65d8 Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Mon, 20 Jan 2025 00:22:48 +0100 Subject: [PATCH 1/2] feat: add CompletionFunc type to help with completions Also document that NoFilesCompletion and FixedCompletion can be used with RegisterFlagCompletionFunc. --- command.go | 2 +- completions.go | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/command.go b/command.go index 19602946e..58f071a32 100644 --- a/command.go +++ b/command.go @@ -83,7 +83,7 @@ type Command struct { // ValidArgsFunction is an optional function that provides valid non-flag arguments for shell completion. // It is a dynamic version of using ValidArgs. // Only one of ValidArgs and ValidArgsFunction can be used for a command. - ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + ValidArgsFunction CompletionFunc // Expected arguments Args PositionalArgs diff --git a/completions.go b/completions.go index 0862d3f6c..f5daa1447 100644 --- a/completions.go +++ b/completions.go @@ -35,7 +35,7 @@ const ( ) // Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it. -var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} +var flagCompletionFunctions = map[*pflag.Flag]CompletionFunc{} // lock for reading and writing from flagCompletionFunctions var flagCompletionMutex = &sync.RWMutex{} @@ -117,22 +117,31 @@ type CompletionOptions struct { HiddenDefaultCmd bool } +// CompletionFunc is a function that provides completion results. +type CompletionFunc func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + // NoFileCompletions can be used to disable file completion for commands that should // not trigger file completions. +// +// This method satisfies [CompletionFunc] and can be used with [Command.RegisterFlagCompletionFunc] func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return nil, ShellCompDirectiveNoFileComp } // FixedCompletions can be used to create a completion function which always // returns the same results. -func FixedCompletions(choices []string, directive ShellCompDirective) func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { +// +// This method returns a function that satisfies [CompletionFunc] and can be used with [Command.RegisterFlagCompletionFunc] +func FixedCompletions(choices []string, directive ShellCompDirective) CompletionFunc { return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return choices, directive } } // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag. -func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error { +// +// You can use [FixedCompletions] or [NoFileCompletions] as completion functions. +func (c *Command) RegisterFlagCompletionFunc(flagName string, f CompletionFunc) error { flag := c.Flag(flagName) if flag == nil { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName) @@ -148,7 +157,7 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman } // GetFlagCompletionFunc returns the completion function for the given flag of the command, if available. -func (c *Command) GetFlagCompletionFunc(flagName string) (func(*Command, []string, string) ([]string, ShellCompDirective), bool) { +func (c *Command) GetFlagCompletionFunc(flagName string) (CompletionFunc, bool) { flag := c.Flag(flagName) if flag == nil { return nil, false @@ -519,7 +528,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } // Find the completion function for the flag or command - var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + var completionFn CompletionFunc if flag != nil && flagCompletion { flagCompletionMutex.RLock() completionFn = flagCompletionFunctions[flag] From 9a528ef57a8ba57404c71fa18ca37d5dbe6ded6f Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Tue, 21 Jan 2025 22:54:52 +0100 Subject: [PATCH 2/2] Apply code review feedback Co-Authored-By: Marc Khouzam --- completions.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/completions.go b/completions.go index f5daa1447..f7be8c73b 100644 --- a/completions.go +++ b/completions.go @@ -123,7 +123,8 @@ type CompletionFunc func(cmd *Command, args []string, toComplete string) ([]stri // NoFileCompletions can be used to disable file completion for commands that should // not trigger file completions. // -// This method satisfies [CompletionFunc] and can be used with [Command.RegisterFlagCompletionFunc] +// This method satisfies [CompletionFunc]. +// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction]. func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return nil, ShellCompDirectiveNoFileComp } @@ -131,7 +132,8 @@ func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string // FixedCompletions can be used to create a completion function which always // returns the same results. // -// This method returns a function that satisfies [CompletionFunc] and can be used with [Command.RegisterFlagCompletionFunc] +// This method returns a function that satisfies [CompletionFunc] +// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction]. func FixedCompletions(choices []string, directive ShellCompDirective) CompletionFunc { return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return choices, directive @@ -140,7 +142,8 @@ func FixedCompletions(choices []string, directive ShellCompDirective) Completion // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag. // -// You can use [FixedCompletions] or [NoFileCompletions] as completion functions. +// You can use pre-defined completion functions such as [FixedCompletions] or [NoFileCompletions], +// or you can define your own. func (c *Command) RegisterFlagCompletionFunc(flagName string, f CompletionFunc) error { flag := c.Flag(flagName) if flag == nil {