-
Notifications
You must be signed in to change notification settings - Fork 1
/
completer.go
219 lines (209 loc) · 5.66 KB
/
completer.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package completer
import (
"fmt"
"github.com/c-bata/go-prompt"
"github.com/cxweilai/kubectlx/internal/command"
"strings"
)
const LIMIT_SUGGEST = 5
type Completer struct {
systemCmd *command.Command
contextCmd *command.Command
kubeCmd *command.Command
extensionCmd *command.Command
cmd *command.Command
}
func NewCompleter() *Completer {
contextCmd := &command.Command{
Name: "Context Command",
Commands: []*command.Command{
NewContextCommand(),
NewUseCommand(),
},
}
systemCmd := &command.Command{
Name: "System Command",
Commands: NewSystemCommand(),
}
kubeCmd := &command.Command{
Name: "Kube Command",
Commands: []*command.Command{
NewKubeLogsCommand(),
NewKubeGetCommand(),
NewKubeDescribeCommand(),
NewKubeDeleteCommand(),
NewKubeApplyCommand(),
NewKubeEditCommand(),
NewKubeExecCommand(),
},
}
extendedCmd := &command.Command{
Name: "Extension Command",
Commands: []*command.Command{
NewExtendedStatusCommand(),
NewExtendedTreeCommand(),
NewExtendedEventCommand(),
NewExtendedForceDelCommand(),
NewExtendedSecretCommand(),
},
}
var commands []*command.Command
commands = append(commands, systemCmd.Commands...)
commands = append(commands, contextCmd.Commands...)
commands = append(commands, kubeCmd.Commands...)
commands = append(commands, extendedCmd.Commands...)
c := &Completer{
contextCmd: contextCmd,
systemCmd: systemCmd,
kubeCmd: kubeCmd,
extensionCmd: extendedCmd,
cmd: &command.Command{
Commands: commands,
},
}
for _, cmd := range c.cmd.Commands {
if err := cmd.Check(); err != nil {
panic(err)
}
}
// 追加help命令到头部
c.cmd.Commands = append([]*command.Command{
{
Name: "help",
Description: "帮助命令",
Run: func(cmd *command.ExecCmd) {
c.Help()
},
},
}, c.cmd.Commands...)
return c
}
func (c *Completer) Registry(cmd ...*command.Command) {
for _, tcmd := range cmd {
if err := tcmd.Check(); err != nil {
panic(err)
}
}
c.extensionCmd.Commands = append(c.extensionCmd.Commands, cmd...)
c.cmd.Commands = append(c.cmd.Commands, cmd...)
}
func (c *Completer) Help() {
c.systemCmd.Help()
c.contextCmd.Help()
c.kubeCmd.Help()
if len(c.extensionCmd.Commands) > 0 {
c.extensionCmd.Help()
}
}
func (c *Completer) Exec(cmd string) {
execCmd := command.ParseExecCommand(c.cmd.Commands, strings.TrimSpace(cmd))
if execCmd == nil {
return
}
if execCmd.Exec() {
return
}
fmt.Println(fmt.Sprintf("cmd '%s' not found.", cmd))
fmt.Println("Use the \"help\" command to view supported commands.")
}
func (c *Completer) Complete(d prompt.Document) []prompt.Suggest {
// 正常空字符串分割后数组长度为1,元素为空字符串:"" => [""]
args := strings.Split(d.TextBeforeCursor(), " ")
suggests := c.doGetLastCmd(c.cmd, args)
return prompt.FilterHasPrefix(suggests, d.GetWordBeforeCursor(), true)
}
func (c *Completer) doGetLastCmd(cmd *command.Command, args []string) []prompt.Suggest {
if len(args) == 0 {
return []prompt.Suggest{}
}
firstArg := args[0]
if cmd.Commands != nil {
for _, subCmd := range cmd.Commands {
if strings.EqualFold(subCmd.Name, firstArg) {
return c.doGetLastCmd(subCmd, args[1:])
}
}
}
// 支持动态命令
if cmd.DynamicCommands != nil {
for _, subCmd := range cmd.DynamicCommands() {
if strings.EqualFold(subCmd.Name, firstArg) {
suggest := c.doGetLastCmd(subCmd, args[1:])
if len(suggest) > 0 {
return suggest
}
return c.getCommandSuggests(subCmd, args)
}
}
}
return c.getCommandSuggests(cmd, args)
}
func (c *Completer) getCommandSuggests(cmd *command.Command, args []string) []prompt.Suggest {
// 1.子命令
if cmd.Commands != nil || cmd.DynamicCommands != nil {
// 不合法校验
// 例如:'c ',输入c之后再输入空格,由于c不匹配,此时args有两个参数,一个是'c'一个是' ',空格之后就不应该模糊匹配了
if len(args) > 1 {
return []prompt.Suggest{}
}
// 做模糊匹配,避免忽略前面的输入
// 例如:'xxx ',由于xxx不存在,不过滤的话,空格之后会匹配到根命令,拿根命令的子命令;
// 'use xxx ',由于xxx不存在,不过滤的话,空格之后会匹配到父命令'use',拿‘use’的子命令。
lastArg := ""
if len(args) > 0 {
lastArg = args[0]
}
var suggests []prompt.Suggest
if cmd.Commands != nil {
for _, subCmd := range cmd.Commands {
if !strings.HasPrefix(subCmd.Name, lastArg) {
continue
}
suggests = append(suggests, prompt.Suggest{
Text: subCmd.Name,
Description: subCmd.Description,
})
}
}
// 支持动态命令
if cmd.DynamicCommands != nil {
for _, subCmd := range cmd.DynamicCommands() {
if !strings.HasPrefix(subCmd.Name, lastArg) {
continue
}
suggests = append(suggests, prompt.Suggest{
Text: subCmd.Name,
Description: subCmd.Description,
})
}
}
return suggests
}
// 2.参数
// len(args) <= 1 ==> 避免参数输入完后还提示输入参数,例如:'logs xxx ',在xxx之后、空格之后,还提示输入参数
if cmd.DynamicParam != nil && len(args) <= 1 {
lastArg := ""
if len(args) > 0 {
lastArg = args[len(args)-1]
}
var suggests []prompt.Suggest
for _, p := range cmd.DynamicParam.Func(lastArg) {
suggests = append(suggests, prompt.Suggest{
Text: p.Name,
Description: p.Description,
})
}
return suggests
}
// 3. options
var suggests []prompt.Suggest
if len(cmd.Options) > 0 {
for _, option := range cmd.Options {
suggests = append(suggests, prompt.Suggest{
Text: option.Name,
Description: option.Description,
})
}
}
return suggests
}