From cd847affb79ea6438c9721635724efc6f58e2215 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 12 Jan 2015 12:56:17 +0900 Subject: [PATCH] Reorganize source code --- src/Makefile | 2 +- src/{ => algo}/algo.go | 2 +- src/{ => algo}/algo_test.go | 2 +- src/chunklist.go | 10 ++++---- src/constants.go | 9 +++---- src/core.go | 10 ++++---- src/matcher.go | 18 +++++++------- src/options.go | 3 ++- src/pattern.go | 12 ++++++---- src/pattern_test.go | 8 +++++-- src/reader.go | 9 ++++--- src/reader_test.go | 10 +++++--- src/terminal.go | 39 +++++++++++++++++-------------- src/tokenizer.go | 4 +++- src/{ => util}/atomicbool.go | 2 +- src/{ => util}/atomicbool_test.go | 2 +- src/{ => util}/eventbox.go | 5 +++- src/{ => util}/eventbox_test.go | 12 +++++++++- src/{ => util}/util.go | 31 ++++++++++++++++-------- src/util/util_test.go | 22 +++++++++++++++++ src/util_test.go | 18 -------------- 21 files changed, 139 insertions(+), 91 deletions(-) rename src/{ => algo}/algo.go (99%) rename src/{ => algo}/algo_test.go (99%) rename src/{ => util}/atomicbool.go (98%) rename src/{ => util}/atomicbool_test.go (95%) rename src/{ => util}/eventbox.go (95%) rename src/{ => util}/eventbox_test.go (85%) rename src/{ => util}/util.go (60%) create mode 100644 src/util/util_test.go delete mode 100644 src/util_test.go diff --git a/src/Makefile b/src/Makefile index 7108f0d..43a7bc0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,7 +33,7 @@ build: fzf/$(BINARY32) fzf/$(BINARY64) test: go get - go test -v + go test -v ./... install: $(BINDIR)/fzf diff --git a/src/algo.go b/src/algo/algo.go similarity index 99% rename from src/algo.go rename to src/algo/algo.go index 5f15ab3..bc4e538 100644 --- a/src/algo.go +++ b/src/algo/algo.go @@ -1,4 +1,4 @@ -package fzf +package algo import "strings" diff --git a/src/algo_test.go b/src/algo/algo_test.go similarity index 99% rename from src/algo_test.go rename to src/algo/algo_test.go index 5da01a6..363b6ee 100644 --- a/src/algo_test.go +++ b/src/algo/algo_test.go @@ -1,4 +1,4 @@ -package fzf +package algo import ( "strings" diff --git a/src/chunklist.go b/src/chunklist.go index 73983b1..571a59a 100644 --- a/src/chunklist.go +++ b/src/chunklist.go @@ -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)) } diff --git a/src/constants.go b/src/constants.go index 80eb634..a871570 100644 --- a/src/constants.go +++ b/src/constants.go @@ -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 diff --git a/src/core.go b/src/core.go index 65e641c..ee90413 100644 --- a/src/core.go +++ b/src/core.go @@ -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) diff --git a/src/matcher.go b/src/matcher.go index b8be287..1ea9541 100644 --- a/src/matcher.go +++ b/src/matcher.go @@ -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 { diff --git a/src/options.go b/src/options.go index cf0608b..e1dba29 100644 --- a/src/options.go +++ b/src/options.go @@ -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] diff --git a/src/pattern.go b/src/pattern.go index 9f32de6..17e3b6b 100644 --- a/src/pattern.go +++ b/src/pattern.go @@ -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)}})) } diff --git a/src/pattern_test.go b/src/pattern_test.go index c006c45..4d36eda 100644 --- a/src/pattern_test.go +++ b/src/pattern_test.go @@ -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) } diff --git a/src/reader.go b/src/reader.go index 269a2fd..2c10b8a 100644 --- a/src/reader.go +++ b/src/reader.go @@ -1,13 +1,12 @@ package fzf -// #include -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 diff --git a/src/reader_test.go b/src/reader_test.go index 630f6fa..5800b3f 100644 --- a/src/reader_test.go +++ b/src/reader_test.go @@ -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) } diff --git a/src/terminal.go b/src/terminal.go index daf63c5..4204a1d 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -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 } diff --git a/src/tokenizer.go b/src/tokenizer.go index 294329b..26aebd9 100644 --- a/src/tokenizer.go +++ b/src/tokenizer.go @@ -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 diff --git a/src/atomicbool.go b/src/util/atomicbool.go similarity index 98% rename from src/atomicbool.go rename to src/util/atomicbool.go index b264724..9e1bdc8 100644 --- a/src/atomicbool.go +++ b/src/util/atomicbool.go @@ -1,4 +1,4 @@ -package fzf +package util import "sync" diff --git a/src/atomicbool_test.go b/src/util/atomicbool_test.go similarity index 95% rename from src/atomicbool_test.go rename to src/util/atomicbool_test.go index 0af4570..1feff79 100644 --- a/src/atomicbool_test.go +++ b/src/util/atomicbool_test.go @@ -1,4 +1,4 @@ -package fzf +package util import "testing" diff --git a/src/eventbox.go b/src/util/eventbox.go similarity index 95% rename from src/eventbox.go rename to src/util/eventbox.go index 0c8f922..568ad9f 100644 --- a/src/eventbox.go +++ b/src/util/eventbox.go @@ -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{} diff --git a/src/eventbox_test.go b/src/util/eventbox_test.go similarity index 85% rename from src/eventbox_test.go rename to src/util/eventbox_test.go index 1cd7f22..5a9dc30 100644 --- a/src/eventbox_test.go +++ b/src/util/eventbox_test.go @@ -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() diff --git a/src/util.go b/src/util/util.go similarity index 60% rename from src/util.go rename to src/util/util.go index 5461705..14833c0 100644 --- a/src/util.go +++ b/src/util/util.go @@ -1,6 +1,12 @@ -package fzf +package util -import "time" +// #include +import "C" + +import ( + "os" + "time" +) // Max returns the largest integer func Max(first int, items ...int) int { @@ -21,15 +27,15 @@ 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 } - return min + if val > max { + return max + } + return val } // DurWithin limits the given time.Duration with the upper and lower bounds @@ -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 +} diff --git a/src/util/util_test.go b/src/util/util_test.go new file mode 100644 index 0000000..06cfd4f --- /dev/null +++ b/src/util/util_test.go @@ -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) + } +} diff --git a/src/util_test.go b/src/util_test.go deleted file mode 100644 index 814b42c..0000000 --- a/src/util_test.go +++ /dev/null @@ -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") - } -}