diff --git a/tools/cli/command.go b/tools/cli/command.go index 4099a5a8e..fd1ac51f5 100644 --- a/tools/cli/command.go +++ b/tools/cli/command.go @@ -301,6 +301,31 @@ func (self *Command) GetVisibleOptions() ([]string, map[string][]*Option) { return group_titles, gmap } +func sort_levenshtein_matches(q string, matches []string) { + utils.StableSort(matches, func(a, b string) bool { + la, lb := utils.LevenshteinDistance(a, q, true), utils.LevenshteinDistance(b, q, true) + if la != lb { + return la < lb + } + return a < b + }) + +} + +func (self *Command) SuggestionsForCommand(name string, max_distance int /* good default is 2 */) []string { + ans := make([]string, 0, 8) + q := strings.ToLower(name) + for _, g := range self.SubCommandGroups { + for _, sc := range g.SubCommands { + if utils.LevenshteinDistance(sc.Name, q, true) <= max_distance { + ans = append(ans, sc.Name) + } + } + } + sort_levenshtein_matches(q, ans) + return ans +} + func (self *Command) SuggestionsForOption(name_with_hyphens string, max_distance int /* good default is 2 */) []string { ans := make([]string, 0, 8) q := strings.ToLower(name_with_hyphens) @@ -313,13 +338,7 @@ func (self *Command) SuggestionsForOption(name_with_hyphens string, max_distance } return nil }) - utils.StableSort(ans, func(a, b string) bool { - la, lb := utils.LevenshteinDistance(a, q, true), utils.LevenshteinDistance(b, q, true) - if la != lb { - return la < lb - } - return a < b - }) + sort_levenshtein_matches(q, ans) return ans } diff --git a/tools/cli/parse-args.go b/tools/cli/parse-args.go index b67a4b7dd..f2710586f 100644 --- a/tools/cli/parse-args.go +++ b/tools/cli/parse-args.go @@ -101,6 +101,10 @@ func (self *Command) parse_args(ctx *Context, args []string) error { } if !self.SubCommandIsOptional { if len(possible_cmds) == 0 { + possibles := self.SuggestionsForCommand(arg, 2) + if len(possibles) > 0 { + return &ParseError{Message: fmt.Sprintf("Unknown subcommand: :yellow:`%s`. Did you mean:\n\t%s", arg, strings.Join(possibles, "\n\t"))} + } 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)} } cn := make([]string, len(possible_cmds))