Merge remote-tracking branch 'upstream/master'

pull/2131/head
MenkeTechnologies 2 months ago
commit de6eda107e

@ -1,6 +1,25 @@
CHANGELOG
=========
0.49.0
------
- Added two environment variables exported to the child processes
- `FZF_PREVIEW_LABEL`
- `FZF_BORDER_LABEL`
```sh
# Use the current value of $FZF_PREVIEW_LABEL to determine which actions to perform
git ls-files |
fzf --header 'Press CTRL-P to change preview mode' \
--bind='ctrl-p:transform:[[ $FZF_PREVIEW_LABEL =~ cat ]] \
&& echo "change-preview(git log --color=always \{})+change-preview-label([[ log ]])" \
|| echo "change-preview(bat --color=always \{})+change-preview-label([[ cat ]])"'
```
- Renamed `track` action to `track-current` to highlight the difference between the global tracking state set by `--track` and a one-off tracking action
- `track` is still available as an alias
- Added `untrack-current` and `toggle-track-current` actions
- `*-current` actions are no-op when the global tracking state is set
- Bug fixes
0.48.1
------
- CTRL-T and ALT-C bindings can be disabled by setting `FZF_CTRL_T_COMMAND` and `FZF_ALT_C_COMMAND` to empty strings respectively when sourcing the script

@ -975,6 +975,10 @@ fzf exports the following environment variables to its child processes.
.br
.BR FZF_PROMPT " Prompt string"
.br
.BR FZF_PREVIEW_LABEL " Preview label string"
.br
.BR FZF_BORDER_LABEL " Border label string"
.br
.BR FZF_ACTION " The name of the last action performed"
.br
.BR FZF_PORT " Port number when --listen option is used"
@ -1326,9 +1330,10 @@ A key or an event can be bound to one or more of the following actions.
\fBtoggle-preview-wrap\fR
\fBtoggle-search\fR (toggle search functionality)
\fBtoggle-sort\fR
\fBtoggle-track\fR
\fBtoggle-track\fR (toggle global tracking option (\fB--track\fR))
\fBtoggle-track-current\fR (toggle tracking of the current item)
\fBtoggle+up\fR \fIbtab (shift-tab)\fR
\fBtrack\fR (track the current item; automatically disabled if focus changes)
\fBtrack-current\fR (track the current item; automatically disabled if focus changes)
\fBtransform(...)\fR (transform states using the output of an external command)
\fBtransform-border-label(...)\fR (transform border label using an external command)
\fBtransform-header(...)\fR (transform header using an external command)
@ -1338,6 +1343,7 @@ A key or an event can be bound to one or more of the following actions.
\fBunbind(...)\fR (unbind bindings)
\fBunix-line-discard\fR \fIctrl-u\fR
\fBunix-word-rubout\fR \fIctrl-w\fR
\fBuntrack-current\fR (stop tracking the current item; no-op if global tracking is enabled)
\fBup\fR \fIctrl-k ctrl-p up\fR
\fByank\fR \fIctrl-y\fR

@ -308,6 +308,7 @@ _fzf_handle_dynamic_completion() {
eval "$orig_complete"
fi
fi
[[ $ret -eq 0 ]] && return 124
return $ret
fi
}

