From 8d3a302a1754a4e28cc1085b95e9a03981372d02 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Wed, 13 Jan 2016 21:36:44 +0900 Subject: [PATCH] Simplify Item structure This commit compensates for the performance overhead from the extended tiebreak option. --- src/chunklist_test.go | 4 ++-- src/core.go | 2 -- src/item.go | 40 +++++++++++++++++----------------------- src/item_test.go | 32 ++++++++++++++++---------------- src/merger_test.go | 2 +- src/options.go | 7 +------ src/pattern.go | 3 +-- src/terminal.go | 11 +++++------ 8 files changed, 43 insertions(+), 58 deletions(-) diff --git a/src/chunklist_test.go b/src/chunklist_test.go index 6ddd336..5f7481d 100644 --- a/src/chunklist_test.go +++ b/src/chunklist_test.go @@ -7,7 +7,7 @@ import ( func TestChunkList(t *testing.T) { // FIXME global - sortCriteria = []criterion{byMatchLen, byLength, byIndex} + sortCriteria = []criterion{byMatchLen, byLength} cl := NewChunkList(func(s []byte, i int) *Item { return &Item{text: []rune(string(s)), rank: buildEmptyRank(int32(i * 2))} @@ -39,7 +39,7 @@ func TestChunkList(t *testing.T) { if len(*chunk1) != 2 { t.Error("Snapshot should contain only two items") } - last := func(arr []int32) int32 { + last := func(arr [5]int32) int32 { return arr[len(arr)-1] } if string((*chunk1)[0].text) != "hello" || last((*chunk1)[0].rank) != 0 || diff --git a/src/core.go b/src/core.go index 1906c50..24af050 100644 --- a/src/core.go +++ b/src/core.go @@ -103,7 +103,6 @@ func Run(opts *Options) { runes, colors := ansiProcessor(data) return &Item{ text: runes, - index: int32(index), colors: colors, rank: buildEmptyRank(int32(index))} }) @@ -120,7 +119,6 @@ func Run(opts *Options) { item := Item{ text: joinTokens(trans), origText: &runes, - index: int32(index), colors: nil, rank: buildEmptyRank(int32(index))} diff --git a/src/item.go b/src/item.go index a4fa609..9200efb 100644 --- a/src/item.go +++ b/src/item.go @@ -20,41 +20,41 @@ type Item struct { text []rune origText *[]rune transformed []Token - index int32 offsets []Offset colors []ansiOffset - rank []int32 + rank [5]int32 } // Sort criteria to use. Never changes once fzf is started. var sortCriteria []criterion -func isRankValid(rank []int32) bool { +func isRankValid(rank [5]int32) bool { // Exclude ordinal index - for i := 0; i < len(rank)-1; i++ { - if rank[i] > 0 { + for _, r := range rank[:4] { + if r > 0 { return true } } return false } -func buildEmptyRank(index int32) []int32 { - len := len(sortCriteria) - arr := make([]int32, len) - arr[len-1] = index - return arr +func buildEmptyRank(index int32) [5]int32 { + return [5]int32{0, 0, 0, 0, index} +} + +func (item *Item) Index() int32 { + return item.rank[4] } // Rank calculates rank of the Item -func (item *Item) Rank(cache bool) []int32 { +func (item *Item) Rank(cache bool) [5]int32 { if cache && isRankValid(item.rank) { return item.rank } matchlen := 0 prevEnd := 0 lenSum := 0 - minBegin := math.MaxUint16 + minBegin := math.MaxInt32 for _, offset := range item.offsets { begin := int(offset[0]) end := int(offset[1]) @@ -76,7 +76,7 @@ func (item *Item) Rank(cache bool) []int32 { if matchlen == 0 { matchlen = math.MaxInt32 } - rank := make([]int32, len(sortCriteria)) + rank := buildEmptyRank(item.Index()) for idx, criterion := range sortCriteria { var val int32 switch criterion { @@ -100,8 +100,6 @@ func (item *Item) Rank(cache bool) []int32 { // Empty offsets due to inverse terms. val = 1 } - case byIndex: - val = item.index } rank[idx] = val } @@ -269,19 +267,15 @@ func (a ByRelevanceTac) Less(i, j int) bool { return compareRanks(irank, jrank, true) } -func compareRanks(irank []int32, jrank []int32, tac bool) bool { - lastIdx := len(irank) - 1 - for idx, left := range irank { +func compareRanks(irank [5]int32, jrank [5]int32, tac bool) bool { + for idx := 0; idx < 4; idx++ { + left := irank[idx] right := jrank[idx] - if tac && idx == lastIdx { - left = left * -1 - right = right * -1 - } if left < right { return true } else if left > right { return false } } - return true + return (irank[4] <= jrank[4]) != tac } diff --git a/src/item_test.go b/src/item_test.go index f26f837..d1c30d7 100644 --- a/src/item_test.go +++ b/src/item_test.go @@ -23,17 +23,17 @@ func TestOffsetSort(t *testing.T) { } func TestRankComparison(t *testing.T) { - if compareRanks([]int32{3, 0, 5}, []int32{2, 0, 7}, false) || - !compareRanks([]int32{3, 0, 5}, []int32{3, 0, 6}, false) || - !compareRanks([]int32{1, 2, 3}, []int32{1, 3, 2}, false) || - !compareRanks([]int32{0, 0, 0}, []int32{0, 0, 0}, false) { + if compareRanks([5]int32{3, 0, 0, 0, 5}, [5]int32{2, 0, 0, 0, 7}, false) || + !compareRanks([5]int32{3, 0, 0, 0, 5}, [5]int32{3, 0, 0, 0, 6}, false) || + !compareRanks([5]int32{1, 2, 0, 0, 3}, [5]int32{1, 3, 0, 0, 2}, false) || + !compareRanks([5]int32{0, 0, 0, 0, 0}, [5]int32{0, 0, 0, 0, 0}, false) { t.Error("Invalid order") } - if compareRanks([]int32{3, 0, 5}, []int32{2, 0, 7}, true) || - !compareRanks([]int32{3, 0, 5}, []int32{3, 0, 6}, false) || - !compareRanks([]int32{1, 2, 3}, []int32{1, 3, 2}, true) || - !compareRanks([]int32{0, 0, 0}, []int32{0, 0, 0}, false) { + if compareRanks([5]int32{3, 0, 0, 0, 5}, [5]int32{2, 0, 0, 0, 7}, true) || + !compareRanks([5]int32{3, 0, 0, 0, 5}, [5]int32{3, 0, 0, 0, 6}, false) || + !compareRanks([5]int32{1, 2, 0, 0, 3}, [5]int32{1, 3, 0, 0, 2}, true) || + !compareRanks([5]int32{0, 0, 0, 0, 0}, [5]int32{0, 0, 0, 0, 0}, false) { t.Error("Invalid order (tac)") } } @@ -41,16 +41,16 @@ func TestRankComparison(t *testing.T) { // Match length, string length, index func TestItemRank(t *testing.T) { // FIXME global - sortCriteria = []criterion{byMatchLen, byLength, byIndex} + sortCriteria = []criterion{byMatchLen, byLength} strs := [][]rune{[]rune("foo"), []rune("foobar"), []rune("bar"), []rune("baz")} - item1 := Item{text: strs[0], index: 1, offsets: []Offset{}} + item1 := Item{text: strs[0], offsets: []Offset{}, rank: [5]int32{0, 0, 0, 0, 1}} rank1 := item1.Rank(true) - if rank1[0] != math.MaxInt32 || rank1[1] != 3 || rank1[2] != 1 { + if rank1[0] != math.MaxInt32 || rank1[1] != 3 || rank1[4] != 1 { t.Error(item1.Rank(true)) } // Only differ in index - item2 := Item{text: strs[0], index: 0, offsets: []Offset{}} + item2 := Item{text: strs[0], offsets: []Offset{}} items := []*Item{&item1, &item2} sort.Sort(ByRelevance(items)) @@ -66,10 +66,10 @@ func TestItemRank(t *testing.T) { } // Sort by relevance - item3 := Item{text: strs[1], rank: []int32{0, 0, 2}, offsets: []Offset{Offset{1, 3}, Offset{5, 7}}} - item4 := Item{text: strs[1], rank: []int32{0, 0, 2}, offsets: []Offset{Offset{1, 2}, Offset{6, 7}}} - item5 := Item{text: strs[2], rank: []int32{0, 0, 2}, offsets: []Offset{Offset{1, 3}, Offset{5, 7}}} - item6 := Item{text: strs[2], rank: []int32{0, 0, 2}, offsets: []Offset{Offset{1, 2}, Offset{6, 7}}} + item3 := Item{text: strs[1], rank: [5]int32{0, 0, 0, 0, 2}, offsets: []Offset{Offset{1, 3}, Offset{5, 7}}} + item4 := Item{text: strs[1], rank: [5]int32{0, 0, 0, 0, 2}, offsets: []Offset{Offset{1, 2}, Offset{6, 7}}} + item5 := Item{text: strs[2], rank: [5]int32{0, 0, 0, 0, 2}, offsets: []Offset{Offset{1, 3}, Offset{5, 7}}} + item6 := Item{text: strs[2], rank: [5]int32{0, 0, 0, 0, 2}, offsets: []Offset{Offset{1, 2}, Offset{6, 7}}} items = []*Item{&item1, &item2, &item3, &item4, &item5, &item6} sort.Sort(ByRelevance(items)) if items[0] != &item6 || items[1] != &item4 || diff --git a/src/merger_test.go b/src/merger_test.go index 34efc84..472e204 100644 --- a/src/merger_test.go +++ b/src/merger_test.go @@ -23,7 +23,7 @@ func randItem() *Item { } return &Item{ text: []rune(str), - index: rand.Int31(), + rank: buildEmptyRank(rand.Int31()), offsets: offsets} } diff --git a/src/options.go b/src/options.go index 30e0016..6399343 100644 --- a/src/options.go +++ b/src/options.go @@ -83,7 +83,6 @@ const ( byLength byBegin byEnd - byIndex ) func defaultMargin() [4]string { @@ -147,7 +146,7 @@ func defaultOptions() *Options { Delimiter: Delimiter{}, Sort: 1000, Tac: false, - Criteria: []criterion{byMatchLen, byLength, byIndex}, + Criteria: []criterion{byMatchLen, byLength}, Multi: false, Ansi: false, Mouse: true, @@ -382,7 +381,6 @@ func parseTiebreak(str string) []criterion { switch str { case "index": check(&hasIndex, "index") - criteria = append(criteria, byIndex) case "length": check(&hasLength, "length") criteria = append(criteria, byLength) @@ -396,9 +394,6 @@ func parseTiebreak(str string) []criterion { errorExit("invalid sort criterion: " + str) } } - if !hasIndex { - criteria = append(criteria, byIndex) - } return criteria } diff --git a/src/pattern.go b/src/pattern.go index 4c61b87..af73b67 100644 --- a/src/pattern.go +++ b/src/pattern.go @@ -306,10 +306,9 @@ func dupItem(item *Item, offsets []Offset) *Item { text: item.text, origText: item.origText, transformed: item.transformed, - index: item.index, offsets: offsets, colors: item.colors, - rank: buildEmptyRank(item.index)} + rank: buildEmptyRank(item.Index())} } func (p *Pattern) basicMatch(item *Item) (int, int, int) { diff --git a/src/terminal.go b/src/terminal.go index c9b8056..381ae7c 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -464,7 +464,6 @@ func (t *Terminal) printHeader() { state = newState item := &Item{ text: []rune(trimmed), - index: 0, colors: colors, rank: buildEmptyRank(0)} @@ -491,7 +490,7 @@ func (t *Terminal) printList() { } func (t *Terminal) printItem(item *Item, current bool) { - _, selected := t.selected[item.index] + _, selected := t.selected[item.Index()] if current { C.CPrint(C.ColCursor, true, ">") if selected { @@ -836,8 +835,8 @@ func (t *Terminal) Loop() { } } selectItem := func(item *Item) bool { - if _, found := t.selected[item.index]; !found { - t.selected[item.index] = selectedItem{time.Now(), item.StringPtr(t.ansi)} + if _, found := t.selected[item.Index()]; !found { + t.selected[item.Index()] = selectedItem{time.Now(), item.StringPtr(t.ansi)} return true } return false @@ -845,7 +844,7 @@ func (t *Terminal) Loop() { toggleY := func(y int) { item := t.merger.Get(y) if !selectItem(item) { - delete(t.selected, item.index) + delete(t.selected, item.Index()) } } toggle := func() { @@ -934,7 +933,7 @@ func (t *Terminal) Loop() { if t.multi { for i := 0; i < t.merger.Length(); i++ { item := t.merger.Get(i) - delete(t.selected, item.index) + delete(t.selected, item.Index()) } req(reqList, reqInfo) }