mirror of
https://github.com/junegunn/fzf.git
synced 2026-10-11 15:49:20 +08:00
Trigger focus event after reload (#4931)
- Indexes restart after reload, so same-index item was not seen as new - With --id-nth, compare by key. No event if focused item keeps its key. - Skip focus check while track is blocked - Document that binding reload to focus can loop Close #4930
This commit is contained in:
1 parent
33a3456921
commit
25adb67453
4 files changed
+95
-8
No files matched your search
@@ -6,6 +6,9 @@ CHANGELOG
|
||||
- Fixed `--gap-line` cutting a grapheme cluster when filling the last cells of the line (#4920)
|
||||
- Fixed an escape sequence fzf does not recognize being partly consumed, which leaked the rest into the query (#4926)
|
||||
- Fixed a key lost when it arrived in the same read as an escape sequence
|
||||
- Fixed `focus` event not triggered after `reload` when the new item is at the same position as the previous one (#4930)
|
||||
- With `--id-nth`, the event is not triggered if the focus moves directly from an item to another item with the same key
|
||||
- A `focus` binding that runs `reload` now triggers itself again after each reload, unless `--id-nth` is set and the key does not change
|
||||
|
||||
0.74.4
|
||||
------
|
||||
|
||||
+4
-1
@@ -1909,7 +1909,10 @@ e.g.
|
||||
\fIfocus\fR
|
||||
.RS
|
||||
Triggered when the focus changes due to a vertical cursor movement or a search
|
||||
result update.
|
||||
result update. After a reload, the focused item is considered new unless
|
||||
\fB\-\-id\-nth\fR is set and the item has the same key as the previously
|
||||
focused item. Because of this, binding \fBreload\fR to this event can cause an
|
||||
infinite loop.
|
||||
|
||||
e.g.
|
||||
\fBfzf \-\-bind 'focus:transform\-preview\-label:echo [ {} ]' \-\-preview 'cat {}'
|
||||
|
||||
+33
-7
@@ -467,7 +467,7 @@ type Terminal struct {
|
||||
termSize tui.TermSize
|
||||
lastAction actionType
|
||||
lastKey string
|
||||
lastFocus int32
|
||||
lastFocus focusedItem
|
||||
areaLines int
|
||||
areaColumns int
|
||||
forcePreview bool
|
||||
@@ -492,6 +492,11 @@ type selectedItem struct {
|
||||
item *Item
|
||||
}
|
||||
|
||||
type focusedItem struct {
|
||||
major int // revision.major; changes on reload
|
||||
item *Item
|
||||
}
|
||||
|
||||
type byTimeOrder []selectedItem
|
||||
|
||||
func (a byTimeOrder) Len() int {
|
||||
@@ -1186,7 +1191,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
||||
initFunc: func() error { return renderer.Init() },
|
||||
executing: util.NewAtomicBool(false),
|
||||
lastAction: actStart,
|
||||
lastFocus: minItem.Index(),
|
||||
lastFocus: focusedItem{},
|
||||
lastActivity: time.Now(),
|
||||
numLinesCache: make(map[int32]numLinesCacheValue),
|
||||
// The initial load counts as a search in progress ('start:wait').
|
||||
@@ -6066,6 +6071,26 @@ func (t *Terminal) currentIndex() int32 {
|
||||
return minItem.Index()
|
||||
}
|
||||
|
||||
func (t *Terminal) currentFocus() focusedItem {
|
||||
return focusedItem{t.revision.major, t.currentItem()}
|
||||
}
|
||||
|
||||
// Records the current focus and returns whether it differs from the last one.
|
||||
// Indexes restart after a reload, so items are compared by --id-nth key, if
|
||||
// set. Without it, any focused item after a reload is a new one.
|
||||
func (t *Terminal) updateFocus() bool {
|
||||
curr := t.currentFocus()
|
||||
last := t.lastFocus
|
||||
t.lastFocus = curr
|
||||
if curr.item == nil || last.item == nil {
|
||||
return curr.item != last.item
|
||||
}
|
||||
if curr.major == last.major {
|
||||
return curr.item.Index() != last.item.Index()
|
||||
}
|
||||
return len(t.idNth) == 0 || t.trackKeyFor(curr.item, t.idNth) != t.trackKeyFor(last.item, t.idNth)
|
||||
}
|
||||
|
||||
func (t *Terminal) trackKeyFor(item *Item, nth []Range) string {
|
||||
tokens := Tokenize(item.AsString(t.ansi), t.delimiter)
|
||||
return StripLastDelimiter(JoinTokens(Transform(tokens, nth)), t.delimiter)
|
||||
@@ -6631,8 +6656,7 @@ func (t *Terminal) Loop() error {
|
||||
info = true
|
||||
}
|
||||
focusChanged := focusedIndex != currentIndex
|
||||
if (t.hasFocusActions || t.infoCommand != "") && focusChanged && currentIndex != t.lastFocus {
|
||||
t.lastFocus = currentIndex
|
||||
if (t.hasFocusActions || t.infoCommand != "") && !t.trackBlocked && t.updateFocus() {
|
||||
t.eventChan <- tui.Focus.AsEvent()
|
||||
if t.infoCommand != "" {
|
||||
info = true
|
||||
@@ -7017,9 +7041,11 @@ func (t *Terminal) Loop() error {
|
||||
}
|
||||
}
|
||||
|
||||
if onFocus, prs := t.keymap[tui.Focus.AsEvent()]; prs && iter < maxFocusEvents {
|
||||
if newIndex := t.currentIndex(); newIndex != currentIndex {
|
||||
t.lastFocus = newIndex
|
||||
// Actions are ignored while blocked. The render loop will
|
||||
// report the focus after the reload.
|
||||
if onFocus, prs := t.keymap[tui.Focus.AsEvent()]; prs && iter < maxFocusEvents && !t.trackBlocked {
|
||||
if t.currentIndex() != currentIndex {
|
||||
t.lastFocus = t.currentFocus()
|
||||
if t.infoCommand != "" {
|
||||
req(reqInfo)
|
||||
}
|
||||
|
||||
@@ -1532,6 +1532,61 @@ class TestCore < TestInteractive
|
||||
tmux.until { |lines| refute_includes(lines[-1], '[[1]]') }
|
||||
end
|
||||
|
||||
def assert_focus_log(expected)
|
||||
wait do
|
||||
assert_path_exists tempname
|
||||
assert_equal expected, File.readlines(tempname, chomp: true)
|
||||
end
|
||||
end
|
||||
|
||||
def test_focus_event_after_reload
|
||||
# Without --id-nth, the item at the same index after reload is a new item
|
||||
tmux.send_keys %(printf 'a\\nb\\n' | #{FZF} --bind 'focus:execute-silent(echo {} >> #{tempname})' --bind 'ctrl-r:reload(printf "c\\nd\\n")'), :Enter
|
||||
tmux.until { |lines| assert_includes lines, '> a' }
|
||||
assert_focus_log(%w[a])
|
||||
tmux.send_keys 'C-r'
|
||||
tmux.until { |lines| assert_includes lines, '> c' }
|
||||
assert_focus_log(%w[a c])
|
||||
end
|
||||
|
||||
def test_focus_event_after_reload_id_nth_same_key
|
||||
# Same --id-nth key after reload: no focus event
|
||||
tmux.send_keys %(printf '1 a\\n2 b\\n' | #{FZF} --track --id-nth 1 --bind 'focus:execute-silent(echo {} >> #{tempname})' --bind 'ctrl-r:reload(printf "0 x\\n1 aa\\n2 bb\\n")'), :Enter
|
||||
tmux.until { |lines| assert_includes lines, '> 1 a' }
|
||||
assert_focus_log(['1 a'])
|
||||
tmux.send_keys 'C-r'
|
||||
tmux.until { |lines| assert_includes lines, '> 1 aa' }
|
||||
tmux.send_keys :Up
|
||||
tmux.until { |lines| assert_includes lines, '> 2 bb' }
|
||||
assert_focus_log(['1 a', '2 bb'])
|
||||
end
|
||||
|
||||
def test_focus_event_after_reload_id_nth_key_not_found
|
||||
tmux.send_keys %(printf '1 a\\n2 b\\n' | #{FZF} --track --id-nth 1 --bind 'focus:execute-silent(echo {} >> #{tempname})' --bind 'ctrl-r:reload(printf "3 c\\n4 d\\n")'), :Enter
|
||||
tmux.until { |lines| assert_includes lines, '> 1 a' }
|
||||
assert_focus_log(['1 a'])
|
||||
tmux.send_keys 'C-r'
|
||||
tmux.until { |lines| assert_includes lines, '> 3 c' }
|
||||
assert_focus_log(['1 a', '3 c'])
|
||||
end
|
||||
|
||||
def test_focus_event_after_async_reload_id_nth
|
||||
# No focus event for the items shown before the tracked item arrives.
|
||||
# 'result' binding makes fzf redraw the list while blocked.
|
||||
tmux.send_keys %(printf '1 a\\n2 b\\n' | #{FZF} --track --id-nth 1 --bind 'result:change-footer(R)' --bind 'focus:execute-silent(echo {} >> #{tempname})' --bind 'ctrl-r:reload(printf "0 x\\n"; sleep 1; printf "1 aa\\n2 bb\\n")'), :Enter
|
||||
tmux.until { |lines| assert_includes lines, '> 1 a' }
|
||||
assert_focus_log(['1 a'])
|
||||
tmux.send_keys 'C-r'
|
||||
tmux.until { |lines| assert_includes lines[-2], '+T*' }
|
||||
tmux.until do |lines|
|
||||
assert_includes lines, '> 1 aa'
|
||||
refute_includes lines[-2], '+T*'
|
||||
end
|
||||
tmux.send_keys :Up
|
||||
tmux.until { |lines| assert_includes lines, '> 2 bb' }
|
||||
assert_focus_log(['1 a', '2 bb'])
|
||||
end
|
||||
|
||||
def test_result_event
|
||||
tmux.send_keys '(echo 0; seq 10) | fzf --bind "result:pos(2)"', :Enter
|
||||
tmux.until { |lines| assert_equal 11, lines.match_count }
|
||||
|
||||
Reference in new issue
Block a user