Skip to content

Commit 2dfca5d

Browse files
authored
feat(autocomplete): global flag profile values (#3036)
1 parent 0d3def4 commit 2dfca5d

File tree

5 files changed

+120
-56
lines changed

5 files changed

+120
-56
lines changed

internal/core/autocomplete.go

Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -57,48 +57,11 @@ type FlagSpec struct {
5757
EnumValues []string
5858
}
5959

60-
func (node *AutoCompleteNode) addGlobalFlags() {
61-
printerTypes := []string{
62-
PrinterTypeHuman.String(),
63-
PrinterTypeJSON.String(),
64-
PrinterTypeYAML.String(),
65-
PrinterTypeTemplate.String(),
66-
}
67-
68-
node.Children["-c"] = NewAutoCompleteFlagNode(node, &FlagSpec{
69-
Name: "-c",
70-
})
71-
node.Children["--config"] = NewAutoCompleteFlagNode(node, &FlagSpec{
72-
Name: "--config",
73-
})
74-
node.Children["-D"] = NewAutoCompleteFlagNode(node, &FlagSpec{
75-
Name: "-D",
76-
})
77-
node.Children["--debug"] = NewAutoCompleteFlagNode(node, &FlagSpec{
78-
Name: "--debug",
79-
})
80-
node.Children["-h"] = NewAutoCompleteFlagNode(node, &FlagSpec{
81-
Name: "-h",
82-
})
83-
node.Children["--help"] = NewAutoCompleteFlagNode(node, &FlagSpec{
84-
Name: "--help",
85-
})
86-
node.Children["-o"] = NewAutoCompleteFlagNode(node, &FlagSpec{
87-
Name: "-o",
88-
EnumValues: printerTypes,
89-
})
90-
node.Children["--output"] = NewAutoCompleteFlagNode(node, &FlagSpec{
91-
Name: "--output",
92-
EnumValues: printerTypes,
93-
})
94-
node.Children["-p"] = NewAutoCompleteFlagNode(node, &FlagSpec{
95-
Name: "-p",
96-
HasVariableValue: true,
97-
})
98-
node.Children["--profile"] = NewAutoCompleteFlagNode(node, &FlagSpec{
99-
Name: "--profile",
100-
HasVariableValue: true,
101-
})
60+
func (node *AutoCompleteNode) addFlags(flags []FlagSpec) {
61+
for i := range flags {
62+
flag := &flags[i]
63+
node.Children[flag.Name] = NewAutoCompleteFlagNode(node, flag)
64+
}
10265
}
10366

10467
// newAutoCompleteResponse builds a new AutocompleteResponse
@@ -111,11 +74,13 @@ func newAutoCompleteResponse(suggestions []string) *AutocompleteResponse {
11174

11275
// NewAutoCompleteCommandNode creates a new node corresponding to a command or subcommand.
11376
// These nodes are not necessarily leaf nodes.
114-
func NewAutoCompleteCommandNode() *AutoCompleteNode {
115-
return &AutoCompleteNode{
116-
Children: make(map[string]*AutoCompleteNode),
77+
func NewAutoCompleteCommandNode(flags []FlagSpec) *AutoCompleteNode {
78+
node := &AutoCompleteNode{
79+
Children: make(map[string]*AutoCompleteNode, len(flags)),
11780
Type: AutoCompleteNodeTypeCommand,
11881
}
82+
node.addFlags(flags)
83+
return node
11984
}
12085

12186
// NewAutoCompleteArgNode creates a new node corresponding to a command argument.
@@ -136,10 +101,18 @@ func NewAutoCompleteArgNode(cmd *Command, argSpec *ArgSpec) *AutoCompleteNode {
136101
// or the lowest nodes are the possible values if the exist.
137102
func NewAutoCompleteFlagNode(parent *AutoCompleteNode, flagSpec *FlagSpec) *AutoCompleteNode {
138103
node := &AutoCompleteNode{
139-
Children: make(map[string]*AutoCompleteNode),
140-
Type: AutoCompleteNodeTypeFlag,
141-
Name: flagSpec.Name,
104+
Type: AutoCompleteNodeTypeFlag,
105+
Name: flagSpec.Name,
142106
}
107+
childrenCount := len(flagSpec.EnumValues)
108+
if flagSpec.HasVariableValue {
109+
childrenCount++
110+
}
111+
112+
if childrenCount > 0 {
113+
node.Children = make(map[string]*AutoCompleteNode, childrenCount)
114+
}
115+
143116
if flagSpec.HasVariableValue {
144117
node.Children[positionalValueNodeID] = &AutoCompleteNode{
145118
Children: parent.Children,
@@ -161,9 +134,9 @@ func NewAutoCompleteFlagNode(parent *AutoCompleteNode, flagSpec *FlagSpec) *Auto
161134
// GetChildOrCreate search a child node by name,
162135
// and either returns it if found
163136
// or create new children with the given name and aliases, and returns it.
164-
func (node *AutoCompleteNode) GetChildOrCreate(name string, aliases []string) *AutoCompleteNode {
137+
func (node *AutoCompleteNode) GetChildOrCreate(name string, aliases []string, flags []FlagSpec) *AutoCompleteNode {
165138
if _, exist := node.Children[name]; !exist {
166-
childNode := NewAutoCompleteCommandNode()
139+
childNode := NewAutoCompleteCommandNode(flags)
167140
node.Children[name] = childNode
168141
for _, alias := range aliases {
169142
node.Children[alias] = childNode
@@ -209,17 +182,16 @@ func (node *AutoCompleteNode) isLeafCommand() bool {
209182
}
210183

211184
// BuildAutoCompleteTree builds the autocomplete tree from the commands, subcommands and arguments
212-
func BuildAutoCompleteTree(commands *Commands) *AutoCompleteNode {
213-
root := NewAutoCompleteCommandNode()
214-
root.addGlobalFlags()
185+
func BuildAutoCompleteTree(ctx context.Context, commands *Commands) *AutoCompleteNode {
186+
globalFlags := getGlobalFlags(ctx)
187+
root := NewAutoCompleteCommandNode(globalFlags)
215188
for _, cmd := range commands.commands {
216189
node := root
217190

218191
// Creates nodes for namespaces, resources, verbs
219192
for _, part := range []string{cmd.Namespace, cmd.Resource, cmd.Verb} {
220193
if part != "" {
221-
node = node.GetChildOrCreate(part, cmd.Aliases)
222-
node.addGlobalFlags()
194+
node = node.GetChildOrCreate(part, cmd.Aliases, globalFlags)
223195
}
224196
}
225197

@@ -256,7 +228,7 @@ func AutoComplete(ctx context.Context, leftWords []string, wordToComplete string
256228
commands := ExtractCommands(ctx)
257229

258230
// Create AutoComplete Tree
259-
commandTreeRoot := BuildAutoCompleteTree(commands)
231+
commandTreeRoot := BuildAutoCompleteTree(ctx, commands)
260232

261233
// For each left word that is not a flag nor an argument, we try to go deeper in the autocomplete tree and store the current node in `node`.
262234
node := commandTreeRoot

internal/core/autocomplete_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88
"testing"
99

10+
"github.com/scaleway/scaleway-sdk-go/scw"
1011
"github.com/stretchr/testify/assert"
1112
)
1213

@@ -78,6 +79,12 @@ func runAutocompleteTest(ctx context.Context, tc *autoCompleteTestCase) func(*te
7879
if len(words) == 0 {
7980
name := strings.Replace(t.Name(), "TestAutocomplete/", "", -1)
8081
name = strings.Replace(name, "_", " ", -1)
82+
// Test can contain a sharp if duplicated
83+
// MyTest/scw_-flag_#01
84+
sharpIndex := strings.Index(name, "#")
85+
if sharpIndex != -1 {
86+
name = name[:sharpIndex]
87+
}
8188
words = strings.Split(name, " ")
8289
}
8390

@@ -252,3 +259,31 @@ func TestAutocompleteArgs(t *testing.T) {
252259
t.Run("scw test flower get material-name=mat ", run(&testCase{Suggestions: AutocompleteSuggestions{"flower1", "flower2"}}))
253260
t.Run("scw test flower create name=", run(&testCase{Suggestions: AutocompleteSuggestions(nil)}))
254261
}
262+
263+
func TestAutocompleteProfiles(t *testing.T) {
264+
commands := testAutocompleteGetCommands()
265+
ctx := injectMeta(context.Background(), &meta{
266+
Commands: commands,
267+
betaMode: true,
268+
})
269+
270+
type testCase = autoCompleteTestCase
271+
272+
run := func(tc *testCase) func(*testing.T) {
273+
return runAutocompleteTest(ctx, tc)
274+
}
275+
t.Run("scw -p ", run(&testCase{Suggestions: nil}))
276+
t.Run("scw test -p ", run(&testCase{Suggestions: nil}))
277+
t.Run("scw test flower --profile ", run(&testCase{Suggestions: nil}))
278+
279+
injectConfig(ctx, &scw.Config{
280+
Profiles: map[string]*scw.Profile{
281+
"p1": nil,
282+
"p2": nil,
283+
},
284+
})
285+
286+
t.Run("scw -p ", run(&testCase{Suggestions: AutocompleteSuggestions{"p1", "p2"}}))
287+
t.Run("scw test -p ", run(&testCase{Suggestions: AutocompleteSuggestions{"p1", "p2"}}))
288+
t.Run("scw test flower --profile ", run(&testCase{Suggestions: AutocompleteSuggestions{"p1", "p2"}}))
289+
}

internal/core/autocomplete_utils.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,50 @@ import (
1414

1515
var autoCompleteCache *cache.Cache
1616

17+
// getGlobalFlags returns the list of flags that should be added to all commands
18+
func getGlobalFlags(ctx context.Context) []FlagSpec {
19+
printerTypes := []string{
20+
PrinterTypeHuman.String(),
21+
PrinterTypeJSON.String(),
22+
PrinterTypeYAML.String(),
23+
PrinterTypeTemplate.String(),
24+
}
25+
profiles := []string(nil)
26+
cfg := extractConfig(ctx)
27+
if cfg != nil {
28+
for profile := range cfg.Profiles {
29+
profiles = append(profiles, profile)
30+
}
31+
}
32+
33+
return []FlagSpec{
34+
{Name: "-c"},
35+
{Name: "--config"},
36+
{Name: "-D"},
37+
{Name: "--debug"},
38+
{Name: "-h"},
39+
{Name: "--help"},
40+
{
41+
Name: "-o",
42+
EnumValues: printerTypes,
43+
},
44+
{
45+
Name: "--output",
46+
EnumValues: printerTypes,
47+
},
48+
{
49+
Name: "-p",
50+
HasVariableValue: true,
51+
EnumValues: profiles,
52+
},
53+
{
54+
Name: "--profile",
55+
HasVariableValue: true,
56+
EnumValues: profiles,
57+
},
58+
}
59+
}
60+
1761
func AutocompleteProfileName() AutoCompleteArgFunc {
1862
return func(ctx context.Context, prefix string) AutocompleteSuggestions {
1963
res := AutocompleteSuggestions(nil)

internal/core/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ func createClient(ctx context.Context, httpClient *http.Client, buildInfo *Build
3232
return nil, err
3333

3434
default:
35+
// Store latest version of config in context
36+
injectConfig(ctx, config)
37+
3538
// found and loaded a config file -> merge with env
3639
activeProfile, err := config.GetProfile(profileName)
3740
if err != nil {

internal/core/context.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type meta struct {
3333
stdin io.Reader
3434
result interface{}
3535
httpClient *http.Client
36+
config *scw.Config
3637
isClientFromBootstrapConfig bool
3738
betaMode bool
3839
}
@@ -53,6 +54,15 @@ func extractMeta(ctx context.Context) *meta {
5354
return ctx.Value(metaContextKey).(*meta)
5455
}
5556

57+
// injectSDKConfig add config to a meta context
58+
func injectConfig(ctx context.Context, config *scw.Config) {
59+
extractMeta(ctx).config = config
60+
}
61+
62+
func extractConfig(ctx context.Context) *scw.Config {
63+
return extractMeta(ctx).config
64+
}
65+
5666
func ExtractCommands(ctx context.Context) *Commands {
5767
return extractMeta(ctx).Commands
5868
}

0 commit comments

Comments
 (0)