From 14efddf125f775431549696953199ce663b33637 Mon Sep 17 00:00:00 2001 From: Brandon Roehl Date: Tue, 26 Jun 2018 20:28:31 -0500 Subject: [PATCH 1/5] Commands but with the short descriptions now in zsh completion --- zsh_completions.go | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/zsh_completions.go b/zsh_completions.go index 889c22e2..9e8ff087 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -1,10 +1,12 @@ -package cobra +package main import ( + "os" + "io" "bytes" "fmt" - "io" - "os" + "github.com/spf13/cobra" +"github.com/spf13/pflag" "strings" ) @@ -83,8 +85,10 @@ func writeLevel(w io.Writer, root *Command, i int) { for p, c := range byParent { names := names(c) - fmt.Fprintf(w, " %s)\n", p) - fmt.Fprintf(w, " _arguments '%d: :(%s)'\n", i, strings.Join(names, " ")) + //flags := flags(p) + fmt.Fprintf(w, " %s)\n", p.Name()) + fmt.Fprintf(w, " _values '%s command' ", p.Name()) + fmt.Fprintf(w, "'%s'\n", strings.Join(names, "' '")) fmt.Fprintln(w, " ;;") } fmt.Fprintln(w, " *)") @@ -105,14 +109,14 @@ func filterByLevel(c *Command, l int) []*Command { return cs } -func groupByParent(commands []*Command) map[string][]*Command { - m := make(map[string][]*Command) +func groupByParent(commands []*Command) map[*Command][]*Command { + m := make(map[*Command][]*Command) for _, c := range commands { parent := c.Parent() if parent == nil { continue } - m[parent.Name()] = append(m[parent.Name()], c) + m[parent] = append(m[parent], c) } return m } @@ -120,7 +124,20 @@ func groupByParent(commands []*Command) map[string][]*Command { func names(commands []*Command) []string { ns := make([]string, len(commands)) for i, c := range commands { - ns[i] = c.Name() + ns[i] = fmt.Sprintf("%s[%s]", c.Name(), c.Short) + } + return ns +} + +func flags(command *Command) []string { + flags := command.Flags() + ns := make([]string, flags.NArg()) + if flags.NArg() > 0 { + i := 0 + flags.VisitAll(func(f *pflag.Flag) { + ns[i] = fmt.Sprintf("%s[%s]", f.Name, f.Usage) + i += 1 + }) } return ns } From cbe65494b77766d8eb4106d01a255de27178992b Mon Sep 17 00:00:00 2001 From: Brandon Roehl Date: Wed, 27 Jun 2018 10:48:09 -0500 Subject: [PATCH 2/5] Allow flag completion as well --- zsh_completions.go | 100 ++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/zsh_completions.go b/zsh_completions.go index 9e8ff087..4651cf2f 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -5,8 +5,6 @@ import ( "io" "bytes" "fmt" - "github.com/spf13/cobra" -"github.com/spf13/pflag" "strings" ) @@ -53,7 +51,9 @@ func maxDepth(c *Command) int { } func writeLevelMapping(w io.Writer, numLevels int) { - fmt.Fprintln(w, `_arguments \`) + fmt.Fprintln(w, "local -a rails_options") + fmt.Fprintln(w, `_arguments -C \`) + fmt.Fprintln(w, ` $jamf_pro_options \`) for i := 1; i <= numLevels; i++ { fmt.Fprintf(w, ` '%d: :->level%d' \`, i, i) fmt.Fprintln(w) @@ -64,64 +64,64 @@ func writeLevelMapping(w io.Writer, numLevels int) { func writeLevelCases(w io.Writer, maxDepth int, root *Command) { fmt.Fprintln(w, "case $state in") - defer fmt.Fprintln(w, "esac") - for i := 1; i <= maxDepth; i++ { - fmt.Fprintf(w, " level%d)\n", i) writeLevel(w, root, i) - fmt.Fprintln(w, " ;;") } fmt.Fprintln(w, " *)") fmt.Fprintln(w, " _arguments '*: :_files'") fmt.Fprintln(w, " ;;") + fmt.Fprintln(w, "esac") } -func writeLevel(w io.Writer, root *Command, i int) { - fmt.Fprintf(w, " case $words[%d] in\n", i) - defer fmt.Fprintln(w, " esac") - - commands := filterByLevel(root, i) - byParent := groupByParent(commands) - - for p, c := range byParent { - names := names(c) - //flags := flags(p) - fmt.Fprintf(w, " %s)\n", p.Name()) - fmt.Fprintf(w, " _values '%s command' ", p.Name()) - fmt.Fprintf(w, "'%s'\n", strings.Join(names, "' '")) - fmt.Fprintln(w, " ;;") +func writeLevel(w io.Writer, root *Command, level int) { + fmt.Fprintf(w, " level%d)\n", level) + fmt.Fprintf(w, " case $words[%d] in\n", level) + for _, c := range filterByLevel(root, level) { + writeCommandArgsBlock(w, c) } fmt.Fprintln(w, " *)") fmt.Fprintln(w, " _arguments '*: :_files'") fmt.Fprintln(w, " ;;") + fmt.Fprintln(w, " esac") + fmt.Fprintln(w, " ;;") +} +func writeCommandArgsBlock(w io.Writer, c *Command) { + names := commandNames(c) + flags := commandFlags(c) + if len(names) > 0 || len(flags) > 0 { + fmt.Fprintf(w, " %s)\n", c.Name()) + defer fmt.Fprintln(w, " ;;") + } + if len(flags) > 0 { + fmt.Fprintln(w, " jamf_pro_options=(") + for _, flag := range flags { + fmt.Fprintf(w, " %s\n", flag) + } + fmt.Fprintln(w, "\n )") + } + if len(names) > 0 { + fmt.Fprintf(w, " _values 'command' '%s'\n", strings.Join(names, "' '")) + } } func filterByLevel(c *Command, l int) []*Command { - cs := make([]*Command, 0) - if l == 0 { - cs = append(cs, c) - return cs - } - for _, s := range c.Commands() { - cs = append(cs, filterByLevel(s, l-1)...) - } - return cs -} - -func groupByParent(commands []*Command) map[*Command][]*Command { - m := make(map[*Command][]*Command) - for _, c := range commands { - parent := c.Parent() - if parent == nil { - continue + commands := []*Command{c} + for i := 1; i < l; i++ { + var nextLevel []*Command + for _, c := range commands { + if c.HasSubCommands() { + nextLevel = append(nextLevel, c.Commands()...) + } } - m[parent] = append(m[parent], c) + commands = nextLevel } - return m + + return commands } -func names(commands []*Command) []string { +func commandNames(command *Command) []string { + commands := command.Commands() ns := make([]string, len(commands)) for i, c := range commands { ns[i] = fmt.Sprintf("%s[%s]", c.Name(), c.Short) @@ -129,15 +129,15 @@ func names(commands []*Command) []string { return ns } -func flags(command *Command) []string { +func commandFlags(command *Command) []string { flags := command.Flags() - ns := make([]string, flags.NArg()) - if flags.NArg() > 0 { - i := 0 - flags.VisitAll(func(f *pflag.Flag) { - ns[i] = fmt.Sprintf("%s[%s]", f.Name, f.Usage) - i += 1 - }) - } + ns := make([]string, 0) + flags.VisitAll(func(flag *pflag.Flag) { + if len(flag.Shorthand) > 0 { + ns = append(ns, fmt.Sprintf("{-%s,--%s}'[%s]'", flag.Shorthand, flag.Name, flag.Usage)) + } else { + ns = append(ns, fmt.Sprintf("--%s'[%s]'", flag.Name, flag.Usage)) + } + }) return ns } From f76be5b1f06556f9f8b26e1736779a47c91c2111 Mon Sep 17 00:00:00 2001 From: Brandon Roehl Date: Wed, 27 Jun 2018 10:53:11 -0500 Subject: [PATCH 3/5] Generic and less diff --- zsh_completions.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zsh_completions.go b/zsh_completions.go index 4651cf2f..f1f1709c 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -1,10 +1,10 @@ package main import ( - "os" - "io" "bytes" "fmt" + "io" + "os" "strings" ) @@ -51,7 +51,7 @@ func maxDepth(c *Command) int { } func writeLevelMapping(w io.Writer, numLevels int) { - fmt.Fprintln(w, "local -a rails_options") + fmt.Fprintln(w, "local -a cmd_options") fmt.Fprintln(w, `_arguments -C \`) fmt.Fprintln(w, ` $jamf_pro_options \`) for i := 1; i <= numLevels; i++ { @@ -94,7 +94,7 @@ func writeCommandArgsBlock(w io.Writer, c *Command) { defer fmt.Fprintln(w, " ;;") } if len(flags) > 0 { - fmt.Fprintln(w, " jamf_pro_options=(") + fmt.Fprintln(w, " cmd_options=(") for _, flag := range flags { fmt.Fprintf(w, " %s\n", flag) } From 009ebb14c97e823e3e541222b1c58a738b70b975 Mon Sep 17 00:00:00 2001 From: Brandon Roehl Date: Wed, 27 Jun 2018 11:10:57 -0500 Subject: [PATCH 4/5] All test passing --- zsh_completions.go | 21 ++++++++++++++++----- zsh_completions_test.go | 4 ++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/zsh_completions.go b/zsh_completions.go index f1f1709c..d254cfd3 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -1,4 +1,4 @@ -package main +package cobra import ( "bytes" @@ -6,6 +6,8 @@ import ( "io" "os" "strings" + + flag "github.com/spf13/pflag" ) // GenZshCompletionFile generates zsh completion file. @@ -124,7 +126,11 @@ func commandNames(command *Command) []string { commands := command.Commands() ns := make([]string, len(commands)) for i, c := range commands { - ns[i] = fmt.Sprintf("%s[%s]", c.Name(), c.Short) + commandMsg := c.Name() + if len(c.Short) > 0 { + commandMsg += fmt.Sprintf("[%s]", c.Short) + } + ns[i] = commandMsg } return ns } @@ -132,12 +138,17 @@ func commandNames(command *Command) []string { func commandFlags(command *Command) []string { flags := command.Flags() ns := make([]string, 0) - flags.VisitAll(func(flag *pflag.Flag) { + flags.VisitAll(func(flag *flag.Flag) { + var flagMsg string if len(flag.Shorthand) > 0 { - ns = append(ns, fmt.Sprintf("{-%s,--%s}'[%s]'", flag.Shorthand, flag.Name, flag.Usage)) + flagMsg = fmt.Sprintf("{-%s,--%s}", flag.Shorthand, flag.Name) } else { - ns = append(ns, fmt.Sprintf("--%s'[%s]'", flag.Name, flag.Usage)) + flagMsg = fmt.Sprintf("--%s", flag.Name) } + if len(flag.Usage) > 0 { + flagMsg += fmt.Sprintf("'[%s]'", flag.Usage) + } + ns = append(ns, flagMsg) }) return ns } diff --git a/zsh_completions_test.go b/zsh_completions_test.go index 34e69496..f8d5388a 100644 --- a/zsh_completions_test.go +++ b/zsh_completions_test.go @@ -42,7 +42,7 @@ func TestZshCompletion(t *testing.T) { r.AddCommand(&Command{Use: "c2"}) return r }(), - expectedExpressions: []string{"(c1 c2)"}, + expectedExpressions: []string{"'c1' 'c2'"}, }, { name: "tree", @@ -69,7 +69,7 @@ func TestZshCompletion(t *testing.T) { return r }(), - expectedExpressions: []string{"(sub11 sub12)", "(sub21 sub22)"}, + expectedExpressions: []string{"'sub11' 'sub12'", "'sub21' 'sub22'"}, }, } From a66d0ae17ac8e30a345f2b7fca62fd962a6a729a Mon Sep 17 00:00:00 2001 From: Brandon Roehl Date: Wed, 27 Jun 2018 12:46:06 -0500 Subject: [PATCH 5/5] Conform more to the style --- zsh_completions.go | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/zsh_completions.go b/zsh_completions.go index d254cfd3..6f3b4b45 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -21,14 +21,23 @@ func (c *Command) GenZshCompletionFile(filename string) error { return c.GenZshCompletion(outFile) } +func argName(cmd *Command) string { + for cmd.HasParent() { + cmd = cmd.Parent() + } + name := fmt.Sprintf("%s_cmd_args", cmd.Name()) + return strings.Replace(name, "-", "_",-1) +} // GenZshCompletion generates a zsh completion file and writes to the passed writer. func (c *Command) GenZshCompletion(w io.Writer) error { buf := new(bytes.Buffer) writeHeader(buf, c) maxDepth := maxDepth(c) - writeLevelMapping(buf, maxDepth) + fmt.Fprintf(buf, "_%s() {\n", c.Name()) + writeLevelMapping(buf, maxDepth, c) writeLevelCases(buf, maxDepth, c) + fmt.Fprintf(buf, "}\n_%s \"$@\"\n", c.Name()) _, err := buf.WriteTo(w) return err @@ -52,16 +61,15 @@ func maxDepth(c *Command) int { return 1 + maxDepthSub } -func writeLevelMapping(w io.Writer, numLevels int) { - fmt.Fprintln(w, "local -a cmd_options") +func writeLevelMapping(w io.Writer, numLevels int, root *Command) { + fmt.Fprintln(w, `local context curcontext="$curcontext" state line`) + fmt.Fprintln(w, `typeset -A opt_args`) fmt.Fprintln(w, `_arguments -C \`) - fmt.Fprintln(w, ` $jamf_pro_options \`) for i := 1; i <= numLevels; i++ { - fmt.Fprintf(w, ` '%d: :->level%d' \`, i, i) - fmt.Fprintln(w) + fmt.Fprintf(w, " '%d: :->level%d' \\\n", i, i) } - fmt.Fprintf(w, ` '%d: :%s'`, numLevels+1, "_files") - fmt.Fprintln(w) + fmt.Fprintf(w, " $%s \\\n", argName(root)) + fmt.Fprintln(w, ` '*: :_files'`) } func writeLevelCases(w io.Writer, maxDepth int, root *Command) { @@ -75,10 +83,10 @@ func writeLevelCases(w io.Writer, maxDepth int, root *Command) { fmt.Fprintln(w, "esac") } -func writeLevel(w io.Writer, root *Command, level int) { - fmt.Fprintf(w, " level%d)\n", level) - fmt.Fprintf(w, " case $words[%d] in\n", level) - for _, c := range filterByLevel(root, level) { +func writeLevel(w io.Writer, root *Command, l int) { + fmt.Fprintf(w, " level%d)\n", l) + fmt.Fprintf(w, " case $words[%d] in\n", l) + for _, c := range filterByLevel(root, l) { writeCommandArgsBlock(w, c) } fmt.Fprintln(w, " *)") @@ -96,11 +104,11 @@ func writeCommandArgsBlock(w io.Writer, c *Command) { defer fmt.Fprintln(w, " ;;") } if len(flags) > 0 { - fmt.Fprintln(w, " cmd_options=(") + fmt.Fprintf(w, " %s=(\n", argName(c)) for _, flag := range flags { fmt.Fprintf(w, " %s\n", flag) } - fmt.Fprintln(w, "\n )") + fmt.Fprintln(w, " )") } if len(names) > 0 { fmt.Fprintf(w, " _values 'command' '%s'\n", strings.Join(names, "' '"))