-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathcore.go
182 lines (165 loc) · 4.25 KB
/
core.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package fzf
import (
"fmt"
"github.com/reinhrst/fzf-lib/algo"
"github.com/reinhrst/fzf-lib/util"
)
type Options struct {
// If true, each word (separated by non-escaped spaces) is an independent
// searchterm. If false, all spaces are literal
Extended bool
// if true, default is Fuzzy search (' escapes to make exact search)
// if false, default is exact search (' escapes to make fuzzy search)
Fuzzy bool
// CaseRespect, CaseIgnore or CaseSmart
// CaseSmart matches case insensitive if the needle is all lowercase, else case sensitive
CaseMode Case
// set to False to get fzf --literal behaviour:
// "Do not normalize latin script letters for matching."
Normalize bool
// Array with options from {ByScore, ByLength, ByBegin, ByEnd}.
// Matches will first be sorted by the first element, ties will be sorted by
// second element, etc.
// ByScore: Each match is scored (see algo file for more info), higher score
// comes first
// ByLength: Shorter match wins
// ByBegin: Match closer to begin of string wins
// ByEnd: Match closer to end of string wins
//
// If all methods give equal score (including when the Sort slice is empty),
// the result is sorted by HayIndex, the order in which they appeared in
// the input.
Sort []Criterion
}
func DefaultOptions() Options {
return Options{
Extended: true,
Fuzzy: true,
CaseMode: CaseSmart,
Normalize: true,
Sort: []Criterion{ByScore, ByLength},
}
}
type SearchResult struct {
Needle string
SearchOptions Options
Matches []MatchResult
}
type MatchResult struct {
Key string
HayIndex int32
Score int
Positions []int
}
type Fzf struct {
eventBox *util.EventBox
matcher *Matcher
chunkList *ChunkList
slab *util.Slab
resultChannel chan SearchResult
}
// Creates a new Fzf object, with the given haystack and the given options
func New(hayStack []string, opts Options) *Fzf {
var itemIndex int32
var chunkList = NewChunkList(func(item *Item, data []byte) bool {
item.text = util.ToChars(data)
item.text.Index = itemIndex
itemIndex++
return true
})
for _, hayStraw := range hayStack {
chunkList.Push([]byte(hayStraw))
}
eventBox := util.NewEventBox()
forward := true
for _, cri := range opts.Sort {
if cri == ByEnd {
forward = false
break
}
if cri == ByBegin {
break
}
}
patternCache := make(map[string]*Pattern)
patternBuilder := func(needle string) *Pattern {
return BuildPattern(
opts.Fuzzy, algo.FuzzyMatchV2, opts.Extended,
opts.CaseMode, opts.Normalize, forward, needle, opts.Sort,
&patternCache)
}
matcher := NewMatcher(patternBuilder, true, false, eventBox)
resultChannel := make(chan SearchResult)
fzf := &Fzf{
eventBox,
matcher,
chunkList,
util.MakeSlab(slab16Size, slab32Size),
resultChannel,
}
fzf.start()
return fzf
}
func (fzf *Fzf) start() {
go fzf.loop()
go fzf.matcher.Loop()
}
func (fzf *Fzf) GetResultChannel() <-chan SearchResult {
return fzf.resultChannel
}
func (fzf *Fzf) loop() {
for {
var merger *Merger
var progress = false
quit := false
fzf.eventBox.Wait(func(events *util.Events) {
for evt, val := range *events {
switch evt {
case EvtSearchFin:
merger = val.(*Merger)
case EvtSearchProgress:
// log.Println("search progress, ignoring for now")
progress = true
case EvtQuit:
quit = true
default:
panic(fmt.Sprintf("Unexpected type: %T", val))
}
}
events.Clear()
})
if progress && merger == nil {
continue // TODO do something useful here
}
if quit {
break
}
var matchResults []MatchResult
for i := 0; i < merger.Length(); i++ {
result := merger.Get(i)
item := result.item
pos := result.positions
score := result.score
matchResults = append(matchResults, MatchResult{
Key: item.text.ToString(),
HayIndex: item.Index(),
Score: score,
Positions: *pos,
})
}
result := SearchResult{
Needle: merger.pattern.originalText,
Matches: matchResults,
}
fzf.resultChannel <- result
}
}
func (fzf *Fzf) Search(needle string) {
snapshot, _ := fzf.chunkList.Snapshot()
fzf.matcher.Reset(snapshot, needle, false, false, true, false)
}
func (fzf *Fzf) End() {
fzf.matcher.reqBox.Set(EvtQuit, nil)
fzf.eventBox.Set(EvtQuit, nil)
close(fzf.resultChannel)
}