2017-07-30 11:42:35 +03:00
|
|
|
package cobra
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-09-05 19:32:32 +03:00
|
|
|
"os"
|
2017-07-30 11:42:35 +03:00
|
|
|
"strings"
|
2018-02-24 19:53:13 +03:00
|
|
|
"text/template"
|
|
|
|
|
|
|
|
"github.com/spf13/pflag"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
funcMap = template.FuncMap{
|
|
|
|
"constructPath": constructPath,
|
|
|
|
"subCmdList": subCmdList,
|
|
|
|
"extractFlags": extractFlags,
|
2018-02-25 09:20:34 +03:00
|
|
|
"cmdName": cmdName,
|
2018-02-24 19:53:13 +03:00
|
|
|
"simpleFlag": simpleFlag,
|
|
|
|
}
|
|
|
|
zshCompletionText = `
|
|
|
|
{{/* for pflag.Flag (specifically annotations) */}}
|
|
|
|
{{define "flagAnnotations" -}}
|
|
|
|
{{with index .Annotations "cobra_annotation_bash_completion_filename_extensions"}}:filename:_files{{end}}
|
|
|
|
{{- end}}
|
|
|
|
|
|
|
|
{{/* for pflag.Flag with short and long options */}}
|
|
|
|
{{define "complexFlag" -}}
|
|
|
|
"(-{{.Shorthand}} --{{.Name}})"{-{{.Shorthand}},--{{.Name}}}"[{{.Usage}}]{{template "flagAnnotations" .}}"
|
|
|
|
{{- end}}
|
|
|
|
|
|
|
|
{{/* for pflag.Flag with either short or long options */}}
|
|
|
|
{{define "simpleFlag" -}}
|
|
|
|
"{{with .Name}}--{{.}}{{else}}-{{.Shorthand}}{{end}}[{{.Usage}}]{{template "flagAnnotations" .}}"
|
|
|
|
{{- end}}
|
|
|
|
|
|
|
|
{{/* should accept Command (that contains subcommands) as parameter */}}
|
|
|
|
{{define "argumentsC" -}}
|
|
|
|
function {{constructPath .}} {
|
2018-02-26 23:31:06 +03:00
|
|
|
local -a commands
|
2018-02-24 19:53:13 +03:00
|
|
|
|
2018-02-26 23:31:06 +03:00
|
|
|
_arguments -C \{{- range extractFlags .}}
|
|
|
|
{{if simpleFlag .}}{{template "simpleFlag" .}}{{else}}{{template "complexFlag" .}}{{end}} \{{- end}}
|
|
|
|
"1: :->cmnds" \
|
2018-02-24 19:53:13 +03:00
|
|
|
"*::arg:->args"
|
|
|
|
|
2018-02-26 23:31:06 +03:00
|
|
|
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
|
2018-02-24 19:53:13 +03:00
|
|
|
}
|
2018-02-26 23:31:06 +03:00
|
|
|
{{range .Commands}}{{if not .Hidden}}
|
2018-02-24 19:53:13 +03:00
|
|
|
{{template "selectCmdTemplate" .}}
|
2018-02-26 23:31:06 +03:00
|
|
|
{{- end}}{{end}}
|
2018-02-24 19:53:13 +03:00
|
|
|
{{- end}}
|
|
|
|
|
|
|
|
{{/* should accept Command without subcommands as parameter */}}
|
|
|
|
{{define "arguments" -}}
|
|
|
|
function {{constructPath .}} {
|
2018-02-26 23:31:06 +03:00
|
|
|
{{" _arguments"}}{{range extractFlags .}} \
|
2018-02-24 19:53:13 +03:00
|
|
|
{{if simpleFlag .}}{{template "simpleFlag" .}}{{else}}{{template "complexFlag" .}}{{end -}}
|
|
|
|
{{end}}
|
|
|
|
}
|
2018-02-26 23:31:06 +03:00
|
|
|
{{end}}
|
2018-02-24 19:53:13 +03:00
|
|
|
|
2018-02-26 23:31:06 +03:00
|
|
|
{{/* dispatcher for commands with or without subcommands */}}
|
2018-02-24 19:53:13 +03:00
|
|
|
{{define "selectCmdTemplate" -}}
|
2018-02-25 15:12:58 +03:00
|
|
|
{{if .Hidden}}{{/* ignore hidden*/}}{{else -}}
|
2018-02-24 19:53:13 +03:00
|
|
|
{{if .Commands}}{{template "argumentsC" .}}{{else}}{{template "arguments" .}}{{end}}
|
|
|
|
{{- end}}
|
2018-02-25 15:12:58 +03:00
|
|
|
{{- end}}
|
2018-02-24 19:53:13 +03:00
|
|
|
|
2018-02-26 23:31:06 +03:00
|
|
|
{{/* template entry point */}}
|
2018-02-24 19:53:13 +03:00
|
|
|
{{define "Main" -}}
|
2018-02-25 09:20:34 +03:00
|
|
|
#compdef _{{cmdName .}} {{cmdName .}}
|
2018-02-24 19:53:13 +03:00
|
|
|
|
|
|
|
{{template "selectCmdTemplate" .}}
|
|
|
|
{{end}}
|
|
|
|
`
|
2017-07-30 11:42:35 +03:00
|
|
|
)
|
|
|
|
|
2017-09-05 19:32:32 +03:00
|
|
|
// GenZshCompletionFile generates zsh completion file.
|
2017-09-05 20:20:51 +03:00
|
|
|
func (c *Command) GenZshCompletionFile(filename string) error {
|
2017-09-05 19:32:32 +03:00
|
|
|
outFile, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer outFile.Close()
|
|
|
|
|
2017-09-05 20:20:51 +03:00
|
|
|
return c.GenZshCompletion(outFile)
|
2017-09-05 19:32:32 +03:00
|
|
|
}
|
|
|
|
|
2017-07-30 11:42:35 +03:00
|
|
|
// GenZshCompletion generates a zsh completion file and writes to the passed writer.
|
2017-09-05 20:20:51 +03:00
|
|
|
func (c *Command) GenZshCompletion(w io.Writer) error {
|
2018-02-24 19:53:13 +03:00
|
|
|
tmpl, err := template.New("Main").Funcs(funcMap).Parse(zshCompletionText)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error creating zsh completion template: %v", err)
|
|
|
|
}
|
|
|
|
return tmpl.Execute(w, c)
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
func constructPath(c *Command) string {
|
|
|
|
var path []string
|
|
|
|
tmpCmd := c
|
2018-02-25 09:20:34 +03:00
|
|
|
path = append(path, tmpCmd.Name())
|
2017-07-30 11:42:35 +03:00
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
for {
|
|
|
|
if !tmpCmd.HasParent() {
|
|
|
|
break
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|
2018-02-24 19:53:13 +03:00
|
|
|
tmpCmd = tmpCmd.Parent()
|
2018-02-25 09:20:34 +03:00
|
|
|
path = append(path, tmpCmd.Name())
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
// reverse path
|
|
|
|
for left, right := 0, len(path)-1; left < right; left, right = left+1, right-1 {
|
|
|
|
path[left], path[right] = path[right], path[left]
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
return "_" + strings.Join(path, "_")
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
// subCmdList returns a space separated list of subcommands names
|
|
|
|
func subCmdList(c *Command) string {
|
|
|
|
var subCmds []string
|
2017-07-30 11:42:35 +03:00
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
for _, cmd := range c.Commands() {
|
2018-02-25 15:12:58 +03:00
|
|
|
if cmd.Hidden {
|
|
|
|
continue
|
|
|
|
}
|
2018-02-25 09:20:34 +03:00
|
|
|
subCmds = append(subCmds, cmd.Name())
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
return strings.Join(subCmds, " ")
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
func extractFlags(c *Command) []*pflag.Flag {
|
|
|
|
var flags []*pflag.Flag
|
|
|
|
c.LocalFlags().VisitAll(func(f *pflag.Flag) {
|
2018-02-25 15:12:58 +03:00
|
|
|
if !f.Hidden {
|
|
|
|
flags = append(flags, f)
|
|
|
|
}
|
2018-02-24 19:53:13 +03:00
|
|
|
})
|
|
|
|
c.InheritedFlags().VisitAll(func(f *pflag.Flag) {
|
2018-02-25 15:12:58 +03:00
|
|
|
if !f.Hidden {
|
|
|
|
flags = append(flags, f)
|
|
|
|
}
|
2018-02-24 19:53:13 +03:00
|
|
|
})
|
|
|
|
return flags
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|
|
|
|
|
2018-02-25 09:20:34 +03:00
|
|
|
// cmdName returns the command's innvocation
|
|
|
|
func cmdName(c *Command) string {
|
|
|
|
return c.Name()
|
|
|
|
}
|
|
|
|
|
2018-02-24 19:53:13 +03:00
|
|
|
func simpleFlag(p *pflag.Flag) bool {
|
|
|
|
return p.Name == "" || p.Shorthand == ""
|
2017-07-30 11:42:35 +03:00
|
|
|
}
|