// suggestTargets suggests the targets in the given package that might be misspellings of // the requested one. func suggestTargets(pkg *core.Package, label, dependor core.BuildLabel) string { r := []rune(label.Name) options := make(suggestions, 0, len(pkg.Targets)) for t := range pkg.Targets { distance := levenshtein.DistanceForStrings(r, []rune(t), levenshtein.DefaultOptions) if distance <= maxSuggestionDistance { options = append(options, suggestion{name: t, dist: distance}) } } if len(options) == 0 { return "" } sort.Sort(options) // Obviously there's now more code to pretty-print the suggestions than to do the calculation... msg := "\nMaybe you meant " for i, o := range options { if i > 0 { if i < len(options)-1 { msg += ", " } else { msg += " or " } } if pkg.Name == dependor.PackageName { msg += ":" + o.name } else { msg += fmt.Sprintf("//%s:%s", pkg.Name, o.name) } } return msg + " ?" // Leave a space so you can select them without getting the question mark }
// FindNearestCommand heuristically finds a Command the user wanted to type but // failed to type correctly. func FindNearestCommand(a Application, name string) *Command { commands := map[string]*Command{} for _, c := range a.GetCommands() { commands[c.Name()] = c } if c, ok := commands[name]; ok { return c } // Search for unique prefix. withPrefix := []*Command{} for n, c := range commands { if strings.HasPrefix(n, name) { withPrefix = append(withPrefix, c) } } if len(withPrefix) == 1 { return withPrefix[0] } // Search for case insensitivity. withPrefix = []*Command{} lowName := strings.ToLower(name) for n, c := range commands { if strings.HasPrefix(strings.ToLower(n), lowName) { withPrefix = append(withPrefix, c) } } if len(withPrefix) == 1 { return withPrefix[0] } // Calculate the levenshtein distance and take the closest one. closestD := 1000 var closestC *Command secondD := 1000 for n, c := range commands { dist := levenshtein.DistanceForStrings([]rune(n), []rune(name), levenshtein.DefaultOptions) if dist < closestD { secondD = closestD closestD = dist closestC = c } else if dist < secondD { secondD = dist } } if closestD > 3 { // Not similar enough. Don't be a fool and run a random command. return nil } if (secondD - closestD) < 3 { // Too ambiguous. return nil } return closestC }
func findDev(d Chadevs, name string) (Dev, bool) { cleanedname := strings.ToUpper(strings.TrimSpace(name)) distance := int(^uint(0) >> 1) // initialize to "infinity" idx := 0 for i := 0; i < devsCount(d); i++ { cleaneddev := strings.ToUpper(d.Devs[i].Name) if cleanedname == cleaneddev { return d.Devs[i], true } newdistance := levenshtein.DistanceForStrings([]rune(cleanedname), []rune(cleaneddev), levenshtein.DefaultOptions) if newdistance < distance { distance = newdistance idx = i } } return d.Devs[idx], false }
func searchGroups(g Groups, n string) Group { distance := int(^uint(0) >> 1) // nitialize to "infinity" var idx int n = strings.ToUpper(strings.TrimSpace(n)) for i := 0; i < len(g.Group); i++ { cleanGroup := strings.ToUpper(strings.TrimSpace(g.Group[i].Name)) if n == cleanGroup { return g.Group[i] } newdistance := levenshtein.DistanceForStrings([]rune(n), []rune(cleanGroup), levenshtein.DefaultOptions) if newdistance < distance { distance = newdistance idx = i } } return g.Group[idx] }
func suggestUnknownCmd(args []string, root *cmds.Command) []string { arg := args[0] var suggestions []string sortableSuggestions := make(suggestionSlice, 0) var sFinal []string const MIN_LEVENSHTEIN = 3 var options levenshtein.Options = levenshtein.Options{ InsCost: 1, DelCost: 3, SubCost: 2, Matches: func(sourceCharacter rune, targetCharacter rune) bool { return sourceCharacter == targetCharacter }, } // Start with a simple strings.Contains check for name, _ := range root.Subcommands { if strings.Contains(arg, name) { suggestions = append(suggestions, name) } } // If the string compare returns a match, return if len(suggestions) > 0 { return suggestions } for name, _ := range root.Subcommands { lev := levenshtein.DistanceForStrings([]rune(arg), []rune(name), options) if lev <= MIN_LEVENSHTEIN { sortableSuggestions = append(sortableSuggestions, &suggestion{name, lev}) } } sort.Sort(sortableSuggestions) for _, j := range sortableSuggestions { sFinal = append(sFinal, j.cmd) } return sFinal }