Skip to content
This repository has been archived by the owner on Jun 25, 2024. It is now read-only.

Rework which-key--get-current-bindings #144

Closed

Conversation

pdcawley
Copy link

@pdcawley pdcawley commented Oct 5, 2016

Getting the bindings for a a prefix by parsing the buffer produced by
describe-buffer-bindings seems a little convoluted. Especially when
map-keymap seems to be just the job and has the added advantage of
giving us the actual values of the bindings in the current keymap. This
doesn't matter too much for a bound command, but when the binding is to
a keymap it lets us attempt to get (keymap-prompt binding) as a
potential name.

I'd not be surprised if it's faster too since we're not mucking about
with regular expressions.

Getting the bindings for a a prefix by parsing the buffer produced by
`describe-buffer-bindings` seems a little convoluted. Especially when
`map-keymap` seems to be just the job and has the added advantage of
giving us the actual values of the bindings in the current keymap. This
doesn't matter too much for a bound command, but when the binding is to
a keymap it lets us attempt to get `(keymap-prompt binding)` as a
potential name.

I'd not be surprised if it's faster too since we're not mucking about
with regular expressions.
@pdcawley
Copy link
Author

pdcawley commented Oct 5, 2016

Note that it might be better to move the use of map-keymap further up the stack so that rather than using it to build a list of bindings, just use it to populate a buffer.

@justbur
Copy link
Owner

justbur commented Oct 5, 2016

I'm not totally opposed to this idea, but there are some issues.

  1. I get an error on C-x. The problem is that not every key is a character or a symbol from map-keymap. The lambda might receive (67108896 . pop-global-mark) for example (that's C-x C-@).
  2. map-keymap doesn't tell you which binding is active. I have two bindings for C-x v depending on the minor mode that's active and your method would show both, which is not helpful.
  3. I'm not sure what the organization is from map-keymap while describe-buffer-bindings does have a predictable structure.

Speed has never been an issue for me. It also happens to be the case that helm-descbinds, counsel-descbinds and guide-key use very similar techniques to my current one (last time I checked at least).

Anyway, if you find a nice solution to those issues I'll test some more.

Allow things like

  (define-key global-map (kbd "C-c .")
              (lambda ()
                "open .init.el"
                (find-file-existing user-init-file)))

to Just Work without having to tell which which-key anything else
I was trying to be too clever when dealing with the the 'key' part of
map-keymap. If we just do `(vector key)`, we get something compatible
with key-description
@pdcawley
Copy link
Author

pdcawley commented Oct 5, 2016

Fixed the C-x issue at least.

It's not map-keymap that finds out what the active keymap is, it's key-binding that does the job and that's documented as using the current keymap. I'd be very surprised if we'd see two entries for C-x v because, according to the docs, (key-binding (kbd "C-x") returns the same keymap that'd be used if you'd just typed C-x.

The nice thing about map-keymap from which-key's perspective is that it doesn't descend into keymaps, you just get the top level entries from the keymap you're mapping over.

Binding something like:

  `(menu-map "" nil :filter
     ,(lambda (&optional _)
       (if (some-predicate) 'command-name)))

is a cunning trick for setting up bindings that are only active if some
condition holds. This makes which-key show the correct 'final' binding
by evaluating the filter.
@pdcawley
Copy link
Author

pdcawley commented Oct 6, 2016

That last patch does possibly go a little far, but it does demonstrate what's possible by using map-keymap

@justbur
Copy link
Owner

justbur commented Oct 6, 2016

I haven't been through everything you did, but you can see the multiple key bindings problem here

duplicate-bindings

@pdcawley
Copy link
Author

pdcawley commented Oct 6, 2016

Could you attach the result of (key-binding "C-x")? Or whatever the prefix is that shows the problem?

Which minor mode is it?

EDIT

Ah... no matter. Found it.

Yay for free software! After taking a look at the implementation of
`map-keymap` it's apparent that it respects the ordering of a keymap so
the first entry we get for a given key will be the binding that would be
triggered if we were to actually type that key sequence. So
`-get-raw-current-bindings` now does an assoc look up on the key
description and skips any that have already been seen.

Fixes the issue with C-x u showing up twice while `global-undo-tree` is
turned on (and other, similar cases).
Introduces a new `which-key--canonicalize-bindings` function and
rewrites `which-key--get-raw-current-bindings` in terms of it. This will
allow us to test our keymap munging by passing artificial keymaps into
-canonicalize-bindings, which is a good deal easier than trying to set
up a temporary buffer with the bindings we need (I tried. I couldn't do it).
Minor modes can end up creating keymaps analogous to:

  '(keymap (?a . 'active-binding)
           (keymap (?a . 'overriden-binding)))

We should ensure that the output from `which-key--get-current-bindings`
has eliminated any overridden bindings. This tests that.
Here are the thoughts of an unthinking hacker.

  "Right... the tests are running fine, but I've just realised that that
  parameter is poorly named. I'll just fix that and commit, no need to
  re-run the tests.

  "Hmm... the Travis build is failing... why's that?"

  *tapitty tappity*

  "Ah... yeah, that'd do it, I need to change that parameter name in the
  body of the function don't I?"

We now return you to your regular programming.
@justbur
Copy link
Owner

justbur commented Oct 7, 2016

Still not working. I tried this

(define-key global-map (kbd "C-x M-x") (lambda () (interactive) (message "hi")))

and nothing shows up for M-x after hitting C-x.

I don't see any real advantage to doing this way. The only (pretty minor) advantage I can see is to not have to parse a text buffer, but in it's place you now have to know and keep track of all the rules about how keys are represented and which keys have priority. I think you should give up on this to be honest.

@pdcawley pdcawley closed this Oct 7, 2016
@pdcawley pdcawley reopened this Oct 7, 2016
@pdcawley
Copy link
Author

pdcawley commented Oct 8, 2016

"Having to keep track of all the rules" isn't a bug, it's a feature.

The thing is, if I do:

(define-key help-map "M-x" (cons "description" (lambda () (interactive) "doc string" ...)))

Then it seems reasonable to me that which-key would show M-x → description without any further configuration of its alists, whether I used C-h or <f1>. In order to be able to do that, one has to be able to inspect the ("description" . (lambda () ...) value of the binding. There are binding types that describe-binding doesn't seem to know about that can provide useful descriptions.

I'll work up a full featured branch that shows what I mean and submit another pull request I think.

@justbur
Copy link
Owner

justbur commented Oct 8, 2016

It's definitely a bug right now, because all the bindings are not showing up (#144 (comment)). Maybe if you get the binding and event handling rules right it enables some additional features, but it also adds maintenance burden.

@pdcawley
Copy link
Author

Replaced with #147

@pdcawley pdcawley closed this Oct 10, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants