From d56f605b6338842b415ac1ff578f4316455815fe Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 4 Apr 2022 21:54:22 +0900 Subject: [PATCH] Add `rebind` action for restoring bindings after `unbind` Fix #2752 Close #2564 --- CHANGELOG.md | 1 + man/man1/fzf-tmux.1 | 2 +- man/man1/fzf.1 | 3 ++- src/core.go | 12 ++++++++---- src/options.go | 12 +++++++++--- src/terminal.go | 14 ++++++++++++++ test/test_go.rb | 6 ++++-- 7 files changed, 39 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 061bf85..77582a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ CHANGELOG fzf --nth=-1 --no-hscroll --ellipsis='' | awk '{print $2}' ``` +- Added `rebind` action for restoring bindings after `unbind` - Bug fixes and improvements 0.29.0 diff --git a/man/man1/fzf-tmux.1 b/man/man1/fzf-tmux.1 index d34edb4..5667c7f 100644 --- a/man/man1/fzf-tmux.1 +++ b/man/man1/fzf-tmux.1 @@ -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 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 fzf-tmux - open fzf in tmux split pane diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index d5b3539..906ab19 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -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 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 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) \fBput\fR (put the character to the prompt) \fBrefresh-preview\fR + \fBrebind(...)\fR (rebind bindings after \fBunbind\fR) \fBreload(...)\fR (see below for the details) \fBreplace-query\fR (replace query string with the current selection) \fBselect\fR diff --git a/src/core.go b/src/core.go index 6244c99..17198f5 100644 --- a/src/core.go +++ b/src/core.go @@ -242,9 +242,11 @@ func Run(opts *Options, version string, revision string) { for { delay := true ticks++ - input := func() []rune { + input := func(reloaded bool) []rune { paused, input := terminal.Input() - if !paused { + if reloaded && paused { + query = []rune{} + } else if !paused { query = input } return query @@ -274,7 +276,8 @@ func Run(opts *Options, version string, revision string) { opts.Sync = 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: var command *string @@ -293,7 +296,8 @@ func Run(opts *Options, version string, revision string) { break } snapshot, _ := chunkList.Snapshot() - matcher.Reset(snapshot, input(), true, !reading, sort, clearCache()) + reset := clearCache() + matcher.Reset(snapshot, input(reset), true, !reading, sort, reset) delay = false case EvtSearchProgress: diff --git a/src/options.go b/src/options.go index cc77143..b0afacd 100644 --- a/src/options.go +++ b/src/options.go @@ -798,7 +798,7 @@ func init() { // Backreferences are not supported. // "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|') 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) { @@ -818,6 +818,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) { prefix = symbol + "preview" } else if strings.HasPrefix(src[1:], "unbind") { prefix = symbol + "unbind" + } else if strings.HasPrefix(src[1:], "rebind") { + prefix = symbol + "rebind" } else if strings.HasPrefix(src[1:], "change-prompt") { prefix = symbol + "change-prompt" } else if src[len(prefix)] == '-' { @@ -1025,6 +1027,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) { offset = len("change-prompt") case actUnbind: offset = len("unbind") + case actRebind: + offset = len("rebind") case actExecuteSilent: offset = len("execute-silent") case actExecuteMulti: @@ -1045,8 +1049,8 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) { actionArg = spec[offset+1 : len(spec)-1] actions = append(actions, &action{t: t, a: actionArg}) } - if t == actUnbind { - parseKeyChords(actionArg, "unbind target required") + if t == actUnbind || t == actRebind { + parseKeyChords(actionArg, spec[0:offset]+" target required") } else if t == actChangePreviewWindow { opts := previewOpts{} for _, arg := range strings.Split(actionArg, "|") { @@ -1075,6 +1079,8 @@ func isExecuteAction(str string) actionType { return actReload case "unbind": return actUnbind + case "rebind": + return actRebind case "preview": return actPreview case "change-preview-window": diff --git a/src/terminal.go b/src/terminal.go index 7800714..90d2807 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -136,6 +136,7 @@ type Terminal struct { delimiter Delimiter expect map[tui.Event]string keymap map[tui.Event][]*action + keymapOrg map[tui.Event][]*action pressed string printQuery bool history *History @@ -313,6 +314,7 @@ const ( actSelect actDeselect actUnbind + actRebind ) type placeholderFlags struct { @@ -501,6 +503,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { wordRubout = 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{ initDelay: delay, infoStyle: opts.InfoStyle, @@ -526,6 +532,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { delimiter: opts.Delimiter, expect: opts.Expect, keymap: opts.Keymap, + keymapOrg: keymapCopy, pressed: "", printQuery: opts.PrintQuery, history: opts.History, @@ -2734,6 +2741,13 @@ func (t *Terminal) Loop() { for key := range keys { 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: if t.previewOpts.command != a.a { togglePreview(len(a.a) > 0) diff --git a/test/test_go.rb b/test/test_go.rb index 74f9851..3050fa0 100755 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -2043,8 +2043,8 @@ class TestGoFZF < TestBase tmux.until { |lines| assert_equal(%w[1 2 3 4 5], top5[lines]) } end - def test_unbind - tmux.send_keys "seq 100 | #{FZF} --bind 'c:clear-query,d:unbind(c,d)'", :Enter + def test_unbind_rebind + 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.send_keys 'ab' tmux.until { |lines| assert_equal '> ab', lines[-1] } @@ -2052,6 +2052,8 @@ class TestGoFZF < TestBase tmux.until { |lines| assert_equal '>', lines[-1] } tmux.send_keys 'dabcd' tmux.until { |lines| assert_equal '> abcd', lines[-1] } + tmux.send_keys 'ecabddc' + tmux.until { |lines| assert_equal '> abdc', lines[-1] } end def test_item_index_reset_on_reload