Show suggestions for options based on levenshtein distance
This commit is contained in:
parent
654bd23109
commit
75ead358a2
@ -301,6 +301,28 @@ func (self *Command) GetVisibleOptions() ([]string, map[string][]*Option) {
|
||||
return group_titles, gmap
|
||||
}
|
||||
|
||||
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)
|
||||
self.VisitAllOptions(func(opt *Option) error {
|
||||
for _, a := range opt.Aliases {
|
||||
as := a.String()
|
||||
if utils.LevenshteinDistance(as, q, true) <= max_distance {
|
||||
ans = append(ans, as)
|
||||
}
|
||||
}
|
||||
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
|
||||
})
|
||||
return ans
|
||||
}
|
||||
|
||||
func (self *Command) FindSubCommand(name string) *Command {
|
||||
for _, g := range self.SubCommandGroups {
|
||||
c := g.FindSubCommand(name)
|
||||
|
||||
@ -26,6 +26,10 @@ func (self *Command) parse_args(ctx *Context, args []string) error {
|
||||
opt = possible_options[0]
|
||||
opt_str = opt.MatchingAlias(NormalizeOptionName(opt_str), !strings.HasPrefix(opt_str, "--"))
|
||||
} else if len(possible_options) == 0 {
|
||||
possibles := self.SuggestionsForOption(opt_str, 2)
|
||||
if len(possibles) > 0 {
|
||||
return &ParseError{Message: fmt.Sprintf("Unknown option: :yellow:`%s`. Did you mean:\n\t%s", opt_str, strings.Join(possibles, "\n\t"))}
|
||||
}
|
||||
return &ParseError{Message: fmt.Sprintf("Unknown option: :yellow:`%s`", opt_str)}
|
||||
} else {
|
||||
ambi := make([]string, len(possible_options))
|
||||
|
||||
46
tools/utils/levenshtein.go
Normal file
46
tools/utils/levenshtein.go
Normal file
@ -0,0 +1,46 @@
|
||||
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
// compares two strings and returns the Levenshtein distance between them.
|
||||
func LevenshteinDistance(s, t string, ignore_case bool) int {
|
||||
if ignore_case {
|
||||
s = strings.ToLower(s)
|
||||
t = strings.ToLower(t)
|
||||
}
|
||||
d := make([][]int, len(s)+1)
|
||||
for i := range d {
|
||||
d[i] = make([]int, len(t)+1)
|
||||
}
|
||||
for i := range d {
|
||||
d[i][0] = i
|
||||
}
|
||||
for j := range d[0] {
|
||||
d[0][j] = j
|
||||
}
|
||||
for j := 1; j <= len(t); j++ {
|
||||
for i := 1; i <= len(s); i++ {
|
||||
if s[i-1] == t[j-1] {
|
||||
d[i][j] = d[i-1][j-1]
|
||||
} else {
|
||||
min := d[i-1][j]
|
||||
if d[i][j-1] < min {
|
||||
min = d[i][j-1]
|
||||
}
|
||||
if d[i-1][j-1] < min {
|
||||
min = d[i-1][j-1]
|
||||
}
|
||||
d[i][j] = min + 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return d[len(s)][len(t)]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user