fzf/src/result.go

240 lines
4.9 KiB
Go
Raw Normal View History

2016-08-18 18:27:42 +00:00
package fzf
import (
"math"
"sort"
"unicode"
2016-08-18 18:27:42 +00:00
"github.com/junegunn/fzf/src/tui"
2016-08-18 18:27:42 +00:00
"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 tui.ColorPair
attr tui.Attr
2016-08-18 18:27:42 +00:00
index int32
}
type Result struct {
item *Item
points [4]uint16
2016-08-18 18:27:42 +00:00
}
func buildResult(item *Item, offsets []Offset, score int) Result {
2016-08-18 18:27:42 +00:00
if len(offsets) > 1 {
sort.Sort(ByOrder(offsets))
}
result := Result{item: item}
2016-08-18 18:27:42 +00:00
numChars := item.text.Length()
2016-09-07 00:58:18 +00:00
minBegin := math.MaxUint16
minEnd := math.MaxUint16
2016-09-07 00:58:18 +00:00
maxEnd := 0
validOffsetFound := false
2016-08-18 18:27:42 +00:00
for _, offset := range offsets {
2016-09-07 00:58:18 +00:00
b, e := int(offset[0]), int(offset[1])
if b < e {
minBegin = util.Min(b, minBegin)
minEnd = util.Min(e, minEnd)
2016-09-07 00:58:18 +00:00
maxEnd = util.Max(e, maxEnd)
validOffsetFound = true
2016-08-18 18:27:42 +00:00
}
}
for idx, criterion := range sortCriteria {
2016-09-07 00:58:18 +00:00
val := uint16(math.MaxUint16)
2016-08-18 18:27:42 +00:00
switch criterion {
2016-09-07 00:58:18 +00:00
case byScore:
2016-08-18 18:27:42 +00:00
// Higher is better
2016-09-07 00:58:18 +00:00
val = math.MaxUint16 - util.AsUint16(score)
2016-08-18 18:27:42 +00:00
case byLength:
2017-07-16 14:31:19 +00:00
val = item.TrimLength()
case byBegin, byEnd:
2016-09-07 00:58:18 +00:00
if validOffsetFound {
whitePrefixLen := 0
for idx := 0; idx < numChars; idx++ {
r := item.text.Get(idx)
whitePrefixLen = idx
if idx == minBegin || !unicode.IsSpace(r) {
2016-09-07 00:58:18 +00:00
break
}
2016-08-18 18:27:42 +00:00
}
if criterion == byBegin {
val = util.AsUint16(minEnd - whitePrefixLen)
} else {
val = util.AsUint16(math.MaxUint16 - math.MaxUint16*(maxEnd-whitePrefixLen)/int(item.TrimLength()))
}
2016-08-18 18:27:42 +00:00
}
}
result.points[idx] = val
2016-08-18 18:27:42 +00:00
}
return result
2016-08-18 18:27:42 +00:00
}
// 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 {
2017-07-16 14:31:19 +00:00
return result.item.Index()
2016-08-18 18:27:42 +00:00
}
func minRank() Result {
return Result{item: &nilItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
2016-08-18 18:27:42 +00:00
}
func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, color tui.ColorPair, attr tui.Attr, current bool) []colorOffset {
2016-08-18 18:27:42 +00:00
itemColors := result.item.Colors()
// No ANSI code, or --color=no
2016-08-18 18:27:42 +00:00
if len(itemColors) == 0 {
var offsets []colorOffset
2016-08-19 16:46:54 +00:00
for _, off := range matchOffsets {
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, attr: attr})
2016-08-18 18:27:42 +00:00
}
return offsets
}
// Find max column
var maxCol int32
2016-08-19 16:46:54 +00:00
for _, off := range matchOffsets {
2016-08-18 18:27:42 +00:00
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
}
}
2016-08-19 16:46:54 +00:00
for _, off := range matchOffsets {
2016-08-18 18:27:42 +00:00
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, attr: attr})
2016-08-18 18:27:42 +00:00
} else {
ansi := itemColors[curr-1]
fg := ansi.color.fg
bg := ansi.color.bg
if theme != nil {
if fg == -1 {
if current {
fg = theme.Current
} else {
fg = theme.Fg
}
}
if bg == -1 {
if current {
bg = theme.DarkBg
} else {
bg = theme.Bg
}
2016-08-18 18:27:42 +00:00
}
}
colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)},
2017-01-07 16:30:31 +00:00
color: tui.NewColorPair(fg, bg),
attr: ansi.color.attr.Merge(attr)})
2016-08-18 18:27:42 +00:00
}
}
}
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
2016-08-18 18:27:42 +00:00
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], a[j], false)
2016-08-18 18:27:42 +00:00
}
// ByRelevanceTac is for sorting Items
type ByRelevanceTac []Result
2016-08-18 18:27:42 +00:00
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], a[j], true)
2016-08-18 18:27:42 +00:00
}
func compareRanks(irank Result, jrank Result, tac bool) bool {
2016-09-07 00:58:18 +00:00
for idx := 0; idx < 4; idx++ {
2016-08-18 18:27:42 +00:00
left := irank.points[idx]
right := jrank.points[idx]
if left < right {
return true
} else if left > right {
return false
}
}
return (irank.item.Index() <= jrank.item.Index()) != tac
2016-08-18 18:27:42 +00:00
}