Simplify Item structure

This commit compensates for the performance overhead from the
extended tiebreak option.
This commit is contained in:
Junegunn Choi 2016-01-13 21:36:44 +09:00
parent 1d2d32c847
commit 8d3a302a17
8 changed files with 43 additions and 58 deletions

View File

@ -7,7 +7,7 @@ import (
func TestChunkList(t *testing.T) { func TestChunkList(t *testing.T) {
// FIXME global // FIXME global
sortCriteria = []criterion{byMatchLen, byLength, byIndex} sortCriteria = []criterion{byMatchLen, byLength}
cl := NewChunkList(func(s []byte, i int) *Item { cl := NewChunkList(func(s []byte, i int) *Item {
return &Item{text: []rune(string(s)), rank: buildEmptyRank(int32(i * 2))} return &Item{text: []rune(string(s)), rank: buildEmptyRank(int32(i * 2))}
@ -39,7 +39,7 @@ func TestChunkList(t *testing.T) {
if len(*chunk1) != 2 { if len(*chunk1) != 2 {
t.Error("Snapshot should contain only two items") t.Error("Snapshot should contain only two items")
} }
last := func(arr []int32) int32 { last := func(arr [5]int32) int32 {
return arr[len(arr)-1] return arr[len(arr)-1]
} }
if string((*chunk1)[0].text) != "hello" || last((*chunk1)[0].rank) != 0 || if string((*chunk1)[0].text) != "hello" || last((*chunk1)[0].rank) != 0 ||

View File

@ -103,7 +103,6 @@ func Run(opts *Options) {
runes, colors := ansiProcessor(data) runes, colors := ansiProcessor(data)
return &Item{ return &Item{
text: runes, text: runes,
index: int32(index),
colors: colors, colors: colors,
rank: buildEmptyRank(int32(index))} rank: buildEmptyRank(int32(index))}
}) })
@ -120,7 +119,6 @@ func Run(opts *Options) {
item := Item{ item := Item{
text: joinTokens(trans), text: joinTokens(trans),
origText: &runes, origText: &runes,
index: int32(index),
colors: nil, colors: nil,
rank: buildEmptyRank(int32(index))} rank: buildEmptyRank(int32(index))}

View File

@ -20,41 +20,41 @@ type Item struct {
text []rune text []rune
origText *[]rune origText *[]rune
transformed []Token transformed []Token
index int32
offsets []Offset offsets []Offset
colors []ansiOffset colors []ansiOffset
rank []int32 rank [5]int32
} }
// Sort criteria to use. Never changes once fzf is started. // Sort criteria to use. Never changes once fzf is started.
var sortCriteria []criterion var sortCriteria []criterion
func isRankValid(rank []int32) bool { func isRankValid(rank [5]int32) bool {
// Exclude ordinal index // Exclude ordinal index
for i := 0; i < len(rank)-1; i++ { for _, r := range rank[:4] {
if rank[i] > 0 { if r > 0 {
return true return true
} }
} }
return false return false
} }
func buildEmptyRank(index int32) []int32 { func buildEmptyRank(index int32) [5]int32 {
len := len(sortCriteria) return [5]int32{0, 0, 0, 0, index}
arr := make([]int32, len) }
arr[len-1] = index
return arr func (item *Item) Index() int32 {
return item.rank[4]
} }
// Rank calculates rank of the Item // 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) { if cache && isRankValid(item.rank) {
return item.rank return item.rank
} }
matchlen := 0 matchlen := 0
prevEnd := 0 prevEnd := 0
lenSum := 0 lenSum := 0
minBegin := math.MaxUint16 minBegin := math.MaxInt32
for _, offset := range item.offsets { for _, offset := range item.offsets {
begin := int(offset[0]) begin := int(offset[0])
end := int(offset[1]) end := int(offset[1])
@ -76,7 +76,7 @@ func (item *Item) Rank(cache bool) []int32 {
if matchlen == 0 { if matchlen == 0 {
matchlen = math.MaxInt32 matchlen = math.MaxInt32
} }
rank := make([]int32, len(sortCriteria)) rank := buildEmptyRank(item.Index())
for idx, criterion := range sortCriteria { for idx, criterion := range sortCriteria {
var val int32 var val int32
switch criterion { switch criterion {
@ -100,8 +100,6 @@ func (item *Item) Rank(cache bool) []int32 {
// Empty offsets due to inverse terms. // Empty offsets due to inverse terms.
val = 1 val = 1
} }
case byIndex:
val = item.index
} }
rank[idx] = val rank[idx] = val
} }
@ -269,19 +267,15 @@ func (a ByRelevanceTac) Less(i, j int) bool {
return compareRanks(irank, jrank, true) return compareRanks(irank, jrank, true)
} }
func compareRanks(irank []int32, jrank []int32, tac bool) bool { func compareRanks(irank [5]int32, jrank [5]int32, tac bool) bool {
lastIdx := len(irank) - 1 for idx := 0; idx < 4; idx++ {
for idx, left := range irank { left := irank[idx]
right := jrank[idx] right := jrank[idx]
if tac && idx == lastIdx {
left = left * -1
right = right * -1
}
if left < right { if left < right {
return true return true
} else if left > right { } else if left > right {
return false return false
} }
} }
return true return (irank[4] <= jrank[4]) != tac
} }

View File

@ -23,17 +23,17 @@ func TestOffsetSort(t *testing.T) {
} }
func TestRankComparison(t *testing.T) { func TestRankComparison(t *testing.T) {
if compareRanks([]int32{3, 0, 5}, []int32{2, 0, 7}, false) || if compareRanks([5]int32{3, 0, 0, 0, 5}, [5]int32{2, 0, 0, 0, 7}, false) ||
!compareRanks([]int32{3, 0, 5}, []int32{3, 0, 6}, false) || !compareRanks([5]int32{3, 0, 0, 0, 5}, [5]int32{3, 0, 0, 0, 6}, false) ||
!compareRanks([]int32{1, 2, 3}, []int32{1, 3, 2}, false) || !compareRanks([5]int32{1, 2, 0, 0, 3}, [5]int32{1, 3, 0, 0, 2}, false) ||
!compareRanks([]int32{0, 0, 0}, []int32{0, 0, 0}, false) { !compareRanks([5]int32{0, 0, 0, 0, 0}, [5]int32{0, 0, 0, 0, 0}, false) {
t.Error("Invalid order") t.Error("Invalid order")
} }
if compareRanks([]int32{3, 0, 5}, []int32{2, 0, 7}, true) || if compareRanks([5]int32{3, 0, 0, 0, 5}, [5]int32{2, 0, 0, 0, 7}, true) ||
!compareRanks([]int32{3, 0, 5}, []int32{3, 0, 6}, false) || !compareRanks([5]int32{3, 0, 0, 0, 5}, [5]int32{3, 0, 0, 0, 6}, false) ||
!compareRanks([]int32{1, 2, 3}, []int32{1, 3, 2}, true) || !compareRanks([5]int32{1, 2, 0, 0, 3}, [5]int32{1, 3, 0, 0, 2}, true) ||
!compareRanks([]int32{0, 0, 0}, []int32{0, 0, 0}, false) { !compareRanks([5]int32{0, 0, 0, 0, 0}, [5]int32{0, 0, 0, 0, 0}, false) {
t.Error("Invalid order (tac)") t.Error("Invalid order (tac)")
} }
} }
@ -41,16 +41,16 @@ func TestRankComparison(t *testing.T) {
// Match length, string length, index // Match length, string length, index
func TestItemRank(t *testing.T) { func TestItemRank(t *testing.T) {
// FIXME global // FIXME global
sortCriteria = []criterion{byMatchLen, byLength, byIndex} sortCriteria = []criterion{byMatchLen, byLength}
strs := [][]rune{[]rune("foo"), []rune("foobar"), []rune("bar"), []rune("baz")} 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) 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)) t.Error(item1.Rank(true))
} }
// Only differ in index // Only differ in index
item2 := Item{text: strs[0], index: 0, offsets: []Offset{}} item2 := Item{text: strs[0], offsets: []Offset{}}
items := []*Item{&item1, &item2} items := []*Item{&item1, &item2}
sort.Sort(ByRelevance(items)) sort.Sort(ByRelevance(items))
@ -66,10 +66,10 @@ func TestItemRank(t *testing.T) {
} }
// Sort by relevance // Sort by relevance
item3 := Item{text: strs[1], rank: []int32{0, 0, 2}, offsets: []Offset{Offset{1, 3}, Offset{5, 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: []int32{0, 0, 2}, offsets: []Offset{Offset{1, 2}, Offset{6, 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: []int32{0, 0, 2}, offsets: []Offset{Offset{1, 3}, Offset{5, 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: []int32{0, 0, 2}, offsets: []Offset{Offset{1, 2}, Offset{6, 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} items = []*Item{&item1, &item2, &item3, &item4, &item5, &item6}
sort.Sort(ByRelevance(items)) sort.Sort(ByRelevance(items))
if items[0] != &item6 || items[1] != &item4 || if items[0] != &item6 || items[1] != &item4 ||

View File

@ -23,7 +23,7 @@ func randItem() *Item {
} }
return &Item{ return &Item{
text: []rune(str), text: []rune(str),
index: rand.Int31(), rank: buildEmptyRank(rand.Int31()),
offsets: offsets} offsets: offsets}
} }

View File

@ -83,7 +83,6 @@ const (
byLength byLength
byBegin byBegin
byEnd byEnd
byIndex
) )
func defaultMargin() [4]string { func defaultMargin() [4]string {
@ -147,7 +146,7 @@ func defaultOptions() *Options {
Delimiter: Delimiter{}, Delimiter: Delimiter{},
Sort: 1000, Sort: 1000,
Tac: false, Tac: false,
Criteria: []criterion{byMatchLen, byLength, byIndex}, Criteria: []criterion{byMatchLen, byLength},
Multi: false, Multi: false,
Ansi: false, Ansi: false,
Mouse: true, Mouse: true,
@ -382,7 +381,6 @@ func parseTiebreak(str string) []criterion {
switch str { switch str {
case "index": case "index":
check(&hasIndex, "index") check(&hasIndex, "index")
criteria = append(criteria, byIndex)
case "length": case "length":
check(&hasLength, "length") check(&hasLength, "length")
criteria = append(criteria, byLength) criteria = append(criteria, byLength)
@ -396,9 +394,6 @@ func parseTiebreak(str string) []criterion {
errorExit("invalid sort criterion: " + str) errorExit("invalid sort criterion: " + str)
} }
} }
if !hasIndex {
criteria = append(criteria, byIndex)
}
return criteria return criteria
} }

View File

@ -306,10 +306,9 @@ func dupItem(item *Item, offsets []Offset) *Item {
text: item.text, text: item.text,
origText: item.origText, origText: item.origText,
transformed: item.transformed, transformed: item.transformed,
index: item.index,
offsets: offsets, offsets: offsets,
colors: item.colors, colors: item.colors,
rank: buildEmptyRank(item.index)} rank: buildEmptyRank(item.Index())}
} }
func (p *Pattern) basicMatch(item *Item) (int, int, int) { func (p *Pattern) basicMatch(item *Item) (int, int, int) {

View File

@ -464,7 +464,6 @@ func (t *Terminal) printHeader() {
state = newState state = newState
item := &Item{ item := &Item{
text: []rune(trimmed), text: []rune(trimmed),
index: 0,
colors: colors, colors: colors,
rank: buildEmptyRank(0)} rank: buildEmptyRank(0)}
@ -491,7 +490,7 @@ func (t *Terminal) printList() {
} }
func (t *Terminal) printItem(item *Item, current bool) { func (t *Terminal) printItem(item *Item, current bool) {
_, selected := t.selected[item.index] _, selected := t.selected[item.Index()]
if current { if current {
C.CPrint(C.ColCursor, true, ">") C.CPrint(C.ColCursor, true, ">")
if selected { if selected {
@ -836,8 +835,8 @@ func (t *Terminal) Loop() {
} }
} }
selectItem := func(item *Item) bool { selectItem := func(item *Item) bool {
if _, found := t.selected[item.index]; !found { if _, found := t.selected[item.Index()]; !found {
t.selected[item.index] = selectedItem{time.Now(), item.StringPtr(t.ansi)} t.selected[item.Index()] = selectedItem{time.Now(), item.StringPtr(t.ansi)}
return true return true
} }
return false return false
@ -845,7 +844,7 @@ func (t *Terminal) Loop() {
toggleY := func(y int) { toggleY := func(y int) {
item := t.merger.Get(y) item := t.merger.Get(y)
if !selectItem(item) { if !selectItem(item) {
delete(t.selected, item.index) delete(t.selected, item.Index())
} }
} }
toggle := func() { toggle := func() {
@ -934,7 +933,7 @@ func (t *Terminal) Loop() {
if t.multi { if t.multi {
for i := 0; i < t.merger.Length(); i++ { for i := 0; i < t.merger.Length(); i++ {
item := t.merger.Get(i) item := t.merger.Get(i)
delete(t.selected, item.index) delete(t.selected, item.Index())
} }
req(reqList, reqInfo) req(reqList, reqInfo)
} }