Add 'change-multi' action

Close #3754
pull/3746/head
Junegunn Choi 3 weeks ago
parent 7f85beccb5
commit 608232568b
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627

@ -1,6 +1,14 @@
CHANGELOG CHANGELOG
========= =========
0.51.0
------
- Added `change-multi` action for dynamically changing `--multi` option
- `change-multi` - enable multi-select mode with no limit
- `change-multi(NUM)` - enable multi-select mode with a limit
- `change-multi(0)` - disable multi-select mode
- Bug fixes and improvements
0.50.0 0.50.0
------ ------
- Search performance optimization. You can observe 50%+ improvement in some scenarios. - Search performance optimization. You can observe 50%+ improvement in some scenarios.

@ -1282,6 +1282,8 @@ A key or an event can be bound to one or more of the following actions.
\fBcancel\fR (clear query string if not empty, abort fzf otherwise) \fBcancel\fR (clear query string if not empty, abort fzf otherwise)
\fBchange-border-label(...)\fR (change \fB--border-label\fR to the given string) \fBchange-border-label(...)\fR (change \fB--border-label\fR to the given string)
\fBchange-header(...)\fR (change header to the given string; doesn't affect \fB--header-lines\fR) \fBchange-header(...)\fR (change header to the given string; doesn't affect \fB--header-lines\fR)
\fBchange-multi\fR (enable multi-select mode with no limit)
\fBchange-multi(...)\fR (enable multi-select mode with a limit or disable it with 0)
\fBchange-preview(...)\fR (change \fB--preview\fR option) \fBchange-preview(...)\fR (change \fB--preview\fR option)
\fBchange-preview-label(...)\fR (change \fB--preview-label\fR to the given string) \fBchange-preview-label(...)\fR (change \fB--preview-label\fR to the given string)
\fBchange-preview-window(...)\fR (change \fB--preview-window\fR option; rotate through the multiple option sets separated by '|') \fBchange-preview-window(...)\fR (change \fB--preview-window\fR option; rotate through the multiple option sets separated by '|')

