Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Why the widget bound to menuselect isn't called?

Sebastian Gniazdowski wrote:
> function double-accept { echo 1 >> /tmp/reply; }
> zle -N double-accept
> bindkey -M menuselect ' ' double-accept
> Why?

Because custom widgets aren't supported by complist and never have been.
In the documentation for the complist module, just before the effect of
the various zle widgets are described, it states:

       The following zle functions have special meaning during menu
       selection.  Note that the following 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

That doesn't really answer your question of "Why?" but this isn't easy
to explain briefly. What follows is more suited to -workers:

It is a feature to give "special meaning" to existing widgets
for something like menu selection. Consider something like
accept-and-infer-next-history: accepting the current selection and
allowing menu selection to continue is conceptually very similar
to accepting a line from history and moving to the next one. So
the special meaning allows whatever key a user configured to
accept-and-infer-next-history to do something similar in menu selection.
Note that the menuselect keymap is a local keymap so only acts to
override bindings in whatever your main keymap is.

complist is implemented as a module so the code for
accept-and-infer-next-history can't check whether menu selection
is active unlike the checks for, e.g. the region being active.

So the complist code does stuff like:

   cmd = getkeycmd();
   if (cmd == TH(z_viinsert)) {
      /* enter interactive mode */
   } else if (cmd ==  Th(z_acceptandinfernexthistory) {
      /* special meaning code for accept-and-infer-next-history */
   } else if ....
   } else /* any other widget */
      /* accept selected match */

If you search for getkeycmd() in zle_main.c, you'll see it followed
by a call to execzlefunc().

It isn't just menuselect but also the command, isearch, listscroll
keymaps where this happens. When incremental history was implemented, we
didn't have local keymaps or user defined widgets so the implementation
there was perfectly natural at the time.

If you're wondering what it would take to make this work while not
breaking exist user's keybindings, the short answer is quite a lot.
Primarily, we need to handle keymap specific special meanings some other

We could take the concepts embodied in Functions/Zle/keymap+widget and
enshrine it in the C code. I'm not sure I entirely like that because
you end up with menuselect+forward-char and menuselect+vi-forward-char
widgets rather than a single menuselect-next-match. And how do you
reassign forward-char to be column-right rather than next-match (we've
had user questions in the past about right-cursor moving to the next row
from the last column). A user might just rebind l or cursor-right or
whatever but a plugin may want to act independently of the actual keys.
Perhaps menuselect+forward-char etc could be aliases.

We could allow zle widget aliases to be keymap specific. So we'd
predefine the aliases and they could be changed, e.g:
  zle -A forward-char -M menuselect menuselect-column-right

A more flexible approach might be to allow conditional zle widget
aliases. The condition could be zstyle-like encompassing things like
whether the region is active, keymap, PS2 active, completion active,
lastwidget. Does that sound useful or over-complicated? keymap+widget
might be a useful just as a naming convention.

Once before when this was vaguely discussed, Bart mentioned emacs minor
modes. Does anyone know how those are implemented underneath?

Aside from that new mechanism, there'd be lots of factoring out of
special widgets into their own functions and making sure they don't
crash the shell if invoked in the wrong context. It'd inevitably also
lead to some extra APIs to allow finer control of menu selection such as
the example I gave about moving right when already on the last column.


PS. bindkey -s will work. Does the following do what you wanted with double-accept?

  bindkey -M menuselect -s ' ' '^@^M'
  bindkey -M menuselect '^@' auto-suffix-remove

Messages sorted by: Reverse Date, Date, Thread, Author