diff options
author | Junegunn Choi <junegunn.c@gmail.com> | 2016-08-19 03:27:42 +0900 |
---|---|---|
committer | Junegunn Choi <junegunn.c@gmail.com> | 2016-08-19 03:27:42 +0900 |
commit | 608c41620755041d6fc216ae43de8b21d56c969c (patch) | |
tree | c3c574a24544310f30bcaab20c8af8f3a381343b /src/result.go | |
parent | 62f6ff9d6cc414d7c424281c7a6f40e02776b044 (diff) |
Add missing sources
Diffstat (limited to 'src/result.go')
-rw-r--r-- | src/result.go | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/src/result.go b/src/result.go new file mode 100644 index 00000000..c295e7ac --- /dev/null +++ b/src/result.go @@ -0,0 +1,259 @@ +package fzf + +import ( + "math" + "sort" + + "github.com/junegunn/fzf/src/curses" + "github.com/junegunn/fzf/src/util" +) + +// Offset holds two 32-bit integers denoting the offsets of a matched substring +type Offset [2]int32 + +type colorOffset struct { + offset [2]int32 + color int + bold bool + index int32 +} + +type rank struct { + index int32 + // byMatchLen, byBonus, ... + points [5]uint16 +} + +type Result struct { + item *Item + offsets []Offset + rank rank +} + +func buildResult(item *Item, offsets []Offset, bonus int, trimLen int) *Result { + if len(offsets) > 1 { + sort.Sort(ByOrder(offsets)) + } + + result := Result{item: item, offsets: offsets, rank: rank{index: item.index}} + + matchlen := 0 + prevEnd := 0 + minBegin := math.MaxInt32 + numChars := item.text.Length() + for _, offset := range offsets { + begin := int(offset[0]) + end := int(offset[1]) + if prevEnd > begin { + begin = prevEnd + } + if end > prevEnd { + prevEnd = end + } + if end > begin { + if begin < minBegin { + minBegin = begin + } + matchlen += end - begin + } + } + + for idx, criterion := range sortCriteria { + var val uint16 + switch criterion { + case byMatchLen: + if matchlen == 0 { + val = math.MaxUint16 + } else { + val = util.AsUint16(matchlen) + } + case byBonus: + // Higher is better + val = math.MaxUint16 - util.AsUint16(bonus) + case byLength: + // If offsets is empty, trimLen will be 0, but we don't care + val = util.AsUint16(trimLen) + case byBegin: + // We can't just look at item.offsets[0][0] because it can be an inverse term + whitePrefixLen := 0 + for idx := 0; idx < numChars; idx++ { + r := item.text.Get(idx) + whitePrefixLen = idx + if idx == minBegin || r != ' ' && r != '\t' { + break + } + } + val = util.AsUint16(minBegin - whitePrefixLen) + case byEnd: + if prevEnd > 0 { + val = util.AsUint16(1 + numChars - prevEnd) + } else { + // Empty offsets due to inverse terms. + val = 1 + } + } + result.rank.points[idx] = val + } + + return &result +} + +// Sort criteria to use. Never changes once fzf is started. +var sortCriteria []criterion + +// Index returns ordinal index of the Item +func (result *Result) Index() int32 { + return result.item.index +} + +func minRank() rank { + return rank{index: 0, points: [5]uint16{0, math.MaxUint16, 0, 0, 0}} +} + +func (result *Result) colorOffsets(color int, bold bool, current bool) []colorOffset { + itemColors := result.item.Colors() + + if len(itemColors) == 0 { + var offsets []colorOffset + for _, off := range result.offsets { + + offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, bold: bold}) + } + return offsets + } + + // Find max column + var maxCol int32 + for _, off := range result.offsets { + if off[1] > maxCol { + maxCol = off[1] + } + } + for _, ansi := range itemColors { + if ansi.offset[1] > maxCol { + maxCol = ansi.offset[1] + } + } + cols := make([]int, maxCol) + + for colorIndex, ansi := range itemColors { + for i := ansi.offset[0]; i < ansi.offset[1]; i++ { + cols[i] = colorIndex + 1 // XXX + } + } + + for _, off := range result.offsets { + for i := off[0]; i < off[1]; i++ { + cols[i] = -1 + } + } + + // sort.Sort(ByOrder(offsets)) + + // Merge offsets + // ------------ ---- -- ---- + // ++++++++ ++++++++++ + // --++++++++-- --++++++++++--- + curr := 0 + start := 0 + var colors []colorOffset + add := func(idx int) { + if curr != 0 && idx > start { + if curr == -1 { + colors = append(colors, colorOffset{ + offset: [2]int32{int32(start), int32(idx)}, color: color, bold: bold}) + } else { + ansi := itemColors[curr-1] + fg := ansi.color.fg + if fg == -1 { + if current { + fg = curses.CurrentFG + } else { + fg = curses.FG + } + } + bg := ansi.color.bg + if bg == -1 { + if current { + bg = curses.DarkBG + } else { + bg = curses.BG + } + } + colors = append(colors, colorOffset{ + offset: [2]int32{int32(start), int32(idx)}, + color: curses.PairFor(fg, bg), + bold: ansi.color.bold || bold}) + } + } + } + for idx, col := range cols { + if col != curr { + add(idx) + start = idx + curr = col + } + } + add(int(maxCol)) + return colors +} + +// ByOrder is for sorting substring offsets +type ByOrder []Offset + +func (a ByOrder) Len() int { + return len(a) +} + +func (a ByOrder) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a ByOrder) Less(i, j int) bool { + ioff := a[i] + joff := a[j] + return (ioff[0] < joff[0]) || (ioff[0] == joff[0]) && (ioff[1] <= joff[1]) +} + +// ByRelevance is for sorting Items +type ByRelevance []*Result + +func (a ByRelevance) Len() int { + return len(a) +} + +func (a ByRelevance) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a ByRelevance) Less(i, j int) bool { + return compareRanks((*a[i]).rank, (*a[j]).rank, false) +} + +// ByRelevanceTac is for sorting Items +type ByRelevanceTac []*Result + +func (a ByRelevanceTac) Len() int { + return len(a) +} + +func (a ByRelevanceTac) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a ByRelevanceTac) Less(i, j int) bool { + return compareRanks((*a[i]).rank, (*a[j]).rank, true) +} + +func compareRanks(irank rank, jrank rank, tac bool) bool { + for idx := 0; idx < 5; idx++ { + left := irank.points[idx] + right := jrank.points[idx] + if left < right { + return true + } else if left > right { + return false + } + } + return (irank.index <= jrank.index) != tac +} |