Add jump and jump-cancel events

Close #3412

    # Default behavior
    fzf --bind space:jump

    # Same as jump-accept action
    fzf --bind space:jump,jump:accept

    # Accept on jump, abort on cancel
    fzf --bind space:jump,jump:accept,jump-cancel:abort

    # Change header on jump-cancel
    fzf --bind 'space:change-header(Type jump label)+jump,jump-cancel:change-header:Jump cancelled'
This commit is contained in:
Junegunn Choi 2024-04-10 20:11:47 +09:00
parent 17bb7ad278
commit a4745626dd
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
5 changed files with 52 additions and 8 deletions

View File

@ -1160,6 +1160,7 @@ e.g.
\fB# Move cursor to the last item and select all items \fB# Move cursor to the last item and select all items
seq 1000 | fzf --multi --sync --bind start:last+select-all\fR seq 1000 | fzf --multi --sync --bind start:last+select-all\fR
.RE .RE
\fIload\fR \fIload\fR
.RS .RS
Triggered when the input stream is complete and the initial processing of the Triggered when the input stream is complete and the initial processing of the
@ -1169,6 +1170,7 @@ e.g.
\fB# Change the prompt to "loaded" when the input stream is complete \fB# Change the prompt to "loaded" when the input stream is complete
(seq 10; sleep 1; seq 11 20) | fzf --prompt 'Loading> ' --bind 'load:change-prompt:Loaded> '\fR (seq 10; sleep 1; seq 11 20) | fzf --prompt 'Loading> ' --bind 'load:change-prompt:Loaded> '\fR
.RE .RE
\fIresize\fR \fIresize\fR
.RS .RS
Triggered when the terminal size is changed. Triggered when the terminal size is changed.
@ -1176,6 +1178,7 @@ Triggered when the terminal size is changed.
e.g. e.g.
\fBfzf --bind 'resize:transform-header:echo Resized: ${FZF_COLUMNS}x${FZF_LINES}'\fR \fBfzf --bind 'resize:transform-header:echo Resized: ${FZF_COLUMNS}x${FZF_LINES}'\fR
.RE .RE
\fIresult\fR \fIresult\fR
.RS .RS
Triggered when the filtering for the current query is complete and the result list is ready. Triggered when the filtering for the current query is complete and the result list is ready.
@ -1209,6 +1212,7 @@ e.g.
# Beware not to introduce an infinite loop # Beware not to introduce an infinite loop
seq 10 | fzf --bind 'focus:up' --cycle\fR seq 10 | fzf --bind 'focus:up' --cycle\fR
.RE .RE
\fIone\fR \fIone\fR
.RS .RS
Triggered when there's only one match. \fBone:accept\fR binding is comparable Triggered when there's only one match. \fBone:accept\fR binding is comparable
@ -1220,6 +1224,7 @@ e.g.
\fB# Automatically select the only match \fB# Automatically select the only match
seq 10 | fzf --bind one:accept\fR seq 10 | fzf --bind one:accept\fR
.RE .RE
\fIzero\fR \fIzero\fR
.RS .RS
Triggered when there's no match. \fBzero:abort\fR binding is comparable to Triggered when there's no match. \fBzero:abort\fR binding is comparable to
@ -1241,6 +1246,22 @@ e.g.
\fBfzf --bind backward-eof:abort\fR \fBfzf --bind backward-eof:abort\fR
.RE .RE
\fIjump\fR
.RS
Triggered when successfully jumped to the target item in \fB--jump\fR mode.
e.g.
\fBfzf --bind space:jump,jump:accept\fR
.RE
\fIjump-cancel\fR
.RS
Triggered when \fB--jump\fR mode is cancelled.
e.g.
\fBfzf --bind space:jump,jump:accept,jump-cancel:abort\fR
.RE
.SS AVAILABLE ACTIONS: .SS AVAILABLE ACTIONS:
A key or an event can be bound to one or more of the following actions. A key or an event can be bound to one or more of the following actions.