@ -54,72 +54,74 @@ func _() {
_ = x[actToggleIn-43]
_ = x[actToggleOut-44]
_ = x[actToggleTrack-45]
_ = x[actToggleHeader-46]
_ = x[actTrack-47]
_ = x[actDown-48]
_ = x[actUp-49]
_ = x[actPageUp-50]
_ = x[actPageDown-51]
_ = x[actPosition-52]
_ = x[actHalfPageUp-53]
_ = x[actHalfPageDown-54]
_ = x[actOffsetUp-55]
_ = x[actOffsetDown-56]
_ = x[actJump-57]
_ = x[actJumpAccept-58]
_ = x[actPrintQuery-59]
_ = x[actRefreshPreview-60]
_ = x[actReplaceQuery-61]
_ = x[actToggleSort-62]
_ = x[actShowPreview-63]
_ = x[actHidePreview-64]
_ = x[actTogglePreview-65]
_ = x[actTogglePreviewWrap-66]
_ = x[actTransform-67]
_ = x[actTransformBorderLabel-68]
_ = x[actTransformHeader-69]
_ = x[actTransformPreviewLabel-70]
_ = x[actTransformPrompt-71]
_ = x[actTransformQuery-72]
_ = x[actPreview-73]
_ = x[actChangePreview-74]
_ = x[actChangePreviewWindow-75]
_ = x[actPreviewTop-76]
_ = x[actPreviewBottom-77]
_ = x[actPreviewUp-78]
_ = x[actPreviewDown-79]
_ = x[actPreviewPageUp-80]
_ = x[actPreviewPageDown-81]
_ = x[actPreviewHalfPageUp-82]
_ = x[actPreviewHalfPageDown-83]
_ = x[actPrevHistory-84]
_ = x[actPrevSelected-85]
_ = x[actPut-86]
_ = x[actNextHistory-87]
_ = x[actNextSelected-88]
_ = x[actExecute-89]
_ = x[actExecuteSilent-90]
_ = x[actExecuteMulti-91]
_ = x[actSigStop-92]
_ = x[actFirst-93]
_ = x[actLast-94]
_ = x[actReload-95]
_ = x[actReloadSync-96]
_ = x[actDisableSearch-97]
_ = x[actEnableSearch-98]
_ = x[actSelect-99]
_ = x[actDeselect-100]
_ = x[actUnbind-101]
_ = x[actRebind-102]
_ = x[actBecome-103]
_ = x[actResponse-104]
_ = x[actShowHeader-105]
_ = x[actHideHeader-106]
_ = x[actToggleTrackCurrent-46]
_ = x[actToggleHeader-47]
_ = x[actTrackCurrent-48]
_ = x[actUntrackCurrent-49]
_ = x[actDown-50]
_ = x[actUp-51]
_ = x[actPageUp-52]
_ = x[actPageDown-53]
_ = x[actPosition-54]
_ = x[actHalfPageUp-55]
_ = x[actHalfPageDown-56]
_ = x[actOffsetUp-57]
_ = x[actOffsetDown-58]
_ = x[actJump-59]
_ = x[actJumpAccept-60]
_ = x[actPrintQuery-61]
_ = x[actRefreshPreview-62]
_ = x[actReplaceQuery-63]
_ = x[actToggleSort-64]
_ = x[actShowPreview-65]
_ = x[actHidePreview-66]
_ = x[actTogglePreview-67]
_ = x[actTogglePreviewWrap-68]
_ = x[actTransform-69]
_ = x[actTransformBorderLabel-70]
_ = x[actTransformHeader-71]
_ = x[actTransformPreviewLabel-72]
_ = x[actTransformPrompt-73]
_ = x[actTransformQuery-74]
_ = x[actPreview-75]
_ = x[actChangePreview-76]
_ = x[actChangePreviewWindow-77]
_ = x[actPreviewTop-78]
_ = x[actPreviewBottom-79]
_ = x[actPreviewUp-80]
_ = x[actPreviewDown-81]
_ = x[actPreviewPageUp-82]
_ = x[actPreviewPageDown-83]
_ = x[actPreviewHalfPageUp-84]
_ = x[actPreviewHalfPageDown-85]
_ = x[actPrevHistory-86]
_ = x[actPrevSelected-87]
_ = x[actPut-88]
_ = x[actNextHistory-89]
_ = x[actNextSelected-90]
_ = x[actExecute-91]
_ = x[actExecuteSilent-92]
_ = x[actExecuteMulti-93]
_ = x[actSigStop-94]
_ = x[actFirst-95]
_ = x[actLast-96]
_ = x[actReload-97]
_ = x[actReloadSync-98]
_ = x[actDisableSearch-99]
_ = x[actEnableSearch-100]
_ = x[actSelect-101]
_ = x[actDeselect-102]
_ = x[actUnbind-103]
_ = x[actRebind-104]
_ = x[actBecome-105]
_ = x[actResponse-106]
_ = x[actShowHeader-107]
_ = x[actHideHeader-108]
}
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleHeaderactTrackactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader"
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 263, 278, 292, 306, 319, 336, 344, 357, 373, 385, 399, 413, 424, 435, 453, 470, 477, 496, 508, 522, 531, 546, 558, 571, 582, 593, 605, 619, 634, 642, 649, 654, 663, 674, 685, 698, 713, 724, 737, 744, 757, 770, 787, 802, 815, 829, 843, 859, 879, 891, 914, 932, 956, 974, 991, 1001, 1017, 1039, 1052, 1068, 1080, 1094, 1110, 1128, 1148, 1170, 1184, 1199, 1205, 1219, 1234, 1244, 1260, 1275, 1285, 1293, 1300, 1309, 1322, 1338, 1353, 1362, 1373, 1382, 1391, 1400, 1411, 1424, 1437}
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 263, 278, 292, 306, 319, 336, 344, 357, 373, 385, 399, 413, 424, 435, 453, 470, 477, 496, 508, 522, 531, 546, 558, 571, 582, 593, 605, 619, 640, 655, 670, 687, 694, 699, 708, 719, 730, 743, 758, 769, 782, 789, 802, 815, 832, 847, 860, 874, 888, 904, 924, 936, 959, 977, 1001, 1019, 1036, 1046, 1062, 1084, 1097, 1113, 1125, 1139, 1155, 1173, 1193, 1215, 1229, 1244, 1250, 1264, 1279, 1289, 1305, 1320, 1330, 1338, 1345, 1354, 1367, 1383, 1398, 1407, 1418, 1427, 1436, 1445, 1456, 1469, 1482}
func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) {

@ -245,11 +245,8 @@ func Run(opts *Options, version string, revision string) {
delay := true
ticks++
input := func() []rune {
reloaded := snapshotRevision != inputRevision
paused, input := terminal.Input()
if reloaded && paused {
query = []rune{}
} else if !paused {
if !paused {
query = input
}
return query
@ -278,6 +275,9 @@ func Run(opts *Options, version string, revision string) {
useSnapshot = false
}
if !useSnapshot {
if snapshotRevision != inputRevision {
query = []rune{}
}
snapshot, count = chunkList.Snapshot()
snapshotRevision = inputRevision
}
@ -319,10 +319,13 @@ func Run(opts *Options, version string, revision string) {
break
}
if !useSnapshot {
newSnapshot, _ := chunkList.Snapshot()
newSnapshot, newCount := chunkList.Snapshot()
// We want to avoid showing empty list when reload is triggered
// and the query string is changed at the same time i.e. command != nil && changed
if command == nil || len(newSnapshot) > 0 {
if command == nil || newCount > 0 {
if snapshotRevision != inputRevision {
query = []rune{}
}
snapshot = newSnapshot
snapshotRevision = inputRevision
}

@ -1211,14 +1211,18 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actToggleSearch)
case "toggle-track":
appendAction(actToggleTrack)
case "toggle-track-current":
appendAction(actToggleTrackCurrent)
case "toggle-header":
appendAction(actToggleHeader)
case "show-header":
appendAction(actShowHeader)
case "hide-header":
appendAction(actHideHeader)
case "track":
appendAction(actTrack)
case "track", "track-current":
appendAction(actTrackCurrent)
case "untrack-current":
appendAction(actUntrackCurrent)
case "select":
appendAction(actSelect)
case "select-all":

@ -394,8 +394,10 @@ const (
actToggleIn
actToggleOut
actToggleTrack
actToggleTrackCurrent
actToggleHeader
actTrack
actTrackCurrent
actUntrackCurrent
actDown
actUp
actPageUp
@ -850,6 +852,8 @@ func (t *Terminal) environ() []string {
env = append(env, "FZF_QUERY="+string(t.input))
env = append(env, "FZF_ACTION="+t.lastAction.Name())
env = append(env, "FZF_PROMPT="+string(t.promptString))
env = append(env, "FZF_PREVIEW_LABEL="+t.previewLabelOpts.label)
env = append(env, "FZF_BORDER_LABEL="+t.borderLabelOpts.label)
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length()))
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
@ -1645,8 +1649,11 @@ func (t *Terminal) printInfo() {
output += " -S"
}
}
if t.track != trackDisabled {
switch t.track {
case trackEnabled:
output += " +T"
case trackCurrent:
output += " +t"
}
if t.multi > 0 {
if t.multi == maxMulti {
@ -3471,11 +3478,13 @@ func (t *Terminal) Loop() {
req(reqHeader)
}
case actChangeBorderLabel:
t.borderLabelOpts.label = a.a
if t.border != nil {
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(a.a, &tui.ColBorderLabel, false)
req(reqRedrawBorderLabel)
}
case actChangePreviewLabel:
t.previewLabelOpts.label = a.a
if t.pborder != nil {
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(a.a, &tui.ColPreviewLabel, false)
req(reqRedrawPreviewLabel)
@ -3485,14 +3494,16 @@ func (t *Terminal) Loop() {
actions := parseSingleActionList(strings.Trim(body, "\r\n"), func(message string) {})
return doActions(actions)
case actTransformBorderLabel:
label := t.executeCommand(a.a, false, true, true, true)
t.borderLabelOpts.label = label
if t.border != nil {
label := t.executeCommand(a.a, false, true, true, true)
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false)
req(reqRedrawBorderLabel)
}
case actTransformPreviewLabel:
label := t.executeCommand(a.a, false, true, true, true)
t.previewLabelOpts.label = label
if t.pborder != nil {
label := t.executeCommand(a.a, false, true, true, true)
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false)
req(reqRedrawPreviewLabel)
}
@ -3778,6 +3789,14 @@ func (t *Terminal) Loop() {
t.track = trackEnabled
}
req(reqInfo)
case actToggleTrackCurrent:
switch t.track {
case trackCurrent:
t.track = trackDisabled
case trackDisabled:
t.track = trackCurrent
}
req(reqInfo)
case actShowHeader:
t.headerVisible = true
req(reqList, reqInfo, reqPrompt, reqHeader)
@ -3787,11 +3806,16 @@ func (t *Terminal) Loop() {
case actToggleHeader:
t.headerVisible = !t.headerVisible
req(reqList, reqInfo, reqPrompt, reqHeader)
case actTrack:
case actTrackCurrent:
if t.track == trackDisabled {
t.track = trackCurrent
}
req(reqInfo)
case actUntrackCurrent:
if t.track == trackCurrent {
t.track = trackDisabled
}
req(reqInfo)
case actEnableSearch:
t.paused = false
changed = true

@ -2463,6 +2463,84 @@ class TestGoFZF < TestBase
tmux.until { |lines| assert_equal 10, lines.match_count }
end
def test_reload_disabled_case1
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(sleep 2; seq 1000)'", :Enter
tmux.until do |lines|
assert_equal 100, lines.item_count
assert_equal 1, lines.match_count
end
tmux.send_keys :Space
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.send_keys :BSpace
tmux.until { |lines| assert_equal 0, lines.match_count }
tmux.until { |lines| assert_equal 1000, lines.match_count }
end
def test_reload_disabled_case2
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload-sync(sleep 2; seq 1000)'", :Enter
tmux.until do |lines|
assert_equal 100, lines.item_count
assert_equal 1, lines.match_count
end
tmux.send_keys :Space
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.send_keys :BSpace
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.until { |lines| assert_equal 1000, lines.match_count }
end
def test_reload_disabled_case3
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(sleep 2; seq 1000)+backward-delete-char'", :Enter
tmux.until do |lines|
assert_equal 100, lines.item_count
assert_equal 1, lines.match_count
end
tmux.send_keys :Space
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.send_keys :BSpace
tmux.until { |lines| assert_equal 0, lines.match_count }
tmux.until { |lines| assert_equal 1000, lines.match_count }
end
def test_reload_disabled_case4
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload-sync(sleep 2; seq 1000)+backward-delete-char'", :Enter
tmux.until do |lines|
assert_equal 100, lines.item_count
assert_equal 1, lines.match_count
end
tmux.send_keys :Space
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.send_keys :BSpace
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.until { |lines| assert_equal 1000, lines.match_count }
end
def test_reload_disabled_case5
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(echo xx; sleep 2; seq 1000)'", :Enter
tmux.until do |lines|
assert_equal 100, lines.item_count
assert_equal 1, lines.match_count
end
tmux.send_keys :Space
tmux.until do |lines|
assert_equal 1, lines.item_count
assert_equal 1, lines.match_count
end
tmux.send_keys :BSpace
tmux.until { |lines| assert_equal 1001, lines.match_count }
end
def test_reload_disabled_case6
tmux.send_keys "seq 1000 | #{FZF} --disabled --bind 'change:reload:sleep 0.5; seq {q}'", :Enter
tmux.until { |lines| assert_equal 1000, lines.match_count }
tmux.send_keys '9'
tmux.until { |lines| assert_equal 9, lines.match_count }
tmux.send_keys '9'
tmux.until { |lines| assert_equal 99, lines.match_count }
# TODO: How do we verify if an intermediate empty list is not shown?
end
def test_scroll_off
tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
tmux.until { |lines| assert_equal 1000, lines.item_count }
@ -2799,6 +2877,27 @@ class TestGoFZF < TestBase
end
end
def test_labels_variables
tmux.send_keys ': | fzf --border --border-label foobar --preview "echo \$FZF_BORDER_LABEL // \$FZF_PREVIEW_LABEL" --preview-label barfoo --bind "space:change-border-label(barbaz)+change-preview-label(bazbar)+refresh-preview,enter:transform-border-label(echo 123)+transform-preview-label(echo 456)+refresh-preview"', :Enter
tmux.until do
assert_includes(_1[0], '─foobar─')
assert_includes(_1[1], '─barfoo─')
assert_includes(_1[2], ' foobar // barfoo ')
end
tmux.send_keys :Space
tmux.until do
assert_includes(_1[0], '─barbaz─')
assert_includes(_1[1], '─bazbar─')
assert_includes(_1[2], ' barbaz // bazbar ')
end
tmux.send_keys :Enter
tmux.until do
assert_includes(_1[0], '─123─')
assert_includes(_1[1], '─456─')
assert_includes(_1[2], ' 123 // 456 ')
end
end
def test_info_separator_unicode
tmux.send_keys 'seq 100 | fzf -q55', :Enter
tmux.until { assert_includes(_1[-2], ' 1/100 ─') }
@ -3002,7 +3101,7 @@ class TestGoFZF < TestBase
end
tmux.send_keys :t
tmux.until do |lines|
assert_includes lines[-2], '+T'
assert_includes lines[-2], '+t'
end
tmux.send_keys :BSpace
tmux.until do |lines|
@ -3014,7 +3113,7 @@ class TestGoFZF < TestBase
tmux.send_keys '4'
tmux.until do |lines|
assert_equal 28, lines.match_count
refute_includes lines[-2], '+T'
refute_includes lines[-2], '+t'
end
tmux.send_keys :BSpace
tmux.until do |lines|
@ -3023,11 +3122,11 @@ class TestGoFZF < TestBase
end
tmux.send_keys :t
tmux.until do |lines|
assert_includes lines[-2], '+T'
assert_includes lines[-2], '+t'
end
tmux.send_keys :Up
tmux.until do |lines|
refute_includes lines[-2], '+T'
refute_includes lines[-2], '+t'
end
end
@ -3307,7 +3406,10 @@ module CompletionTest
tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
tmux.send_keys 'foobar$'
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.until do |lines|
assert_equal 1, lines.match_count
assert lines.any_include?('> /tmp/fzf test/foobar')
end
tmux.send_keys :Enter
tmux.until(true) { |lines| assert_equal 'cat /tmp/fzf\ test/foobar', lines[-1] }

Loading…
Cancel
Save