@ -26,102 +26,103 @@ func _() {
_ = x[actCancel-15] _ = x[actCancel-15]
_ = x[actChangeBorderLabel-16] _ = x[actChangeBorderLabel-16]
_ = x[actChangeHeader-17] _ = x[actChangeHeader-17]
_ = x[actChangePreviewLabel-18] _ = x[actChangeMulti-18]
_ = x[actChangePrompt-19] _ = x[actChangePreviewLabel-19]
_ = x[actChangeQuery-20] _ = x[actChangePrompt-20]
_ = x[actClearScreen-21] _ = x[actChangeQuery-21]
_ = x[actClearQuery-22] _ = x[actClearScreen-22]
_ = x[actClearSelection-23] _ = x[actClearQuery-23]
_ = x[actClose-24] _ = x[actClearSelection-24]
_ = x[actDeleteChar-25] _ = x[actClose-25]
_ = x[actDeleteCharEof-26] _ = x[actDeleteChar-26]
_ = x[actEndOfLine-27] _ = x[actDeleteCharEof-27]
_ = x[actForwardChar-28] _ = x[actEndOfLine-28]
_ = x[actForwardWord-29] _ = x[actForwardChar-29]
_ = x[actKillLine-30] _ = x[actForwardWord-30]
_ = x[actKillWord-31] _ = x[actKillLine-31]
_ = x[actUnixLineDiscard-32] _ = x[actKillWord-32]
_ = x[actUnixWordRubout-33] _ = x[actUnixLineDiscard-33]
_ = x[actYank-34] _ = x[actUnixWordRubout-34]
_ = x[actBackwardKillWord-35] _ = x[actYank-35]
_ = x[actSelectAll-36] _ = x[actBackwardKillWord-36]
_ = x[actDeselectAll-37] _ = x[actSelectAll-37]
_ = x[actToggle-38] _ = x[actDeselectAll-38]
_ = x[actToggleSearch-39] _ = x[actToggle-39]
_ = x[actToggleAll-40] _ = x[actToggleSearch-40]
_ = x[actToggleDown-41] _ = x[actToggleAll-41]
_ = x[actToggleUp-42] _ = x[actToggleDown-42]
_ = x[actToggleIn-43] _ = x[actToggleUp-43]
_ = x[actToggleOut-44] _ = x[actToggleIn-44]
_ = x[actToggleTrack-45] _ = x[actToggleOut-45]
_ = x[actToggleTrackCurrent-46] _ = x[actToggleTrack-46]
_ = x[actToggleHeader-47] _ = x[actToggleTrackCurrent-47]
_ = x[actTrackCurrent-48] _ = x[actToggleHeader-48]
_ = x[actUntrackCurrent-49] _ = x[actTrackCurrent-49]
_ = x[actDown-50] _ = x[actUntrackCurrent-50]
_ = x[actUp-51] _ = x[actDown-51]
_ = x[actPageUp-52] _ = x[actUp-52]
_ = x[actPageDown-53] _ = x[actPageUp-53]
_ = x[actPosition-54] _ = x[actPageDown-54]
_ = x[actHalfPageUp-55] _ = x[actPosition-55]
_ = x[actHalfPageDown-56] _ = x[actHalfPageUp-56]
_ = x[actOffsetUp-57] _ = x[actHalfPageDown-57]
_ = x[actOffsetDown-58] _ = x[actOffsetUp-58]
_ = x[actJump-59] _ = x[actOffsetDown-59]
_ = x[actJumpAccept-60] _ = x[actJump-60]
_ = x[actPrintQuery-61] _ = x[actJumpAccept-61]
_ = x[actRefreshPreview-62] _ = x[actPrintQuery-62]
_ = x[actReplaceQuery-63] _ = x[actRefreshPreview-63]
_ = x[actToggleSort-64] _ = x[actReplaceQuery-64]
_ = x[actShowPreview-65] _ = x[actToggleSort-65]
_ = x[actHidePreview-66] _ = x[actShowPreview-66]
_ = x[actTogglePreview-67] _ = x[actHidePreview-67]
_ = x[actTogglePreviewWrap-68] _ = x[actTogglePreview-68]
_ = x[actTransform-69] _ = x[actTogglePreviewWrap-69]
_ = x[actTransformBorderLabel-70] _ = x[actTransform-70]
_ = x[actTransformHeader-71] _ = x[actTransformBorderLabel-71]
_ = x[actTransformPreviewLabel-72] _ = x[actTransformHeader-72]
_ = x[actTransformPrompt-73] _ = x[actTransformPreviewLabel-73]
_ = x[actTransformQuery-74] _ = x[actTransformPrompt-74]
_ = x[actPreview-75] _ = x[actTransformQuery-75]
_ = x[actChangePreview-76] _ = x[actPreview-76]
_ = x[actChangePreviewWindow-77] _ = x[actChangePreview-77]
_ = x[actPreviewTop-78] _ = x[actChangePreviewWindow-78]
_ = x[actPreviewBottom-79] _ = x[actPreviewTop-79]
_ = x[actPreviewUp-80] _ = x[actPreviewBottom-80]
_ = x[actPreviewDown-81] _ = x[actPreviewUp-81]
_ = x[actPreviewPageUp-82] _ = x[actPreviewDown-82]
_ = x[actPreviewPageDown-83] _ = x[actPreviewPageUp-83]
_ = x[actPreviewHalfPageUp-84] _ = x[actPreviewPageDown-84]
_ = x[actPreviewHalfPageDown-85] _ = x[actPreviewHalfPageUp-85]
_ = x[actPrevHistory-86] _ = x[actPreviewHalfPageDown-86]
_ = x[actPrevSelected-87] _ = x[actPrevHistory-87]
_ = x[actPut-88] _ = x[actPrevSelected-88]
_ = x[actNextHistory-89] _ = x[actPut-89]
_ = x[actNextSelected-90] _ = x[actNextHistory-90]
_ = x[actExecute-91] _ = x[actNextSelected-91]
_ = x[actExecuteSilent-92] _ = x[actExecute-92]
_ = x[actExecuteMulti-93] _ = x[actExecuteSilent-93]
_ = x[actSigStop-94] _ = x[actExecuteMulti-94]
_ = x[actFirst-95] _ = x[actSigStop-95]
_ = x[actLast-96] _ = x[actFirst-96]
_ = x[actReload-97] _ = x[actLast-97]
_ = x[actReloadSync-98] _ = x[actReload-98]
_ = x[actDisableSearch-99] _ = x[actReloadSync-99]
_ = x[actEnableSearch-100] _ = x[actDisableSearch-100]
_ = x[actSelect-101] _ = x[actEnableSearch-101]
_ = x[actDeselect-102] _ = x[actSelect-102]
_ = x[actUnbind-103] _ = x[actDeselect-103]
_ = x[actRebind-104] _ = x[actUnbind-104]
_ = x[actBecome-105] _ = x[actRebind-105]
_ = x[actResponse-106] _ = x[actBecome-106]
_ = x[actShowHeader-107] _ = x[actResponse-107]
_ = x[actHideHeader-108] _ = x[actShowHeader-108]
_ = x[actHideHeader-109]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader" const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader"
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} var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 256, 277, 292, 306, 320, 333, 350, 358, 371, 387, 399, 413, 427, 438, 449, 467, 484, 491, 510, 522, 536, 545, 560, 572, 585, 596, 607, 619, 633, 654, 669, 684, 701, 708, 713, 722, 733, 744, 757, 772, 783, 796, 803, 816, 829, 846, 861, 874, 888, 902, 918, 938, 950, 973, 991, 1015, 1033, 1050, 1060, 1076, 1098, 1111, 1127, 1139, 1153, 1169, 1187, 1207, 1229, 1243, 1258, 1264, 1278, 1293, 1303, 1319, 1334, 1344, 1352, 1359, 1368, 1381, 1397, 1412, 1421, 1432, 1441, 1450, 1459, 1470, 1483, 1496}
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) {

@ -1055,7 +1055,7 @@ const (
func init() { func init() {
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:header|query|prompt|border-label|preview-label)|transform|change-preview-window|change-preview|(?:re|un)bind|pos|put)`) `(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:header|query|prompt|border-label|preview-label)|transform|change-(?:preview-window|preview|multi)|(?:re|un)bind|pos|put)`)
splitRegexp = regexp.MustCompile("[,:]+") splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
} }
@ -1306,6 +1306,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
if t == actIgnore { if t == actIgnore {
if specIndex == 0 && specLower == "" { if specIndex == 0 && specLower == "" {
actions = append(prevActions, actions...) actions = append(prevActions, actions...)
} else if specLower == "change-multi" {
appendAction(actChangeMulti)
} else { } else {
exit("unknown action: " + spec) exit("unknown action: " + spec)
} }
@ -1407,6 +1409,8 @@ func isExecuteAction(str string) actionType {
return actChangePrompt return actChangePrompt
case "change-query": case "change-query":
return actChangeQuery return actChangeQuery
case "change-multi":
return actChangeMulti
case "pos": case "pos":
return actPosition return actPosition
case "execute": case "execute":

@ -367,6 +367,7 @@ const (
actCancel actCancel
actChangeBorderLabel actChangeBorderLabel
actChangeHeader actChangeHeader
actChangeMulti
actChangePreviewLabel actChangePreviewLabel
actChangePrompt actChangePrompt
actChangeQuery actChangeQuery
@ -3489,6 +3490,19 @@ func (t *Terminal) Loop() {
} }
case actPrintQuery: case actPrintQuery:
req(reqPrintQuery) req(reqPrintQuery)
case actChangeMulti:
multi := t.multi
if a.a == "" {
multi = maxMulti
} else if n, e := strconv.Atoi(a.a); e == nil && n >= 0 {
multi = n
}
if t.multi > 0 && multi != t.multi {
t.selected = make(map[int32]selectedItem)
t.version++
}
t.multi = multi
req(reqList, reqInfo)
case actChangeQuery: case actChangeQuery:
t.input = []rune(a.a) t.input = []rune(a.a)
t.cx = len(t.input) t.cx = len(t.input)

@ -425,6 +425,25 @@ class TestGoFZF < TestBase
end end
end end
def test_multi_action
tmux.send_keys "seq 10 | #{FZF} --bind 'a:change-multi,b:change-multi(3),c:change-multi(xxx),d:change-multi(0)'", :Enter
tmux.until { |lines| assert_equal 10, lines.item_count }
tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 ') }
tmux.send_keys 'a'
tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 (0)') }
tmux.send_keys 'b'
tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 (0/3)') }
tmux.send_keys :BTab
tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 (1/3)') }
tmux.send_keys 'c'
tmux.send_keys :BTab
tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 (2/3)') }
tmux.send_keys 'd'
tmux.until do |lines|
assert lines[-2]&.start_with?(' 10/10 ') && !lines[-2]&.include?('(')
end
end
def test_with_nth def test_with_nth
[true, false].each do |multi| [true, false].each do |multi|
tmux.send_keys "(echo ' 1st 2nd 3rd/'; tmux.send_keys "(echo ' 1st 2nd 3rd/';

Loading…
Cancel
Save