m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-13 05:43:48 -05:00

Compare commits

...

3 Commits

Author SHA1 Message Date
Junegunn Choi
6eb1874c5a 0.41.1 2023-05-27 15:54:22 +09:00
Junegunn Choi
4c70745cc1 Fix bug where preview is not updated after reload when --disabled is set
Fix #3311
2023-05-27 15:51:04 +09:00
Junegunn Choi
7795748a3f Remove dead code 2023-05-27 15:10:47 +09:00
11 changed files with 90 additions and 69 deletions

View File

@@ -1,6 +1,11 @@
CHANGELOG
=========
0.41.1
------
- Fixed a bug where preview window is not updated when `--disabled` is set and
a reload is triggered by `change:reload` binding
0.41.0
------
- Added color name `preview-border` and `preview-scrollbar`

View File

@@ -2,7 +2,7 @@
set -u
version=0.41.0
version=0.41.1
auto_completion=
key_bindings=
update_config=2

View File

@@ -1,4 +1,4 @@
$version="0.41.0"
$version="0.41.1"
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf-tmux 1 "May 2023" "fzf 0.41.0" "fzf-tmux - open fzf in tmux split pane"
.TH fzf-tmux 1 "May 2023" "fzf 0.41.1" "fzf-tmux - open fzf in tmux split pane"
.SH NAME
fzf-tmux - open fzf in tmux split pane

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf 1 "May 2023" "fzf 0.41.0" "fzf - a command-line fuzzy finder"
.TH fzf 1 "May 2023" "fzf 0.41.1" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder

View File

@@ -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)
@@ -231,26 +231,20 @@ func Run(opts *Options, version string, revision string) {
useSnapshot := false
var snapshot []*Chunk
var prevSnapshot []*Chunk
var count int
restart := func(command string) {
reading = true
clearCache = util.Once(true)
clearSelection = util.Once(true)
// We should not update snapshot if reload is triggered again while
// the previous reload is in progress
if useSnapshot && prevSnapshot != nil {
snapshot, count = chunkList.Snapshot()
}
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{}
@@ -280,22 +274,21 @@ func Run(opts *Options, version string, revision string) {
}
if useSnapshot && evt == EvtReadFin {
useSnapshot = false
prevSnapshot = nil
}
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
@@ -320,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:
@@ -370,7 +362,7 @@ func Run(opts *Options, version string, revision string) {
determine(val.final)
}
}
terminal.UpdateList(val, clearSelection())
terminal.UpdateList(val)
}
}
}

View File

@@ -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})
}

View File

@@ -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

View File

@@ -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))

View File

@@ -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 {

View File

@@ -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