diff --git a/tools/cli/command.go b/tools/cli/command.go index d19815ee9..5527865b1 100644 --- a/tools/cli/command.go +++ b/tools/cli/command.go @@ -347,6 +347,30 @@ func (self *Command) Add(s OptionSpec) *Option { return self.AddToGroup("", s) } +func (self *Command) FindOptions(name_with_hyphens string) []*Option { + ans := make([]*Option, 0, 4) + for _, g := range self.OptionGroups { + x := g.FindOptions(name_with_hyphens) + if x != nil { + ans = append(ans, x...) + } + } + depth := 0 + for p := self.Parent; p != nil; p = p.Parent { + depth++ + x := p.FindOptions(name_with_hyphens) + if x != nil { + for _, po := range x { + if po.Depth >= depth { + ans = append(ans, po) + } + } + } + } + return ans + +} + func (self *Command) FindOption(name_with_hyphens string) *Option { for _, g := range self.OptionGroups { q := g.FindOption(name_with_hyphens) diff --git a/tools/cli/group.go b/tools/cli/group.go index 040766635..e69ec35f6 100644 --- a/tools/cli/group.go +++ b/tools/cli/group.go @@ -82,6 +82,19 @@ func (self *OptionGroup) AddOptionFromString(parent *Command, items ...string) ( return ans, err } +func (self *OptionGroup) FindOptions(prefix_with_hyphens string) []*Option { + is_short := !strings.HasPrefix(prefix_with_hyphens, "--") + option_name := NormalizeOptionName(prefix_with_hyphens) + ans := make([]*Option, 0, 4) + for _, q := range self.Options { + if q.MatchingAlias(option_name, is_short) != "" { + ans = append(ans, q) + } + } + return ans + +} + func (self *OptionGroup) FindOption(name_with_hyphens string) *Option { is_short := !strings.HasPrefix(name_with_hyphens, "--") option_name := NormalizeOptionName(name_with_hyphens) diff --git a/tools/cli/option.go b/tools/cli/option.go index 75da94707..d30b45d97 100644 --- a/tools/cli/option.go +++ b/tools/cli/option.go @@ -75,6 +75,15 @@ func (self *Option) needs_argument() bool { return self.OptionType != BoolOption && self.OptionType != CountOption } +func (self *Option) MatchingAlias(prefix_without_hyphens string, is_short bool) string { + for _, a := range self.Aliases { + if a.IsShort == is_short && strings.HasPrefix(a.NameWithoutHyphens, prefix_without_hyphens) { + return a.String() + } + } + return "" +} + func (self *Option) HasAlias(name_without_hyphens string, is_short bool) bool { for _, a := range self.Aliases { if a.IsShort == is_short && a.NameWithoutHyphens == name_without_hyphens { diff --git a/tools/cli/parse-args.go b/tools/cli/parse-args.go index 04e3aa131..1706a3000 100644 --- a/tools/cli/parse-args.go +++ b/tools/cli/parse-args.go @@ -20,9 +20,19 @@ func (self *Command) parse_args(ctx *Context, args []string) error { consume_arg := func() string { ans := args_to_parse[0]; args_to_parse = args_to_parse[1:]; return ans } handle_option := func(opt_str string, has_val bool, opt_val string, val_not_allowed bool) error { - opt := self.FindOption(opt_str) - if opt == nil { + possible_options := self.FindOptions(opt_str) + var opt *Option + if len(possible_options) == 1 { + opt = possible_options[0] + opt_str = opt.MatchingAlias(NormalizeOptionName(opt_str), !strings.HasPrefix(opt_str, "--")) + } else if len(possible_options) == 0 { return &ParseError{Message: fmt.Sprintf("Unknown option: :yellow:`%s`", opt_str)} + } else { + ambi := make([]string, len(possible_options)) + for i, o := range possible_options { + ambi[i] = o.MatchingAlias(NormalizeOptionName(opt_str), !strings.HasPrefix(opt_str, "--")) + } + return &ParseError{Message: fmt.Sprintf("Ambiguous option: :yellow:`%s` could be any of: %s", opt_str, strings.Join(ambi, ", "))} } opt.seen_option = opt_str needs_arg := opt.needs_argument() diff --git a/tools/cli/types_test.go b/tools/cli/types_test.go index 415640528..4309502ae 100644 --- a/tools/cli/types_test.go +++ b/tools/cli/types_test.go @@ -85,8 +85,9 @@ func TestCLIParsing(t *testing.T) { ) rt(child1, "test child1", &options{}) rt(child1, "test child1 --set-me --simple-string=foo one", &options{SimpleString: "foo", SetMe: true}, "one") + rt(child1, "test child1 --set-me --simp=foo one", &options{SimpleString: "foo", SetMe: true}, "one") rt(child1, "test child1 --set-me --simple-string= one", &options{SetMe: true}, "one") - rt(child1, "test child1 --int -3 --simple-string -s --float=3.3", &options{SimpleString: "-s", Int: -3, Float: 3.3}) + rt(child1, "test child1 --int -3 --simple-s -s --float=3.3", &options{SimpleString: "-s", Int: -3, Float: 3.3}) rt(child1, "test child1 --list -3 -p --list one", &options{FromParent: 1, List: []string{"-3", "one"}}) rt(gc1, "test -p child1 -p gc1 xxx", &empty_options{}, "xxx")