fzf/src/item.go

296 lines
6.0 KiB
Go
Raw Normal View History

2015-01-01 19:49:30 +00:00
package fzf
2015-03-18 16:59:14 +00:00
import (
"math"
2015-03-18 16:59:14 +00:00
"github.com/junegunn/fzf/src/curses"
"github.com/junegunn/fzf/src/util"
2015-03-18 16:59:14 +00:00
)
// Offset holds three 32-bit integers denoting the offsets of a matched substring
type Offset [3]int32
2015-01-01 19:49:30 +00:00
2015-03-22 07:05:54 +00:00
type colorOffset struct {
2015-03-18 16:59:14 +00:00
offset [2]int32
color int
bold bool
}
2015-01-11 18:01:24 +00:00
// Item represents each input line
2015-01-01 19:49:30 +00:00
type Item struct {
text util.Chars
origText *[]byte
transformed []Token
2015-01-01 19:49:30 +00:00
offsets []Offset
2015-03-22 07:05:54 +00:00
colors []ansiOffset
rank [5]int32
bonus int32
2015-01-01 19:49:30 +00:00
}
// Sort criteria to use. Never changes once fzf is started.
var sortCriteria []criterion
func isRankValid(rank [5]int32) bool {
// Exclude ordinal index
for _, r := range rank[:4] {
if r > 0 {
return true
}
}
return false
2015-01-08 17:37:08 +00:00
}
2015-01-01 19:49:30 +00:00
func buildEmptyRank(index int32) [5]int32 {
return [5]int32{0, 0, 0, 0, index}
}
2016-08-14 08:51:34 +00:00
// Index returns ordinal index of the Item
func (item *Item) Index() int32 {
return item.rank[4]
}
2015-01-11 18:01:24 +00:00
// Rank calculates rank of the Item
func (item *Item) Rank(cache bool) [5]int32 {
if cache && isRankValid(item.rank) {
2015-08-02 14:54:53 +00:00
return item.rank
2015-01-01 19:49:30 +00:00
}
matchlen := 0
prevEnd := 0
lenSum := 0
minBegin := math.MaxInt32
2015-08-02 14:54:53 +00:00
for _, offset := range item.offsets {
2015-01-08 17:37:08 +00:00
begin := int(offset[0])
end := int(offset[1])
trimLen := int(offset[2])
lenSum += trimLen
2015-01-01 19:49:30 +00:00
if prevEnd > begin {
begin = prevEnd
}
if end > prevEnd {
prevEnd = end
}
if end > begin {
if begin < minBegin {
minBegin = begin
}
2015-01-01 19:49:30 +00:00
matchlen += end - begin
}
}
rank := buildEmptyRank(item.Index())
for idx, criterion := range sortCriteria {
var val int32
switch criterion {
case byMatchLen:
if matchlen == 0 {
val = math.MaxInt32
} else {
// It is extremely unlikely that bonus exceeds 128
val = 128*int32(matchlen) - item.bonus
}
case byLength:
// It is guaranteed that .transformed in not null in normal execution
if item.transformed != nil {
// If offsets is empty, lenSum will be 0, but we don't care
val = int32(lenSum)
} else {
val = int32(item.text.Length())
}
case byBegin:
// We can't just look at item.offsets[0][0] because it can be an inverse term
whitePrefixLen := 0
numChars := item.text.Length()
for idx := 0; idx < numChars; idx++ {
r := item.text.Get(idx)
whitePrefixLen = idx
if idx == minBegin || r != ' ' && r != '\t' {
break
}
}
val = int32(minBegin - whitePrefixLen)
case byEnd:
if prevEnd > 0 {
val = int32(1 + item.text.Length() - prevEnd)
} else {
// Empty offsets due to inverse terms.
val = 1
}
}
rank[idx] = val
}
if cache {
2015-08-02 14:54:53 +00:00
item.rank = rank
}
return rank
2015-01-01 19:49:30 +00:00
}
2015-01-11 18:01:24 +00:00
// AsString returns the original string
func (item *Item) AsString(stripAnsi bool) string {
return *item.StringPtr(stripAnsi)
}
// StringPtr returns the pointer to the original string
func (item *Item) StringPtr(stripAnsi bool) *string {
2015-08-02 14:54:53 +00:00
if item.origText != nil {
if stripAnsi {
trimmed, _, _ := extractColor(string(*item.origText), nil, nil)
return &trimmed
}
orig := string(*item.origText)
return &orig
2015-01-01 19:49:30 +00:00
}
str := item.text.ToString()
return &str
2015-01-01 19:49:30 +00:00
}
2015-03-22 07:05:54 +00:00
func (item *Item) colorOffsets(color int, bold bool, current bool) []colorOffset {
2015-03-18 16:59:14 +00:00
if len(item.colors) == 0 {
2015-03-22 07:05:54 +00:00
var offsets []colorOffset
2015-03-18 16:59:14 +00:00
for _, off := range item.offsets {
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, bold: bold})
2015-03-18 16:59:14 +00:00
}
return offsets
}
// Find max column
2015-03-22 07:05:54 +00:00
var maxCol int32
2015-03-18 16:59:14 +00:00
for _, off := range item.offsets {
if off[1] > maxCol {
maxCol = off[1]
}
}
for _, ansi := range item.colors {
if ansi.offset[1] > maxCol {
maxCol = ansi.offset[1]
}
}
cols := make([]int, maxCol)
for colorIndex, ansi := range item.colors {
for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
cols[i] = colorIndex + 1 // XXX
}
}
for _, off := range item.offsets {
for i := off[0]; i < off[1]; i++ {
cols[i] = -1
}
}
// sort.Sort(ByOrder(offsets))
// Merge offsets
// ------------ ---- -- ----
// ++++++++ ++++++++++
// --++++++++-- --++++++++++---
curr := 0
start := 0
2015-03-22 07:05:54 +00:00
var offsets []colorOffset
2015-03-18 16:59:14 +00:00
add := func(idx int) {
if curr != 0 && idx > start {
if curr == -1 {
2015-03-22 07:05:54 +00:00
offsets = append(offsets, colorOffset{
offset: [2]int32{int32(start), int32(idx)}, color: color, bold: bold})
2015-03-18 16:59:14 +00:00
} else {
ansi := item.colors[curr-1]
2015-05-31 07:46:54 +00:00
fg := ansi.color.fg
if fg == -1 {
if current {
fg = curses.CurrentFG
} else {
fg = curses.FG
}
}
2015-03-18 16:59:14 +00:00
bg := ansi.color.bg
2015-05-31 07:46:54 +00:00
if bg == -1 {
if current {
bg = curses.DarkBG
} else {
bg = curses.BG
}
2015-03-18 16:59:14 +00:00
}
2015-03-22 07:05:54 +00:00
offsets = append(offsets, colorOffset{
offset: [2]int32{int32(start), int32(idx)},
2015-05-31 07:46:54 +00:00
color: curses.PairFor(fg, bg),
2015-03-18 16:59:14 +00:00
bold: ansi.color.bold || bold})
}
}
}
for idx, col := range cols {
if col != curr {
add(idx)
start = idx
curr = col
}
}
add(int(maxCol))
return offsets
}
2015-01-11 18:01:24 +00:00
// ByOrder is for sorting substring offsets
2015-01-01 19:49:30 +00:00
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])
}
2015-01-11 18:01:24 +00:00
// ByRelevance is for sorting Items
2015-01-01 19:49:30 +00:00
type ByRelevance []*Item
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 {
irank := a[i].Rank(true)
jrank := a[j].Rank(true)
2015-01-01 19:49:30 +00:00
return compareRanks(irank, jrank, false)
2015-01-01 19:49:30 +00:00
}
// ByRelevanceTac is for sorting Items
type ByRelevanceTac []*Item
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 {
irank := a[i].Rank(true)
jrank := a[j].Rank(true)
return compareRanks(irank, jrank, true)
}
func compareRanks(irank [5]int32, jrank [5]int32, tac bool) bool {
for idx := 0; idx < 4; idx++ {
left := irank[idx]
right := jrank[idx]
if left < right {
return true
} else if left > right {
return false
}
2015-01-08 17:37:08 +00:00
}
return (irank[4] <= jrank[4]) != tac
2015-01-01 19:49:30 +00:00
}