diff --git a/src/chunklist.go b/src/chunklist.go index bd98999d..ce4a56a0 100644 --- a/src/chunklist.go +++ b/src/chunklist.go @@ -41,6 +41,13 @@ func (c *Chunk) IsFull() bool { return c.count == chunkSize } +func (c *Chunk) lastIndex(minValue int32) int32 { + if c.count == 0 { + return minValue + } + return c.items[c.count-1].Index() + 1 // Exclusive +} + func (cl *ChunkList) lastChunk() *Chunk { return cl.chunks[len(cl.chunks)-1] } diff --git a/src/matcher.go b/src/matcher.go index 49d39730..daaa69ce 100644 --- a/src/matcher.go +++ b/src/matcher.go @@ -165,6 +165,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) { } minIndex := request.chunks[0].items[0].Index() + maxIndex := request.chunks[numChunks-1].lastIndex(minIndex) cancelled := util.NewAtomicBool(false) slices := m.sliceChunks(request.chunks) @@ -236,7 +237,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) { partialResult := <-resultChan partialResults[partialResult.index] = partialResult.matches } - return NewMerger(pattern, partialResults, m.sort && request.pattern.sortable, m.tac, request.revision, minIndex), false + return NewMerger(pattern, partialResults, m.sort && request.pattern.sortable, m.tac, request.revision, minIndex, maxIndex), false } // Reset is called to interrupt/signal the ongoing search diff --git a/src/merger.go b/src/merger.go index fee8a59a..688f3571 100644 --- a/src/merger.go +++ b/src/merger.go @@ -4,7 +4,7 @@ import "fmt" // EmptyMerger is a Merger with no data func EmptyMerger(revision revision) *Merger { - return NewMerger(nil, [][]Result{}, false, false, revision, 0) + return NewMerger(nil, [][]Result{}, false, false, revision, 0, 0) } // Merger holds a set of locally sorted lists of items and provides the view of @@ -22,14 +22,16 @@ type Merger struct { pass bool revision revision minIndex int32 + maxIndex int32 } // PassMerger returns a new Merger that simply returns the items in the // original order func PassMerger(chunks *[]*Chunk, tac bool, revision revision) *Merger { - var minIndex int32 + var minIndex, maxIndex int32 if len(*chunks) > 0 { minIndex = (*chunks)[0].items[0].Index() + maxIndex = (*chunks)[len(*chunks)-1].lastIndex(minIndex) } mg := Merger{ pattern: nil, @@ -38,7 +40,8 @@ func PassMerger(chunks *[]*Chunk, tac bool, revision revision) *Merger { count: 0, pass: true, revision: revision, - minIndex: minIndex} + minIndex: minIndex, + maxIndex: maxIndex} for _, chunk := range *mg.chunks { mg.count += chunk.count @@ -47,7 +50,7 @@ func PassMerger(chunks *[]*Chunk, tac bool, revision revision) *Merger { } // NewMerger returns a new Merger -func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool, revision revision, minIndex int32) *Merger { +func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool, revision revision, minIndex int32, maxIndex int32) *Merger { mg := Merger{ pattern: pattern, lists: lists, @@ -59,7 +62,8 @@ func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool, revisi final: false, count: 0, revision: revision, - minIndex: minIndex} + minIndex: minIndex, + maxIndex: maxIndex} for _, list := range mg.lists { mg.count += len(list) diff --git a/src/merger_test.go b/src/merger_test.go index a6b28f6e..85e4795d 100644 --- a/src/merger_test.go +++ b/src/merger_test.go @@ -58,7 +58,7 @@ func TestMergerUnsorted(t *testing.T) { cnt := len(items) // Not sorted: same order - mg := NewMerger(nil, lists, false, false, revision{}, 0) + mg := NewMerger(nil, lists, false, false, revision{}, 0, 0) assert(t, cnt == mg.Length(), "Invalid Length") for i := 0; i < cnt; i++ { assert(t, items[i] == mg.Get(i), "Invalid Get") @@ -70,7 +70,7 @@ func TestMergerSorted(t *testing.T) { cnt := len(items) // Sorted sorted order - mg := NewMerger(nil, lists, true, false, revision{}, 0) + mg := NewMerger(nil, lists, true, false, revision{}, 0, 0) assert(t, cnt == mg.Length(), "Invalid Length") sort.Sort(ByRelevance(items)) for i := 0; i < cnt; i++ { @@ -80,7 +80,7 @@ func TestMergerSorted(t *testing.T) { } // Inverse order - mg2 := NewMerger(nil, lists, true, false, revision{}, 0) + mg2 := NewMerger(nil, lists, true, false, revision{}, 0, 0) 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/terminal.go b/src/terminal.go index adffbab5..748a2dfc 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -1680,12 +1680,12 @@ func (t *Terminal) UpdateList(merger *Merger) { // Trimmed by --tail: filter selection by index filtered := make(map[int32]selectedItem) minIndex := merger.minIndex - maxIndex := minIndex + int32(merger.Length()) + maxIndex := merger.maxIndex for k, v := range t.selected { var included bool if maxIndex > minIndex { included = k >= minIndex && k < maxIndex - } else { // int32 overflow [==> <==] + } else if maxIndex < minIndex { // int32 overflow [==> <==] included = k >= minIndex || k < maxIndex } if included { diff --git a/test/test_core.rb b/test/test_core.rb index cf4cd237..4d21c19c 100644 --- a/test/test_core.rb +++ b/test/test_core.rb @@ -2002,4 +2002,37 @@ class TestCore < TestInteractive tmux.until { assert_equal 0, it.select_count } tmux.until { refute it.any_include?('Selected') } end + + def test_preserve_selection_on_revision_bump + tmux.send_keys %(seq 100 | #{FZF} --multi --sync --query "'1" --bind 'a:select-all+change-header(pressed a),b:change-header(pressed b)+change-nth(1),c:exclude'), :Enter + tmux.until do + assert_equal 20, it.match_count + assert_equal 0, it.select_count + end + tmux.send_keys :a + tmux.until do + assert_equal 20, it.match_count + assert_equal 20, it.select_count + assert it.any_include?('pressed a') + end + tmux.send_keys :b + tmux.until do + assert_equal 20, it.match_count + assert_equal 20, it.select_count + refute it.any_include?('pressed a') + assert it.any_include?('pressed b') + end + tmux.send_keys :a + tmux.until do + assert_equal 20, it.match_count + assert_equal 20, it.select_count + assert it.any_include?('pressed a') + refute it.any_include?('pressed b') + end + tmux.send_keys :c + tmux.until do + assert_equal 19, it.match_count + assert_equal 19, it.select_count + end + end end