Reorganize source code

This commit is contained in:
Junegunn Choi 2015-01-12 12:56:17 +09:00
parent 7a2bc2cada
commit cd847affb7
21 changed files with 139 additions and 91 deletions

View File

@ -33,7 +33,7 @@ build: fzf/$(BINARY32) fzf/$(BINARY64)
test:
go get
go test -v
go test -v ./...
install: $(BINDIR)/fzf

View File

@ -1,4 +1,4 @@
package fzf
package algo
import "strings"

View File

@ -1,4 +1,4 @@
package fzf
package algo
import (
"strings"

View File

@ -8,20 +8,20 @@ const ChunkSize int = 100
// Chunk is a list of Item pointers whose size has the upper limit of ChunkSize
type Chunk []*Item // >>> []Item
// Transformer is a closure type that builds Item object from a pointer to a
// ItemBuilder is a closure type that builds Item object from a pointer to a
// string and an integer
type Transformer func(*string, int) *Item
type ItemBuilder func(*string, int) *Item
// ChunkList is a list of Chunks
type ChunkList struct {
chunks []*Chunk
count int
mutex sync.Mutex
trans Transformer
trans ItemBuilder
}
// NewChunkList returns a new ChunkList
func NewChunkList(trans Transformer) *ChunkList {
func NewChunkList(trans ItemBuilder) *ChunkList {
return &ChunkList{
chunks: []*Chunk{},
count: 0,
@ -29,7 +29,7 @@ func NewChunkList(trans Transformer) *ChunkList {
trans: trans}
}
func (c *Chunk) push(trans Transformer, data *string, index int) {
func (c *Chunk) push(trans ItemBuilder, data *string, index int) {
*c = append(*c, trans(data, index))
}

View File

@ -1,14 +1,15 @@
package fzf
import (
"github.com/junegunn/fzf/src/util"
)
// Current version
const Version = "0.9.0"
// EventType is the type for fzf events
type EventType int
// fzf events
const (
EvtReadNew EventType = iota
EvtReadNew util.EventType = iota
EvtReadFin
EvtSearchNew
EvtSearchProgress

View File

@ -30,6 +30,8 @@ import (
"os"
"runtime"
"time"
"github.com/junegunn/fzf/src/util"
)
const coordinatorDelayMax time.Duration = 100 * time.Millisecond
@ -59,7 +61,7 @@ func Run(options *Options) {
}
// Event channel
eventBox := NewEventBox()
eventBox := util.NewEventBox()
// Chunk list
var chunkList *ChunkList
@ -111,7 +113,7 @@ func Run(options *Options) {
looping := true
eventBox.Unwatch(EvtReadNew)
for looping {
eventBox.Wait(func(events *Events) {
eventBox.Wait(func(events *util.Events) {
for evt := range *events {
switch evt {
case EvtReadFin:
@ -154,7 +156,7 @@ func Run(options *Options) {
for {
delay := true
ticks++
eventBox.Wait(func(events *Events) {
eventBox.Wait(func(events *util.Events) {
defer events.Clear()
for evt, value := range *events {
switch evt {
@ -185,7 +187,7 @@ func Run(options *Options) {
}
})
if delay && reading {
dur := DurWithin(
dur := util.DurWithin(
time.Duration(ticks)*coordinatorDelayStep,
0, coordinatorDelayMax)
time.Sleep(dur)

View File

@ -6,6 +6,8 @@ import (
"sort"
"sync"
"time"
"github.com/junegunn/fzf/src/util"
)
// MatchRequest represents a search request
@ -18,14 +20,14 @@ type MatchRequest struct {
type Matcher struct {
patternBuilder func([]rune) *Pattern
sort bool
eventBox *EventBox
reqBox *EventBox
eventBox *util.EventBox
reqBox *util.EventBox
partitions int
mergerCache map[string]*Merger
}
const (
reqRetry EventType = iota
reqRetry util.EventType = iota
reqReset
)
@ -35,12 +37,12 @@ const (
// NewMatcher returns a new Matcher
func NewMatcher(patternBuilder func([]rune) *Pattern,
sort bool, eventBox *EventBox) *Matcher {
sort bool, eventBox *util.EventBox) *Matcher {
return &Matcher{
patternBuilder: patternBuilder,
sort: sort,
eventBox: eventBox,
reqBox: NewEventBox(),
reqBox: util.NewEventBox(),
partitions: runtime.NumCPU(),
mergerCache: make(map[string]*Merger)}
}
@ -52,7 +54,7 @@ func (m *Matcher) Loop() {
for {
var request MatchRequest
m.reqBox.Wait(func(events *Events) {
m.reqBox.Wait(func(events *util.Events) {
for _, val := range *events {
switch val := val.(type) {
case MatchRequest:
@ -128,7 +130,7 @@ func (m *Matcher) scan(request MatchRequest, limit int) (*Merger, bool) {
}
pattern := request.pattern
empty := pattern.IsEmpty()
cancelled := NewAtomicBool(false)
cancelled := util.NewAtomicBool(false)
slices := m.sliceChunks(request.chunks)
numSlices := len(slices)
@ -202,7 +204,7 @@ func (m *Matcher) scan(request MatchRequest, limit int) (*Merger, bool) {
func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool) {
pattern := m.patternBuilder(patternRunes)
var event EventType
var event util.EventType
if cancel {
event = reqReset
} else {

View File

@ -2,10 +2,11 @@ package fzf
import (
"fmt"
"github.com/junegunn/go-shellwords"
"os"
"regexp"
"strings"
"github.com/junegunn/go-shellwords"
)
const usage = `usage: fzf [options]

View File

@ -4,6 +4,8 @@ import (
"regexp"
"sort"
"strings"
"github.com/junegunn/fzf/src/algo"
)
const uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@ -112,10 +114,10 @@ func BuildPattern(mode Mode, caseMode Case,
delimiter: delimiter,
procFun: make(map[termType]func(bool, *string, []rune) (int, int))}
ptr.procFun[termFuzzy] = FuzzyMatch
ptr.procFun[termExact] = ExactMatchNaive
ptr.procFun[termPrefix] = PrefixMatch
ptr.procFun[termSuffix] = SuffixMatch
ptr.procFun[termFuzzy] = algo.FuzzyMatch
ptr.procFun[termExact] = algo.ExactMatchNaive
ptr.procFun[termPrefix] = algo.PrefixMatch
ptr.procFun[termSuffix] = algo.SuffixMatch
_patternCache[asString] = ptr
return ptr
@ -245,7 +247,7 @@ func (p *Pattern) fuzzyMatch(chunk *Chunk) []*Item {
matches := []*Item{}
for _, item := range *chunk {
input := p.prepareInput(item)
if sidx, eidx := p.iter(FuzzyMatch, input, p.text); sidx >= 0 {
if sidx, eidx := p.iter(algo.FuzzyMatch, input, p.text); sidx >= 0 {
matches = append(matches,
dupItem(item, []Offset{Offset{int32(sidx), int32(eidx)}}))
}

View File

@ -1,6 +1,10 @@
package fzf
import "testing"
import (
"testing"
"github.com/junegunn/fzf/src/algo"
)
func TestParseTermsExtended(t *testing.T) {
terms := parseTerms(ModeExtended,
@ -55,7 +59,7 @@ func TestExact(t *testing.T) {
pattern := BuildPattern(ModeExtended, CaseSmart,
[]Range{}, nil, []rune("'abc"))
str := "aabbcc abc"
sidx, eidx := ExactMatchNaive(pattern.caseSensitive, &str, pattern.terms[0].text)
sidx, eidx := algo.ExactMatchNaive(pattern.caseSensitive, &str, pattern.terms[0].text)
if sidx != 7 || eidx != 10 {
t.Errorf("%s / %d / %d", pattern.terms, sidx, eidx)
}

View File

@ -1,13 +1,12 @@
package fzf
// #include <unistd.h>
import "C"
import (
"bufio"
"io"
"os"
"os/exec"
"github.com/junegunn/fzf/src/util"
)
const defaultCommand = `find * -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null`
@ -15,12 +14,12 @@ const defaultCommand = `find * -path '*/\.*' -prune -o -type f -print -o -type l
// Reader reads from command or standard input
type Reader struct {
pusher func(string)
eventBox *EventBox
eventBox *util.EventBox
}
// ReadSource reads data from the default command or from standard input
func (r *Reader) ReadSource() {
if int(C.isatty(C.int(os.Stdin.Fd()))) != 0 {
if util.IsTty() {
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
if len(cmd) == 0 {
cmd = defaultCommand

View File

@ -1,10 +1,14 @@
package fzf
import "testing"
import (
"testing"
"github.com/junegunn/fzf/src/util"
)
func TestReadFromCommand(t *testing.T) {
strs := []string{}
eb := NewEventBox()
eb := util.NewEventBox()
reader := Reader{
pusher: func(s string) { strs = append(strs, s) },
eventBox: eb}
@ -26,7 +30,7 @@ func TestReadFromCommand(t *testing.T) {
}
// Wait should return immediately
eb.Wait(func(events *Events) {
eb.Wait(func(events *util.Events) {
if _, found := (*events)[EvtReadNew]; !found {
t.Errorf("%s", events)
}

View File

@ -2,13 +2,16 @@ package fzf
import (
"fmt"
C "github.com/junegunn/fzf/src/curses"
"github.com/junegunn/go-runewidth"
"os"
"regexp"
"sort"
"sync"
"time"
C "github.com/junegunn/fzf/src/curses"
"github.com/junegunn/fzf/src/util"
"github.com/junegunn/go-runewidth"
)
// Terminal represents terminal input/output
@ -28,8 +31,8 @@ type Terminal struct {
reading bool
merger *Merger
selected map[*string]*string
reqBox *EventBox
eventBox *EventBox
reqBox *util.EventBox
eventBox *util.EventBox
mutex sync.Mutex
initFunc func()
suppress bool
@ -38,7 +41,7 @@ type Terminal struct {
var _spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
const (
reqPrompt EventType = iota
reqPrompt util.EventType = iota
reqInfo
reqList
reqRefresh
@ -53,7 +56,7 @@ const (
)
// NewTerminal returns new Terminal object
func NewTerminal(opts *Options, eventBox *EventBox) *Terminal {
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
input := []rune(opts.Query)
return &Terminal{
prompt: opts.Prompt,
@ -68,7 +71,7 @@ func NewTerminal(opts *Options, eventBox *EventBox) *Terminal {
printQuery: opts.PrintQuery,
merger: EmptyMerger,
selected: make(map[*string]*string),
reqBox: NewEventBox(),
reqBox: util.NewEventBox(),
eventBox: eventBox,
mutex: sync.Mutex{},
suppress: true,
@ -288,7 +291,7 @@ func (*Terminal) printHighlighted(item *Item, bold bool, col1 int, col2 int) {
b, e := offset[0], offset[1]
b += 2 - diff
e += 2 - diff
b = Max32(b, 2)
b = util.Max32(b, 2)
if b < e {
offsets[idx] = Offset{b, e}
}
@ -300,8 +303,8 @@ func (*Terminal) printHighlighted(item *Item, bold bool, col1 int, col2 int) {
sort.Sort(ByOrder(offsets))
var index int32
for _, offset := range offsets {
b := Max32(index, offset[0])
e := Max32(index, offset[1])
b := util.Max32(index, offset[0])
e := util.Max32(index, offset[1])
C.CPrint(col1, bold, string(text[index:b]))
C.CPrint(col2, bold, string(text[b:e]))
index = e
@ -388,7 +391,7 @@ func (t *Terminal) Loop() {
go func() {
for {
t.reqBox.Wait(func(events *Events) {
t.reqBox.Wait(func(events *util.Events) {
defer events.Clear()
t.mutex.Lock()
for req := range *events {
@ -426,8 +429,8 @@ func (t *Terminal) Loop() {
t.mutex.Lock()
previousInput := t.input
events := []EventType{reqPrompt}
req := func(evts ...EventType) {
events := []util.EventType{reqPrompt}
req := func(evts ...util.EventType) {
for _, event := range evts {
events = append(events, event)
if event == reqClose || event == reqQuit {
@ -538,7 +541,7 @@ func (t *Terminal) Loop() {
t.cx++
case C.Mouse:
me := event.MouseEvent
mx, my := Min(len(t.input), Max(0, me.X-len(t.prompt))), me.Y
mx, my := util.Constrain(me.X-len(t.prompt), 0, len(t.input)), me.Y
if !t.reverse {
my = C.MaxY() - my - 1
}
@ -588,7 +591,7 @@ func (t *Terminal) constrain() {
height := C.MaxY() - 2
diffpos := t.cy - t.offset
t.cy = Max(0, Min(t.cy, count-1))
t.cy = util.Constrain(t.cy, 0, count-1)
if t.cy > t.offset+(height-1) {
// Ceil
@ -600,8 +603,8 @@ func (t *Terminal) constrain() {
// Adjustment
if count-t.offset < height {
t.offset = Max(0, count-height)
t.cy = Max(0, Min(t.offset+diffpos, count-1))
t.offset = util.Max(0, count-height)
t.cy = util.Constrain(t.offset+diffpos, 0, count-1)
}
}
@ -614,7 +617,7 @@ func (t *Terminal) vmove(o int) {
}
func (t *Terminal) vset(o int) bool {
t.cy = Max(0, Min(o, t.merger.Length()-1))
t.cy = util.Constrain(o, 0, t.merger.Length()-1)
return t.cy == o
}

View File

@ -4,6 +4,8 @@ import (
"regexp"
"strconv"
"strings"
"github.com/junegunn/fzf/src/util"
)
const rangeEllipsis = 0
@ -180,7 +182,7 @@ func Transform(tokens []Token, withNth []Range) *Transformed {
end += numTokens + 1
}
}
minIdx = Max(0, begin-1)
minIdx = util.Max(0, begin-1)
for idx := begin; idx <= end; idx++ {
if idx >= 1 && idx <= numTokens {
part += *tokens[idx-1].text

View File

@ -1,4 +1,4 @@
package fzf
package util
import "sync"

View File

@ -1,4 +1,4 @@
package fzf
package util
import "testing"

View File

@ -1,7 +1,10 @@
package fzf
package util
import "sync"
// EventType is the type for fzf events
type EventType int
// Events is a type that associates EventType to any data
type Events map[EventType]interface{}

View File

@ -1,7 +1,17 @@
package fzf
package util
import "testing"
// fzf events
const (
EvtReadNew EventType = iota
EvtReadFin
EvtSearchNew
EvtSearchProgress
EvtSearchFin
EvtClose
)
func TestEventBox(t *testing.T) {
eb := NewEventBox()

View File

@ -1,6 +1,12 @@
package fzf
package util
import "time"
// #include <unistd.h>
import "C"
import (
"os"
"time"
)
// Max returns the largest integer
func Max(first int, items ...int) int {
@ -21,16 +27,16 @@ func Max32(first int32, second int32) int32 {
return second
}
// Min returns the smallest integer
func Min(first int, items ...int) int {
min := first
for _, item := range items {
if item < min {
min = item
}
}
// Constrain limits the given integer with the upper and lower bounds
func Constrain(val int, min int, max int) int {
if val < min {
return min
}
if val > max {
return max
}
return val
}
// DurWithin limits the given time.Duration with the upper and lower bounds
func DurWithin(
@ -43,3 +49,8 @@ func DurWithin(
}
return val
}
// IsTty returns true is stdin is a terminal
func IsTty() bool {
return int(C.isatty(C.int(os.Stdin.Fd()))) != 0
}

22
src/util/util_test.go Normal file
View File

@ -0,0 +1,22 @@
package util
import "testing"
func TestMax(t *testing.T) {
if Max(-2, 5, 1, 4, 3) != 5 {
t.Error("Invalid result")
}
}
func TestContrain(t *testing.T) {
if Constrain(-3, -1, 3) != -1 {
t.Error("Expected", -1)
}
if Constrain(2, -1, 3) != 2 {
t.Error("Expected", 2)
}
if Constrain(5, -1, 3) != 3 {
t.Error("Expected", 3)
}
}

View File

@ -1,18 +0,0 @@
package fzf
import "testing"
func TestMax(t *testing.T) {
if Max(-2, 5, 1, 4, 3) != 5 {
t.Error("Invalid result")
}
}
func TestMin(t *testing.T) {
if Min(2, -3) != -3 {
t.Error("Invalid result")
}
if Min(-2, 5, 1, 4, 3) != -2 {
t.Error("Invalid result")
}
}