From 0dab006733ba21b6234932ee8f14bb6b378269c7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 30 Sep 2022 15:37:03 +0530 Subject: [PATCH] Allow un-ambiguous prefixes for command names --- tools/cli/command.go | 12 ++++++++++++ tools/cli/group.go | 9 +++++++++ tools/cli/parse-args.go | 17 ++++++++++++----- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/tools/cli/command.go b/tools/cli/command.go index 2a2dab134..4099a5a8e 100644 --- a/tools/cli/command.go +++ b/tools/cli/command.go @@ -333,6 +333,18 @@ func (self *Command) FindSubCommand(name string) *Command { return nil } +func (self *Command) FindSubCommands(prefix string) []*Command { + c := self.FindSubCommand(prefix) + if c != nil { + return []*Command{c} + } + ans := make([]*Command, 0, 4) + for _, g := range self.SubCommandGroups { + ans = g.FindSubCommands(prefix, ans) + } + return ans +} + func (self *Command) AddOptionGroup(title string) *OptionGroup { for _, g := range self.OptionGroups { if g.Title == title { diff --git a/tools/cli/group.go b/tools/cli/group.go index e69ec35f6..dbc3d2312 100644 --- a/tools/cli/group.go +++ b/tools/cli/group.go @@ -48,6 +48,15 @@ func (self *CommandGroup) FindSubCommand(name string) *Command { return nil } +func (self *CommandGroup) FindSubCommands(prefix string, matches []*Command) []*Command { + for _, c := range self.SubCommands { + if strings.HasPrefix(c.Name, prefix) { + matches = append(matches, c) + } + } + return matches +} + type OptionGroup struct { Options []*Option Title string diff --git a/tools/cli/parse-args.go b/tools/cli/parse-args.go index c3b39f876..b67a4b7dd 100644 --- a/tools/cli/parse-args.go +++ b/tools/cli/parse-args.go @@ -95,13 +95,20 @@ func (self *Command) parse_args(ctx *Context, args []string) error { options_allowed = false } if self.HasSubCommands() { - sc := self.FindSubCommand(arg) - if sc == nil { - if !self.SubCommandIsOptional { + possible_cmds := self.FindSubCommands(arg) + if len(possible_cmds) == 1 { + return possible_cmds[0].parse_args(ctx, args_to_parse) + } + if !self.SubCommandIsOptional { + if len(possible_cmds) == 0 { return &ParseError{Message: fmt.Sprintf(":yellow:`%s` is not a known subcommand for :emph:`%s`. Use --help to get a list of valid subcommands.", arg, self.Name)} } - } else { - return sc.parse_args(ctx, args_to_parse) + cn := make([]string, len(possible_cmds)) + for i, x := range possible_cmds { + cn[i] = x.Name + } + return &ParseError{Message: fmt.Sprintf( + ":yellow:`%s` is not a known subcommand for :emph:`%s`. Did you mean:\n\t%s", arg, self.Name, strings.Join(cn, "\n\t"))} } } self.Args = append(self.Args, arg)