diff --git a/zsh_completions.go b/zsh_completions.go index 7fa0df1..9dbc9c7 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -37,43 +37,50 @@ var ( {{/* should accept Command (that contains subcommands) as parameter */}} {{define "argumentsC" -}} function {{constructPath .}} { - local line + local -a commands - _arguments -C \ -{{range extractFlags . -}} -{{" "}}{{if simpleFlag .}}{{template "simpleFlag" .}}{{else}}{{template "complexFlag" .}}{{end}} \ -{{end}} "1: :({{subCmdList .}})" \ + _arguments -C \{{- range extractFlags .}} + {{if simpleFlag .}}{{template "simpleFlag" .}}{{else}}{{template "complexFlag" .}}{{end}} \{{- end}} + "1: :->cmnds" \ "*::arg:->args" - case $line[1] in {{- range .Commands}}{{if not .Hidden}} - {{cmdName .}}) - {{constructPath .}} - ;; -{{end}}{{end}} esac + case $state in + cmnds) + commands=({{range .Commands}}{{if not .Hidden}} + "{{cmdName .}}:{{.Short}}"{{end}}{{end}} + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in {{- range .Commands}}{{if not .Hidden}} + {{cmdName .}}) + {{constructPath .}} + ;;{{end}}{{end}} + esac } -{{range .Commands}} +{{range .Commands}}{{if not .Hidden}} {{template "selectCmdTemplate" .}} -{{- end}} +{{- end}}{{end}} {{- end}} {{/* should accept Command without subcommands as parameter */}} {{define "arguments" -}} function {{constructPath .}} { -{{with extractFlags . -}} -{{ " _arguments" -}} -{{range .}} \ +{{" _arguments"}}{{range extractFlags .}} \ {{if simpleFlag .}}{{template "simpleFlag" .}}{{else}}{{template "complexFlag" .}}{{end -}} {{end}} -{{end -}} } -{{- end}} +{{end}} +{{/* dispatcher for commands with or without subcommands */}} {{define "selectCmdTemplate" -}} {{if .Hidden}}{{/* ignore hidden*/}}{{else -}} {{if .Commands}}{{template "argumentsC" .}}{{else}}{{template "arguments" .}}{{end}} {{- end}} {{- end}} +{{/* template entry point */}} {{define "Main" -}} #compdef _{{cmdName .}} {{cmdName .}} diff --git a/zsh_completions_test.go b/zsh_completions_test.go index d523762..c8d80c8 100644 --- a/zsh_completions_test.go +++ b/zsh_completions_test.go @@ -70,7 +70,7 @@ func TestGenZshCompletion(t *testing.T) { return r }(), expectedExpressions: []string{ - `\\\n\s+"1: :\(help subcmd1 subcmd2\)" \\\n`, + `commands=\(\n\s+"help:.*\n\s+"subcmd1:.*\n\s+"subcmd2:.*\n\s+\)`, `_arguments \\\n.*"--debug\[description]"`, `_arguments -C \\\n.*"--debug\[description]"`, `function _rootcmd_subcmd1 {`, @@ -185,24 +185,17 @@ func TestGenZshCompletionHidden(t *testing.T) { } } -func BenchmarkConstructPath(b *testing.B) { - c := &Command{ - Use: "main", - Long: "main long description which is very long", - Short: "main short description", - } - d := &Command{ - Use: "hello", - } - e := &Command{ - Use: "world", - } - c.AddCommand(d) - d.AddCommand(e) +func BenchmarkMediumSizeConstruct(b *testing.B) { + root := constructLargeCommandHeirarchy() + // if err := root.GenZshCompletionFile("_mycmd"); err != nil { + // b.Error(err) + // } + for i := 0; i < b.N; i++ { - res := constructPath(e) - if res != "_main_hello_world" { - b.Errorf("expeced path to be '_main_hello_world', got %s", res) + buf := new(bytes.Buffer) + err := root.GenZshCompletion(buf) + if err != nil { + b.Error(err) } } } @@ -232,3 +225,52 @@ func TestExtractFlags(t *testing.T) { t.Errorf("expected Command D to return 2 flags, got %d", len(resD)) } } + +func constructLargeCommandHeirarchy() *Command { + var config, st1, st2 string + var long, debug bool + var in1, in2 int + + r := genTestCommand("mycmd", false) + r.PersistentFlags().StringVarP(&config, "config", "c", config, "config usage") + if err := r.MarkPersistentFlagFilename("config", "*"); err != nil { + panic(err) + } + s1 := genTestCommand("sub1", true) + s1.Flags().BoolVar(&long, "long", long, "long descriptin") + s2 := genTestCommand("sub2", true) + s2.PersistentFlags().BoolVar(&debug, "debug", debug, "debug description") + s3 := genTestCommand("sub3", true) + s3.Hidden = true + s1_1 := genTestCommand("sub1sub1", true) + s1_1.Flags().StringVar(&st1, "st1", st1, "st1 description") + s1_1.Flags().StringVar(&st2, "st2", st2, "st2 description") + s1_2 := genTestCommand("sub1sub2", true) + s1_3 := genTestCommand("sub1sub3", true) + s1_3.Flags().IntVar(&in1, "int1", in1, "int1 descriptionn") + s1_3.Flags().IntVar(&in2, "int2", in2, "int2 descriptionn") + s2_1 := genTestCommand("sub2sub1", true) + s2_2 := genTestCommand("sub2sub2", true) + s2_3 := genTestCommand("sub2sub3", true) + s2_4 := genTestCommand("sub2sub4", true) + s2_5 := genTestCommand("sub2sub5", true) + + s1.AddCommand(s1_1, s1_2, s1_3) + s2.AddCommand(s2_1, s2_2, s2_3, s2_4, s2_5) + r.AddCommand(s1, s2, s3) + r.Execute() + return r +} + +func genTestCommand(name string, withRun bool) *Command { + r := &Command{ + Use: name, + Short: name + " short description", + Long: "Long description for " + name, + } + if withRun { + r.Run = emptyRun + } + + return r +}