View File

@ -698,6 +698,10 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E
add(tui.One) add(tui.One)
case "zero": case "zero":
add(tui.Zero) add(tui.Zero)
case "jump":
add(tui.Jump)
case "jump-cancel":
add(tui.JumpCancel)
case "alt-enter", "alt-return": case "alt-enter", "alt-return":
chords[tui.CtrlAltKey('m')] = key chords[tui.CtrlAltKey('m')] = key
case "alt-space": case "alt-space":

View File

@ -4108,6 +4108,9 @@ func (t *Terminal) Loop() {
// Break out of jump mode if any action is submitted to the server // Break out of jump mode if any action is submitted to the server
if t.jumping != jumpDisabled { if t.jumping != jumpDisabled {
t.jumping = jumpDisabled t.jumping = jumpDisabled
if acts, prs := t.keymap[tui.JumpCancel.AsEvent()]; prs && !doActions(acts) {
continue
}
req(reqList) req(reqList)
} }
if len(actions) == 0 { if len(actions) == 0 {
@ -4121,19 +4124,17 @@ func (t *Terminal) Loop() {
t.truncateQuery() t.truncateQuery()
queryChanged = string(previousInput) != string(t.input) queryChanged = string(previousInput) != string(t.input)
changed = changed || queryChanged changed = changed || queryChanged
if onChanges, prs := t.keymap[tui.Change.AsEvent()]; queryChanged && prs { if onChanges, prs := t.keymap[tui.Change.AsEvent()]; queryChanged && prs && !doActions(onChanges) {
if !doActions(onChanges) {
continue continue
} }
} if onEOFs, prs := t.keymap[tui.BackwardEOF.AsEvent()]; beof && prs && !doActions(onEOFs) {
if onEOFs, prs := t.keymap[tui.BackwardEOF.AsEvent()]; beof && prs {
if !doActions(onEOFs) {
continue continue
} }
}
} else { } else {
jumpEvent := tui.JumpCancel
if event.Type == tui.Rune { if event.Type == tui.Rune {
if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() { if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() {
jumpEvent = tui.Jump
t.cy = idx + t.offset t.cy = idx + t.offset
if t.jumping == jumpAcceptEnabled { if t.jumping == jumpAcceptEnabled {
req(reqClose) req(reqClose)
@ -4141,6 +4142,9 @@ func (t *Terminal) Loop() {
} }
} }
t.jumping = jumpDisabled t.jumping = jumpDisabled
if acts, prs := t.keymap[jumpEvent.AsEvent()]; prs && !doActions(acts) {
continue
}
req(reqList) req(reqList)
} }

View File

@ -108,6 +108,8 @@ const (
One One
Zero Zero
Result Result
Jump
JumpCancel
AltBS AltBS

View File

@ -1468,6 +1468,19 @@ class TestGoFZF < TestBase
assert_equal '3', readonce.chomp assert_equal '3', readonce.chomp
end end
def test_jump_events
tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump,jump:preview(echo jumped to {}),jump-cancel:preview(echo jump cancelled at {})'")}", :Enter
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
tmux.send_keys 'C-j'
tmux.until { |lines| assert_includes lines[-7], '5 5' }
tmux.send_keys '3'
tmux.until { |lines| assert(lines.any? { _1.include?('jumped to 3') }) }
tmux.send_keys 'C-j'
tmux.until { |lines| assert_includes lines[-7], '5 5' }
tmux.send_keys 'C-c'
tmux.until { |lines| assert(lines.any? { _1.include?('jump cancelled at 3') }) }
end
def test_pointer def test_pointer
tmux.send_keys "seq 10 | #{fzf("--pointer '>>'")}", :Enter tmux.send_keys "seq 10 | #{fzf("--pointer '>>'")}", :Enter
# Assert that specified pointer is displayed # Assert that specified pointer is displayed