mirror of
https://github.com/junegunn/fzf.git
synced 2026-08-01 21:50:28 +08:00
Run bg-transform callbacks while wait/track-blocked
Background transform results are unrelated to the block; dropping them would silently lose completed work. Applies both the dedicated bg-transform-* setters and actions parsed from generic bg-transform output (inBgCallback). Searches initiated by a bg result are not waited for.
This commit is contained in:
@@ -2323,6 +2323,14 @@ default), which cancel the wait and discard the pending actions instead of
|
|||||||
performing their usual role. The remaining actions of such a binding still run,
|
performing their usual role. The remaining actions of such a binding still run,
|
||||||
so a binding like \fBesc:cancel+first\fR is possible.
|
so a binding like \fBesc:cancel+first\fR is possible.
|
||||||
|
|
||||||
|
Asynchronous \fBbg\-transform\-*\fR actions are not affected; their results are
|
||||||
|
applied as soon as they arrive, even while waiting. For the same reason,
|
||||||
|
\fBwait\fR does not pair with them: in
|
||||||
|
\fBbg\-transform\-query(...)+wait\fR, the background command completes only
|
||||||
|
after \fBwait\fR has already been evaluated, so the search its result
|
||||||
|
eventually triggers is not waited for. Use the synchronous
|
||||||
|
\fBtransform\-query(...)\fR variant instead when chaining with \fBwait\fR.
|
||||||
|
|
||||||
If the search takes long enough, fzf indicates that it is waiting by dimming the
|
If the search takes long enough, fzf indicates that it is waiting by dimming the
|
||||||
input, hiding the cursor, and showing \fB(..)\fR on the info line. This visual
|
input, hiding the cursor, and showing \fB(..)\fR on the info line. This visual
|
||||||
feedback is debounced so that quick searches do not cause flickering.
|
feedback is debounced so that quick searches do not cause flickering.
|
||||||
|
|||||||
+22
-7
@@ -1890,10 +1890,10 @@ func (t *Terminal) UpdateList(result MatchResult) {
|
|||||||
t.searchInProgress = false
|
t.searchInProgress = false
|
||||||
// If waiting, unblock so main loop can execute pending actions.
|
// If waiting, unblock so main loop can execute pending actions.
|
||||||
// Note: any final result unblocks the wait, not just the one for the
|
// Note: any final result unblocks the wait, not just the one for the
|
||||||
// search that armed it. Back-to-back searches (--listen, reload+wait)
|
// search that armed it. Back-to-back searches (--listen, bg-transform
|
||||||
// can therefore unblock early and run the pending actions on the
|
// callbacks, reload+wait) can therefore unblock early and run the
|
||||||
// previous result set. Accepted; a per-search generation token
|
// pending actions on the previous result set. Accepted; a per-search
|
||||||
// through the matcher isn't worth the complexity.
|
// generation token through the matcher isn't worth the complexity.
|
||||||
if t.waitBlocked {
|
if t.waitBlocked {
|
||||||
t.unblockWait()
|
t.unblockWait()
|
||||||
wakeUp = len(t.pendingActions) > 0
|
wakeUp = len(t.pendingActions) > 0
|
||||||
@@ -6523,6 +6523,10 @@ func (t *Terminal) Loop() error {
|
|||||||
var newCommand *commandSpec
|
var newCommand *commandSpec
|
||||||
var reloadSync bool
|
var reloadSync bool
|
||||||
var denylist []int32
|
var denylist []int32
|
||||||
|
// True while running bg-transform callbacks. Declared outside the loop
|
||||||
|
// because callbacks invoke the doActions closure of the iteration that
|
||||||
|
// scheduled them, not the one executing actAsync.
|
||||||
|
inBgCallback := false
|
||||||
req := func(evts ...util.EventType) {
|
req := func(evts ...util.EventType) {
|
||||||
for _, event := range evts {
|
for _, event := range evts {
|
||||||
events = append(events, event)
|
events = append(events, event)
|
||||||
@@ -6677,8 +6681,13 @@ func (t *Terminal) Loop() error {
|
|||||||
currentIndex := t.currentIndex()
|
currentIndex := t.currentIndex()
|
||||||
for i, action := range actions {
|
for i, action := range actions {
|
||||||
if action.t == actWait {
|
if action.t == actWait {
|
||||||
// Already waiting; ignore so input can't reset the blocked state
|
// Already waiting. Actions parsed from a bg-transform
|
||||||
|
// result join the current wait; user input can't reset
|
||||||
|
// the blocked state
|
||||||
if t.waitBlocked {
|
if t.waitBlocked {
|
||||||
|
if inBgCallback {
|
||||||
|
t.pendingActions = append(t.pendingActions, actions[i+1:]...)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Block if search is in progress or will be triggered
|
// Block if search is in progress or will be triggered
|
||||||
@@ -6761,8 +6770,12 @@ func (t *Terminal) Loop() error {
|
|||||||
callback(a.a)
|
callback(a.a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Actions that run even while wait/track-blocked: bg-transform
|
||||||
|
// callbacks and their parsed actions (results of processes
|
||||||
|
// started before the block).
|
||||||
|
passthrough := inBgCallback || a.t == actAsync
|
||||||
// When wait-blocked, only allow abort/cancel
|
// When wait-blocked, only allow abort/cancel
|
||||||
if t.waitBlocked {
|
if t.waitBlocked && !passthrough {
|
||||||
if a.t == actAbort || a.t == actCancel {
|
if a.t == actAbort || a.t == actCancel {
|
||||||
t.unblockWait()
|
t.unblockWait()
|
||||||
t.pendingActions = nil
|
t.pendingActions = nil
|
||||||
@@ -6771,7 +6784,7 @@ func (t *Terminal) Loop() error {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// When track-blocked, only allow abort/cancel and track-disabling actions
|
// When track-blocked, only allow abort/cancel and track-disabling actions
|
||||||
if t.trackBlocked && a.t != actToggleTrack && a.t != actToggleTrackCurrent && a.t != actUntrackCurrent {
|
if t.trackBlocked && !passthrough && a.t != actToggleTrack && a.t != actToggleTrackCurrent && a.t != actUntrackCurrent {
|
||||||
if a.t == actAbort || a.t == actCancel {
|
if a.t == actAbort || a.t == actCancel {
|
||||||
t.unblockTrack()
|
t.unblockTrack()
|
||||||
req(reqPrompt, reqInfo)
|
req(reqPrompt, reqInfo)
|
||||||
@@ -6782,11 +6795,13 @@ func (t *Terminal) Loop() error {
|
|||||||
switch a.t {
|
switch a.t {
|
||||||
case actIgnore, actStart, actClick:
|
case actIgnore, actStart, actClick:
|
||||||
case actAsync:
|
case actAsync:
|
||||||
|
inBgCallback = true
|
||||||
for _, callback := range callbacks {
|
for _, callback := range callbacks {
|
||||||
if t.bgVersion == callback.version {
|
if t.bgVersion == callback.version {
|
||||||
callback.callback()
|
callback.callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
inBgCallback = false
|
||||||
case actBecome:
|
case actBecome:
|
||||||
valid, list := t.buildPlusList(a.a, false)
|
valid, list := t.buildPlusList(a.a, false)
|
||||||
if valid {
|
if valid {
|
||||||
|
|||||||
@@ -1185,6 +1185,30 @@ class TestCore < TestInteractive
|
|||||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_wait_action_bg_transform
|
||||||
|
# A bg-transform result is unrelated to the wait, so it's applied while the
|
||||||
|
# wait is still blocking rather than dropped. The long read keeps the wait
|
||||||
|
# blocked so the (instant) bg-transform completes during the block; the
|
||||||
|
# header must show while '(..)' is still displayed.
|
||||||
|
tmux.send_keys %((seq 100; sleep 2) | #{FZF} --bind 'start:bg-transform-header(echo hello)+search(5)+wait'), :Enter
|
||||||
|
tmux.until { |lines| assert(lines.any_include?('(..)') && lines.any_include?('hello')) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_wait_action_bg_transform_actions
|
||||||
|
# Actions parsed from a generic bg-transform result are also applied
|
||||||
|
# while wait-blocked
|
||||||
|
tmux.send_keys %((seq 100; sleep 2) | #{FZF} --bind 'start:bg-transform(echo change-header:hello)+search(5)+wait'), :Enter
|
||||||
|
tmux.until { |lines| assert(lines.any_include?('(..)') && lines.any_include?('hello')) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_wait_action_bg_transform_join
|
||||||
|
# A 'wait' in a bg-transform result body joins the ongoing wait; the
|
||||||
|
# actions after it run when the wait unblocks instead of being dropped
|
||||||
|
tmux.send_keys %((seq 100; sleep 2) | #{FZF} --bind 'start:bg-transform(echo change-header{hello}+wait+change-footer{world})+search(5)+wait'), :Enter
|
||||||
|
tmux.until { |lines| assert(lines.any_include?('(..)') && lines.any_include?('hello') && !lines.any_include?('world')) }
|
||||||
|
tmux.until { |lines| assert(lines.any_include?('world') && !lines.any_include?('(..)')) }
|
||||||
|
end
|
||||||
|
|
||||||
def test_wait_action_query_change
|
def test_wait_action_query_change
|
||||||
# Query-editing actions must also make wait block; accept must run on the
|
# Query-editing actions must also make wait block; accept must run on the
|
||||||
# results of the new query, not the stale ones
|
# results of the new query, not the stale ones
|
||||||
@@ -1227,6 +1251,15 @@ class TestCore < TestInteractive
|
|||||||
tmux.until { |lines| assert_equal '> 5', lines[-3] }
|
tmux.until { |lines| assert_equal '> 5', lines[-3] }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_track_blocked_bg_transform
|
||||||
|
# A bg-transform result completing while track-blocked is applied, not
|
||||||
|
# dropped. The header must show while '+T*' is still displayed.
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --track --id-nth .. --bind 'ctrl-r:bg-transform-header(echo hello)+reload(sleep 2; seq 100)'", :Enter
|
||||||
|
tmux.until { |lines| assert_includes lines[-2], '+T' }
|
||||||
|
tmux.send_keys 'C-r'
|
||||||
|
tmux.until { |lines| assert(lines.any_include?('+T*') && lines.any_include?('hello')) }
|
||||||
|
end
|
||||||
|
|
||||||
def test_clear_selection
|
def test_clear_selection
|
||||||
tmux.send_keys %(seq 100 | #{FZF} --multi --bind space:clear-selection), :Enter
|
tmux.send_keys %(seq 100 | #{FZF} --multi --bind space:clear-selection), :Enter
|
||||||
tmux.until { |lines| assert_equal 100, lines.match_count }
|
tmux.until { |lines| assert_equal 100, lines.match_count }
|
||||||
|
|||||||
Reference in New Issue
Block a user