diff --git a/CHANGELOG.md b/CHANGELOG.md index df2db318..51e9a603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ 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 ------ - Search performance optimization. You can observe 50%+ improvement in some scenarios. diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index 0d131c7a..458c6a5f 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -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) \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-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-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 '|') diff --git a/src/actiontype_string.go b/src/actiontype_string.go index 341c4bd4..a9d931d7 100644 --- a/src/actiontype_string.go +++ b/src/actiontype_string.go @@ -26,102 +26,103 @@ func _() { _ = x[actCancel-15] _ = x[actChangeBorderLabel-16] _ = x[actChangeHeader-17] - _ = x[actChangePreviewLabel-18] - _ = x[actChangePrompt-19] - _ = x[actChangeQuery-20] - _ = x[actClearScreen-21] - _ = x[actClearQuery-22] - _ = x[actClearSelection-23] - _ = x[actClose-24] - _ = x[actDeleteChar-25] - _ = x[actDeleteCharEof-26] - _ = x[actEndOfLine-27] - _ = x[actForwardChar-28] - _ = x[actForwardWord-29] - _ = x[actKillLine-30] - _ = x[actKillWord-31] - _ = x[actUnixLineDiscard-32] - _ = x[actUnixWordRubout-33] - _ = x[actYank-34] - _ = x[actBackwardKillWord-35] - _ = x[actSelectAll-36] - _ = x[actDeselectAll-37] - _ = x[actToggle-38] - _ = x[actToggleSearch-39] - _ = x[actToggleAll-40] - _ = x[actToggleDown-41] - _ = x[actToggleUp-42] - _ = x[actToggleIn-43] - _ = x[actToggleOut-44] - _ = x[actToggleTrack-45] - _ = 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] + _ = x[actChangeMulti-18] + _ = x[actChangePreviewLabel-19] + _ = x[actChangePrompt-20] + _ = x[actChangeQuery-21] + _ = x[actClearScreen-22] + _ = x[actClearQuery-23] + _ = x[actClearSelection-24] + _ = x[actClose-25] + _ = x[actDeleteChar-26] + _ = x[actDeleteCharEof-27] + _ = x[actEndOfLine-28] + _ = x[actForwardChar-29] + _ = x[actForwardWord-30] + _ = x[actKillLine-31] + _ = x[actKillWord-32] + _ = x[actUnixLineDiscard-33] + _ = x[actUnixWordRubout-34] + _ = x[actYank-35] + _ = x[actBackwardKillWord-36] + _ = x[actSelectAll-37] + _ = x[actDeselectAll-38] + _ = x[actToggle-39] + _ = x[actToggleSearch-40] + _ = x[actToggleAll-41] + _ = x[actToggleDown-42] + _ = x[actToggleUp-43] + _ = x[actToggleIn-44] + _ = x[actToggleOut-45] + _ = x[actToggleTrack-46] + _ = x[actToggleTrackCurrent-47] + _ = x[actToggleHeader-48] + _ = x[actTrackCurrent-49] + _ = x[actUntrackCurrent-50] + _ = x[actDown-51] + _ = x[actUp-52] + _ = x[actPageUp-53] + _ = x[actPageDown-54] + _ = x[actPosition-55] + _ = x[actHalfPageUp-56] + _ = x[actHalfPageDown-57] + _ = x[actOffsetUp-58] + _ = x[actOffsetDown-59] + _ = x[actJump-60] + _ = x[actJumpAccept-61] + _ = x[actPrintQuery-62] + _ = x[actRefreshPreview-63] + _ = x[actReplaceQuery-64] + _ = x[actToggleSort-65] + _ = x[actShowPreview-66] + _ = x[actHidePreview-67] + _ = x[actTogglePreview-68] + _ = x[actTogglePreviewWrap-69] + _ = x[actTransform-70] + _ = x[actTransformBorderLabel-71] + _ = x[actTransformHeader-72] + _ = x[actTransformPreviewLabel-73] + _ = x[actTransformPrompt-74] + _ = x[actTransformQuery-75] + _ = x[actPreview-76] + _ = x[actChangePreview-77] + _ = x[actChangePreviewWindow-78] + _ = x[actPreviewTop-79] + _ = x[actPreviewBottom-80] + _ = x[actPreviewUp-81] + _ = x[actPreviewDown-82] + _ = x[actPreviewPageUp-83] + _ = x[actPreviewPageDown-84] + _ = x[actPreviewHalfPageUp-85] + _ = x[actPreviewHalfPageDown-86] + _ = x[actPrevHistory-87] + _ = x[actPrevSelected-88] + _ = x[actPut-89] + _ = x[actNextHistory-90] + _ = x[actNextSelected-91] + _ = x[actExecute-92] + _ = x[actExecuteSilent-93] + _ = x[actExecuteMulti-94] + _ = x[actSigStop-95] + _ = x[actFirst-96] + _ = x[actLast-97] + _ = x[actReload-98] + _ = x[actReloadSync-99] + _ = x[actDisableSearch-100] + _ = x[actEnableSearch-101] + _ = x[actSelect-102] + _ = x[actDeselect-103] + _ = x[actUnbind-104] + _ = x[actRebind-105] + _ = x[actBecome-106] + _ = x[actResponse-107] + _ = 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 { if i < 0 || i >= actionType(len(_actionType_index)-1) { diff --git a/src/options.go b/src/options.go index 4ba8538f..c8a3fa15 100644 --- a/src/options.go +++ b/src/options.go @@ -1055,7 +1055,7 @@ const ( func init() { 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("[,:]+") actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") } @@ -1306,6 +1306,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA if t == actIgnore { if specIndex == 0 && specLower == "" { actions = append(prevActions, actions...) + } else if specLower == "change-multi" { + appendAction(actChangeMulti) } else { exit("unknown action: " + spec) } @@ -1407,6 +1409,8 @@ func isExecuteAction(str string) actionType { return actChangePrompt case "change-query": return actChangeQuery + case "change-multi": + return actChangeMulti case "pos": return actPosition case "execute": diff --git a/src/terminal.go b/src/terminal.go index b885ce25..25f30150 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -367,6 +367,7 @@ const ( actCancel actChangeBorderLabel actChangeHeader + actChangeMulti actChangePreviewLabel actChangePrompt actChangeQuery @@ -3489,6 +3490,19 @@ func (t *Terminal) Loop() { } case actPrintQuery: 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: t.input = []rune(a.a) t.cx = len(t.input) diff --git a/test/test_go.rb b/test/test_go.rb index ba067997..b08ac72b 100755 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -425,6 +425,25 @@ class TestGoFZF < TestBase 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 [true, false].each do |multi| tmux.send_keys "(echo ' 1st 2nd 3rd/';