From ae12e94b1f2d0659f9b1b32ae6380a677ca19d68 Mon Sep 17 00:00:00 2001 From: Massimo Mund Date: Fri, 5 Sep 2025 12:38:22 +0200 Subject: [PATCH] Add sub-word actions (#3997) Add `backward-subword`, `forward-subword`, `kill-subword`, `backward-kill-subword` actions. --- man/man1/fzf.1 | 4 + src/actiontype_string.go | 294 ++++++++++++++++++++------------------- src/options.go | 8 ++ src/terminal.go | 26 ++++ test/test_core.rb | 34 +++++ 5 files changed, 221 insertions(+), 145 deletions(-) diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index e8d33642..9707c97e 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -1807,7 +1807,9 @@ A key or an event can be bound to one or more of the following actions. \fBbackward\-char\fR \fIctrl\-b left\fR \fBbackward\-delete\-char\fR \fIctrl\-h ctrl\-bspace bspace\fR \fBbackward\-delete\-char/eof\fR (same as \fBbackward\-delete\-char\fR except aborts fzf if query is empty) + \fBbackward\-kill\-subword\fR \fBbackward\-kill\-word\fR \fIalt\-bs\fR + \fBbackward\-subword\fR \fBbackward\-word\fR \fIalt\-b shift\-left\fR \fBbecome(...)\fR (replace fzf process with the specified command; see below for the details) \fBbeginning\-of\-line\fR \fIctrl\-a home\fR @@ -1847,10 +1849,12 @@ A key or an event can be bound to one or more of the following actions. \fBexecute\-silent(...)\fR (see below for the details) \fBfirst\fR (move to the first match; same as \fBpos(1)\fR) \fBforward\-char\fR \fIctrl\-f right\fR + \fBforward\-subword\fR \fBforward\-word\fR \fIalt\-f shift\-right\fR \fBignore\fR \fBjump\fR (EasyMotion-like 2-keystroke movement) \fBkill\-line\fR + \fBkill\-subword\fR \fBkill\-word\fR \fIalt\-d\fR \fBlast\fR (move to the last match; same as \fBpos(\-1)\fR) \fBnext\-history\fR (\fIctrl\-n\fR on \fB\-\-history\fR) diff --git a/src/actiontype_string.go b/src/actiontype_string.go index 27d76b32..2a6873b8 100644 --- a/src/actiontype_string.go +++ b/src/actiontype_string.go @@ -25,154 +25,158 @@ func _() { _ = x[actBackwardDeleteChar-14] _ = x[actBackwardDeleteCharEof-15] _ = x[actBackwardWord-16] - _ = x[actCancel-17] - _ = x[actChangeBorderLabel-18] - _ = x[actChangeGhost-19] - _ = x[actChangeHeader-20] - _ = x[actChangeFooter-21] - _ = x[actChangeHeaderLabel-22] - _ = x[actChangeFooterLabel-23] - _ = x[actChangeInputLabel-24] - _ = x[actChangeListLabel-25] - _ = x[actChangeMulti-26] - _ = x[actChangeNth-27] - _ = x[actChangePointer-28] - _ = x[actChangePreview-29] - _ = x[actChangePreviewLabel-30] - _ = x[actChangePreviewWindow-31] - _ = x[actChangePrompt-32] - _ = x[actChangeQuery-33] - _ = x[actClearScreen-34] - _ = x[actClearQuery-35] - _ = x[actClearSelection-36] - _ = x[actClose-37] - _ = x[actDeleteChar-38] - _ = x[actDeleteCharEof-39] - _ = x[actEndOfLine-40] - _ = x[actFatal-41] - _ = x[actForwardChar-42] - _ = x[actForwardWord-43] - _ = x[actKillLine-44] - _ = x[actKillWord-45] - _ = x[actUnixLineDiscard-46] - _ = x[actUnixWordRubout-47] - _ = x[actYank-48] - _ = x[actBackwardKillWord-49] - _ = x[actSelectAll-50] - _ = x[actDeselectAll-51] - _ = x[actToggle-52] - _ = x[actToggleSearch-53] - _ = x[actToggleAll-54] - _ = x[actToggleDown-55] - _ = x[actToggleUp-56] - _ = x[actToggleIn-57] - _ = x[actToggleOut-58] - _ = x[actToggleTrack-59] - _ = x[actToggleTrackCurrent-60] - _ = x[actToggleHeader-61] - _ = x[actToggleWrap-62] - _ = x[actToggleMultiLine-63] - _ = x[actToggleHscroll-64] - _ = x[actTrackCurrent-65] - _ = x[actToggleInput-66] - _ = x[actHideInput-67] - _ = x[actShowInput-68] - _ = x[actUntrackCurrent-69] - _ = x[actDown-70] - _ = x[actUp-71] - _ = x[actPageUp-72] - _ = x[actPageDown-73] - _ = x[actPosition-74] - _ = x[actHalfPageUp-75] - _ = x[actHalfPageDown-76] - _ = x[actOffsetUp-77] - _ = x[actOffsetDown-78] - _ = x[actOffsetMiddle-79] - _ = x[actJump-80] - _ = x[actJumpAccept-81] - _ = x[actPrintQuery-82] - _ = x[actRefreshPreview-83] - _ = x[actReplaceQuery-84] - _ = x[actToggleSort-85] - _ = x[actShowPreview-86] - _ = x[actHidePreview-87] - _ = x[actTogglePreview-88] - _ = x[actTogglePreviewWrap-89] - _ = x[actTransform-90] - _ = x[actTransformBorderLabel-91] - _ = x[actTransformGhost-92] - _ = x[actTransformHeader-93] - _ = x[actTransformFooter-94] - _ = x[actTransformHeaderLabel-95] - _ = x[actTransformFooterLabel-96] - _ = x[actTransformInputLabel-97] - _ = x[actTransformListLabel-98] - _ = x[actTransformNth-99] - _ = x[actTransformPointer-100] - _ = x[actTransformPreviewLabel-101] - _ = x[actTransformPrompt-102] - _ = x[actTransformQuery-103] - _ = x[actTransformSearch-104] - _ = x[actTrigger-105] - _ = x[actBgTransform-106] - _ = x[actBgTransformBorderLabel-107] - _ = x[actBgTransformGhost-108] - _ = x[actBgTransformHeader-109] - _ = x[actBgTransformFooter-110] - _ = x[actBgTransformHeaderLabel-111] - _ = x[actBgTransformFooterLabel-112] - _ = x[actBgTransformInputLabel-113] - _ = x[actBgTransformListLabel-114] - _ = x[actBgTransformNth-115] - _ = x[actBgTransformPointer-116] - _ = x[actBgTransformPreviewLabel-117] - _ = x[actBgTransformPrompt-118] - _ = x[actBgTransformQuery-119] - _ = x[actBgTransformSearch-120] - _ = x[actBgCancel-121] - _ = x[actSearch-122] - _ = x[actPreview-123] - _ = x[actPreviewTop-124] - _ = x[actPreviewBottom-125] - _ = x[actPreviewUp-126] - _ = x[actPreviewDown-127] - _ = x[actPreviewPageUp-128] - _ = x[actPreviewPageDown-129] - _ = x[actPreviewHalfPageUp-130] - _ = x[actPreviewHalfPageDown-131] - _ = x[actPrevHistory-132] - _ = x[actPrevSelected-133] - _ = x[actPrint-134] - _ = x[actPut-135] - _ = x[actNextHistory-136] - _ = x[actNextSelected-137] - _ = x[actExecute-138] - _ = x[actExecuteSilent-139] - _ = x[actExecuteMulti-140] - _ = x[actSigStop-141] - _ = x[actFirst-142] - _ = x[actLast-143] - _ = x[actReload-144] - _ = x[actReloadSync-145] - _ = x[actDisableSearch-146] - _ = x[actEnableSearch-147] - _ = x[actSelect-148] - _ = x[actDeselect-149] - _ = x[actUnbind-150] - _ = x[actRebind-151] - _ = x[actToggleBind-152] - _ = x[actBecome-153] - _ = x[actShowHeader-154] - _ = x[actHideHeader-155] - _ = x[actBell-156] - _ = x[actExclude-157] - _ = x[actExcludeMulti-158] - _ = x[actAsync-159] + _ = x[actBackwardSubWord-17] + _ = x[actCancel-18] + _ = x[actChangeBorderLabel-19] + _ = x[actChangeGhost-20] + _ = x[actChangeHeader-21] + _ = x[actChangeFooter-22] + _ = x[actChangeHeaderLabel-23] + _ = x[actChangeFooterLabel-24] + _ = x[actChangeInputLabel-25] + _ = x[actChangeListLabel-26] + _ = x[actChangeMulti-27] + _ = x[actChangeNth-28] + _ = x[actChangePointer-29] + _ = x[actChangePreview-30] + _ = x[actChangePreviewLabel-31] + _ = x[actChangePreviewWindow-32] + _ = x[actChangePrompt-33] + _ = x[actChangeQuery-34] + _ = x[actClearScreen-35] + _ = x[actClearQuery-36] + _ = x[actClearSelection-37] + _ = x[actClose-38] + _ = x[actDeleteChar-39] + _ = x[actDeleteCharEof-40] + _ = x[actEndOfLine-41] + _ = x[actFatal-42] + _ = x[actForwardChar-43] + _ = x[actForwardWord-44] + _ = x[actForwardSubWord-45] + _ = x[actKillLine-46] + _ = x[actKillWord-47] + _ = x[actKillSubWord-48] + _ = x[actUnixLineDiscard-49] + _ = x[actUnixWordRubout-50] + _ = x[actYank-51] + _ = x[actBackwardKillWord-52] + _ = x[actBackwardKillSubWord-53] + _ = x[actSelectAll-54] + _ = x[actDeselectAll-55] + _ = x[actToggle-56] + _ = x[actToggleSearch-57] + _ = x[actToggleAll-58] + _ = x[actToggleDown-59] + _ = x[actToggleUp-60] + _ = x[actToggleIn-61] + _ = x[actToggleOut-62] + _ = x[actToggleTrack-63] + _ = x[actToggleTrackCurrent-64] + _ = x[actToggleHeader-65] + _ = x[actToggleWrap-66] + _ = x[actToggleMultiLine-67] + _ = x[actToggleHscroll-68] + _ = x[actTrackCurrent-69] + _ = x[actToggleInput-70] + _ = x[actHideInput-71] + _ = x[actShowInput-72] + _ = x[actUntrackCurrent-73] + _ = x[actDown-74] + _ = x[actUp-75] + _ = x[actPageUp-76] + _ = x[actPageDown-77] + _ = x[actPosition-78] + _ = x[actHalfPageUp-79] + _ = x[actHalfPageDown-80] + _ = x[actOffsetUp-81] + _ = x[actOffsetDown-82] + _ = x[actOffsetMiddle-83] + _ = x[actJump-84] + _ = x[actJumpAccept-85] + _ = x[actPrintQuery-86] + _ = x[actRefreshPreview-87] + _ = x[actReplaceQuery-88] + _ = x[actToggleSort-89] + _ = x[actShowPreview-90] + _ = x[actHidePreview-91] + _ = x[actTogglePreview-92] + _ = x[actTogglePreviewWrap-93] + _ = x[actTransform-94] + _ = x[actTransformBorderLabel-95] + _ = x[actTransformGhost-96] + _ = x[actTransformHeader-97] + _ = x[actTransformFooter-98] + _ = x[actTransformHeaderLabel-99] + _ = x[actTransformFooterLabel-100] + _ = x[actTransformInputLabel-101] + _ = x[actTransformListLabel-102] + _ = x[actTransformNth-103] + _ = x[actTransformPointer-104] + _ = x[actTransformPreviewLabel-105] + _ = x[actTransformPrompt-106] + _ = x[actTransformQuery-107] + _ = x[actTransformSearch-108] + _ = x[actTrigger-109] + _ = x[actBgTransform-110] + _ = x[actBgTransformBorderLabel-111] + _ = x[actBgTransformGhost-112] + _ = x[actBgTransformHeader-113] + _ = x[actBgTransformFooter-114] + _ = x[actBgTransformHeaderLabel-115] + _ = x[actBgTransformFooterLabel-116] + _ = x[actBgTransformInputLabel-117] + _ = x[actBgTransformListLabel-118] + _ = x[actBgTransformNth-119] + _ = x[actBgTransformPointer-120] + _ = x[actBgTransformPreviewLabel-121] + _ = x[actBgTransformPrompt-122] + _ = x[actBgTransformQuery-123] + _ = x[actBgTransformSearch-124] + _ = x[actBgCancel-125] + _ = x[actSearch-126] + _ = x[actPreview-127] + _ = x[actPreviewTop-128] + _ = x[actPreviewBottom-129] + _ = x[actPreviewUp-130] + _ = x[actPreviewDown-131] + _ = x[actPreviewPageUp-132] + _ = x[actPreviewPageDown-133] + _ = x[actPreviewHalfPageUp-134] + _ = x[actPreviewHalfPageDown-135] + _ = x[actPrevHistory-136] + _ = x[actPrevSelected-137] + _ = x[actPrint-138] + _ = x[actPut-139] + _ = x[actNextHistory-140] + _ = x[actNextSelected-141] + _ = x[actExecute-142] + _ = x[actExecuteSilent-143] + _ = x[actExecuteMulti-144] + _ = x[actSigStop-145] + _ = x[actFirst-146] + _ = x[actLast-147] + _ = x[actReload-148] + _ = x[actReloadSync-149] + _ = x[actDisableSearch-150] + _ = x[actEnableSearch-151] + _ = x[actSelect-152] + _ = x[actDeselect-153] + _ = x[actUnbind-154] + _ = x[actRebind-155] + _ = x[actToggleBind-156] + _ = x[actBecome-157] + _ = x[actShowHeader-158] + _ = x[actHideHeader-159] + _ = x[actBell-160] + _ = x[actExclude-161] + _ = x[actExcludeMulti-162] + _ = x[actAsync-163] } -const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync" +const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactBackwardSubWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactForwardSubWordactKillLineactKillWordactKillSubWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactBackwardKillSubWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync" -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, 1558, 1572, 1597, 1616, 1636, 1656, 1681, 1706, 1730, 1753, 1770, 1791, 1817, 1837, 1856, 1876, 1887, 1896, 1906, 1919, 1935, 1947, 1961, 1977, 1995, 2015, 2037, 2051, 2066, 2074, 2080, 2094, 2109, 2119, 2135, 2150, 2160, 2168, 2175, 2184, 2197, 2213, 2228, 2237, 2248, 2257, 2266, 2279, 2288, 2301, 2314, 2321, 2331, 2346, 2354} +var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 258, 267, 287, 301, 316, 331, 351, 371, 390, 408, 422, 434, 450, 466, 487, 509, 524, 538, 552, 565, 582, 590, 603, 619, 631, 639, 653, 667, 684, 695, 706, 720, 738, 755, 762, 781, 803, 815, 829, 838, 853, 865, 878, 889, 900, 912, 926, 947, 962, 975, 993, 1009, 1024, 1038, 1050, 1062, 1079, 1086, 1091, 1100, 1111, 1122, 1135, 1150, 1161, 1174, 1189, 1196, 1209, 1222, 1239, 1254, 1267, 1281, 1295, 1311, 1331, 1343, 1366, 1383, 1401, 1419, 1442, 1465, 1487, 1508, 1523, 1542, 1566, 1584, 1601, 1619, 1629, 1643, 1668, 1687, 1707, 1727, 1752, 1777, 1801, 1824, 1841, 1862, 1888, 1908, 1927, 1947, 1958, 1967, 1977, 1990, 2006, 2018, 2032, 2048, 2066, 2086, 2108, 2122, 2137, 2145, 2151, 2165, 2180, 2190, 2206, 2221, 2231, 2239, 2246, 2255, 2268, 2284, 2299, 2308, 2319, 2328, 2337, 2350, 2359, 2372, 2385, 2392, 2402, 2417, 2425} 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 f8cb578f..76081ca6 100644 --- a/src/options.go +++ b/src/options.go @@ -1663,6 +1663,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA appendAction(actBackwardDeleteCharEof) case "backward-word": appendAction(actBackwardWord) + case "backward-subword": + appendAction(actBackwardSubWord) case "clear-screen": appendAction(actClearScreen) case "delete-char": @@ -1683,6 +1685,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA appendAction(actForwardChar) case "forward-word": appendAction(actForwardWord) + case "forward-subword": + appendAction(actForwardSubWord) case "jump": appendAction(actJump) case "jump-accept": @@ -1691,6 +1695,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA appendAction(actKillLine) case "kill-word": appendAction(actKillWord) + case "kill-subword": + appendAction(actKillSubWord) case "unix-line-discard", "line-discard": appendAction(actUnixLineDiscard) case "unix-word-rubout", "word-rubout": @@ -1699,6 +1705,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA appendAction(actYank) case "backward-kill-word": appendAction(actBackwardKillWord) + case "backward-kill-subword": + appendAction(actBackwardKillSubWord) case "toggle-down": appendAction(actToggle, actDown) case "toggle-up": diff --git a/src/terminal.go b/src/terminal.go index f3f5298b..7e179bea 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -291,6 +291,8 @@ type Terminal struct { gapLineLen int wordRubout string wordNext string + subWordRubout string + subWordNext string cx int cy int offset int @@ -512,6 +514,7 @@ const ( actBackwardDeleteChar actBackwardDeleteCharEof actBackwardWord + actBackwardSubWord actCancel actChangeBorderLabel @@ -541,12 +544,15 @@ const ( actFatal actForwardChar actForwardWord + actForwardSubWord actKillLine actKillWord + actKillSubWord actUnixLineDiscard actUnixWordRubout actYank actBackwardKillWord + actBackwardKillSubWord actSelectAll actDeselectAll actToggle @@ -937,6 +943,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor } wordRubout := "[^\\pL\\pN][\\pL\\pN]" wordNext := "[\\pL\\pN][^\\pL\\pN]|(.$)" + subWordRubout := "[a-z][A-Z]|[^\\pL\\pN][\\pL\\pN]" + subWordNext := "[a-z][A-Z]|[\\pL\\pN][^\\pL\\pN]|(.$)" if opts.FileWord { sep := regexp.QuoteMeta(string(os.PathSeparator)) wordRubout = fmt.Sprintf("%s[^%s]", sep, sep) @@ -970,6 +978,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor markerMultiLine: *opts.MarkerMulti, wordRubout: wordRubout, wordNext: wordNext, + subWordRubout: subWordRubout, + subWordNext: subWordNext, cx: len(input), cy: 0, offset: 0, @@ -6021,6 +6031,11 @@ func (t *Terminal) Loop() error { if t.cx > 0 { t.rubout(t.wordRubout) } + case actBackwardKillSubWord: + beof = len(t.input) == 0 + if t.cx > 0 { + t.rubout(t.subWordRubout) + } case actYank: suffix := copySlice(t.input[t.cx:]) t.input = append(append(t.input[:t.cx], t.yanked...), suffix...) @@ -6132,6 +6147,10 @@ func (t *Terminal) Loop() error { t.cx = findLastMatch(t.wordRubout, string(t.input[:t.cx])) + 1 case actForwardWord: t.cx += findFirstMatch(t.wordNext, string(t.input[t.cx:])) + 1 + case actBackwardSubWord: + t.cx = findLastMatch(t.subWordRubout, string(t.input[:t.cx])) + 1 + case actForwardSubWord: + t.cx += findFirstMatch(t.subWordNext, string(t.input[t.cx:])) + 1 case actKillWord: ncx := t.cx + findFirstMatch(t.wordNext, string(t.input[t.cx:])) + 1 @@ -6139,6 +6158,13 @@ func (t *Terminal) Loop() error { t.yanked = copySlice(t.input[t.cx:ncx]) t.input = append(t.input[:t.cx], t.input[ncx:]...) } + case actKillSubWord: + ncx := t.cx + + findFirstMatch(t.subWordNext, string(t.input[t.cx:])) + 1 + if ncx > t.cx { + t.yanked = copySlice(t.input[t.cx:ncx]) + t.input = append(t.input[:t.cx], t.input[ncx:]...) + } case actKillLine: if t.cx < len(t.input) { t.yanked = copySlice(t.input[t.cx:]) diff --git a/test/test_core.rb b/test/test_core.rb index 0b6d5db5..c9f55803 100644 --- a/test/test_core.rb +++ b/test/test_core.rb @@ -108,6 +108,40 @@ class TestCore < TestInteractive assert_equal %w[3 2 5 6 8 7], fzf_output_lines end + def test_subword_forward + tmux.send_keys "#{FZF} --bind K:kill-subword,F:forward-subword -q 'foo bar foo-bar fooFooBar'", :Enter, :Home + tmux.until { |lines| assert_equal '> foo bar foo-bar fooFooBar', lines.last } + + tmux.send_keys 'F', :Delete + tmux.until { |lines| assert_equal '> foobar foo-bar fooFooBar', lines.last } + + tmux.send_keys 'K' + tmux.until { |lines| assert_equal '> foo foo-bar fooFooBar', lines.last } + + tmux.send_keys 'F', 'K' + tmux.until { |lines| assert_equal '> foo foo fooFooBar', lines.last } + + tmux.send_keys 'F', 'F', 'K' + tmux.until { |lines| assert_equal '> foo foo fooFoo', lines.last } + end + + def test_subword_backward + tmux.send_keys "#{FZF} --bind K:backward-kill-subword,B:backward-subword -q 'foo bar foo-bar fooBar'", :Enter + tmux.until { |lines| assert_equal '> foo bar foo-bar fooBar', lines.last } + + tmux.send_keys 'B', :BSpace + tmux.until { |lines| assert_equal '> foo bar foo-bar foBar', lines.last } + + tmux.send_keys 'K' + tmux.until { |lines| assert_equal '> foo bar foo-bar Bar', lines.last } + + tmux.send_keys 'B', :BSpace + tmux.until { |lines| assert_equal '> foo bar foobar Bar', lines.last } + + tmux.send_keys 'B', 'B', :BSpace + tmux.until { |lines| assert_equal '> foobar foobar Bar', lines.last } + end + def test_multi_max tmux.send_keys "seq 1 10 | #{FZF} -m 3 --bind A:select-all,T:toggle-all --preview 'echo [{+}]/{}'", :Enter