Skip to content

Commit 38a74cf

Browse files
authored
Handle global flags appearing in subcommands
Kong allows global flags to appear in the subcommand. From the point of view of `complete`, *all* flags are global flags. However, in the custom PositionalPredictor, we were only handling flags at the current level - causing us to skip over, and get the wrong predictor in this case. This can be resolved by tracking all parent args as well. Signed-off-by: Justin Chadwell <me@jedevc.com>
1 parent cc1122e commit 38a74cf

2 files changed

Lines changed: 38 additions & 12 deletions

File tree

prediction.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package kongcompletion
22

33
import (
44
"fmt"
5+
"slices"
56

67
"github.com/alecthomas/kong"
78
"github.com/posener/complete"
@@ -89,7 +90,7 @@ func Command(parser *kong.Kong, opt ...Option) (complete.Command, error) {
8990
if parser == nil || parser.Model == nil {
9091
return complete.Command{}, nil
9192
}
92-
command, err := nodeCommand(parser.Model.Node, opts, nil)
93+
command, err := nodeCommand(parser.Model.Node, opts, nil, flags{})
9394
if err != nil {
9495
return complete.Command{}, err
9596
}
@@ -126,7 +127,12 @@ func Register(parser *kong.Kong, opt ...Option) {
126127
}
127128
}
128129

129-
func nodeCommand(node *kong.Node, opts *options, vars kong.Vars) (*complete.Command, error) {
130+
type flags struct {
131+
argFlags []*kong.Flag
132+
boolFlags []*kong.Flag
133+
}
134+
135+
func nodeCommand(node *kong.Node, opts *options, vars kong.Vars, flags flags) (*complete.Command, error) {
130136
if node == nil {
131137
return nil, nil
132138
}
@@ -137,11 +143,15 @@ func nodeCommand(node *kong.Node, opts *options, vars kong.Vars) (*complete.Comm
137143
GlobalFlags: complete.Flags{},
138144
}
139145

146+
boolFlags, argFlags := boolAndNonBoolFlags(node.Flags)
147+
flags.boolFlags = append(slices.Clone(flags.boolFlags), boolFlags...)
148+
flags.argFlags = append(slices.Clone(flags.argFlags), argFlags...)
149+
140150
for _, child := range node.Children {
141151
if child == nil || child.Hidden {
142152
continue
143153
}
144-
childCmd, err := nodeCommand(child, opts, vars)
154+
childCmd, err := nodeCommand(child, opts, vars, flags)
145155
if err != nil {
146156
return nil, err
147157
}
@@ -166,15 +176,14 @@ func nodeCommand(node *kong.Node, opts *options, vars kong.Vars) (*complete.Comm
166176
}
167177
}
168178

169-
boolFlags, nonBoolFlags := boolAndNonBoolFlags(node.Flags)
170179
pps, err := positionalPredictors(node.Positional, opts.predictors, vars)
171180
if err != nil {
172181
return nil, err
173182
}
174183
cmd.Args = &PositionalPredictor{
175184
Predictors: pps,
176-
ArgFlags: flagNamesWithHyphens(nonBoolFlags...),
177-
BoolFlags: flagNamesWithHyphens(boolFlags...),
185+
ArgFlags: flagNamesWithHyphens(flags.argFlags...),
186+
BoolFlags: flagNamesWithHyphens(flags.boolFlags...),
178187
}
179188

180189
return &cmd, nil

prediction_test.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ func TestComplete(t *testing.T) {
5050
BooFlag bool `kong:"name=boofl,short=b"`
5151
} `kong:"cmd,set=a=other"`
5252
Baz struct{} `kong:"cmd,hidden"`
53+
54+
Global string `kong:""`
5355
}
5456

5557
for _, td := range []completeTest{
@@ -75,13 +77,13 @@ func TestComplete(t *testing.T) {
7577
},
7678
{
7779
parser: kong.Must(&cli),
78-
want: []string{"--bar", "--baz", "--tata", "--titi", "--xuxu", "--xoxo", "--xixi", "--quz", "--lion", "--help", "-h"},
80+
want: []string{"--bar", "--baz", "--tata", "--titi", "--xuxu", "--xoxo", "--xixi", "--quz", "--lion", "--help", "-h", "--global"},
7981
line: "myApp foo -",
8082
},
8183
{
8284
parser: kong.Must(&cli),
83-
want: []string{"--bar", "--baz", "--tata", "--titi", "--xuxu", "--xoxo", "--xixi", "--quz", "--lion", "--help", "-h"},
84-
line: "myApp foo -",
85+
want: []string{"--bar", "--baz", "--tata", "--titi", "--xuxu", "--xoxo", "--xixi", "--quz", "--lion", "--help", "--global"},
86+
line: "myApp foo --",
8587
},
8688
{
8789
parser: kong.Must(&cli),
@@ -95,7 +97,7 @@ func TestComplete(t *testing.T) {
9597
},
9698
{
9799
parser: kong.Must(&cli),
98-
want: []string{"--bar", "--baz", "--tata", "--titi", "--xuxu", "--xoxo", "--xixi", "--quz", "--lion", "--help", "-h"},
100+
want: []string{"--bar", "--baz", "--tata", "--titi", "--xuxu", "--xoxo", "--xixi", "--quz", "--lion", "--help", "-h", "--global"},
99101
line: "myApp foo --baz -",
100102
},
101103
{
@@ -125,7 +127,7 @@ func TestComplete(t *testing.T) {
125127
},
126128
{
127129
parser: kong.Must(&cli),
128-
want: []string{"-n", "--number", "--omg", "--help", "-h", "--boofl", "-b"},
130+
want: []string{"-n", "--number", "--omg", "--help", "-h", "--boofl", "-b", "--global"},
129131
line: "myApp bar -",
130132
},
131133
{
@@ -135,7 +137,7 @@ func TestComplete(t *testing.T) {
135137
},
136138
{
137139
parser: kong.Must(&cli),
138-
want: []string{"-n", "--number", "--omg", "--help", "-h", "--boofl", "-b"},
140+
want: []string{"-n", "--number", "--omg", "--help", "-h", "--boofl", "-b", "--global"},
139141
line: "myApp bar -b thing1 -",
140142
},
141143
{
@@ -148,6 +150,21 @@ func TestComplete(t *testing.T) {
148150
want: []string{"otherthing1", "otherthing2"},
149151
line: "myApp bar -b thing1 --omg gizzles ",
150152
},
153+
{
154+
parser: kong.Must(&cli),
155+
want: []string{"foo", "bar"},
156+
line: "myApp --global=test ",
157+
},
158+
{
159+
parser: kong.Must(&cli),
160+
want: []string{"rabbit", "duck", "bird"},
161+
line: "myApp foo --global=test ",
162+
},
163+
{
164+
parser: kong.Must(&cli),
165+
want: []string{"thing1", "thing2"},
166+
line: "myApp bar --global=test ",
167+
},
151168
} {
152169
name := td.name
153170
if name == "" {

0 commit comments

Comments
 (0)