mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-15 23:03:47 -05:00
Add bg-cancel action to ignore running background transforms
Close #4430 Example: # Implement popup that disappears after 1 second # * Use footer as the popup # * Use `bell` to ring the terminal bell # * Use `bg-transform-footer` to clear the footer after 1 second # * Use `bg-cancel` to ignore currently running background transform actions fzf --multi --list-border \ --bind 'enter:execute-silent(echo -n {+} | pbcopy)+bell' \ --bind 'enter:+transform-footer(echo Copied {} to clipboard)' \ --bind 'enter:+bg-cancel+bg-transform-footer(sleep 1)'
This commit is contained in:
15
CHANGELOG.md
15
CHANGELOG.md
@@ -33,8 +33,19 @@ CHANGELOG
|
|||||||
seq 10000 | fzf --preview "awk '{sum += \$1} END {print sum}' {*f}"
|
seq 10000 | fzf --preview "awk '{sum += \$1} END {print sum}' {*f}"
|
||||||
```
|
```
|
||||||
- Use this with caution, as it can make fzf sluggish for large lists.
|
- Use this with caution, as it can make fzf sluggish for large lists.
|
||||||
- Added background variants of transform actions with `bg-` prefix that run asynchronously in the background
|
- Added asynchronous transform actions with `bg-` prefix that run asynchronously in the background, along with `bg-cancel` action to ignore currently running `bg-transform` actions.
|
||||||
```sh
|
```sh
|
||||||
|
# Implement popup that disappears after 1 second
|
||||||
|
# * Use footer as the popup
|
||||||
|
# * Use `bell` to ring the terminal bell
|
||||||
|
# * Use `bg-transform-footer` to clear the footer after 1 second
|
||||||
|
# * Use `bg-cancel` to ignore currently running background transform actions
|
||||||
|
fzf --multi --list-border \
|
||||||
|
--bind 'enter:execute-silent(echo -n {+} | pbcopy)+bell' \
|
||||||
|
--bind 'enter:+transform-footer(echo Copied {} to clipboard)' \
|
||||||
|
--bind 'enter:+bg-cancel+bg-transform-footer(sleep 1)'
|
||||||
|
|
||||||
|
# It's okay for the commands to take a little while because they run in the background
|
||||||
GETTER='curl -s http://metaphorpsum.com/sentences/1'
|
GETTER='curl -s http://metaphorpsum.com/sentences/1'
|
||||||
fzf --style full --border --preview : \
|
fzf --style full --border --preview : \
|
||||||
--bind "focus:bg-transform-header:$GETTER" \
|
--bind "focus:bg-transform-header:$GETTER" \
|
||||||
@@ -48,6 +59,8 @@ CHANGELOG
|
|||||||
--bind "focus:+bg-transform-ghost:$GETTER" \
|
--bind "focus:+bg-transform-ghost:$GETTER" \
|
||||||
--bind "focus:+bg-transform-prompt:$GETTER"
|
--bind "focus:+bg-transform-prompt:$GETTER"
|
||||||
```
|
```
|
||||||
|
- SSH completion enhancements by @akinomyoga
|
||||||
|
- Bug fixes and improvements
|
||||||
|
|
||||||
0.62.0
|
0.62.0
|
||||||
------
|
------
|
||||||
|
|||||||
@@ -128,49 +128,50 @@ func _() {
|
|||||||
_ = x[actBgTransformPrompt-117]
|
_ = x[actBgTransformPrompt-117]
|
||||||
_ = x[actBgTransformQuery-118]
|
_ = x[actBgTransformQuery-118]
|
||||||
_ = x[actBgTransformSearch-119]
|
_ = x[actBgTransformSearch-119]
|
||||||
_ = x[actSearch-120]
|
_ = x[actBgCancel-120]
|
||||||
_ = x[actPreview-121]
|
_ = x[actSearch-121]
|
||||||
_ = x[actPreviewTop-122]
|
_ = x[actPreview-122]
|
||||||
_ = x[actPreviewBottom-123]
|
_ = x[actPreviewTop-123]
|
||||||
_ = x[actPreviewUp-124]
|
_ = x[actPreviewBottom-124]
|
||||||
_ = x[actPreviewDown-125]
|
_ = x[actPreviewUp-125]
|
||||||
_ = x[actPreviewPageUp-126]
|
_ = x[actPreviewDown-126]
|
||||||
_ = x[actPreviewPageDown-127]
|
_ = x[actPreviewPageUp-127]
|
||||||
_ = x[actPreviewHalfPageUp-128]
|
_ = x[actPreviewPageDown-128]
|
||||||
_ = x[actPreviewHalfPageDown-129]
|
_ = x[actPreviewHalfPageUp-129]
|
||||||
_ = x[actPrevHistory-130]
|
_ = x[actPreviewHalfPageDown-130]
|
||||||
_ = x[actPrevSelected-131]
|
_ = x[actPrevHistory-131]
|
||||||
_ = x[actPrint-132]
|
_ = x[actPrevSelected-132]
|
||||||
_ = x[actPut-133]
|
_ = x[actPrint-133]
|
||||||
_ = x[actNextHistory-134]
|
_ = x[actPut-134]
|
||||||
_ = x[actNextSelected-135]
|
_ = x[actNextHistory-135]
|
||||||
_ = x[actExecute-136]
|
_ = x[actNextSelected-136]
|
||||||
_ = x[actExecuteSilent-137]
|
_ = x[actExecute-137]
|
||||||
_ = x[actExecuteMulti-138]
|
_ = x[actExecuteSilent-138]
|
||||||
_ = x[actSigStop-139]
|
_ = x[actExecuteMulti-139]
|
||||||
_ = x[actFirst-140]
|
_ = x[actSigStop-140]
|
||||||
_ = x[actLast-141]
|
_ = x[actFirst-141]
|
||||||
_ = x[actReload-142]
|
_ = x[actLast-142]
|
||||||
_ = x[actReloadSync-143]
|
_ = x[actReload-143]
|
||||||
_ = x[actDisableSearch-144]
|
_ = x[actReloadSync-144]
|
||||||
_ = x[actEnableSearch-145]
|
_ = x[actDisableSearch-145]
|
||||||
_ = x[actSelect-146]
|
_ = x[actEnableSearch-146]
|
||||||
_ = x[actDeselect-147]
|
_ = x[actSelect-147]
|
||||||
_ = x[actUnbind-148]
|
_ = x[actDeselect-148]
|
||||||
_ = x[actRebind-149]
|
_ = x[actUnbind-149]
|
||||||
_ = x[actToggleBind-150]
|
_ = x[actRebind-150]
|
||||||
_ = x[actBecome-151]
|
_ = x[actToggleBind-151]
|
||||||
_ = x[actShowHeader-152]
|
_ = x[actBecome-152]
|
||||||
_ = x[actHideHeader-153]
|
_ = x[actShowHeader-153]
|
||||||
_ = x[actBell-154]
|
_ = x[actHideHeader-154]
|
||||||
_ = x[actExclude-155]
|
_ = x[actBell-155]
|
||||||
_ = x[actExcludeMulti-156]
|
_ = x[actExclude-156]
|
||||||
_ = x[actAsync-157]
|
_ = x[actExcludeMulti-157]
|
||||||
|
_ = x[actAsync-158]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
|
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
|
||||||
|
|
||||||
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 249, 269, 283, 298, 313, 333, 353, 372, 390, 404, 416, 432, 448, 469, 491, 506, 520, 534, 547, 564, 572, 585, 601, 613, 621, 635, 649, 660, 671, 689, 706, 713, 732, 744, 758, 767, 782, 794, 807, 818, 829, 841, 855, 876, 891, 904, 922, 938, 953, 967, 979, 991, 1008, 1015, 1020, 1029, 1040, 1051, 1064, 1079, 1090, 1103, 1118, 1125, 1138, 1151, 1168, 1183, 1196, 1210, 1224, 1240, 1260, 1272, 1295, 1312, 1330, 1348, 1371, 1394, 1416, 1437, 1452, 1471, 1495, 1513, 1530, 1548, 1562, 1587, 1606, 1626, 1646, 1671, 1696, 1720, 1743, 1760, 1781, 1807, 1827, 1846, 1866, 1875, 1885, 1898, 1914, 1926, 1940, 1956, 1974, 1994, 2016, 2030, 2045, 2053, 2059, 2073, 2088, 2098, 2114, 2129, 2139, 2147, 2154, 2163, 2176, 2192, 2207, 2216, 2227, 2236, 2245, 2258, 2267, 2280, 2293, 2300, 2310, 2325, 2333}
|
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 249, 269, 283, 298, 313, 333, 353, 372, 390, 404, 416, 432, 448, 469, 491, 506, 520, 534, 547, 564, 572, 585, 601, 613, 621, 635, 649, 660, 671, 689, 706, 713, 732, 744, 758, 767, 782, 794, 807, 818, 829, 841, 855, 876, 891, 904, 922, 938, 953, 967, 979, 991, 1008, 1015, 1020, 1029, 1040, 1051, 1064, 1079, 1090, 1103, 1118, 1125, 1138, 1151, 1168, 1183, 1196, 1210, 1224, 1240, 1260, 1272, 1295, 1312, 1330, 1348, 1371, 1394, 1416, 1437, 1452, 1471, 1495, 1513, 1530, 1548, 1562, 1587, 1606, 1626, 1646, 1671, 1696, 1720, 1743, 1760, 1781, 1807, 1827, 1846, 1866, 1877, 1886, 1896, 1909, 1925, 1937, 1951, 1967, 1985, 2005, 2027, 2041, 2056, 2064, 2070, 2084, 2099, 2109, 2125, 2140, 2150, 2158, 2165, 2174, 2187, 2203, 2218, 2227, 2238, 2247, 2256, 2269, 2278, 2291, 2304, 2311, 2321, 2336, 2344}
|
||||||
|
|
||||||
func (i actionType) String() string {
|
func (i actionType) String() string {
|
||||||
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
||||||
|
|||||||
@@ -1703,6 +1703,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
|||||||
appendAction(actExclude)
|
appendAction(actExclude)
|
||||||
case "exclude-multi":
|
case "exclude-multi":
|
||||||
appendAction(actExcludeMulti)
|
appendAction(actExcludeMulti)
|
||||||
|
case "bg-cancel":
|
||||||
|
appendAction(actBgCancel)
|
||||||
default:
|
default:
|
||||||
t := isExecuteAction(specLower)
|
t := isExecuteAction(specLower)
|
||||||
if t == actIgnore {
|
if t == actIgnore {
|
||||||
|
|||||||
@@ -230,6 +230,11 @@ type Status struct {
|
|||||||
Selected []StatusItem `json:"selected"`
|
Selected []StatusItem `json:"selected"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type versionedCallback struct {
|
||||||
|
version int64
|
||||||
|
callback func()
|
||||||
|
}
|
||||||
|
|
||||||
// Terminal represents terminal input/output
|
// Terminal represents terminal input/output
|
||||||
type Terminal struct {
|
type Terminal struct {
|
||||||
initDelay time.Duration
|
initDelay time.Duration
|
||||||
@@ -371,6 +376,7 @@ type Terminal struct {
|
|||||||
selected map[int32]selectedItem
|
selected map[int32]selectedItem
|
||||||
version int64
|
version int64
|
||||||
revision revision
|
revision revision
|
||||||
|
bgVersion int64
|
||||||
reqBox *util.EventBox
|
reqBox *util.EventBox
|
||||||
initialPreviewOpts previewOpts
|
initialPreviewOpts previewOpts
|
||||||
previewOpts previewOpts
|
previewOpts previewOpts
|
||||||
@@ -388,7 +394,7 @@ type Terminal struct {
|
|||||||
startChan chan fitpad
|
startChan chan fitpad
|
||||||
killChan chan bool
|
killChan chan bool
|
||||||
serverInputChan chan []*action
|
serverInputChan chan []*action
|
||||||
callbackChan chan func()
|
callbackChan chan versionedCallback
|
||||||
bgQueue map[action][]func()
|
bgQueue map[action][]func()
|
||||||
bgSemaphore chan struct{}
|
bgSemaphore chan struct{}
|
||||||
bgSemaphores map[action]chan struct{}
|
bgSemaphores map[action]chan struct{}
|
||||||
@@ -601,6 +607,8 @@ const (
|
|||||||
actBgTransformQuery
|
actBgTransformQuery
|
||||||
actBgTransformSearch
|
actBgTransformSearch
|
||||||
|
|
||||||
|
actBgCancel
|
||||||
|
|
||||||
actSearch
|
actSearch
|
||||||
actPreview
|
actPreview
|
||||||
actPreviewTop
|
actPreviewTop
|
||||||
@@ -1038,7 +1046,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
startChan: make(chan fitpad, 1),
|
startChan: make(chan fitpad, 1),
|
||||||
killChan: make(chan bool),
|
killChan: make(chan bool),
|
||||||
serverInputChan: make(chan []*action, 100),
|
serverInputChan: make(chan []*action, 100),
|
||||||
callbackChan: make(chan func(), maxBgProcesses),
|
callbackChan: make(chan versionedCallback, maxBgProcesses),
|
||||||
bgQueue: make(map[action][]func()),
|
bgQueue: make(map[action][]func()),
|
||||||
bgSemaphore: make(chan struct{}, maxBgProcesses),
|
bgSemaphore: make(chan struct{}, maxBgProcesses),
|
||||||
bgSemaphores: make(map[action]chan struct{}),
|
bgSemaphores: make(map[action]chan struct{}),
|
||||||
@@ -4360,10 +4368,10 @@ func (t *Terminal) captureLines(template string) string {
|
|||||||
func (t *Terminal) captureAsync(a action, firstLineOnly bool, callback func(string)) {
|
func (t *Terminal) captureAsync(a action, firstLineOnly bool, callback func(string)) {
|
||||||
_, list := t.buildPlusList(a.a, false)
|
_, list := t.buildPlusList(a.a, false)
|
||||||
command, tempFiles := t.replacePlaceholder(a.a, false, string(t.input), list)
|
command, tempFiles := t.replacePlaceholder(a.a, false, string(t.input), list)
|
||||||
item := func() {
|
version := t.bgVersion
|
||||||
cmd := t.executor.ExecCommand(command, false)
|
cmd := t.executor.ExecCommand(command, false)
|
||||||
cmd.Env = t.environ()
|
cmd.Env = t.environ()
|
||||||
|
item := func() {
|
||||||
out, _ := cmd.StdoutPipe()
|
out, _ := cmd.StdoutPipe()
|
||||||
reader := bufio.NewReader(out)
|
reader := bufio.NewReader(out)
|
||||||
var output string
|
var output string
|
||||||
@@ -4379,7 +4387,7 @@ func (t *Terminal) captureAsync(a action, firstLineOnly bool, callback func(stri
|
|||||||
}
|
}
|
||||||
removeFiles(tempFiles)
|
removeFiles(tempFiles)
|
||||||
|
|
||||||
t.callbackChan <- func() { callback(output) }
|
t.callbackChan <- versionedCallback{version, func() { callback(output) }}
|
||||||
}
|
}
|
||||||
queue, prs := t.bgQueue[a]
|
queue, prs := t.bgQueue[a]
|
||||||
if !prs {
|
if !prs {
|
||||||
@@ -5318,12 +5326,16 @@ func (t *Terminal) Loop() error {
|
|||||||
case callback := <-t.callbackChan:
|
case callback := <-t.callbackChan:
|
||||||
event = tui.Invalid.AsEvent()
|
event = tui.Invalid.AsEvent()
|
||||||
actions = append(actions, &action{t: actAsync})
|
actions = append(actions, &action{t: actAsync})
|
||||||
callbacks = append(callbacks, callback)
|
if callback.version == t.bgVersion {
|
||||||
|
callbacks = append(callbacks, callback.callback)
|
||||||
|
}
|
||||||
DrainCallback:
|
DrainCallback:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case callback = <-t.callbackChan:
|
case callback = <-t.callbackChan:
|
||||||
callbacks = append(callbacks, callback)
|
if callback.version == t.bgVersion {
|
||||||
|
callbacks = append(callbacks, callback.callback)
|
||||||
|
}
|
||||||
continue DrainCallback
|
continue DrainCallback
|
||||||
default:
|
default:
|
||||||
break DrainCallback
|
break DrainCallback
|
||||||
@@ -5696,6 +5708,8 @@ func (t *Terminal) Loop() error {
|
|||||||
doActions(actions)
|
doActions(actions)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
case actBgCancel:
|
||||||
|
t.bgVersion++
|
||||||
case actChangePrompt:
|
case actChangePrompt:
|
||||||
t.promptString = a.a
|
t.promptString = a.a
|
||||||
t.prompt, t.promptLen = t.parsePrompt(a.a)
|
t.prompt, t.promptLen = t.parsePrompt(a.a)
|
||||||
|
|||||||
@@ -1964,4 +1964,21 @@ class TestCore < TestInteractive
|
|||||||
elapsed = Time.now - time
|
elapsed = Time.now - time
|
||||||
assert elapsed < 2
|
assert elapsed < 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_bg_cancel
|
||||||
|
tmux.send_keys %(seq 0 1 | #{FZF} --bind 'space:bg-cancel+bg-transform-header(sleep {}; echo [{}])'), :Enter
|
||||||
|
tmux.until { assert_equal 2, it.match_count }
|
||||||
|
tmux.send_keys '1'
|
||||||
|
tmux.until { assert_equal 1, it.match_count }
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { assert_equal 2, it.match_count }
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert lines.any_include?('[0]') }
|
||||||
|
sleep 2
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert lines.any_include?('[0]')
|
||||||
|
refute lines.any_include?('[1]')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user