Subject: bug#13948: no key-binding-locus

From: Nicolas Richard <[email-masked]>
Date: Mon, 02 Jun 2014 12:15:34 +0200

Brian Malehorn <[email-masked]> writes:
> Why isn't there a key equivalent to variable-binding-locus? As
> in, a way to figure out where a particular keybinding is coming
> from. For example,

> So implementing key-binding-locus would only be a small tweak of
> the key lookup code: the first time you find the key, just return
> the map you found it in, rather than the command it's supposed to
> call.

IIUC, it's slightly more complicated than just modifying the return
value of a function. One reason is that "finding active keymaps" and
"looking up keys in keymaps" are done by different bits of the code. By
the time keys are being looked up, we don't know where they keymaps came
from anymore. Another reason is that some keymaps might not even be
stored in any variable (symbol's value cell) at all.

Anyway, here's some code that seems to work in some cases (but not when
the command was remapped, and not for finding where a mouse event is
bound). First is a helper function, then the actual function. Not the
cleanest code, but good enough for my .emacs.

(defun yf/find-object-in-variables (object &optional pred)
  "Find all symbols (variables) whose content is the same as OBJECT.
PRED defaults to `eq'"
  (unless pred (setq pred #'eq))
  (let ((result))
    (mapatoms (lambda (x)
                (when (and (boundp x)
                           (funcall pred (symbol-value x) object))
                  (push x result))))
    result))
(defun yf/key-binding-locus (key)
  "Return a list of symbols whose value is the active keymap
which holds a binding for the given KEY."
  (interactive "KKey seq: ")
  (let ((active-maps (current-active-maps t))
        map found)
    ;; we loop over active-maps like key-binding does.
    (while (not
            (setq found
                  (lookup-key
                   (setq map
                         (pop active-maps))
                   key
                   t)))
      ;; do nothing
      )
    (if (not found)
        (message "Key not found (which is weird, if you want my opinion).")
      (if (and (symbolp found) (command-remapping found))
          ;; fixme. We should mimic command-remapping ?
          (message "Found key but it got remapped and I don't know how to search that.")
        (let ((res (yf/find-object-in-variables map)))
          (if res
              (message "Found key (bound to %s) in a keymap bound to: %S"
                       found
                       res)
            (message "Found key (bound to %s) in a keymap which isn't in any variable."
                     found)))))))

-- 
Nico.