Skip to content

Commit 553c870

Browse files
committed
feat(console): improve context-aware CLI completion
1 parent 8d6b382 commit 553c870

7 files changed

Lines changed: 492 additions & 68 deletions

File tree

runner/console/completer.go

Lines changed: 109 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,6 @@ import (
99
"github.com/404tk/go-prompt"
1010
)
1111

12-
var core = []prompt.Suggest{
13-
{Text: "help", Description: "help menu"},
14-
{Text: "use", Description: "use provider"},
15-
{Text: "sessions", Description: "list cache credential"},
16-
{Text: "note", Description: "add remarks to the session"},
17-
{Text: "clear", Description: "clear screen"},
18-
{Text: "exit", Description: "exit console"},
19-
}
20-
21-
var action = []prompt.Suggest{
22-
{Text: "show", Description: "show options or payloads"},
23-
{Text: "set", Description: "set option or parameter"},
24-
{Text: "run", Description: "run selected payload"},
25-
{Text: "shell", Description: "open instance-cmd-check session"},
26-
}
27-
2812
var modules = func() (m []prompt.Suggest) {
2913
for k, v := range plugins.Providers {
3014
m = append(m, prompt.Suggest{Text: k, Description: v.Desc()})
@@ -54,10 +38,31 @@ var opt = []prompt.Suggest{
5438
}
5539

5640
func Complete(d prompt.Document) []prompt.Suggest {
41+
return completeForMode(d, HelpModeRoot)
42+
}
43+
44+
func actionCompleter(d prompt.Document) []prompt.Suggest {
45+
return completeForMode(d, HelpModeProvider)
46+
}
47+
48+
func completeForMode(d prompt.Document, mode HelpMode) []prompt.Suggest {
49+
ctx := completionContextForMode(mode)
5750
args := completionArgs(d)
5851
word := d.GetWordBeforeCursor()
52+
53+
switch mode {
54+
case HelpModeProvider:
55+
return providerSuggestions(ctx, args, word)
56+
case HelpModeShell:
57+
return shellSuggestions(ctx, args, word)
58+
default:
59+
return rootSuggestions(ctx, args, word)
60+
}
61+
}
62+
63+
func rootSuggestions(_ CompletionContext, args []string, word string) []prompt.Suggest {
5964
if len(args) <= 1 {
60-
return prompt.FilterHasPrefix(core, word, true)
65+
return prompt.FilterHasPrefix(rootCommandSuggestions, word, true)
6166
}
6267
switch args[0] {
6368
case "help":
@@ -66,15 +71,17 @@ func Complete(d prompt.Document) []prompt.Suggest {
6671
if len(args) == 2 {
6772
return prompt.FilterContains(modules, word, true)
6873
}
74+
case "sessions":
75+
return sessionsSuggestions(args, word)
76+
case "note":
77+
return noteSuggestions(args, word)
6978
}
7079
return []prompt.Suggest{}
7180
}
7281

73-
func actionCompleter(d prompt.Document) []prompt.Suggest {
74-
args := completionArgs(d)
75-
word := d.GetWordBeforeCursor()
82+
func providerSuggestions(ctx CompletionContext, args []string, word string) []prompt.Suggest {
7683
if len(args) <= 1 {
77-
return prompt.FilterHasPrefix(append(core, action...), word, true)
84+
return prompt.FilterHasPrefix(providerCommandSuggestions(), word, true)
7885
}
7986
switch args[0] {
8087
case "help":
@@ -84,23 +91,62 @@ func actionCompleter(d prompt.Document) []prompt.Suggest {
8491
return prompt.FilterContains(modules, word, true)
8592
}
8693
case "show":
87-
if len(args) == 2 {
88-
return prompt.FilterContains(opt, word, true)
89-
}
94+
return showSuggestions(args, word)
9095
case "set":
91-
if len(args) == 2 {
92-
return prompt.FilterContains(optionSuggestions(), word, true)
93-
}
94-
if len(args) == 3 && args[1] == utils.Payload {
95-
return prompt.FilterContains(payloadSuggestions(), word, true)
96-
}
97-
if len(args) == 3 && args[1] == utils.Version {
98-
var versions = []prompt.Suggest{
99-
{Text: "Intl", Description: "International Edition"},
100-
{Text: "China", Description: "Chinese Edition"},
101-
}
102-
return prompt.FilterContains(versions, word, true)
96+
return setSuggestions(ctx, args, word)
97+
case "shell":
98+
return shellTargetSuggestions(ctx, args, word)
99+
case "sessions":
100+
return sessionsSuggestions(args, word)
101+
case "note":
102+
return noteSuggestions(args, word)
103+
}
104+
return []prompt.Suggest{}
105+
}
106+
107+
func shellSuggestions(_ CompletionContext, args []string, word string) []prompt.Suggest {
108+
if len(args) <= 1 {
109+
return prompt.FilterHasPrefix(shellCommandSuggestions, word, true)
110+
}
111+
switch args[0] {
112+
case "help":
113+
return helpSuggestions(args, word)
114+
}
115+
return []prompt.Suggest{}
116+
}
117+
118+
func showSuggestions(args []string, word string) []prompt.Suggest {
119+
if len(args) == 2 {
120+
return prompt.FilterContains(showTopicSuggestions(), word, true)
121+
}
122+
return []prompt.Suggest{}
123+
}
124+
125+
func setSuggestions(ctx CompletionContext, args []string, word string) []prompt.Suggest {
126+
if len(args) == 2 {
127+
return prompt.FilterContains(optionSuggestions(ctx), word, true)
128+
}
129+
if len(args) != 3 {
130+
return []prompt.Suggest{}
131+
}
132+
switch args[1] {
133+
case utils.Payload:
134+
return prompt.FilterContains(payloadSuggestions(), word, true)
135+
case utils.Version:
136+
if _, ok := ctx.Config[utils.Version]; ok {
137+
return prompt.FilterContains(versionSuggestionsData, word, true)
103138
}
139+
case utils.Region:
140+
return prompt.FilterContains(getProviderRegionSuggestions(ctx.Provider), word, true)
141+
case utils.Metadata:
142+
return prompt.FilterContains(getPayloadMetadataSuggestions(ctx.Payload), word, true)
143+
}
144+
return []prompt.Suggest{}
145+
}
146+
147+
func shellTargetSuggestions(ctx CompletionContext, args []string, word string) []prompt.Suggest {
148+
if len(args) == 2 {
149+
return prompt.FilterContains(getShellTargetSuggestions(ctx), word, true)
104150
}
105151
return []prompt.Suggest{}
106152
}
@@ -137,9 +183,33 @@ func helpTopicSuggestions() []prompt.Suggest {
137183
return suggestions
138184
}
139185

140-
func optionSuggestions() []prompt.Suggest {
141-
suggestions := make([]prompt.Suggest, 0, len(config))
142-
for k := range config {
186+
func optionSuggestions(ctx CompletionContext) []prompt.Suggest {
187+
keys := []string{utils.Payload, utils.Metadata}
188+
seen := map[string]struct{}{
189+
utils.Payload: {},
190+
utils.Metadata: {},
191+
}
192+
193+
for _, key := range providerConfigKeys() {
194+
if _, ok := seen[key]; ok {
195+
continue
196+
}
197+
keys = append(keys, key)
198+
seen[key] = struct{}{}
199+
}
200+
for _, key := range []string{utils.Region, utils.Version} {
201+
if _, ok := ctx.Config[key]; !ok {
202+
continue
203+
}
204+
if _, ok := seen[key]; ok {
205+
continue
206+
}
207+
keys = append(keys, key)
208+
seen[key] = struct{}{}
209+
}
210+
211+
suggestions := make([]prompt.Suggest, 0, len(keys))
212+
for _, k := range keys {
143213
if v, ok := optionsDesc[k]; ok {
144214
suggestions = append(suggestions, prompt.Suggest{Text: k, Description: v})
145215
}

0 commit comments

Comments
 (0)