github.com/koron/hk@v0.0.0-20150303213137-b8aeaa3ab34c/suggest.go (about) 1 package main 2 3 import ( 4 "sort" 5 "strconv" 6 ) 7 8 type Suggestion struct { 9 s string 10 d int 11 } 12 13 type Suggestions []Suggestion 14 15 func (a Suggestions) Len() int { return len(a) } 16 func (a Suggestions) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 17 func (a Suggestions) Less(i, j int) bool { return a[i].d < a[j].d } 18 19 // suggest returns command names that are similar to s. 20 func suggest(s string) (a []string) { 21 var g Suggestions 22 for _, c := range commands { 23 if d := editDistance(s, c.Name()); d < 4 { 24 if c.Runnable() { 25 g = append(g, Suggestion{c.Name(), d}) 26 } else { 27 g = append(g, Suggestion{strconv.Quote("help " + c.Name()), d}) 28 } 29 } 30 } 31 sort.Sort(g) 32 for i, s := range g { 33 a = append(a, s.s) 34 if i >= 4 { 35 break 36 } 37 } 38 return a 39 } 40 41 func editDistance(a, b string) int { 42 var d [][]int 43 d = append(d, make([]int, len(b)+1)) 44 for i := range b { 45 d[0][i+1] = i + 1 46 } 47 for i := range a { 48 v := make([]int, len(b)+1) 49 d = append(d, v) 50 v[0] = i + 1 51 } 52 for j, cb := range []byte(b) { 53 for i, ca := range []byte(a) { 54 if ca == cb { 55 d[i+1][j+1] = d[i][j] 56 } else { 57 cost := d[i][j+1] // delete 58 if v := d[i+1][j]; v < cost { // insert 59 cost = v 60 } 61 if v := d[i][j]; v < cost { // substitute 62 cost = v 63 } 64 if i > 0 && j > 0 { 65 if ca == b[j-1] && cb == a[i-1] { 66 if v := d[i-1][j-1]; v < cost { // transpose 67 cost = v 68 } 69 } 70 } 71 d[i+1][j+1] = cost + 1 72 } 73 } 74 } 75 return d[len(a)][len(b)] 76 }