Subject: bug#43709: minibuffer keybinding cheatsheet and launcher [CODE SUBMISSION]

From: Boruch Baum <[email-masked]>
Date: Thu, 1 Oct 2020 11:31:47 -0400

On 2020-10-01 09:51, Stefan Monnier wrote:
> I'm not completely sure what is the intended use scenario.

1) Interactively...

1.1) A user finds himself in an unfamiliar mode buffer, and isn't sure
     what that mode's options are and how to use them. You mention
     further that you tried it in a dired-mode, so you saw its default
     operation in such a case. For dired-mode, the result-list is very
     long; for other modes, such as occur-mode or a packages buffer,
     you'll get a shorter list. The more obscure the mode, the more
     useful this use-case.

1.2) A user has a vague idea of a command name of interest, so enters a
     regex or substring of it to see a list of keybindings and
     descriptions of matches (as relevant for the current buffer).

2) Programmatically...

2.1) A developer wants to offer a cheat sheet / launcher for a package
     (similar in function to transient/hydra) but with less programming.
     This package offers wide latitude of how to compose a command list,
     what to display, how to sort, how to prompt. The args can be
     trivially simple to quite complex.

     (defun my-mode-low-budget-transient ()
       (interactive)
       (when (eq major-mode my-mode)
         (key-assist ...)))
     (define-key my-mode-map "?" 'my-mode-low-budget-transient)

BTW, On the emacs-bug thread/URL, I shared with Drew Adams the
original inspiration for the package, which includes the motivation for
what seems like an obscure arg option (cmd . function)

> [ To see why I say it's not quite ready, here's my experience with it:
>   I just tried it in a fresh new Emacs session, and after `M-x
>   key-assist` (which itself is a problem because most users won't
>   know/bother to run this command way, so we'd need some more "obvious"
>   way to run it)

Always a problem with a new feature. When you say 'obvious', you seem to
mean more than the normal publicizing of a new feature?

>   I get a prompt for something

You mean the prompt "Optional: Enter command regexp or keymap name:"?

>   and without reading more the description I wouldn't know what the
>   prompt wants

True of very many emacs commands. Not a justification though. It could
be a multi-line verbose explanatory prompt.

>   (I think this prompt should only appear if requested explicitly)

Reasonable. Could be argued both ways. I have no preference, and just
thought it friendlier this way to explicitly present to the user all the
options, especially since this is supposed to be a 'help' function.
Programmatically, this prompt shouldn't ever appear.

>    and then it said "no choices found".

That's curious. I haven't seen that in my testing. What was your
response to the first prompt? What was the value of 'major-mode' of the
'current-buffer' when you ran the command?

>   So I tried again in a dired buffer, where it instead showed me a
>   minibuffer prompt "Select: " with no indication of what it is I
>   should be selecting,

I had difficulty deciding what text to use for this prompt because many
users can be expected to be using some non-default minibuffer completion
supplemental package (eg. ido, icicles, ivy), and those all have
slightly different behavior; many present a result-list without needing
to TAB.

>   after that I hit TAB (because I read that it uses `completing-read`,
>   but for the un-initiated it might not be obvious)

Again, true of very many emacs commands. Not a justification though. I'm
not a regular on this list, but even slashdot.org is reporting that the
list has been discussing making emacs friendlier to new users, and I'm
on board with that, so I'll take any suggestions for improvement here,
but it might be better done as a patch to 'completing-read'.

>   at which point it did show me a list of bindings and short
>   descriptions and when I selected the first entry (i.e. `!`) it
>   signal'd an error about wrong number of arguments. ]

Got it. Thanks much for your concrete suggestion further down on how to
correct this bug. Many of the other options *DO* work though; this is a
bug in the improper way the function calls some interactive functions.

> I do think it would be nice to have something like it, since it's an
> alternative to the menus which has the advantage of being
> auto-constructed, doesn't need the mouse, and focuses on the keybindings.

Yep. Also, for me it was important that it present command descriptions,
even if in the end it came at the expense of omitting command names to
make the strings shorter.

> It would also be nice to make it usable from minibuffers as well ;-)

Hmm, I wonder.. Maybe it already does programmatically on a per-case
basis. Interactively, do you mean integrating into the minibuffer
codebase? I've long wanted something like this for the minibuffer
options to isearch-

> See some comments below based on a quick look at the code.

I appreciate the comments a lot. Thanks for the constructive review.

> `where-is-internal` is supposed to return the shortest binding already
> when asked to return the "firstonly".

I wasn't getting my desired result with it..

> Your notion of length is slightly different, admittedly, but I wonder
> what were the concrete cases that motivated doing your own sorting

Oops. Forgetting just now. A secondary motivation for the sort was to
offer multiple sort options. In the end, I left it as an option for
programmers to pass their own sort function. For example, someone may
want to treat prefixes / upper-case differently.

> >      (while (not (setq choice
> >                    (cl-position
> >                      (substring-no-properties ; Is -no-properties necessary?
> >                        (completing-read prompt choices nil t))
> >                      choices :test 'equal))))
>
> Why do you have this loop?  Doesn't the `require-match` arg of
> `completing-read` make it unnecessary?

I remember thinking so, too, and then needing to add the loop because
completing-read was accepting a RET. My recollection is that it was
sending an empty string to cl-position.

> ....

But besides all that, it's great, right?

--
hkp://keys.gnupg.net
CA45 09B5 5351 7C11 A9D1  7286 0036 9E45 1595 8BC0