mirror of https://github.com/Llewellynvdm/fzf.git
Fix bug where preview is not updated after reload when --disabled is set
Fix #3311
This commit is contained in:
parent
7795748a3f
commit
4c70745cc1
25
src/core.go
25
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.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos,
|
||||||
opts.Filter == nil, opts.Nth, opts.Delimiter, runes)
|
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
|
// Filtering mode
|
||||||
if opts.Filter != nil {
|
if opts.Filter != nil {
|
||||||
|
@ -209,8 +211,6 @@ func Run(opts *Options, version string, revision string) {
|
||||||
|
|
||||||
// Event coordination
|
// Event coordination
|
||||||
reading := true
|
reading := true
|
||||||
clearCache := util.Once(false)
|
|
||||||
clearSelection := util.Once(false)
|
|
||||||
ticks := 0
|
ticks := 0
|
||||||
var nextCommand *string
|
var nextCommand *string
|
||||||
eventBox.Watch(EvtReadNew)
|
eventBox.Watch(EvtReadNew)
|
||||||
|
@ -234,17 +234,17 @@ func Run(opts *Options, version string, revision string) {
|
||||||
var count int
|
var count int
|
||||||
restart := func(command string) {
|
restart := func(command string) {
|
||||||
reading = true
|
reading = true
|
||||||
clearCache = util.Once(true)
|
|
||||||
clearSelection = util.Once(true)
|
|
||||||
chunkList.Clear()
|
chunkList.Clear()
|
||||||
itemIndex = 0
|
itemIndex = 0
|
||||||
|
inputRevision++
|
||||||
header = make([]string, 0, opts.HeaderLines)
|
header = make([]string, 0, opts.HeaderLines)
|
||||||
go reader.restart(command)
|
go reader.restart(command)
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
delay := true
|
delay := true
|
||||||
ticks++
|
ticks++
|
||||||
input := func(reloaded bool) []rune {
|
input := func() []rune {
|
||||||
|
reloaded := snapshotRevision != inputRevision
|
||||||
paused, input := terminal.Input()
|
paused, input := terminal.Input()
|
||||||
if reloaded && paused {
|
if reloaded && paused {
|
||||||
query = []rune{}
|
query = []rune{}
|
||||||
|
@ -277,18 +277,18 @@ func Run(opts *Options, version string, revision string) {
|
||||||
}
|
}
|
||||||
if !useSnapshot {
|
if !useSnapshot {
|
||||||
snapshot, count = chunkList.Snapshot()
|
snapshot, count = chunkList.Snapshot()
|
||||||
|
snapshotRevision = inputRevision
|
||||||
}
|
}
|
||||||
total = count
|
total = count
|
||||||
terminal.UpdateCount(total, !reading, value.(*string))
|
terminal.UpdateCount(total, !reading, value.(*string))
|
||||||
if opts.Sync {
|
if opts.Sync {
|
||||||
opts.Sync = false
|
opts.Sync = false
|
||||||
terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false)
|
terminal.UpdateList(PassMerger(&snapshot, opts.Tac, snapshotRevision))
|
||||||
}
|
}
|
||||||
if heightUnknown && !deferred {
|
if heightUnknown && !deferred {
|
||||||
determine(!reading)
|
determine(!reading)
|
||||||
}
|
}
|
||||||
reset := !useSnapshot && clearCache()
|
matcher.Reset(snapshot, input(), false, !reading, sort, snapshotRevision)
|
||||||
matcher.Reset(snapshot, input(reset), false, !reading, sort, reset)
|
|
||||||
|
|
||||||
case EvtSearchNew:
|
case EvtSearchNew:
|
||||||
var command *string
|
var command *string
|
||||||
|
@ -313,17 +313,16 @@ func Run(opts *Options, version string, revision string) {
|
||||||
if !changed {
|
if !changed {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
reset := false
|
|
||||||
if !useSnapshot {
|
if !useSnapshot {
|
||||||
newSnapshot, _ := chunkList.Snapshot()
|
newSnapshot, _ := chunkList.Snapshot()
|
||||||
// We want to avoid showing empty list when reload is triggered
|
// 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
|
// and the query string is changed at the same time i.e. command != nil && changed
|
||||||
if command == nil || len(newSnapshot) > 0 {
|
if command == nil || len(newSnapshot) > 0 {
|
||||||
snapshot = newSnapshot
|
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
|
delay = false
|
||||||
|
|
||||||
case EvtSearchProgress:
|
case EvtSearchProgress:
|
||||||
|
@ -363,7 +362,7 @@ func Run(opts *Options, version string, revision string) {
|
||||||
determine(val.final)
|
determine(val.final)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
terminal.UpdateList(val, clearSelection())
|
terminal.UpdateList(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,11 @@ import (
|
||||||
|
|
||||||
// MatchRequest represents a search request
|
// MatchRequest represents a search request
|
||||||
type MatchRequest struct {
|
type MatchRequest struct {
|
||||||
chunks []*Chunk
|
chunks []*Chunk
|
||||||
pattern *Pattern
|
pattern *Pattern
|
||||||
final bool
|
final bool
|
||||||
sort bool
|
sort bool
|
||||||
clearCache bool
|
revision int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matcher is responsible for performing search
|
// Matcher is responsible for performing search
|
||||||
|
@ -29,6 +29,7 @@ type Matcher struct {
|
||||||
partitions int
|
partitions int
|
||||||
slab []*util.Slab
|
slab []*util.Slab
|
||||||
mergerCache map[string]*Merger
|
mergerCache map[string]*Merger
|
||||||
|
revision int
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -38,7 +39,7 @@ const (
|
||||||
|
|
||||||
// NewMatcher returns a new Matcher
|
// NewMatcher returns a new Matcher
|
||||||
func NewMatcher(patternBuilder func([]rune) *Pattern,
|
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)
|
partitions := util.Min(numPartitionsMultiplier*runtime.NumCPU(), maxPartitions)
|
||||||
return &Matcher{
|
return &Matcher{
|
||||||
patternBuilder: patternBuilder,
|
patternBuilder: patternBuilder,
|
||||||
|
@ -48,7 +49,8 @@ func NewMatcher(patternBuilder func([]rune) *Pattern,
|
||||||
reqBox: util.NewEventBox(),
|
reqBox: util.NewEventBox(),
|
||||||
partitions: partitions,
|
partitions: partitions,
|
||||||
slab: make([]*util.Slab, partitions),
|
slab: make([]*util.Slab, partitions),
|
||||||
mergerCache: make(map[string]*Merger)}
|
mergerCache: make(map[string]*Merger),
|
||||||
|
revision: revision}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop puts Matcher in action
|
// Loop puts Matcher in action
|
||||||
|
@ -70,8 +72,9 @@ func (m *Matcher) Loop() {
|
||||||
events.Clear()
|
events.Clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
if request.sort != m.sort || request.clearCache {
|
if request.sort != m.sort || request.revision != m.revision {
|
||||||
m.sort = request.sort
|
m.sort = request.sort
|
||||||
|
m.revision = request.revision
|
||||||
m.mergerCache = make(map[string]*Merger)
|
m.mergerCache = make(map[string]*Merger)
|
||||||
clearChunkCache()
|
clearChunkCache()
|
||||||
}
|
}
|
||||||
|
@ -140,11 +143,11 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
||||||
|
|
||||||
numChunks := len(request.chunks)
|
numChunks := len(request.chunks)
|
||||||
if numChunks == 0 {
|
if numChunks == 0 {
|
||||||
return EmptyMerger, false
|
return EmptyMerger(request.revision), false
|
||||||
}
|
}
|
||||||
pattern := request.pattern
|
pattern := request.pattern
|
||||||
if pattern.IsEmpty() {
|
if pattern.IsEmpty() {
|
||||||
return PassMerger(&request.chunks, m.tac), false
|
return PassMerger(&request.chunks, m.tac, request.revision), false
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelled := util.NewAtomicBool(false)
|
cancelled := util.NewAtomicBool(false)
|
||||||
|
@ -218,11 +221,11 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
||||||
partialResult := <-resultChan
|
partialResult := <-resultChan
|
||||||
partialResults[partialResult.index] = partialResult.matches
|
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
|
// 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)
|
pattern := m.patternBuilder(patternRunes)
|
||||||
|
|
||||||
var event util.EventType
|
var event util.EventType
|
||||||
|
@ -231,5 +234,5 @@ func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final
|
||||||
} else {
|
} else {
|
||||||
event = reqRetry
|
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})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,32 +3,36 @@ package fzf
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// EmptyMerger is a Merger with no data
|
// 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
|
// Merger holds a set of locally sorted lists of items and provides the view of
|
||||||
// a single, globally-sorted list
|
// a single, globally-sorted list
|
||||||
type Merger struct {
|
type Merger struct {
|
||||||
pattern *Pattern
|
pattern *Pattern
|
||||||
lists [][]Result
|
lists [][]Result
|
||||||
merged []Result
|
merged []Result
|
||||||
chunks *[]*Chunk
|
chunks *[]*Chunk
|
||||||
cursors []int
|
cursors []int
|
||||||
sorted bool
|
sorted bool
|
||||||
tac bool
|
tac bool
|
||||||
final bool
|
final bool
|
||||||
count int
|
count int
|
||||||
pass bool
|
pass bool
|
||||||
|
revision int
|
||||||
}
|
}
|
||||||
|
|
||||||
// PassMerger returns a new Merger that simply returns the items in the
|
// PassMerger returns a new Merger that simply returns the items in the
|
||||||
// original order
|
// original order
|
||||||
func PassMerger(chunks *[]*Chunk, tac bool) *Merger {
|
func PassMerger(chunks *[]*Chunk, tac bool, revision int) *Merger {
|
||||||
mg := Merger{
|
mg := Merger{
|
||||||
pattern: nil,
|
pattern: nil,
|
||||||
chunks: chunks,
|
chunks: chunks,
|
||||||
tac: tac,
|
tac: tac,
|
||||||
count: 0,
|
count: 0,
|
||||||
pass: true}
|
pass: true,
|
||||||
|
revision: revision}
|
||||||
|
|
||||||
for _, chunk := range *mg.chunks {
|
for _, chunk := range *mg.chunks {
|
||||||
mg.count += chunk.count
|
mg.count += chunk.count
|
||||||
|
@ -37,17 +41,18 @@ func PassMerger(chunks *[]*Chunk, tac bool) *Merger {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMerger returns a new 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{
|
mg := Merger{
|
||||||
pattern: pattern,
|
pattern: pattern,
|
||||||
lists: lists,
|
lists: lists,
|
||||||
merged: []Result{},
|
merged: []Result{},
|
||||||
chunks: nil,
|
chunks: nil,
|
||||||
cursors: make([]int, len(lists)),
|
cursors: make([]int, len(lists)),
|
||||||
sorted: sorted,
|
sorted: sorted,
|
||||||
tac: tac,
|
tac: tac,
|
||||||
final: false,
|
final: false,
|
||||||
count: 0}
|
count: 0,
|
||||||
|
revision: revision}
|
||||||
|
|
||||||
for _, list := range mg.lists {
|
for _, list := range mg.lists {
|
||||||
mg.count += len(list)
|
mg.count += len(list)
|
||||||
|
@ -55,6 +60,11 @@ func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool) *Merge
|
||||||
return &mg
|
return &mg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Revision returns revision number
|
||||||
|
func (mg *Merger) Revision() int {
|
||||||
|
return mg.revision
|
||||||
|
}
|
||||||
|
|
||||||
// Length returns the number of items
|
// Length returns the number of items
|
||||||
func (mg *Merger) Length() int {
|
func (mg *Merger) Length() int {
|
||||||
return mg.count
|
return mg.count
|
||||||
|
|
|
@ -57,7 +57,7 @@ func TestMergerUnsorted(t *testing.T) {
|
||||||
cnt := len(items)
|
cnt := len(items)
|
||||||
|
|
||||||
// Not sorted: same order
|
// Not sorted: same order
|
||||||
mg := NewMerger(nil, lists, false, false)
|
mg := NewMerger(nil, lists, false, false, 0)
|
||||||
assert(t, cnt == mg.Length(), "Invalid Length")
|
assert(t, cnt == mg.Length(), "Invalid Length")
|
||||||
for i := 0; i < cnt; i++ {
|
for i := 0; i < cnt; i++ {
|
||||||
assert(t, items[i] == mg.Get(i), "Invalid Get")
|
assert(t, items[i] == mg.Get(i), "Invalid Get")
|
||||||
|
@ -69,7 +69,7 @@ func TestMergerSorted(t *testing.T) {
|
||||||
cnt := len(items)
|
cnt := len(items)
|
||||||
|
|
||||||
// Sorted sorted order
|
// Sorted sorted order
|
||||||
mg := NewMerger(nil, lists, true, false)
|
mg := NewMerger(nil, lists, true, false, 0)
|
||||||
assert(t, cnt == mg.Length(), "Invalid Length")
|
assert(t, cnt == mg.Length(), "Invalid Length")
|
||||||
sort.Sort(ByRelevance(items))
|
sort.Sort(ByRelevance(items))
|
||||||
for i := 0; i < cnt; i++ {
|
for i := 0; i < cnt; i++ {
|
||||||
|
@ -79,7 +79,7 @@ func TestMergerSorted(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inverse order
|
// Inverse order
|
||||||
mg2 := NewMerger(nil, lists, true, false)
|
mg2 := NewMerger(nil, lists, true, false, 0)
|
||||||
for i := cnt - 1; i >= 0; i-- {
|
for i := cnt - 1; i >= 0; i-- {
|
||||||
if items[i] != mg2.Get(i) {
|
if items[i] != mg2.Get(i) {
|
||||||
t.Error("Not sorted", items[i], mg2.Get(i))
|
t.Error("Not sorted", items[i], mg2.Get(i))
|
||||||
|
|
|
@ -228,6 +228,7 @@ type Terminal struct {
|
||||||
merger *Merger
|
merger *Merger
|
||||||
selected map[int32]selectedItem
|
selected map[int32]selectedItem
|
||||||
version int64
|
version int64
|
||||||
|
revision int
|
||||||
reqBox *util.EventBox
|
reqBox *util.EventBox
|
||||||
initialPreviewOpts previewOpts
|
initialPreviewOpts previewOpts
|
||||||
previewOpts previewOpts
|
previewOpts previewOpts
|
||||||
|
@ -644,7 +645,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||||
jumpLabels: opts.JumpLabels,
|
jumpLabels: opts.JumpLabels,
|
||||||
printer: opts.Printer,
|
printer: opts.Printer,
|
||||||
printsep: opts.PrintSep,
|
printsep: opts.PrintSep,
|
||||||
merger: EmptyMerger,
|
merger: EmptyMerger(0),
|
||||||
selected: make(map[int32]selectedItem),
|
selected: make(map[int32]selectedItem),
|
||||||
reqBox: util.NewEventBox(),
|
reqBox: util.NewEventBox(),
|
||||||
initialPreviewOpts: opts.Preview,
|
initialPreviewOpts: opts.Preview,
|
||||||
|
@ -930,9 +931,10 @@ func (t *Terminal) UpdateProgress(progress float32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateList updates Merger to display the list
|
// UpdateList updates Merger to display the list
|
||||||
func (t *Terminal) UpdateList(merger *Merger, reset bool) {
|
func (t *Terminal) UpdateList(merger *Merger) {
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
var prevIndex int32 = -1
|
var prevIndex int32 = -1
|
||||||
|
reset := t.revision != merger.Revision()
|
||||||
if !reset && t.track != trackDisabled {
|
if !reset && t.track != trackDisabled {
|
||||||
if t.merger.Length() > 0 {
|
if t.merger.Length() > 0 {
|
||||||
prevIndex = t.merger.Get(t.cy).item.Index()
|
prevIndex = t.merger.Get(t.cy).item.Index()
|
||||||
|
@ -944,6 +946,7 @@ func (t *Terminal) UpdateList(merger *Merger, reset bool) {
|
||||||
t.merger = merger
|
t.merger = merger
|
||||||
if reset {
|
if reset {
|
||||||
t.selected = make(map[int32]selectedItem)
|
t.selected = make(map[int32]selectedItem)
|
||||||
|
t.revision = merger.Revision()
|
||||||
t.version++
|
t.version++
|
||||||
}
|
}
|
||||||
if t.triggerLoad {
|
if t.triggerLoad {
|
||||||
|
|
|
@ -2935,6 +2935,14 @@ class TestGoFZF < TestBase
|
||||||
tmux.send_keys "sleep 0.5 | #{FZF} --bind 'start:reload:ls' --bind 'load:become:tty'", :Enter
|
tmux.send_keys "sleep 0.5 | #{FZF} --bind 'start:reload:ls' --bind 'load:become:tty'", :Enter
|
||||||
tmux.until { |lines| assert_includes lines, '/dev/tty' }
|
tmux.until { |lines| assert_includes lines, '/dev/tty' }
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
module TestShell
|
module TestShell
|
||||||
|
|
Loading…
Reference in New Issue