mirror of
https://github.com/junegunn/fzf.git
synced 2026-07-10 02:29:14 +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:
@@ -2322,6 +2322,12 @@ While waiting, user input is ignored, except for keys bound to \fBabort\fR or
|
||||
default), which cancel the wait and discard the pending actions instead of
|
||||
performing their usual role.
|
||||
|
||||
Asynchronous \fBbg\-transform\-*\fR actions are not affected; their results are
|
||||
applied as soon as they arrive, even while waiting. Note that \fBwait\fR only
|
||||
considers searches already in progress or triggered by the preceding actions in
|
||||
the same binding; a search initiated later by a \fBbg\-transform\fR result is
|
||||
not waited for.
|
||||
|
||||
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
|
||||
feedback is debounced so that quick searches do not cause flickering.
|
||||
|
||||
+21
-7
@@ -1892,11 +1892,12 @@ func (t *Terminal) UpdateList(result MatchResult) {
|
||||
// If waiting, unblock so main loop can execute pending actions.
|
||||
// Note: we unblock on any final result, not the specific search that
|
||||
// armed the wait. A stale final from an earlier search can race in
|
||||
// when searches are dispatched back-to-back: via --listen, or
|
||||
// 'reload(...)+wait' racing the previous input's final. The worst
|
||||
// case is an early unblock running the pending actions on the
|
||||
// previous result set; we accept it rather than thread a per-search
|
||||
// generation token through the matcher.
|
||||
// when searches are dispatched back-to-back: via --listen, a
|
||||
// bg-transform callback applying mid-wait, or 'reload(...)+wait'
|
||||
// racing the previous input's final. The worst case is an early
|
||||
// unblock running the pending actions on the previous result set;
|
||||
// we accept it rather than thread a per-search generation token
|
||||
// through the matcher.
|
||||
if t.waitBlocked {
|
||||
t.unblockWait()
|
||||
wakeUp = len(t.pendingActions) > 0
|
||||
@@ -6526,6 +6527,12 @@ func (t *Terminal) Loop() error {
|
||||
var newCommand *commandSpec
|
||||
var reloadSync bool
|
||||
var denylist []int32
|
||||
// Whether we're running background transform callbacks. Actions parsed
|
||||
// from their results are allowed to run even while wait/track-blocked,
|
||||
// because they're the results of processes started before the block.
|
||||
// Declared here because the callbacks invoke the doActions closure of the
|
||||
// iteration that scheduled them, not the one executing actAsync.
|
||||
inBgCallback := false
|
||||
req := func(evts ...util.EventType) {
|
||||
for _, event := range evts {
|
||||
events = append(events, event)
|
||||
@@ -6756,8 +6763,13 @@ func (t *Terminal) Loop() error {
|
||||
callback(a.a)
|
||||
}
|
||||
}
|
||||
// Actions that must execute even while wait/track-blocked:
|
||||
// background transform callbacks (and actions parsed from their
|
||||
// results) since they're results of processes started before the
|
||||
// block.
|
||||
passthrough := inBgCallback || a.t == actAsync
|
||||
// When wait-blocked, only allow abort/cancel
|
||||
if t.waitBlocked {
|
||||
if t.waitBlocked && !passthrough {
|
||||
if a.t == actAbort || a.t == actCancel {
|
||||
t.unblockWait()
|
||||
t.pendingActions = nil
|
||||
@@ -6766,7 +6778,7 @@ func (t *Terminal) Loop() error {
|
||||
return true
|
||||
}
|
||||
// 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 {
|
||||
t.unblockTrack()
|
||||
req(reqPrompt, reqInfo)
|
||||
@@ -6777,11 +6789,13 @@ func (t *Terminal) Loop() error {
|
||||
switch a.t {
|
||||
case actIgnore, actStart, actClick:
|
||||
case actAsync:
|
||||
inBgCallback = true
|
||||
for _, callback := range callbacks {
|
||||
if t.bgVersion == callback.version {
|
||||
callback.callback()
|
||||
}
|
||||
}
|
||||
inBgCallback = false
|
||||
case actBecome:
|
||||
valid, list := t.buildPlusList(a.a, false)
|
||||
if valid {
|
||||
|
||||
@@ -1185,6 +1185,22 @@ class TestCore < TestInteractive
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
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_query_change
|
||||
# Query-editing actions must also make wait block; accept must run on the
|
||||
# results of the new query, not the stale ones
|
||||
@@ -1202,6 +1218,15 @@ class TestCore < TestInteractive
|
||||
tmux.until { |lines| assert_equal '> 5', lines[-3] }
|
||||
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
|
||||
tmux.send_keys %(seq 100 | #{FZF} --multi --bind space:clear-selection), :Enter
|
||||
tmux.until { |lines| assert_equal 100, lines.match_count }
|
||||
|
||||
Reference in New Issue
Block a user