diff --git a/src/core.go b/src/core.go index 290c0af..78dddc0 100644 --- a/src/core.go +++ b/src/core.go @@ -161,9 +161,11 @@ func Run(opts *Options) { reader := Reader{ func(runes []byte) bool { item := chunkList.trans(runes, 0) - if item != nil && pattern.MatchItem(item) != nil { - fmt.Println(item.text.ToString()) - found = true + if item != nil { + if result, _ := pattern.MatchItem(item); result != nil { + fmt.Println(item.text.ToString()) + found = true + } } return false }, eventBox, opts.ReadZero} diff --git a/src/matcher.go b/src/matcher.go index 8cd9a9f..9814ba9 100644 --- a/src/matcher.go +++ b/src/matcher.go @@ -211,7 +211,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) { partialResult := <-resultChan partialResults[partialResult.index] = partialResult.matches } - return NewMerger(partialResults, m.sort, m.tac), false + return NewMerger(pattern, partialResults, m.sort, m.tac), false } // Reset is called to interrupt/signal the ongoing search diff --git a/src/merger.go b/src/merger.go index 3879ab7..2c7675d 100644 --- a/src/merger.go +++ b/src/merger.go @@ -3,11 +3,12 @@ package fzf import "fmt" // EmptyMerger is a Merger with no data -var EmptyMerger = NewMerger([][]*Result{}, false, false) +var EmptyMerger = NewMerger(nil, [][]*Result{}, false, false) // Merger holds a set of locally sorted lists of items and provides the view of // a single, globally-sorted list type Merger struct { + pattern *Pattern lists [][]*Result merged []*Result chunks *[]*Chunk @@ -22,9 +23,10 @@ type Merger struct { // original order func PassMerger(chunks *[]*Chunk, tac bool) *Merger { mg := Merger{ - chunks: chunks, - tac: tac, - count: 0} + pattern: nil, + chunks: chunks, + tac: tac, + count: 0} for _, chunk := range *mg.chunks { mg.count += len(*chunk) @@ -33,8 +35,9 @@ func PassMerger(chunks *[]*Chunk, tac bool) *Merger { } // NewMerger returns a new Merger -func NewMerger(lists [][]*Result, sorted bool, tac bool) *Merger { +func NewMerger(pattern *Pattern, lists [][]*Result, sorted bool, tac bool) *Merger { mg := Merger{ + pattern: pattern, lists: lists, merged: []*Result{}, chunks: nil, diff --git a/src/merger_test.go b/src/merger_test.go index d50470f..a4adee1 100644 --- a/src/merger_test.go +++ b/src/merger_test.go @@ -17,16 +17,9 @@ func assert(t *testing.T, cond bool, msg ...string) { func randResult() *Result { str := fmt.Sprintf("%d", rand.Uint32()) - offsets := make([]Offset, rand.Int()%3) - for idx := range offsets { - sidx := int32(rand.Uint32() % 20) - eidx := sidx + int32(rand.Uint32()%20) - offsets[idx] = Offset{sidx, eidx} - } return &Result{ - item: &Item{text: util.RunesToChars([]rune(str))}, - offsets: offsets, - rank: rank{index: rand.Int31()}} + item: &Item{text: util.RunesToChars([]rune(str))}, + rank: rank{index: rand.Int31()}} } func TestEmptyMerger(t *testing.T) { @@ -64,7 +57,7 @@ func TestMergerUnsorted(t *testing.T) { cnt := len(items) // Not sorted: same order - mg := NewMerger(lists, false, false) + mg := NewMerger(nil, lists, false, false) assert(t, cnt == mg.Length(), "Invalid Length") for i := 0; i < cnt; i++ { assert(t, items[i] == mg.Get(i), "Invalid Get") @@ -76,7 +69,7 @@ func TestMergerSorted(t *testing.T) { cnt := len(items) // Sorted sorted order - mg := NewMerger(lists, true, false) + mg := NewMerger(nil, lists, true, false) assert(t, cnt == mg.Length(), "Invalid Length") sort.Sort(ByRelevance(items)) for i := 0; i < cnt; i++ { @@ -86,7 +79,7 @@ func TestMergerSorted(t *testing.T) { } // Inverse order - mg2 := NewMerger(lists, true, false) + mg2 := NewMerger(nil, lists, true, false) for i := cnt - 1; i >= 0; i-- { if items[i] != mg2.Get(i) { t.Error("Not sorted", items[i], mg2.Get(i)) diff --git a/src/pattern.go b/src/pattern.go index 4c35a12..ef14826 100644 --- a/src/pattern.go +++ b/src/pattern.go @@ -273,13 +273,13 @@ func (p *Pattern) matchChunk(chunk *Chunk, space []*Result) []*Result { if space == nil { for _, item := range *chunk { - if match := p.MatchItem(item); match != nil { + if match, _ := p.MatchItem(item); match != nil { matches = append(matches, match) } } } else { for _, result := range space { - if match := p.MatchItem(result.item); match != nil { + if match, _ := p.MatchItem(result.item); match != nil { matches = append(matches, match) } } @@ -288,18 +288,19 @@ func (p *Pattern) matchChunk(chunk *Chunk, space []*Result) []*Result { } // MatchItem returns true if the Item is a match -func (p *Pattern) MatchItem(item *Item) *Result { +func (p *Pattern) MatchItem(item *Item) (*Result, []Offset) { if p.extended { if offsets, bonus, trimLen := p.extendedMatch(item); len(offsets) == len(p.termSets) { - return buildResult(item, offsets, bonus, trimLen) + return buildResult(item, offsets, bonus, trimLen), offsets } - return nil + return nil, nil } offset, bonus, trimLen := p.basicMatch(item) if sidx := offset[0]; sidx >= 0 { - return buildResult(item, []Offset{offset}, bonus, trimLen) + offsets := []Offset{offset} + return buildResult(item, offsets, bonus, trimLen), offsets } - return nil + return nil, nil } func (p *Pattern) basicMatch(item *Item) (Offset, int, int) { diff --git a/src/pattern_test.go b/src/pattern_test.go index 3069110..2a391f4 100644 --- a/src/pattern_test.go +++ b/src/pattern_test.go @@ -135,10 +135,16 @@ func TestOrigTextAndTransformed(t *testing.T) { pattern.extended = extended matches := pattern.matchChunk(&chunk, nil) // No cache if matches[0].item.text.ToString() != "junegunn" || string(*matches[0].item.origText) != "junegunn.choi" || - matches[0].offsets[0][0] != 0 || matches[0].offsets[0][1] != 5 || !reflect.DeepEqual(matches[0].item.transformed, trans) { t.Error("Invalid match result", matches) } + + match, offsets := pattern.MatchItem(chunk[0]) + if match.item.text.ToString() != "junegunn" || string(*match.item.origText) != "junegunn.choi" || + offsets[0][0] != 0 || offsets[0][1] != 5 || + !reflect.DeepEqual(match.item.transformed, trans) { + t.Error("Invalid match result", match) + } } } diff --git a/src/result.go b/src/result.go index c295e7a..2b66a02 100644 --- a/src/result.go +++ b/src/result.go @@ -19,15 +19,14 @@ type colorOffset struct { } type rank struct { - index int32 // byMatchLen, byBonus, ... points [5]uint16 + index int32 } type Result struct { - item *Item - offsets []Offset - rank rank + item *Item + rank rank } func buildResult(item *Item, offsets []Offset, bonus int, trimLen int) *Result { @@ -35,7 +34,7 @@ func buildResult(item *Item, offsets []Offset, bonus int, trimLen int) *Result { sort.Sort(ByOrder(offsets)) } - result := Result{item: item, offsets: offsets, rank: rank{index: item.index}} + result := Result{item: item, rank: rank{index: item.index}} matchlen := 0 prevEnd := 0 @@ -110,12 +109,12 @@ 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 { +func (result *Result) colorOffsets(matchOffsets []Offset, color int, bold bool, current bool) []colorOffset { itemColors := result.item.Colors() if len(itemColors) == 0 { var offsets []colorOffset - for _, off := range result.offsets { + for _, off := range matchOffsets { offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, bold: bold}) } @@ -124,7 +123,7 @@ func (result *Result) colorOffsets(color int, bold bool, current bool) []colorOf // Find max column var maxCol int32 - for _, off := range result.offsets { + for _, off := range matchOffsets { if off[1] > maxCol { maxCol = off[1] } @@ -142,7 +141,7 @@ func (result *Result) colorOffsets(color int, bold bool, current bool) []colorOf } } - for _, off := range result.offsets { + for _, off := range matchOffsets { for i := off[0]; i < off[1]; i++ { cols[i] = -1 } diff --git a/src/result_test.go b/src/result_test.go index c8478fd..60551fd 100644 --- a/src/result_test.go +++ b/src/result_test.go @@ -88,8 +88,9 @@ func TestColorOffset(t *testing.T) { // ------------ 20 ---- -- ---- // ++++++++ ++++++++++ // --++++++++-- --++++++++++--- + + offsets := []Offset{Offset{5, 15}, Offset{25, 35}} item := Result{ - offsets: []Offset{Offset{5, 15}, Offset{25, 35}}, item: &Item{ colors: &[]ansiOffset{ ansiOffset{[2]int32{0, 20}, ansiState{1, 5, false}}, @@ -98,9 +99,9 @@ func TestColorOffset(t *testing.T) { ansiOffset{[2]int32{33, 40}, ansiState{4, 8, true}}}}} // [{[0 5] 9 false} {[5 15] 99 false} {[15 20] 9 false} {[22 25] 10 true} {[25 35] 99 false} {[35 40] 11 true}] - offsets := item.colorOffsets(99, false, true) + colors := item.colorOffsets(offsets, 99, false, true) assert := func(idx int, b int32, e int32, c int, bold bool) { - o := offsets[idx] + o := colors[idx] if o.offset[0] != b || o.offset[1] != e || o.color != c || o.bold != bold { t.Error(o) } diff --git a/src/terminal.go b/src/terminal.go index f7bbccf..d87a20b 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -669,15 +669,19 @@ func overflow(runes []rune, max int) bool { func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 int, current bool) { item := result.item - var maxe int - for _, offset := range result.offsets { - maxe = util.Max(maxe, int(offset[1])) - } // Overflow text := make([]rune, item.text.Length()) copy(text, item.text.ToRunes()) - offsets := result.colorOffsets(col2, bold, current) + matchOffsets := []Offset{} + if t.merger.pattern != nil { + _, matchOffsets = t.merger.pattern.MatchItem(item) + } + var maxe int + for _, offset := range matchOffsets { + maxe = util.Max(maxe, int(offset[1])) + } + offsets := result.colorOffsets(matchOffsets, col2, bold, current) maxWidth := t.window.Width - 3 maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text)) if overflow(text, maxWidth) {