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

Fix #2752
Close #2564
This commit is contained in:
Junegunn Choi 2022-04-04 21:54:22 +09:00
parent f8b713f425
commit d56f605b63
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
7 changed files with 39 additions and 11 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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":

View File

@ -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)

View File

@ -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