Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Rebinding a widget within a keymap
- X-seq: zsh-users 10559
- From: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>
- To: zsh-users@xxxxxxxxxx
- Subject: Rebinding a widget within a keymap
- Date: Sat, 29 Jul 2006 23:29:39 -0700
- Mailing-list: contact zsh-users-help@xxxxxxxxxx; run by ezmlm
The zsh bindkey and zle commands allow one to do most things one might
want to do with keymaps, such as copying one, setting bindings, etc.
However, there isn't a straightforward way to express transformations
such as "temporarily change all keys bound to 'self-insert' to instead
be bound to 'frob'".
This leads to workarounds such as
zle -N self-insert frob
zle recursive-edit
zle -A .self-insert self-insert
This is unsatisfactory, as it's sometimes difficult to guarantee that
the "zle -A" step occurs; and if it doesn't, you're left frobbing in
a context where you should be inserting. Furthermore, this fails if
self-insert was previously replaced by yet some other widget, so to get
that handled properly it's necessary to do
zle -A self-insert saved-self-insert
zle -N self-insert frob
zle recursive-edit
zle -A saved-self-insert self-insert
zle -D saved-self-insert
This introduces possible name clashes on saved-self-insert; e.g., it
can't be invoked again from inside recursive-edit, and every added bit
of code to repair such a problem makes the unwinding more precarious.
Automatically saving and restoring keymaps with
bindkey -N frobber main
zle recursive-edit -K frobber
was meant to help with this, but without doing some sort of processing
on $(bindkey -L) it's not possible to determine which keys are already
bound to self-insert and thus need rebinding in frobber, and we're back
where we started.
Having given you all that background, I'm obviously about to propose a
solution. Here it is.
zmodload -i zsh/zleparameter
for k in $keymaps
do
if (( $+widgets[self-insert-$k] == 0 ))
then zle -A self-insert self-insert-$k
fi
done
self-insert-by-keymap() {
if (( $+widgets[$WIDGET-$KEYMAP] == 1 ))
then zle $WIDGET-$KEYMAP "$@"
else zle .$WIDGET "$@"
fi
}
zle -N self-insert self-insert-by-keymap
This can of course be extended to any/all other widgets; just put a loop
"for w in ${(k)widgets}" around the whole thing and replace "self-insert"
with "$w" throughout. (It's at that point that the lack of control over
autoremoval, which I've mentioned in other threads, becomes an issue, so
if you want to go beyond self-insert you'll have to write it out yourself.)
With this in place, you should rarely need "zle -N self-insert frob" again.
Instead you do this:
bindkey -N frobber main
zle -N self-insert-frobber frob
Then, whenever you wish to replace self-insert with frob, change keymaps:
zle recursive-edit -K frobber
Here's a simple example, which improves upon the caps-lock example in the
zsh manual entry for recursive-edit:
# Assumes self-insert-by-keymap installed as self-insert!
self-insert-ucase() {
LBUFFER+=${(U)KEYS[-1]}
}
zle -N self-insert-ucase
caps-lock() {
bindkey -N ucase $KEYMAP
bindkey -M ucase "$KEYS" .accept-line
zle recursive-edit -K ucase || zle send-break
}
zle -N caps-lock
To turn this on, pick a key sequence (I've chosen ctrl-x shift-L) and
bind the caps-lock widget to it:
bindkey -M main '^XL' caps-lock
An obvious extension to this scheme is to create a variant of accept-line
that notifies the caller of recursive-edit as to whether it should treat
the end of the recursive-edit as final acceptance of the buffer, so that
it's possible e.g. to execute a command without first explicitly leaving
caps-lock "mode".
The other remaining drawback to this scheme is that it can't be employed
at the topmost level of ZLE, because the value of $KEYMAP is always "main"
(or "vicmd" as a special case) at that level.
Note that for builtin widgets the "for k in $keymaps" loop is extraneous;
so it would be possible to embed this widget-name-by-keymap logic in the
C code in ZLE, thereby making it possible to insert an "override" widget
into any specific keymap simply by creating a new widget with the keymap
name appended. Old code that uses "zle -N builtin-widget user-function"
would continue to work, but would break new code that relies on the by-
keymap technique.
--
Messages sorted by:
Reverse Date,
Date,
Thread,
Author