Skip to content

Commit 2479d83

Browse files
Add support for command aliases (#130)
1 parent d78d607 commit 2479d83

File tree

6 files changed

+75
-0
lines changed

6 files changed

+75
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ Tag | Description
436436
`default:"X"` | Default value.
437437
`default:"1"` | On a command, make it the default.
438438
`short:"X"` | Short name, if flag.
439+
`aliases:"X,Y"` | One or more aliases (for cmd).
439440
`required` | If present, flag/arg is required.
440441
`optional` | If present, flag/arg is optional.
441442
`hidden` | If present, command or flag is hidden.

build.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ func buildChild(k *Kong, node *Node, typ NodeType, v reflect.Value, ft reflect.S
143143
child.Help = tag.Help
144144
child.Hidden = tag.Hidden
145145
child.Group = tag.Group
146+
child.Aliases = tag.Aliases
146147

147148
if provider, ok := fv.Addr().Interface().(HelpProvider); ok {
148149
child.Detail = provider.Help()

context.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,24 @@ func (c *Context) trace(node *Node) (err error) { // nolint: gocyclo
425425
break
426426
}
427427

428+
// Assign token value to a branch name if tagged as an alias
429+
// An alias will be ignored in the case of an existing command
430+
cmds := make(map[string]bool)
431+
for _, branch := range node.Children {
432+
if branch.Type == CommandNode {
433+
cmds[branch.Name] = true
434+
}
435+
}
436+
for _, branch := range node.Children {
437+
for _, a := range branch.Aliases {
438+
_, ok := cmds[a]
439+
if token.Value == a && !ok {
440+
token.Value = branch.Name
441+
break
442+
}
443+
}
444+
}
445+
428446
// After positional arguments have been consumed, check commands next...
429447
for _, branch := range node.Children {
430448
if branch.Type == CommandNode && !branch.Hidden {

model.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type Node struct {
5353
Children []*Node
5454
Target reflect.Value // Pointer to the value in the grammar that this Node is associated with.
5555
Tag *Tag
56+
Aliases []string
5657

5758
Argument *Value // Populated when Type is ArgumentNode.
5859
}

tag.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type Tag struct {
3232
Vars Vars
3333
Prefix string // Optional prefix on anonymous structs. All sub-flags will have this prefix.
3434
Embed bool
35+
Aliases []string
3536

3637
// Storage for all tag keys for arbitrary lookups.
3738
items map[string][]string
@@ -157,6 +158,13 @@ func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
157158
t.Xor = t.Get("xor")
158159
t.Prefix = t.Get("prefix")
159160
t.Embed = t.Has("embed")
161+
splitFn := func(r rune) bool {
162+
return r == ',' || r == ' '
163+
}
164+
aliases := t.Get("aliases")
165+
if len(aliases) > 0 {
166+
t.Aliases = append(t.Aliases, strings.FieldsFunc(aliases, splitFn)...)
167+
}
160168
t.Vars = Vars{}
161169
for _, set := range t.GetAll("set") {
162170
parts := strings.SplitN(set, "=", 2)

tag_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,49 @@ func TestTagSetOnFlag(t *testing.T) {
149149
require.NoError(t, err)
150150
require.Contains(t, buf.String(), `A key from somewhere.`)
151151
}
152+
153+
func TestTagAliases(t *testing.T) {
154+
type Command struct {
155+
Arg string `arg help:"Some arg"`
156+
}
157+
var cli struct {
158+
Cmd Command `cmd aliases:"alias1, alias2"`
159+
}
160+
p := mustNew(t, &cli)
161+
_, err := p.Parse([]string{"alias1", "arg"})
162+
require.NoError(t, err)
163+
require.Equal(t, "arg", cli.Cmd.Arg)
164+
_, err = p.Parse([]string{"alias2", "arg"})
165+
require.NoError(t, err)
166+
require.Equal(t, "arg", cli.Cmd.Arg)
167+
}
168+
169+
func TestTagAliasesConflict(t *testing.T) {
170+
type Command struct {
171+
Arg string `arg help:"Some arg"`
172+
}
173+
var cli struct {
174+
Cmd Command `cmd hidden aliases:"other-cmd"`
175+
OtherCmd Command `cmd`
176+
}
177+
p := mustNew(t, &cli)
178+
_, err := p.Parse([]string{"other-cmd", "arg"})
179+
require.NoError(t, err)
180+
require.Equal(t, "arg", cli.OtherCmd.Arg)
181+
}
182+
183+
func TestTagAliasesSub(t *testing.T) {
184+
type SubCommand struct {
185+
Arg string `arg help:"Some arg"`
186+
}
187+
type Command struct {
188+
SubCmd SubCommand `cmd aliases:"other-sub-cmd"`
189+
}
190+
var cli struct {
191+
Cmd Command `cmd hidden`
192+
}
193+
p := mustNew(t, &cli)
194+
_, err := p.Parse([]string{"cmd", "other-sub-cmd", "arg"})
195+
require.NoError(t, err)
196+
require.Equal(t, "arg", cli.Cmd.SubCmd.Arg)
197+
}

0 commit comments

Comments
 (0)