forked from ktr0731/go-fuzzyfinder
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
matching.go
99 lines (88 loc) · 2.22 KB
/
matching.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
// Package matching provides matching features that find appropriate strings
// by using a passed input string.
package matching
import (
"sort"
"strings"
"unicode"
"github.com/ktr0731/go-fuzzyfinder/scoring"
)
// Matched represents a result of FindAll.
type Matched struct {
// Idx is the index of an item of the original slice which was used to
// search matched strings.
Idx int
// Pos is the range of matched position.
// [2]int represents a open interval of a position.
Pos [2]int
// score is the value that indicates how it similar to the input string.
// The bigger score, the more similar it is.
score int
}
// Option represents available matching options.
type Option func(*opt)
type Mode int
const (
ModeSmart Mode = iota
ModeCaseSensitive
ModeCaseInsensitive
)
// opt represents available options and its default values.
type opt struct {
mode Mode
}
// WithMode specifies a matching mode. The default mode is ModeSmart.
func WithMode(m Mode) Option {
return func(o *opt) {
o.mode = m
}
}
// FindAll tries to find out sub-strings from slice that match the passed argument in.
// The returned slice is sorted by similarity scores in descending order.
func FindAll(in string, slice []string, opts ...Option) []Matched {
var opt opt
for _, o := range opts {
o(&opt)
}
m := match(in, slice, opt)
sort.Slice(m, func(i, j int) bool {
return m[i].score > m[j].score
})
return m
}
// match iterates each string of slice for check whether it is matched to the input string.
func match(input string, slice []string, opt opt) (res []Matched) {
if opt.mode == ModeSmart {
// Find an upper-case rune
n := strings.IndexFunc(input, unicode.IsUpper)
if n == -1 {
opt.mode = ModeCaseInsensitive
input = strings.ToLower(input)
} else {
opt.mode = ModeCaseSensitive
}
}
in := []rune(input)
for idxOfSlice, s := range slice {
var idx int
if opt.mode == ModeCaseInsensitive {
s = strings.ToLower(s)
}
LINE_MATCHING:
for _, r := range []rune(s) {
if r == in[idx] {
idx++
if idx == len(in) {
score, pos := scoring.Calculate(s, input)
res = append(res, Matched{
Idx: idxOfSlice,
Pos: pos,
score: score,
})
break LINE_MATCHING
}
}
}
}
return
}