diff --git a/api.go b/api.go index 7631138..88144fc 100644 --- a/api.go +++ b/api.go @@ -26,10 +26,11 @@ type programTree struct { CommandFn CommandFn HelpCommandName string mode Mode - unknownMode UnknownMode // Unknown option mode - requireOrder bool // stop parsing args as soon as an unknown is found - skipOptionsCopy bool // skips copying options from parent to child. Required when doing wrapper commands. - Suggestions []string // Suggestions used for completions + unknownMode UnknownMode // Unknown option mode + requireOrder bool // stop parsing args as soon as an unknown is found + skipOptionsCopy bool // skips copying options from parent to child. Required when doing wrapper commands. + Suggestions []string // Suggestions used for completions + SuggestionFns []CompletionFn // SuggestionsFns used for completions mapKeysToLower bool // controls wether or not map keys are normalized to lowercase @@ -275,6 +276,12 @@ ARGS_LOOP: } } } + // SuggestionFns + { + for _, fn := range currentProgramNode.SuggestionFns { + completions = append(completions, fn(completionMode, iterator.Value())...) + } + } // Provide other kinds of completions, like file completions. diff --git a/changelog.adoc b/changelog.adoc index 7a0a649..59efd64 100644 --- a/changelog.adoc +++ b/changelog.adoc @@ -1,6 +1,17 @@ = Changelog :toc: +== wip v0.30.0: New Features + +As the releases before, this release has 100% test coverage. +Tested with Go 1.16, 1.17, 1.18, 1.19, 1.20 and 1.21. + +=== New Features + +* add `opt.CustomCompletionFn` which allows to provide a custom completion function that gets lazily called. ++ +The completion function receives the target shell (bash or zsh) and the current partial completion. + == v0.29.0: New Features As the releases before, this release has 100% test coverage. diff --git a/examples/complex/complete/complete.go b/examples/complex/complete/complete.go new file mode 100644 index 0000000..88b67d2 --- /dev/null +++ b/examples/complex/complete/complete.go @@ -0,0 +1,41 @@ +package complete + +import ( + "context" + "io" + "log" + "strings" + + "github.com/DavidGamba/go-getoptions" +) + +var Logger = log.New(io.Discard, "log ", log.LstdFlags) + +// NewCommand - Populate Options definition +func NewCommand(parent *getoptions.GetOpt) *getoptions.GetOpt { + opt := parent.NewCommand("complete", "Example completions") + opt.SetCommandFn(Run) + opt.CustomCompletion("dev-east", "dev-west", "staging-east", "prod-east", "prod-west", "prod-south") + opt.CustomCompletionFn(func(target, s string) []string { + if strings.HasPrefix("dev-", s) { + return []string{"dev-hola/", "dev-hello"} + } + if strings.HasPrefix("dev-h", s) { + return []string{"dev-hola/", "dev-hello"} + } + if strings.HasPrefix("dev-hello", s) { + return []string{"dev-hello"} + } + if strings.HasPrefix("dev-hola/", s) { + return []string{"dev-hola/a", "dev-hola/b", "dev-hola/" + target} + } + return []string{} + }) + return opt +} + +// Run - Command entry point +func Run(ctx context.Context, opt *getoptions.GetOpt, args []string) error { + Logger.Printf("args: %v\n", args) + return nil +} diff --git a/examples/complex/main.go b/examples/complex/main.go index ac412ff..f03d11b 100644 --- a/examples/complex/main.go +++ b/examples/complex/main.go @@ -7,12 +7,14 @@ import ( "log" "os" - "github.com/DavidGamba/go-getoptions" + complexcomplete "github.com/DavidGamba/go-getoptions/examples/complex/complete" complexgreet "github.com/DavidGamba/go-getoptions/examples/complex/greet" complexlog "github.com/DavidGamba/go-getoptions/examples/complex/log" complexlswrapper "github.com/DavidGamba/go-getoptions/examples/complex/lswrapper" complexshow "github.com/DavidGamba/go-getoptions/examples/complex/show" complexslow "github.com/DavidGamba/go-getoptions/examples/complex/slow" + + "github.com/DavidGamba/go-getoptions" ) var Logger = log.New(io.Discard, "", log.LstdFlags) @@ -34,6 +36,7 @@ func program(args []string) int { complexlswrapper.NewCommand(opt) complexshow.NewCommand(opt) complexslow.NewCommand(opt) + complexcomplete.NewCommand(opt) opt.HelpCommand("help", opt.Alias("?")) remaining, err := opt.Parse(args[1:]) if err != nil { @@ -47,6 +50,7 @@ func program(args []string) int { complexlswrapper.Logger.SetOutput(os.Stderr) complexshow.Logger.SetOutput(os.Stderr) complexslow.Logger.SetOutput(os.Stderr) + complexcomplete.Logger.SetOutput(os.Stderr) } if opt.Called("profile") { Logger.Printf("profile: %s\n", opt.Value("profile")) diff --git a/user.go b/user.go index 2f3e6ac..dad88e1 100644 --- a/user.go +++ b/user.go @@ -187,6 +187,22 @@ func (gopt *GetOpt) CustomCompletion(list ...string) *GetOpt { return gopt } +// CompletionFn - Function receiver for custom completions. +// The `target` argument indicates "bash" or "zsh" for the completion targets. +// +// NOTE: Bash completions have = as a special char and results should be trimmed from the = on. +// This should not be done for Zsh. +type CompletionFn func(target, partialCompletion string) []string + +// CustomCompletionFn - Allows to define custom completion functions that get lazily called. +// +// NOTE: Bash completions have = as a special char and results should be trimmed from the = on. +// This should not be done for Zsh. +func (gopt *GetOpt) CustomCompletionFn(fn ...CompletionFn) *GetOpt { + gopt.programTree.SuggestionFns = append(gopt.programTree.SuggestionFns, fn...) + return gopt +} + // UnsetOptions - Unsets inherited options from parent program and parent commands. // This is useful when writing wrappers around other commands. //