Add --track option to track the current selection

Close #3186
Related #1890
pull/3226/head
Junegunn Choi 1 year ago
parent ae745d9397
commit 1c7534f009
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627

@ -3,6 +3,13 @@ CHANGELOG
0.39.0
------
- Added `--track` option that makes fzf track the current selection when the
result list is updated. This can be useful when browsing logs using fzf with
sorting disabled.
```sh
git log --oneline --graph --color=always | nl |
fzf --ansi --track --no-sort --layout=reverse-list
```
- If you use `--listen` option without a port number fzf will automatically
allocate an available port and export it as `$FZF_PORT` environment
variable.

@ -92,6 +92,16 @@ interface rather than a "fuzzy finder". You can later enable the search using
.B "+s, --no-sort"
Do not sort the result
.TP
.B "--track"
Make fzf track the current selection when the result list is updated.
This can be useful when browsing logs using fzf with sorting disabled.
.RS
e.g.
\fBgit log --oneline --graph --color=always | nl |
fzf --ansi --track --no-sort --layout=reverse-list\fR
.RE
.TP
.B "--tac"
Reverse the order of the input

@ -17,6 +17,7 @@ type Merger struct {
tac bool
final bool
count int
pass bool
}
// PassMerger returns a new Merger that simply returns the items in the
@ -26,7 +27,8 @@ func PassMerger(chunks *[]*Chunk, tac bool) *Merger {
pattern: nil,
chunks: chunks,
tac: tac,
count: 0}
count: 0,
pass: true}
for _, chunk := range *mg.chunks {
mg.count += chunk.count
@ -58,6 +60,19 @@ func (mg *Merger) Length() int {
return mg.count
}
// FindIndex returns the index of the item with the given item index
func (mg *Merger) FindIndex(itemIndex int32) int {
if mg.pass {
return int(itemIndex)
}
for i := 0; i < mg.count; i++ {
if mg.Get(i).item.Index() == itemIndex {
return i
}
}
return -1
}
// Get returns the pointer to the Result object indexed by the given integer
func (mg *Merger) Get(idx int) Result {
if mg.chunks != nil {

@ -33,6 +33,7 @@ const usage = `usage: fzf [options]
field index expressions
-d, --delimiter=STR Field delimiter regex (default: AWK-style)
+s, --no-sort Do not sort the result
--track Track the current selection when the result is updated
--tac Reverse the order of the input
--disabled Do not perform search
--tiebreak=CRI[,..] Comma-separated list of sort criteria to apply
@ -266,6 +267,7 @@ type Options struct {
WithNth []Range
Delimiter Delimiter
Sort int
Track bool
Tac bool
Criteria []criterion
Multi int
@ -338,6 +340,7 @@ func defaultOptions() *Options {
WithNth: make([]Range, 0),
Delimiter: Delimiter{},
Sort: 1000,
Track: false,
Tac: false,
Criteria: []criterion{byScore, byLength},
Multi: 0,
@ -1562,6 +1565,10 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Sort = optionalNumeric(allArgs, &i, 1)
case "+s", "--no-sort":
opts.Sort = 0
case "--track":
opts.Track = true
case "--no-track":
opts.Track = false
case "--tac":
opts.Tac = true
case "--no-tac":

@ -183,6 +183,7 @@ type Terminal struct {
multi int
sort bool
toggleSort bool
track bool
delimiter Delimiter
expect map[tui.Event]string
keymap map[tui.Event][]*action
@ -599,6 +600,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
multi: opts.Multi,
sort: opts.Sort > 0,
toggleSort: opts.ToggleSort,
track: opts.Track,
delimiter: opts.Delimiter,
expect: opts.Expect,
keymap: opts.Keymap,
@ -904,6 +906,10 @@ func (t *Terminal) UpdateProgress(progress float32) {
// UpdateList updates Merger to display the list
func (t *Terminal) UpdateList(merger *Merger, reset bool) {
t.mutex.Lock()
var prevIndex int32 = -1
if !reset && t.track && t.merger.Length() > 0 {
prevIndex = t.merger.Get(t.cy).item.Index()
}
t.progress = 100
t.merger = merger
if reset {
@ -914,6 +920,18 @@ func (t *Terminal) UpdateList(merger *Merger, reset bool) {
t.triggerLoad = false
t.eventChan <- tui.Load.AsEvent()
}
if prevIndex >= 0 {
pos := t.cy - t.offset
count := t.merger.Length()
i := t.merger.FindIndex(prevIndex)
if i >= 0 {
t.cy = i
t.offset = t.cy - pos
} else if t.cy > count {
// Try to keep the vertical position when the list shrinks
t.cy = count - util.Min(count, t.maxItems()) + pos
}
}
t.mutex.Unlock()
t.reqBox.Set(reqInfo, nil)
t.reqBox.Set(reqList, nil)

@ -2679,6 +2679,29 @@ class TestGoFZF < TestBase
OUTPUT
tmux.until { assert_block(expected, _1) }
end
def test_track
tmux.send_keys "seq 1000 | #{FZF} --query 555 --track", :Enter
tmux.until do |lines|
assert_equal 1, lines.match_count
assert_includes lines, '> 555'
end
tmux.send_keys :BSpace
index = tmux.until do |lines|
assert_equal 28, lines.match_count
assert_includes lines, '> 555'
end.index('> 555')
tmux.send_keys :BSpace
tmux.until do |lines|
assert_equal 271, lines.match_count
assert_equal '> 555', lines[index]
end
tmux.send_keys :BSpace
tmux.until do |lines|
assert_equal 1000, lines.match_count
assert_equal '> 555', lines[index]
end
end
end
module TestShell

Loading…
Cancel
Save