1
0
mirror of https://github.com/Llewellynvdm/fzf.git synced 2025-01-23 23:28:31 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
pull[bot]
f4871884d1
Merge d83eb2800a09d86e17c0339d86bd1f22f68164a8 into a30181e2407057435763ef1bfb444cc8aaa29d38 2025-01-12 15:15:37 +00:00
Junegunn Choi
d83eb2800a
Add change-nth action
Example:
  # Start with --nth 1, then 2, then 3, then back to the default, 1
  echo 'foo foobar foobarbaz' | fzf --bind 'space:change-nth(2|3|)' --nth 1 -q foo

Close 
Close 
2025-01-13 00:13:31 +09:00
7 changed files with 163 additions and 102 deletions

@ -67,6 +67,11 @@ Also, fzf now offers "style presets" for quick customization, which can be activ
``` ```
- Added `toggle-multi-line` action - Added `toggle-multi-line` action
- Added `toggle-hscroll` action - Added `toggle-hscroll` action
- Added `change-nth` action for dynamically changing the value of the `--nth` option
```sh
# Start with --nth 1, then 2, then 3, then back to the default, 1
echo 'foo foobar foobarbaz' | fzf --bind 'space:change-nth(2|3|)' --nth 1 -q foo
```
- A single-character delimiter is now treated as a plain string delimiter rather than a regular expression delimiter, even if it's a regular expression meta-character. - A single-character delimiter is now treated as a plain string delimiter rather than a regular expression delimiter, even if it's a regular expression meta-character.
- This means you can just write `--delimiter '|'` instead of escaping it as `--delimiter '\|'` - This means you can just write `--delimiter '|'` instead of escaping it as `--delimiter '\|'`
- Bug fixes - Bug fixes

@ -496,7 +496,7 @@ the label. Label is printed on the top border line by default, add
.SS LIST SECTION .SS LIST SECTION
.TP .TP
.B "\-m, \-\-multi" .BI "\-m, \-\-multi" "[=MAX]"
Enable multi-select with tab/shift\-tab. It optionally takes an integer argument Enable multi-select with tab/shift\-tab. It optionally takes an integer argument
which denotes the maximum number of items that can be selected. which denotes the maximum number of items that can be selected.
.TP .TP
@ -1525,6 +1525,7 @@ A key or an event can be bound to one or more of the following actions.
\fBchange\-list\-label(...)\fR (change \fB\-\-list\-label\fR to the given string) \fBchange\-list\-label(...)\fR (change \fB\-\-list\-label\fR to the given string)
\fBchange\-multi\fR (enable multi-select mode with no limit) \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\-multi(...)\fR (enable multi-select mode with a limit or disable it with 0)
\fBchange\-nth(...)\fR (change \fB\-\-nth\fR option; rotate through the multiple options separated by '|')
\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 '|')

