2015-01-01 14:49:30 -05:00
|
|
|
package fzf
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"runtime"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2015-01-08 08:07:04 -05:00
|
|
|
const COORDINATOR_DELAY_MAX time.Duration = 100 * time.Millisecond
|
|
|
|
const COORDINATOR_DELAY_STEP time.Duration = 10 * time.Millisecond
|
2015-01-01 14:49:30 -05:00
|
|
|
|
|
|
|
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
|
2015-01-03 15:00:28 -05:00
|
|
|
eventBox.Unwatch(EVT_READ_NEW)
|
2015-01-01 14:49:30 -05:00
|
|
|
for looping {
|
|
|
|
eventBox.Wait(func(events *Events) {
|
|
|
|
for evt, _ := range *events {
|
|
|
|
switch evt {
|
|
|
|
case EVT_READ_FIN:
|
|
|
|
looping = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-01-03 15:01:13 -05:00
|
|
|
snapshot, _ := chunkList.Snapshot()
|
2015-01-01 14:49:30 -05:00
|
|
|
matches, cancelled := matcher.scan(MatchRequest{
|
2015-01-03 15:01:13 -05:00
|
|
|
chunks: snapshot,
|
2015-01-01 14:49:30 -05:00
|
|
|
pattern: pattern}, limit)
|
|
|
|
|
2015-01-03 11:36:33 -05:00
|
|
|
if !cancelled && (filtering ||
|
|
|
|
opts.Exit0 && len(matches) == 0 || opts.Select1 && len(matches) == 1) {
|
2015-01-01 14:49:30 -05:00
|
|
|
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
|
2015-01-03 15:00:28 -05:00
|
|
|
eventBox.Watch(EVT_READ_NEW)
|
2015-01-01 14:49:30 -05:00
|
|
|
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
|
2015-01-03 15:01:13 -05:00
|
|
|
snapshot, count := chunkList.Snapshot()
|
|
|
|
terminal.UpdateCount(count, !reading)
|
|
|
|
matcher.Reset(snapshot, terminal.Input(), false)
|
2015-01-01 14:49:30 -05:00
|
|
|
|
|
|
|
case EVT_SEARCH_NEW:
|
2015-01-03 15:01:13 -05:00
|
|
|
snapshot, _ := chunkList.Snapshot()
|
|
|
|
matcher.Reset(snapshot, terminal.Input(), true)
|
2015-01-01 14:49:30 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2015-01-08 08:07:04 -05:00
|
|
|
if delay && reading {
|
|
|
|
dur := DurWithin(
|
|
|
|
time.Duration(ticks)*COORDINATOR_DELAY_STEP,
|
|
|
|
0, COORDINATOR_DELAY_MAX)
|
|
|
|
time.Sleep(dur)
|
2015-01-01 14:49:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|