I'm using the zsh
shell, and I'm trying to install a few key bindings to use keys similar to the ones I would use in a Vim buffer, when a completion menu is open.
So, inside the menuselect
keymap I've bound the keys j
and k
to the zle
widgets down-line-or-history
and up-line-or-history
, by adding the following lines inside ~/.zshrc
:
bindkey -M menuselect 'j' down-line-or-history
bindkey -M menuselect 'k' up-line-or-history
down-line-or-history
and up-line-or-history
are described in man zshzle
as follows:
down-line-or-history (^N ESC-[B) (j) (ESC-[B)
Move down a line in the buffer, or if already at the bottom line, move to the next event in the his‐
tory list.
up-line-or-history (^P ESC-[A) (k) (ESC-[A)
Move up a line in the buffer, or if already at the top line, move to the previous event in the history
list.
Now, I would like to bind C-d
and C-u
to the same widgets, but repeating them an arbitrary number of times, for example 5
.
At first I tried this simple code:
some-widget() {
zle backward-char -n 5
}
zle -N some-widget
bindkey '^D' some-widget
It binds C-d
to the zle widget backward-char
, but repeats it 5
times.
Then, I tried to rewrite the code, moving the key binding from the default keymap to the menuselect
keymap:
some-widget() {
zle backward-char -n 5
}
zle -N some-widget
bindkey -M menuselect '^D' some-widget
But it doesn't work as I expected, because when I hit C-d
while a completion menu is open, zle
seems to execute the default widget bound to C-d
, which is delete-char-or-list
:
delete-char-or-list (^D) (unbound) (unbound)
Delete the character under the cursor. If the cursor is at the end of the line, list possible comple‐
tions for the current word.
It exits the current completion menu, and lists possible completions for the current word, instead of moving the cursor backward 5
times.
If it had worked as I expected, I would probably have ended up using this final code:
fast-down-line-or-history() {
zle down-line-or-history -n 5
}
zle -N fast-down-line-or-history
bindkey -M menuselect '^D' fast-down-line-or-history
fast-up-line-or-history() {
zle up-line-or-history -n 5
}
zle -N fast-up-line-or-history
bindkey -M menuselect '^U' fast-up-line-or-history
But since it doesn't, I need to find how to repeat a zle
widget when a completion menu is open.
How to modify the previous code, so that down-line-or-history
is repeated 5
times when hitting C-d
while the completion menu is open?
Best Answer
Your code doesn't work because the widgets "always perform the same task within the menu selection map and cannot be replaced by user defined widgets, nor can the set of functions be extended".
However, the following does work:
The
-s
option lets you specify a literal string that will be input to the Zsh Line Editor (ZLE) as if you had typed it yourself. So, now, when you press ctrlD, will press j five times for you, giving you the desired effect.Note, though, that it's not particularly fast. I can literally see the cursor moving 5 steps up or down, one step at a time. ?
Instead, if you have
group-name
s enabled, you might be better off bindingvi-forward-blank-word
, which (according to the documentation) "moves the mark to the first line of the next group of matches" andvi-backward-blank-word
, which "moves the mark to the last line of the previous group of matches".