@ -33,107 +33,108 @@ func _() {
_ = x[actChangePreviewLabel-22] _ = x[actChangePreviewLabel-22]
_ = x[actChangePrompt-23] _ = x[actChangePrompt-23]
_ = x[actChangeQuery-24] _ = x[actChangeQuery-24]
_ = x[actClearScreen-25] _ = x[actChangeNth-25]
_ = x[actClearQuery-26] _ = x[actClearScreen-26]
_ = x[actClearSelection-27] _ = x[actClearQuery-27]
_ = x[actClose-28] _ = x[actClearSelection-28]
_ = x[actDeleteChar-29] _ = x[actClose-29]
_ = x[actDeleteCharEof-30] _ = x[actDeleteChar-30]
_ = x[actEndOfLine-31] _ = x[actDeleteCharEof-31]
_ = x[actFatal-32] _ = x[actEndOfLine-32]
_ = x[actForwardChar-33] _ = x[actFatal-33]
_ = x[actForwardWord-34] _ = x[actForwardChar-34]
_ = x[actKillLine-35] _ = x[actForwardWord-35]
_ = x[actKillWord-36] _ = x[actKillLine-36]
_ = x[actUnixLineDiscard-37] _ = x[actKillWord-37]
_ = x[actUnixWordRubout-38] _ = x[actUnixLineDiscard-38]
_ = x[actYank-39] _ = x[actUnixWordRubout-39]
_ = x[actBackwardKillWord-40] _ = x[actYank-40]
_ = x[actSelectAll-41] _ = x[actBackwardKillWord-41]
_ = x[actDeselectAll-42] _ = x[actSelectAll-42]
_ = x[actToggle-43] _ = x[actDeselectAll-43]
_ = x[actToggleSearch-44] _ = x[actToggle-44]
_ = x[actToggleAll-45] _ = x[actToggleSearch-45]
_ = x[actToggleDown-46] _ = x[actToggleAll-46]
_ = x[actToggleUp-47] _ = x[actToggleDown-47]
_ = x[actToggleIn-48] _ = x[actToggleUp-48]
_ = x[actToggleOut-49] _ = x[actToggleIn-49]
_ = x[actToggleTrack-50] _ = x[actToggleOut-50]
_ = x[actToggleTrackCurrent-51] _ = x[actToggleTrack-51]
_ = x[actToggleHeader-52] _ = x[actToggleTrackCurrent-52]
_ = x[actToggleWrap-53] _ = x[actToggleHeader-53]
_ = x[actToggleMultiLine-54] _ = x[actToggleWrap-54]
_ = x[actToggleHscroll-55] _ = x[actToggleMultiLine-55]
_ = x[actTrackCurrent-56] _ = x[actToggleHscroll-56]
_ = x[actUntrackCurrent-57] _ = x[actTrackCurrent-57]
_ = x[actDown-58] _ = x[actUntrackCurrent-58]
_ = x[actUp-59] _ = x[actDown-59]
_ = x[actPageUp-60] _ = x[actUp-60]
_ = x[actPageDown-61] _ = x[actPageUp-61]
_ = x[actPosition-62] _ = x[actPageDown-62]
_ = x[actHalfPageUp-63] _ = x[actPosition-63]
_ = x[actHalfPageDown-64] _ = x[actHalfPageUp-64]
_ = x[actOffsetUp-65] _ = x[actHalfPageDown-65]
_ = x[actOffsetDown-66] _ = x[actOffsetUp-66]
_ = x[actOffsetMiddle-67] _ = x[actOffsetDown-67]
_ = x[actJump-68] _ = x[actOffsetMiddle-68]
_ = x[actJumpAccept-69] _ = x[actJump-69]
_ = x[actPrintQuery-70] _ = x[actJumpAccept-70]
_ = x[actRefreshPreview-71] _ = x[actPrintQuery-71]
_ = x[actReplaceQuery-72] _ = x[actRefreshPreview-72]
_ = x[actToggleSort-73] _ = x[actReplaceQuery-73]
_ = x[actShowPreview-74] _ = x[actToggleSort-74]
_ = x[actHidePreview-75] _ = x[actShowPreview-75]
_ = x[actTogglePreview-76] _ = x[actHidePreview-76]
_ = x[actTogglePreviewWrap-77] _ = x[actTogglePreview-77]
_ = x[actTransform-78] _ = x[actTogglePreviewWrap-78]
_ = x[actTransformBorderLabel-79] _ = x[actTransform-79]
_ = x[actTransformListLabel-80] _ = x[actTransformBorderLabel-80]
_ = x[actTransformInputLabel-81] _ = x[actTransformListLabel-81]
_ = x[actTransformHeader-82] _ = x[actTransformInputLabel-82]
_ = x[actTransformHeaderLabel-83] _ = x[actTransformHeader-83]
_ = x[actTransformPreviewLabel-84] _ = x[actTransformHeaderLabel-84]
_ = x[actTransformPrompt-85] _ = x[actTransformPreviewLabel-85]
_ = x[actTransformQuery-86] _ = x[actTransformPrompt-86]
_ = x[actPreview-87] _ = x[actTransformQuery-87]
_ = x[actChangePreview-88] _ = x[actPreview-88]
_ = x[actChangePreviewWindow-89] _ = x[actChangePreview-89]
_ = x[actPreviewTop-90] _ = x[actChangePreviewWindow-90]
_ = x[actPreviewBottom-91] _ = x[actPreviewTop-91]
_ = x[actPreviewUp-92] _ = x[actPreviewBottom-92]
_ = x[actPreviewDown-93] _ = x[actPreviewUp-93]
_ = x[actPreviewPageUp-94] _ = x[actPreviewDown-94]
_ = x[actPreviewPageDown-95] _ = x[actPreviewPageUp-95]
_ = x[actPreviewHalfPageUp-96] _ = x[actPreviewPageDown-96]
_ = x[actPreviewHalfPageDown-97] _ = x[actPreviewHalfPageUp-97]
_ = x[actPrevHistory-98] _ = x[actPreviewHalfPageDown-98]
_ = x[actPrevSelected-99] _ = x[actPrevHistory-99]
_ = x[actPrint-100] _ = x[actPrevSelected-100]
_ = x[actPut-101] _ = x[actPrint-101]
_ = x[actNextHistory-102] _ = x[actPut-102]
_ = x[actNextSelected-103] _ = x[actNextHistory-103]
_ = x[actExecute-104] _ = x[actNextSelected-104]
_ = x[actExecuteSilent-105] _ = x[actExecute-105]
_ = x[actExecuteMulti-106] _ = x[actExecuteSilent-106]
_ = x[actSigStop-107] _ = x[actExecuteMulti-107]
_ = x[actFirst-108] _ = x[actSigStop-108]
_ = x[actLast-109] _ = x[actFirst-109]
_ = x[actReload-110] _ = x[actLast-110]
_ = x[actReloadSync-111] _ = x[actReload-111]
_ = x[actDisableSearch-112] _ = x[actReloadSync-112]
_ = x[actEnableSearch-113] _ = x[actDisableSearch-113]
_ = x[actSelect-114] _ = x[actEnableSearch-114]
_ = x[actDeselect-115] _ = x[actSelect-115]
_ = x[actUnbind-116] _ = x[actDeselect-116]
_ = x[actRebind-117] _ = x[actUnbind-117]
_ = x[actBecome-118] _ = x[actRebind-118]
_ = x[actShowHeader-119] _ = x[actBecome-119]
_ = x[actHideHeader-120] _ = x[actShowHeader-120]
_ = x[actHideHeader-121]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeHeaderLabelactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformHeaderLabelactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader" const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeHeaderLabelactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactChangeNthactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformHeaderLabelactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 245, 264, 279, 299, 313, 334, 349, 363, 377, 390, 407, 415, 428, 444, 456, 464, 478, 492, 503, 514, 532, 549, 556, 575, 587, 601, 610, 625, 637, 650, 661, 672, 684, 698, 719, 734, 747, 765, 781, 796, 813, 820, 825, 834, 845, 856, 869, 884, 895, 908, 923, 930, 943, 956, 973, 988, 1001, 1015, 1029, 1045, 1065, 1077, 1100, 1121, 1143, 1161, 1184, 1208, 1226, 1243, 1253, 1269, 1291, 1304, 1320, 1332, 1346, 1362, 1380, 1400, 1422, 1436, 1451, 1459, 1465, 1479, 1494, 1504, 1520, 1535, 1545, 1553, 1560, 1569, 1582, 1598, 1613, 1622, 1633, 1642, 1651, 1660, 1673, 1686} var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 245, 264, 279, 299, 313, 334, 349, 363, 375, 389, 402, 419, 427, 440, 456, 468, 476, 490, 504, 515, 526, 544, 561, 568, 587, 599, 613, 622, 637, 649, 662, 673, 684, 696, 710, 731, 746, 759, 777, 793, 808, 825, 832, 837, 846, 857, 868, 881, 896, 907, 920, 935, 942, 955, 968, 985, 1000, 1013, 1027, 1041, 1057, 1077, 1089, 1112, 1133, 1155, 1173, 1196, 1220, 1238, 1255, 1265, 1281, 1303, 1316, 1332, 1344, 1358, 1374, 1392, 1412, 1434, 1448, 1463, 1471, 1477, 1491, 1506, 1516, 1532, 1547, 1557, 1565, 1572, 1581, 1594, 1610, 1625, 1634, 1645, 1654, 1663, 1672, 1685, 1698}
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) {

@ -190,11 +190,13 @@ func Run(opts *Options) (int, error) {
forward = true forward = true
} }
} }
nth := opts.Nth
patternCache := make(map[string]*Pattern) patternCache := make(map[string]*Pattern)
patternBuilder := func(runes []rune) *Pattern { patternBuilder := func(runes []rune) *Pattern {
return BuildPattern(cache, patternCache, return BuildPattern(cache, patternCache,
opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos, opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos,
opts.Filter == nil, opts.Nth, opts.Delimiter, runes) opts.Filter == nil, nth, opts.Delimiter, runes)
} }
inputRevision := revision{} inputRevision := revision{}
snapshotRevision := revision{} snapshotRevision := revision{}
@ -373,6 +375,12 @@ func Run(opts *Options) (int, error) {
command = val.command command = val.command
environ = val.environ environ = val.environ
changed = val.changed changed = val.changed
if val.nth != nil {
// Change nth and clear caches
nth = *val.nth
patternCache = make(map[string]*Pattern)
inputRevision.bumpMajor()
}
if command != nil { if command != nil {
useSnapshot = val.sync useSnapshot = val.sync
} }

@ -1306,7 +1306,7 @@ const (
func init() { func init() {
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|(?:border|list|preview|input|header)-label|header)|transform|change-(?:preview-window|preview|multi)|(?:re|un)bind|pos|put|print)`) `(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|(?:border|list|preview|input|header)-label|header)|transform|change-(?:preview-window|preview|multi|nth)|(?:re|un)bind|pos|put|print)`)
splitRegexp = regexp.MustCompile("[,:]+") splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
} }
@ -1684,6 +1684,8 @@ func isExecuteAction(str string) actionType {
return actChangeQuery return actChangeQuery
case "change-multi": case "change-multi":
return actChangeMulti return actChangeMulti
case "change-nth":
return actChangeNth
case "pos": case "pos":
return actPosition return actPosition
case "execute": case "execute":

@ -299,6 +299,7 @@ type Terminal struct {
scrollbar string scrollbar string
previewScrollbar string previewScrollbar string
ansi bool ansi bool
nth []Range
tabstop int tabstop int
margin [4]sizeSpec margin [4]sizeSpec
padding [4]sizeSpec padding [4]sizeSpec
@ -462,6 +463,7 @@ const (
actChangePreviewLabel actChangePreviewLabel
actChangePrompt actChangePrompt
actChangeQuery actChangeQuery
actChangeNth
actClearScreen actClearScreen
actClearQuery actClearQuery
actClearSelection actClearSelection
@ -597,6 +599,7 @@ type placeholderFlags struct {
type searchRequest struct { type searchRequest struct {
sort bool sort bool
sync bool sync bool
nth *[]Range
command *commandSpec command *commandSpec
environ []string environ []string
changed bool changed bool
@ -880,6 +883,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
header: []string{}, header: []string{},
header0: opts.Header, header0: opts.Header,
ansi: opts.Ansi, ansi: opts.Ansi,
nth: opts.Nth,
tabstop: opts.Tabstop, tabstop: opts.Tabstop,
hasStartActions: false, hasStartActions: false,
hasResultActions: false, hasResultActions: false,
@ -4359,6 +4363,7 @@ func (t *Terminal) Loop() error {
} }
for loopIndex := int64(0); looping; loopIndex++ { for loopIndex := int64(0); looping; loopIndex++ {
var newCommand *commandSpec var newCommand *commandSpec
var newNth *[]Range
var reloadSync bool var reloadSync bool
changed := false changed := false
beof := false beof := false
@ -4618,6 +4623,22 @@ func (t *Terminal) Loop() error {
} }
t.multi = multi t.multi = multi
req(reqList, reqInfo) req(reqList, reqInfo)
case actChangeNth:
changed = true
// Split nth expression
tokens := strings.Split(a.a, "|")
if nth, err := splitNth(tokens[0]); err == nil {
// Changed
newNth = &nth
} else {
// The default
newNth = &t.nth
}
// Cycle
if len(tokens) > 1 {
a.a = strings.Join(append(tokens[1:], tokens[0]), "|")
}
case actChangeQuery: case actChangeQuery:
t.input = []rune(a.a) t.input = []rune(a.a)
t.cx = len(t.input) t.cx = len(t.input)
@ -5537,7 +5558,7 @@ func (t *Terminal) Loop() error {
reload := changed || newCommand != nil reload := changed || newCommand != nil
var reloadRequest *searchRequest var reloadRequest *searchRequest
if reload { if reload {
reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, command: newCommand, environ: t.environ(), changed: changed} reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, nth: newNth, command: newCommand, environ: t.environ(), changed: changed}
} }
t.mutex.Unlock() // Must be unlocked before touching reqBox t.mutex.Unlock() // Must be unlocked before touching reqBox

@ -3718,6 +3718,29 @@ class TestGoFZF < TestBase
BLOCK BLOCK
tmux.until { assert_block(block, _1) } tmux.until { assert_block(block, _1) }
end end
def test_change_nth
input = [
'foo bar bar bar bar',
'foo foo bar bar bar',
'foo foo foo bar bar',
'foo foo foo foo bar'
]
writelines(input)
tmux.send_keys %(#{FZF} -qfoo -n1 --bind 'space:change-nth:2|3|4|5|' < #{tempname}), :Enter
tmux.until { |lines| assert_equal 4, lines.match_count }
tmux.send_keys :Space
tmux.until { |lines| assert_equal 3, lines.match_count }
tmux.send_keys :Space
tmux.until { |lines| assert_equal 2, lines.match_count }
tmux.send_keys :Space
tmux.until { |lines| assert_equal 1, lines.match_count }
tmux.send_keys :Space
tmux.until { |lines| assert_equal 0, lines.match_count }
tmux.send_keys :Space
tmux.until { |lines| assert_equal 4, lines.match_count }
end
end end
module TestShell module TestShell