summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2023-05-27 15:43:31 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2023-05-27 15:51:04 +0900
commit4c70745cc1c8856486dab9e07985cbcf5081e490 (patch)
tree84a742cb75d32c13142a9354e4aa40858abc0f07
parent7795748a3f42e86a802cc9ea9569429892f47f95 (diff)
Fix bug where preview is not updated after reload when --disabled is set
Fix #3311
-rw-r--r--src/core.go25
-rw-r--r--src/matcher.go29
-rw-r--r--src/merger.go64
-rw-r--r--src/merger_test.go6
-rw-r--r--src/terminal.go7
-rwxr-xr-xtest/test_go.rb8
6 files changed, 81 insertions, 58 deletions
diff --git a/src/core.go b/src/core.go
index e049fb43..e21e8c0a 100644
--- a/src/core.go
+++ b/src/core.go
@@ -138,7 +138,9 @@ func Run(opts *Options, version string, revision string) {
opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos,
opts.Filter == nil, opts.Nth, opts.Delimiter, runes)
}
- matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox)
+ inputRevision := 0
+ snapshotRevision := 0
+ matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox, inputRevision)
// Filtering mode
if opts.Filter != nil {
@@ -209,8 +211,6 @@ func Run(opts *Options, version string, revision string) {
// Event coordination
reading := true
- clearCache := util.Once(false)
- clearSelection := util.Once(false)
ticks := 0
var nextCommand *string
eventBox.Watch(EvtReadNew)
@@ -234,17 +234,17 @@ func Run(opts *Options, version string, revision string) {
var count int
restart := func(command string) {
reading = true
- clearCache = util.Once(true)
- clearSelection = util.Once(true)
chunkList.Clear()
itemIndex = 0
+ inputRevision++
header = make([]string, 0, opts.HeaderLines)
go reader.restart(command)
}
for {
delay := true
ticks++
- input := func(reloaded bool) []rune {
+ input := func() []rune {
+ reloaded := snapshotRevision != inputRevision
paused, input := terminal.Input()
if reloaded && paused {
query = []rune{}
@@ -277,18 +277,18 @@ func Run(opts *Options, version string, revision string) {
}
if !useSnapshot {
snapshot, count = chunkList.Snapshot()
+ snapshotRevision = inputRevision
}
total = count
terminal.UpdateCount(total, !reading, value.(*string))
if opts.Sync {
opts.Sync = false
- terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false)
+ terminal.UpdateList(PassMerger(&snapshot, opts.Tac, snapshotRevision))
}
if heightUnknown && !deferred {
determine(!reading)
}
- reset := !useSnapshot && clearCache()
- matcher.Reset(snapshot, input(reset), false, !reading, sort, reset)
+ matcher.Reset(snapshot, input(), false, !reading, sort, snapshotRevision)
case EvtSearchNew:
var command *string
@@ -313,17 +313,16 @@ func Run(opts *Options, version string, revision string) {
if !changed {
break
}
- reset := false
if !useSnapshot {
newSnapshot, _ := chunkList.Snapshot()
// We want to avoid showing empty list when reload is triggered
// and the query string is changed at the same time i.e. command != nil && changed
if command == nil || len(newSnapshot) > 0 {
snapshot = newSnapshot
- reset = clearCache()
+ snapshotRevision = inputRevision
}
}
- matcher.Reset(snapshot, input(reset), true, !reading, sort, reset)
+ matcher.Reset(snapshot, input(), true, !reading, sort, snapshotRevision)
delay = false
case EvtSearchProgress:
@@ -363,7 +362,7 @@ func Run(opts *Options, version string, revision string) {
determine(val.final)
}
}
- terminal.UpdateList(val, clearSelection())
+ terminal.UpdateList(val)
}
}
}
diff --git a/src/matcher.go b/src/matcher.go
index 22aa819c..b9288bb6 100644
--- a/src/matcher.go
+++ b/src/matcher.go
@@ -12,11 +12,11 @@ import (
// MatchRequest represents a search request
type MatchRequest struct {
- chunks []*Chunk
- pattern *Pattern
- final bool
- sort bool
- clearCache bool
+ chunks []*Chunk
+ pattern *Pattern
+ final bool
+ sort bool
+ revision int
}
// Matcher is responsible for performing search
@@ -29,6 +29,7 @@ type Matcher struct {
partitions int
slab []*util.Slab
mergerCache map[string]*Merger
+ revision int
}
const (
@@ -38,7 +39,7 @@ const (
// NewMatcher returns a new Matcher
func NewMatcher(patternBuilder func([]rune) *Pattern,
- sort bool, tac bool, eventBox *util.EventBox) *Matcher {
+ sort bool, tac bool, eventBox *util.EventBox, revision int) *Matcher {
partitions := util.Min(numPartitionsMultiplier*runtime.NumCPU(), maxPartitions)
return &Matcher{
patternBuilder: patternBuilder,
@@ -48,7 +49,8 @@ func NewMatcher(patternBuilder func([]rune) *Pattern,
reqBox: util.NewEventBox(),
partitions: partitions,
slab: make([]*util.Slab, partitions),
- mergerCache: make(map[string]*Merger)}
+ mergerCache: make(map[string]*Merger),
+ revision: revision}
}
// Loop puts Matcher in action
@@ -70,8 +72,9 @@ func (m *Matcher) Loop() {
events.Clear()
})
- if request.sort != m.sort || request.clearCache {
+ if request.sort != m.sort || request.revision != m.revision {
m.sort = request.sort
+ m.revision = request.revision
m.mergerCache = make(map[string]*Merger)
clearChunkCache()
}
@@ -140,11 +143,11 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
numChunks := len(request.chunks)
if numChunks == 0 {
- return EmptyMerger, false
+ return EmptyMerger(request.revision), false
}
pattern := request.pattern
if pattern.IsEmpty() {
- return PassMerger(&request.chunks, m.tac), false
+ return PassMerger(&request.chunks, m.tac, request.revision), false
}
cancelled := util.NewAtomicBool(false)
@@ -218,11 +221,11 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
partialResult := <-resultChan
partialResults[partialResult.index] = partialResult.matches
}
- return NewMerger(pattern, partialResults, m.sort, m.tac), false
+ return NewMerger(pattern, partialResults, m.sort, m.tac, request.revision), false
}
// Reset is called to interrupt/signal the ongoing search
-func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final bool, sort bool, clearCache bool) {
+func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final bool, sort bool, revision int) {
pattern := m.patternBuilder(patternRunes)
var event util.EventType
@@ -231,5 +234,5 @@ func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final
} else {
event = reqRetry
}
- m.reqBox.Set(event, MatchRequest{chunks, pattern, final, sort && pattern.sortable, clearCache})
+ m.reqBox.Set(event, MatchRequest{chunks, pattern, final, sort && pattern.sortable, revision})
}
diff --git a/src/merger.go b/src/merger.go
index 5c12575c..6a235043 100644
--- a/src/merger.go
+++ b/src/merger.go
@@ -3,32 +3,36 @@ package fzf
import "fmt"
// EmptyMerger is a Merger with no data
-var EmptyMerger = NewMerger(nil, [][]Result{}, false, false)
+func EmptyMerger(revision int) *Merger {
+ return NewMerger(nil, [][]Result{}, false, false, revision)
+}
// 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
- cursors []int
- sorted bool
- tac bool
- final bool
- count int
- pass bool
+ pattern *Pattern
+ lists [][]Result
+ merged []Result
+ chunks *[]*Chunk
+ cursors []int
+ sorted bool
+ tac bool
+ final bool
+ count int
+ pass bool
+ revision int
}
// PassMerger returns a new Merger that simply returns the items in the
// original order
-func PassMerger(chunks *[]*Chunk, tac bool) *Merger {
+func PassMerger(chunks *[]*Chunk, tac bool, revision int) *Merger {
mg := Merger{
- pattern: nil,
- chunks: chunks,
- tac: tac,
- count: 0,
- pass: true}
+ pattern: nil,
+ chunks: chunks,
+ tac: tac,
+ count: 0,
+ pass: true,
+ revision: revision}
for _, chunk := range *mg.chunks {
mg.count += chunk.count
@@ -37,17 +41,18 @@ func PassMerger(chunks *[]*Chunk, tac bool) *Merger {
}
// NewMerger returns a new Merger
-func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool) *Merger {
+func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool, revision int) *Merger {
mg := Merger{
- pattern: pattern,
- lists: lists,
- merged: []Result{},
- chunks: nil,
- cursors: make([]int, len(lists)),
- sorted: sorted,
- tac: tac,
- final: false,
- count: 0}
+ pattern: pattern,
+ lists: lists,
+ merged: []Result{},
+ chunks: nil,
+ cursors: make([]int, len(lists)),
+ sorted: sorted,
+ tac: tac,
+ final: false,
+ count: 0,
+ revision: revision}
for _, list := range mg.lists {
mg.count += len(list)
@@ -55,6 +60,11 @@ func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool) *Merge
return &mg
}
+// Revision returns revision number
+func (mg *Merger) Revision() int {
+ return mg.revision
+}
+
// Length returns the number of items
func (mg *Merger) Length() int {
return mg.count
diff --git a/src/merger_test.go b/src/merger_test.go
index c6af4f66..5894bea0 100644
--- a/src/merger_test.go
+++ b/src/merger_test.go
@@ -57,7 +57,7 @@ func TestMergerUnsorted(t *testing.T) {
cnt := len(items)
// Not sorted: same order
- mg := NewMerger(nil, lists, false, false)
+ mg := NewMerger(nil, lists, false, false, 0)
assert(t, cnt == mg.Length(), "Invalid Length")
for i := 0; i < cnt; i++ {
assert(t, items[i] == mg.Get(i), "Invalid Get")
@@ -69,7 +69,7 @@ func TestMergerSorted(t *testing.T) {
cnt := len(items)
// Sorted sorted order
- mg := NewMerger(nil, lists, true, false)
+ mg := NewMerger(nil, lists, true, false, 0)
assert(t, cnt == mg.Length(), "Invalid Length")
sort.Sort(ByRelevance(items))
for i := 0; i < cnt; i++ {
@@ -79,7 +79,7 @@ func TestMergerSorted(t *testing.T) {
}
// Inverse order
- mg2 := NewMerger(nil, lists, true, false)
+ mg2 := NewMerger(nil, lists, true, false, 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 7f6adade..3179d929 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -228,6 +228,7 @@ type Terminal struct {
merger *Merger
selected map[int32]selectedItem
version int64
+ revision int
reqBox *util.EventBox
initialPreviewOpts previewOpts
previewOpts previewOpts
@@ -644,7 +645,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
jumpLabels: opts.JumpLabels,
printer: opts.Printer,
printsep: opts.PrintSep,
- merger: EmptyMerger,
+ merger: EmptyMerger(0),
selected: make(map[int32]selectedItem),
reqBox: util.NewEventBox(),
initialPreviewOpts: opts.Preview,
@@ -930,9 +931,10 @@ func (t *Terminal) UpdateProgress(progress float32) {
}
// UpdateList updates Merger to display the list
-func (t *Terminal) UpdateList(merger *Merger, reset bool) {
+func (t *Terminal) UpdateList(merger *Merger) {
t.mutex.Lock()
var prevIndex int32 = -1
+ reset := t.revision != merger.Revision()
if !reset && t.track != trackDisabled {
if t.merger.Length() > 0 {
prevIndex = t.merger.Get(t.cy).item.Index()
@@ -944,6 +946,7 @@ func (t *Terminal) UpdateList(merger *Merger, reset bool) {
t.merger = merger
if reset {
t.selected = make(map[int32]selectedItem)
+ t.revision = merger.Revision()
t.version++
}
if t.triggerLoad {
diff --git a/test/test_go.rb b/test/test_go.rb
index 469d007e..5ac0eee0 100755
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -2935,6 +2935,14 @@ class TestGoFZF < TestBase
tmux.send_keys "sleep 0.5 | #{FZF} --bind 'start:reload:ls' --bind 'load:become:tty'", :Enter
tmux.until { |lines| assert_includes lines, '/dev/tty' }
end
+
+ def test_disabled_preview_update
+ tmux.send_keys "echo bar | #{FZF} --disabled --bind 'change:reload:echo foo' --preview 'echo [{q}-{}]'", :Enter
+ tmux.until { |lines| assert_equal 1, lines.match_count }
+ tmux.until { |lines| assert(lines.any? { |line| line.include?('[-bar]') }) }
+ tmux.send_keys :x
+ tmux.until { |lines| assert(lines.any? { |line| line.include?('[x-foo]') }) }
+ end
end
module TestShell