Add `rebind` action for restoring bindings after `unbind`

Fix #2752
Close #2564
pull/2782/head
Junegunn Choi 2 years ago
parent f8b713f425
commit d56f605b63
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627

@ -12,6 +12,7 @@ CHANGELOG
fzf --nth=-1 --no-hscroll --ellipsis='' | fzf --nth=-1 --no-hscroll --ellipsis='' |
awk '{print $2}' awk '{print $2}'
``` ```
- Added `rebind` action for restoring bindings after `unbind`
- Bug fixes and improvements - Bug fixes and improvements
0.29.0 0.29.0

@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf-tmux 1 "Mar 2022" "fzf 0.30.0" "fzf-tmux - open fzf in tmux split pane" .TH fzf-tmux 1 "Apr 2022" "fzf 0.30.0" "fzf-tmux - open fzf in tmux split pane"
.SH NAME .SH NAME
fzf-tmux - open fzf in tmux split pane fzf-tmux - open fzf in tmux split pane

@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf 1 "Mar 2022" "fzf 0.30.0" "fzf - a command-line fuzzy finder" .TH fzf 1 "Apr 2022" "fzf 0.30.0" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@ -867,6 +867,7 @@ A key or an event can be bound to one or more of the following actions.
\fBprint-query\fR (print query and exit) \fBprint-query\fR (print query and exit)
\fBput\fR (put the character to the prompt) \fBput\fR (put the character to the prompt)
\fBrefresh-preview\fR \fBrefresh-preview\fR
\fBrebind(...)\fR (rebind bindings after \fBunbind\fR)
\fBreload(...)\fR (see below for the details) \fBreload(...)\fR (see below for the details)
\fBreplace-query\fR (replace query string with the current selection) \fBreplace-query\fR (replace query string with the current selection)
\fBselect\fR \fBselect\fR

