-
Notifications
You must be signed in to change notification settings - Fork 196
/
command.go
141 lines (125 loc) · 3.11 KB
/
command.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package ishell
import (
"bytes"
"fmt"
"sort"
"text/tabwriter"
)
// Cmd is a shell command handler.
type Cmd struct {
// Command name.
Name string
// Command name aliases.
Aliases []string
// Function to execute for the command.
Func func(c *Context)
// One liner help message for the command.
Help string
// More descriptive help message for the command.
LongHelp string
// Completer is custom autocomplete for command.
// It takes in command arguments and returns
// autocomplete options.
// By default all commands get autocomplete of
// subcommands.
// A non-nil Completer overrides the default behaviour.
Completer func(args []string) []string
// CompleterWithPrefix is custom autocomplete like
// for Completer, but also provides the prefix
// already so far to the completion function
// If both Completer and CompleterWithPrefix are given,
// CompleterWithPrefix takes precedence
CompleterWithPrefix func(prefix string, args []string) []string
// subcommands.
children map[string]*Cmd
}
// AddCmd adds cmd as a subcommand.
func (c *Cmd) AddCmd(cmd *Cmd) {
if c.children == nil {
c.children = make(map[string]*Cmd)
}
c.children[cmd.Name] = cmd
}
// DeleteCmd deletes cmd from subcommands.
func (c *Cmd) DeleteCmd(name string) {
delete(c.children, name)
}
// Children returns the subcommands of c.
func (c *Cmd) Children() []*Cmd {
var cmds []*Cmd
for _, cmd := range c.children {
cmds = append(cmds, cmd)
}
sort.Sort(cmdSorter(cmds))
return cmds
}
func (c *Cmd) hasSubcommand() bool {
if len(c.children) > 1 {
return true
}
if _, ok := c.children["help"]; !ok {
return len(c.children) > 0
}
return false
}
// HelpText returns the computed help of the command and its subcommands.
func (c Cmd) HelpText() string {
var b bytes.Buffer
p := func(s ...interface{}) {
fmt.Fprintln(&b)
if len(s) > 0 {
fmt.Fprintln(&b, s...)
}
}
if c.LongHelp != "" {
p(c.LongHelp)
} else if c.Help != "" {
p(c.Help)
} else if c.Name != "" {
p(c.Name, "has no help")
}
if c.hasSubcommand() {
p("Commands:")
w := tabwriter.NewWriter(&b, 0, 4, 2, ' ', 0)
for _, child := range c.Children() {
fmt.Fprintf(w, "\t%s\t\t\t%s\n", child.Name, child.Help)
}
w.Flush()
p()
}
return b.String()
}
// findChildCmd returns the subcommand with matching name or alias.
func (c *Cmd) findChildCmd(name string) *Cmd {
// find perfect matches first
if cmd, ok := c.children[name]; ok {
return cmd
}
// find alias matching the name
for _, cmd := range c.children {
for _, alias := range cmd.Aliases {
if alias == name {
return cmd
}
}
}
return nil
}
// FindCmd finds the matching Cmd for args.
// It returns the Cmd and the remaining args.
func (c Cmd) FindCmd(args []string) (*Cmd, []string) {
var cmd *Cmd
for i, arg := range args {
if cmd1 := c.findChildCmd(arg); cmd1 != nil {
cmd = cmd1
c = *cmd
continue
}
return cmd, args[i:]
}
return cmd, nil
}
type cmdSorter []*Cmd
func (c cmdSorter) Len() int { return len(c) }
func (c cmdSorter) Less(i, j int) bool { return c[i].Name < c[j].Name }
func (c cmdSorter) Swap(i, j int) { c[i], c[j] = c[j], c[i] }