From 2c26eda5e2f56d6bbfd28a165f51e76fcd57ee33 Mon Sep 17 00:00:00 2001 From: Evan Fiordeliso Date: Mon, 13 Nov 2023 13:20:43 -0500 Subject: [PATCH] Add bash implementation of autocomplete and add names helper for command and option sets --- command.go | 15 +++++++-------- completions_bash.go | 43 +++++++++++++++++++++++++++++++++++++++---- completions_fish.go | 4 ++-- opt/set.go | 10 ++++++++++ set.go | 9 +++++++++ tpl_funcs.go | 12 +++++++++++- 6 files changed, 78 insertions(+), 15 deletions(-) diff --git a/command.go b/command.go index 5112b00..e82b256 100644 --- a/command.go +++ b/command.go @@ -71,6 +71,13 @@ func (c *Command) Execute(args []string) { if c.isRoot { args = args[1:] } + if len(args) > 0 { + sc, ok := c.Subcommands.Get(args[0]) + if ok { + sc.Execute(args[1:]) + return + } + } parser := opt.NewParser(args, c.Opts, false) restArgs, err := parser.Parse() @@ -80,14 +87,6 @@ func (c *Command) Execute(args []string) { os.Exit(1) } - if len(restArgs) > 0 { - sc, ok := c.Subcommands.Get(restArgs[0]) - if ok { - sc.Execute(restArgs[1:]) - return - } - } - helpOpt, ok := opt.Globals().GetBool("help") if ok && helpOpt.Value() { c.ShowHelp() diff --git a/completions_bash.go b/completions_bash.go index a790090..b402e91 100644 --- a/completions_bash.go +++ b/completions_bash.go @@ -9,11 +9,46 @@ import ( func WriteBashCompletions(out io.Writer, rootCmd *Command) error { return bashTpl.Execute(out, map[string]any{ - "rootCmd": rootCmd, - "globalOpts": opt.Globals(), + "RootCmd": rootCmd, + "GlobalOpts": opt.Globals(), }) } -var bashTpl = template.Must(template.New("bash").Parse(` - +var bashTpl = template.Must(template.New("bash").Funcs(tplFuncs).Parse(` +{{- $rootCmd := .RootCmd -}} +{{- $progName := $rootCmd.Name -}} +{{- $varName := under $rootCmd.Name -}} + +{{- define "cmd" }} + {{ $currIdx := .Index }} + {{ $nextIdx := inc .Index }} + "{{ .Cmd.Name }}") + case ${COMP_WORDS[{{ $currIdx }}]} in + {{ range .Cmd.Subcommands -}} + {{ template "cmd" (map "Cmd" . "Index" $nextIdx) -}} + {{ end }} + *) + COMPREPLY=($(compgen -W "{{ join .Cmd.Subcommands.Names " " }} {{ join .Cmd.Opts.Names " " }}" -- $curr)) + esac + ;; +{{ end -}} + +_{{$varName}}_completions() +{ + local curr prev + COMPREPLY=() + curr=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + case ${COMP_WORDS[1]} in + {{ range .RootCmd.Subcommands -}} + {{ template "cmd" (map "Cmd" . "Index" 2) -}} + {{ end }} + + *) + COMPREPLY=($(compgen -W "{{ join .RootCmd.Subcommands.Names " " }} {{ join .RootCmd.Opts.Names " " }}" -- $curr)) + esac +} + +complete -F _{{$varName}}_completions {{$progName}} `)) diff --git a/completions_fish.go b/completions_fish.go index fd6ab09..8125f2d 100644 --- a/completions_fish.go +++ b/completions_fish.go @@ -19,7 +19,7 @@ var fishTpl = template.Must(template.New("fish").Funcs(tplFuncs).Parse(` {{- $rootCmd := .RootCmd -}} {{- $progName := $rootCmd.Name -}} {{- $varName := under $rootCmd.Name -}} -set -l commands {{- range $rootCmd.Subcommands }} {{ .Name }}{{ end }} +set -l commands {{ join $rootCmd.Subcommands.Names " " }} function __fish_{{ $varName }}_needs_command set -l cmd (commandline -opc) @@ -75,7 +75,7 @@ complete -f -c {{ $progName }} -n "__fish_{{ $varName }}_using_command {{.Cmd.Pa {{ end -}} {{ if gt (len .Cmd.Subcommands) 0 }} -set -l {{ $varPrefix }}commands {{- range .Cmd.Subcommands }} {{ .Name }}{{ end -}} +set -l {{ $varPrefix }}commands {{ join .Cmd.Subcommands.Names " " }} {{ $cmdName := .Cmd.Name }} {{- range .Cmd.Subcommands }} {{- template "cmd" (map "Cmd" . "ProgName" $progName "VarName" $varName) -}} diff --git a/opt/set.go b/opt/set.go index 7a76aca..8b43aa9 100644 --- a/opt/set.go +++ b/opt/set.go @@ -33,6 +33,16 @@ func (s Set) Get(name string) (Option, bool) { return nil, false } +func (s Set) Names() []string { + names := []string{} + for _, o := range s { + names = append(names, "--"+o.Name()) + names = append(names, "-"+o.ShortName()) + } + return names + +} + func (s Set) GetByLongName(longName string) (Option, bool) { for _, o := range s { if o.Name() == longName { diff --git a/set.go b/set.go index 2ef3976..8e756ea 100644 --- a/set.go +++ b/set.go @@ -35,6 +35,15 @@ func (s Set) MaxNameWidth() int { return max } +func (s Set) Names() []string { + names := make([]string, 0, len(s)) + for _, c := range s { + names = append(names, c.Name) + names = append(names, c.Aliases...) + } + return names +} + func (s Set) Len() int { return len(s) } diff --git a/tpl_funcs.go b/tpl_funcs.go index dd41bfa..a41e10b 100644 --- a/tpl_funcs.go +++ b/tpl_funcs.go @@ -8,9 +8,11 @@ import ( var tplFuncs = template.FuncMap{ "map": tplMap, + "cat": tplCat, "join": tplJoin, "under": tplUnder, "varPrefix": tplVarPrefix, + "inc": tplInc, } func tplMap(vals ...any) (map[string]any, error) { @@ -25,10 +27,14 @@ func tplMap(vals ...any) (map[string]any, error) { return m, nil } -func tplJoin(strs ...string) string { +func tplCat(strs ...string) string { return strings.Join(strs, "") } +func tplJoin(strs []string, sep string) string { + return strings.Join(strs, sep) +} + func tplUnder(s string) string { return strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(s, " ", "_"), "-", "_")) } @@ -40,3 +46,7 @@ func tplVarPrefix(s string) string { return tplUnder(s) + "_" } + +func tplInc(i int) int { + return i + 1 +}