@ -242,9 +242,11 @@ func Run(opts *Options, version string, revision string) {
for { for {
delay := true delay := true
ticks++ ticks++
input := func() []rune { input := func(reloaded bool) []rune {
paused, input := terminal.Input() paused, input := terminal.Input()
if !paused { if reloaded && paused {
query = []rune{}
} else if !paused {
query = input query = input
} }
return query return query
@ -274,7 +276,8 @@ func Run(opts *Options, version string, revision string) {
opts.Sync = false opts.Sync = false
terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false) terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false)
} }
matcher.Reset(snapshot, input(), false, !reading, sort, clearCache()) reset := clearCache()
matcher.Reset(snapshot, input(reset), false, !reading, sort, reset)
case EvtSearchNew: case EvtSearchNew:
var command *string var command *string
@ -293,7 +296,8 @@ func Run(opts *Options, version string, revision string) {
break break
} }
snapshot, _ := chunkList.Snapshot() snapshot, _ := chunkList.Snapshot()
matcher.Reset(snapshot, input(), true, !reading, sort, clearCache()) reset := clearCache()
matcher.Reset(snapshot, input(reset), true, !reading, sort, reset)
delay = false delay = false
case EvtSearchProgress: case EvtSearchProgress:

@ -798,7 +798,7 @@ func init() {
// Backreferences are not supported. // Backreferences are not supported.
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|') // "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|unbind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|unbind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`) `(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
} }
func parseKeymap(keymap map[tui.Event][]*action, str string) { func parseKeymap(keymap map[tui.Event][]*action, str string) {
@ -818,6 +818,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) {
prefix = symbol + "preview" prefix = symbol + "preview"
} else if strings.HasPrefix(src[1:], "unbind") { } else if strings.HasPrefix(src[1:], "unbind") {
prefix = symbol + "unbind" prefix = symbol + "unbind"
} else if strings.HasPrefix(src[1:], "rebind") {
prefix = symbol + "rebind"
} else if strings.HasPrefix(src[1:], "change-prompt") { } else if strings.HasPrefix(src[1:], "change-prompt") {
prefix = symbol + "change-prompt" prefix = symbol + "change-prompt"
} else if src[len(prefix)] == '-' { } else if src[len(prefix)] == '-' {
@ -1025,6 +1027,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) {
offset = len("change-prompt") offset = len("change-prompt")
case actUnbind: case actUnbind:
offset = len("unbind") offset = len("unbind")
case actRebind:
offset = len("rebind")
case actExecuteSilent: case actExecuteSilent:
offset = len("execute-silent") offset = len("execute-silent")
case actExecuteMulti: case actExecuteMulti:
@ -1045,8 +1049,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) {
actionArg = spec[offset+1 : len(spec)-1] actionArg = spec[offset+1 : len(spec)-1]
actions = append(actions, &action{t: t, a: actionArg}) actions = append(actions, &action{t: t, a: actionArg})
} }
if t == actUnbind { if t == actUnbind || t == actRebind {
parseKeyChords(actionArg, "unbind target required") parseKeyChords(actionArg, spec[0:offset]+" target required")
} else if t == actChangePreviewWindow { } else if t == actChangePreviewWindow {
opts := previewOpts{} opts := previewOpts{}
for _, arg := range strings.Split(actionArg, "|") { for _, arg := range strings.Split(actionArg, "|") {
@ -1075,6 +1079,8 @@ func isExecuteAction(str string) actionType {
return actReload return actReload
case "unbind": case "unbind":
return actUnbind return actUnbind
case "rebind":
return actRebind
case "preview": case "preview":
return actPreview return actPreview
case "change-preview-window": case "change-preview-window":

@ -136,6 +136,7 @@ type Terminal struct {
delimiter Delimiter delimiter Delimiter
expect map[tui.Event]string expect map[tui.Event]string
keymap map[tui.Event][]*action keymap map[tui.Event][]*action
keymapOrg map[tui.Event][]*action
pressed string pressed string
printQuery bool printQuery bool
history *History history *History
@ -313,6 +314,7 @@ const (
actSelect actSelect
actDeselect actDeselect
actUnbind actUnbind
actRebind
) )
type placeholderFlags struct { type placeholderFlags struct {
@ -501,6 +503,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep) wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep) wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
} }
keymapCopy := make(map[tui.Event][]*action)
for key, action := range opts.Keymap {
keymapCopy[key] = action
}
t := Terminal{ t := Terminal{
initDelay: delay, initDelay: delay,
infoStyle: opts.InfoStyle, infoStyle: opts.InfoStyle,
@ -526,6 +532,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
delimiter: opts.Delimiter, delimiter: opts.Delimiter,
expect: opts.Expect, expect: opts.Expect,
keymap: opts.Keymap, keymap: opts.Keymap,
keymapOrg: keymapCopy,
pressed: "", pressed: "",
printQuery: opts.PrintQuery, printQuery: opts.PrintQuery,
history: opts.History, history: opts.History,
@ -2734,6 +2741,13 @@ func (t *Terminal) Loop() {
for key := range keys { for key := range keys {
delete(t.keymap, key) delete(t.keymap, key)
} }
case actRebind:
keys := parseKeyChords(a.a, "PANIC")
for key := range keys {
if originalAction, found := t.keymapOrg[key]; found {
t.keymap[key] = originalAction
}
}
case actChangePreview: case actChangePreview:
if t.previewOpts.command != a.a { if t.previewOpts.command != a.a {
togglePreview(len(a.a) > 0) togglePreview(len(a.a) > 0)

@ -2043,8 +2043,8 @@ class TestGoFZF < TestBase
tmux.until { |lines| assert_equal(%w[1 2 3 4 5], top5[lines]) } tmux.until { |lines| assert_equal(%w[1 2 3 4 5], top5[lines]) }
end end
def test_unbind def test_unbind_rebind
tmux.send_keys "seq 100 | #{FZF} --bind 'c:clear-query,d:unbind(c,d)'", :Enter tmux.send_keys "seq 100 | #{FZF} --bind 'c:clear-query,d:unbind(c,d),e:rebind(c,d)'", :Enter
tmux.until { |lines| assert_equal 100, lines.item_count } tmux.until { |lines| assert_equal 100, lines.item_count }
tmux.send_keys 'ab' tmux.send_keys 'ab'
tmux.until { |lines| assert_equal '> ab', lines[-1] } tmux.until { |lines| assert_equal '> ab', lines[-1] }
@ -2052,6 +2052,8 @@ class TestGoFZF < TestBase
tmux.until { |lines| assert_equal '>', lines[-1] } tmux.until { |lines| assert_equal '>', lines[-1] }
tmux.send_keys 'dabcd' tmux.send_keys 'dabcd'
tmux.until { |lines| assert_equal '> abcd', lines[-1] } tmux.until { |lines| assert_equal '> abcd', lines[-1] }
tmux.send_keys 'ecabddc'
tmux.until { |lines| assert_equal '> abdc', lines[-1] }
end end
def test_item_index_reset_on_reload def test_item_index_reset_on_reload

Loading…
Cancel
Save