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
|
package fzf
import (
"fmt"
"os"
"runtime"
"time"
)
const COORDINATOR_DELAY time.Duration = 100 * time.Millisecond
func initProcs() {
runtime.GOMAXPROCS(runtime.NumCPU())
}
/*
Reader -> EVT_READ_FIN
Reader -> EVT_READ_NEW -> Matcher (restart)
Terminal -> EVT_SEARCH_NEW -> Matcher (restart)
Matcher -> EVT_SEARCH_PROGRESS -> Terminal (update info)
Matcher -> EVT_SEARCH_FIN -> Terminal (update list)
*/
func Run(options *Options) {
initProcs()
opts := ParseOptions()
if opts.Version {
fmt.Println(VERSION)
os.Exit(0)
}
// Event channel
eventBox := NewEventBox()
// Chunk list
var chunkList *ChunkList
if len(opts.WithNth) == 0 {
chunkList = NewChunkList(func(data *string, index int) *Item {
return &Item{text: data, index: index}
})
} else {
chunkList = NewChunkList(func(data *string, index int) *Item {
item := Item{text: data, index: index}
tokens := Tokenize(item.text, opts.Delimiter)
item.origText = item.text
item.text = Transform(tokens, opts.WithNth).whole
return &item
})
}
// Reader
reader := Reader{func(str string) { chunkList.Push(str) }, eventBox}
go reader.ReadSource()
// Matcher
patternBuilder := func(runes []rune) *Pattern {
return BuildPattern(
opts.Mode, opts.Case, opts.Nth, opts.Delimiter, runes)
}
matcher := NewMatcher(patternBuilder, opts.Sort > 0, eventBox)
// Defered-interactive / Non-interactive
// --select-1 | --exit-0 | --filter
if filtering := opts.Filter != nil; filtering || opts.Select1 || opts.Exit0 {
limit := 0
var patternString string
if filtering {
patternString = *opts.Filter
} else {
if opts.Select1 || opts.Exit0 {
limit = 1
}
patternString = opts.Query
}
pattern := patternBuilder([]rune(patternString))
looping := true
for looping {
eventBox.Wait(func(events *Events) {
for evt, _ := range *events {
switch evt {
case EVT_READ_FIN:
looping = false
return
}
}
})
time.Sleep(COORDINATOR_DELAY)
}
matches, cancelled := matcher.scan(MatchRequest{
chunks: chunkList.Snapshot(),
pattern: pattern}, limit)
if !cancelled && (filtering || opts.Exit0) {
if opts.PrintQuery {
fmt.Println(patternString)
}
for _, item := range matches {
item.Print()
}
os.Exit(0)
}
}
// Go interactive
go matcher.Loop()
// Terminal I/O
terminal := NewTerminal(opts, eventBox)
go terminal.Loop()
// Event coordination
reading := true
ticks := 0
for {
delay := true
ticks += 1
eventBox.Wait(func(events *Events) {
defer events.Clear()
for evt, value := range *events {
switch evt {
case EVT_READ_NEW, EVT_READ_FIN:
reading = reading && evt == EVT_READ_NEW
terminal.UpdateCount(chunkList.Count(), !reading)
matcher.Reset(chunkList.Snapshot(), terminal.Input(), false)
case EVT_SEARCH_NEW:
matcher.Reset(chunkList.Snapshot(), terminal.Input(), true)
delay = false
case EVT_SEARCH_PROGRESS:
switch val := value.(type) {
case float32:
terminal.UpdateProgress(val)
}
case EVT_SEARCH_FIN:
switch val := value.(type) {
case []*Item:
terminal.UpdateList(val)
}
}
}
})
if ticks > 3 && delay && reading {
time.Sleep(COORDINATOR_DELAY)
}
}
}
|