-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
match.go
101 lines (89 loc) · 2.12 KB
/
match.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package main
import (
"os"
"path/filepath"
"regexp"
"strings"
"github.com/lithammer/fuzzysearch/fuzzy"
)
type matcher func([]*entry, []string) []string
func bestGuess(entries []*entry, args []string) string {
candidates := getCandidates(entries, args, 1)
if len(candidates) > 0 {
return candidates[0]
}
return "."
}
var matchConsecutive = func(entries []*entry, args []string) []string {
nArgs := len(args)
var matches []string
loop_entries:
for _, e := range entries {
parts := strings.Split(e.val, string(os.PathSeparator))
parts = parts[1:]
for i, j := len(parts)-1, nArgs-1; i >= 0 && j >= 0; i, j = i-1, j-1 {
if !strings.Contains(
strings.ToLower(parts[i]),
strings.ToLower(args[j]),
) {
continue loop_entries
}
}
matches = append(matches, e.val)
}
return matches
}
var matchFuzzy = func(entries []*entry, args []string) []string {
var matches []string
// Only match the last part
arg := args[len(args)-1]
distanceThreshold := len(arg) * 2
for _, e := range entries {
_, lastPart := filepath.Split(e.val)
rank := fuzzy.RankMatch(arg, lastPart)
if rank == -1 {
continue
}
if rank < distanceThreshold {
matches = append(matches, e.val)
}
}
return matches
}
var matchAnywhere = func(entries []*entry, args []string) []string {
var matches []string
any := ".*"
regexParts := []string{"(?i)", any, strings.Join(args, any), any}
regex := strings.Join(regexParts, "")
pattern, err := regexp.Compile(regex)
if err != nil {
return matches
}
for _, e := range entries {
if pattern.MatchString(e.val) {
matches = append(matches, e.val)
}
}
return matches
}
func getCandidates(entries []*entry, args []string, limit int) []string {
var candidates []string
seen := make(map[string]bool)
matchers := []matcher{matchConsecutive, matchFuzzy, matchAnywhere}
for _, m := range matchers {
paths := m(entries, args)
if len(paths) > 0 {
for _, p := range paths {
if seen[p] || !isValidPath(p) {
continue
}
candidates = append(candidates, p)
seen[p] = true
if len(candidates) >= limit {
return candidates
}
}
}
}
return candidates
}