-
Notifications
You must be signed in to change notification settings - Fork 47
/
cmd_zsh.go
127 lines (109 loc) · 2.37 KB
/
cmd_zsh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"html/template"
"io"
"os"
"os/exec"
"strings"
)
func init() {
commands["zsh-completion"] = cmdZshCompletion
}
func cmdZshCompletion(input io.Reader, output io.Writer, args []string) error {
fl := flag.NewFlagSet("", flag.ExitOnError)
fl.Usage = func() {
fmt.Fprintln(flag.CommandLine.Output(), `
Generate autocompletion for ZSH. To install it, run:
$ source <( bnscli zsh-completion)
`)
fl.PrintDefaults()
}
fl.Parse(args)
var cmds []cmd
for name := range commands {
if name == "zsh-completion" {
// Do not self loop.
continue
}
cmds = append(cmds, cmd{
Name: name,
ShName: strings.Replace(name, "-", "_", -1),
Args: extractArguments(name),
})
}
var b bytes.Buffer
if err := tmpl.Execute(&b, struct{ Cmds []cmd }{Cmds: cmds}); err != nil {
return err
}
b.WriteTo(output)
return nil
}
type cmd struct {
Name string
ShName string
Args []argument
}
// extractArguments returns flag information from given command. This is a
// quite hacky implementation. A separte process is used, because every command
// handler function is allowed to call os.Exit.
//
// This is a "the best result for the least effort" approach implementation.
func extractArguments(cmdName string) []argument {
var args []argument
var b bytes.Buffer
cmd := exec.Command(os.Args[0], cmdName, "-h")
cmd.Env = os.Environ()
cmd.Stderr = &b
cmd.Run()
rd := bufio.NewReader(&b)
var arg argument
for {
line, err := rd.ReadString('\n')
if err != nil {
return args
}
if arg.Flag != "" {
arg.Description = strings.TrimSpace(line)
args = append(args, arg)
arg = argument{}
} else if strings.HasPrefix(line, " -") {
fields := strings.Fields(line)
arg.Flag = fields[0]
//arg.Kind = fields[1]
}
}
}
type argument struct {
Flag string
Kind string
Description string
}
var tmpl = template.Must(template.New("").Parse(`
compdef _bnscli bnscli
function _bnscli {
local line
_arguments -C \
"-help[Show help information]" \
"1: :({{range .Cmds}}{{.Name}} {{end}})" \
"*::arg:->args"
case $line[1] in
{{range .Cmds -}}
{{.Name}})
_bnscli_cmd_{{.ShName}}
;;
{{- end}}
esac
}
{{range .Cmds}}
function _bnscli_cmd_{{.ShName}} {
_arguments \
{{range .Args -}}
"{{.Flag}}[{{.Description}}]" \
{{end}}
}
{{end}}
`))