commit 937ab3e1d97426ea344510104172bac7bff6f71f
parent 800cd18ec8ec417c7338eed6c5d5a158af8f0458
Author: Wilfred Hughes <me@wilfred.me.uk>
Date: Wed, 30 May 2018 08:38:38 +0100
Extract keybindings from minor-mode-map-alist too
Fixes #112.
Diffstat:
3 files changed, 82 insertions(+), 24 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
@@ -1,6 +1,7 @@
# v0.11
-No changes yet.
+Fixed an issue where Helpful didn't find keybindings if the command
+was only referenced in `minor-mode-map-alist`.
# v0.10
diff --git a/helpful.el b/helpful.el
@@ -1134,36 +1134,68 @@ buffer."
(push sym keymaps))))
keymaps))
+(defun helpful--key-sequences (command-sym keymap)
+ "Return all the key sequences of COMMAND-SYM in KEYMAP."
+ (let* ((keycodes
+ ;; Look up this command in the keymap, its parent and the
+ ;; global map. We need to include the global map to find
+ ;; remapped commands.
+ (where-is-internal command-sym keymap nil t))
+ ;; Look up this command in the parent keymap.
+ (parent-keymap (keymap-parent keymap))
+ (parent-keycodes
+ (when parent-keymap
+ (where-is-internal
+ command-sym (list parent-keymap) nil t)))
+ ;; Look up this command in the global map.
+ (global-keycodes
+ (unless (eq keymap global-map)
+ (where-is-internal
+ command-sym (list global-map) nil t))))
+ (->> keycodes
+ ;; Ignore keybindings from the parent or global map.
+ (--remove (-contains-p parent-keycodes it))
+ (--remove (-contains-p global-keycodes it))
+ ;; Convert raw keycode vectors into human-readable strings.
+ (-map #'key-description))))
+
(defun helpful--keymaps-containing (command-sym)
- "Return a list of pairs listing keymap symbols that contain COMMAND-SYM,
+ "Return a list of pairs listing keymap names that contain COMMAND-SYM,
along with the keybindings in each keymap.
+Keymap names are typically variable names, but may also be
+descriptions of values in `minor-mode-map-alist'.
+
We ignore keybindings that are menu items, and ignore keybindings
from parent keymaps.
`widget-global-map' is also ignored as it generally contains the
same bindings as `global-map'."
- (let (matching-keymaps)
- ;; Look for this command in all keymaps.
- (dolist (keymap-sym (helpful--all-keymap-syms))
- (let* ((keymap (symbol-value keymap-sym))
- (keycodes
- (where-is-internal
- command-sym (list keymap) nil t))
- ;; Look up this command in the parent keymap.
- (parent-keymap (keymap-parent keymap))
- (parent-keycodes
- (when parent-keymap
- (where-is-internal
- command-sym (list parent-keymap) nil t))))
- (setq keycodes
- ;; Ignore keybindings that we've just inherited from the
- ;; parent.
- (-difference keycodes parent-keycodes))
- (when (and keycodes (not (eq keymap-sym 'widget-global-map)))
- (push (cons keymap-sym
- (-map #'key-description keycodes))
- matching-keymaps))))
+ (let* ((keymap-syms (helpful--all-keymap-syms))
+ (keymap-sym-vals (-map #'symbol-value keymap-syms))
+ matching-keymaps)
+ ;; Look for this command in all keymaps bound to variables.
+ (-map
+ (-lambda ((keymap-sym . keymap))
+ (let ((key-sequences (helpful--key-sequences command-sym keymap)))
+ (when (and key-sequences (not (eq keymap-sym 'widget-global-map)))
+ (push (cons (symbol-name keymap-sym) key-sequences)
+ matching-keymaps))))
+ (-zip keymap-syms keymap-sym-vals))
+
+ ;; Look for this command in keymaps used by minor modes that
+ ;; aren't bound to variables.
+ (-map
+ (-lambda ((minor-mode . keymap))
+ ;; Only consider this keymap if we didn't find it bound to a variable.
+ (unless (memq keymap keymap-sym-vals)
+ (let ((key-sequences (helpful--key-sequences command-sym keymap)))
+ (when key-sequences
+ (push (cons (format "minor-mode-map-alist (%s)" minor-mode)
+ key-sequences)
+ matching-keymaps)))))
+ minor-mode-map-alist)
+
matching-keymaps))
(defun helpful--format-keys (command-sym)
@@ -1175,7 +1207,7 @@ same bindings as `global-map'."
(dolist (key keys)
(push
(format "%s %s"
- (propertize (symbol-name map) 'face 'font-lock-variable-name-face)
+ (propertize map 'face 'font-lock-variable-name-face)
key)
(if (eq map 'global-map) global-lines mode-lines)))))
(setq global-lines (-sort #'string< global-lines))
diff --git a/test/unit-test.el b/test/unit-test.el
@@ -468,6 +468,31 @@ associated a lambda with a keybinding."
([17] forward-line)
([97] forward-char))))))
+(defun helpful--dummy-command ()
+ (interactive))
+
+(ert-deftest helpful--keymaps-containing ()
+ "Ensure that we find keymaps for variables with bindings."
+ ;; This is defined in the global map.
+ (should
+ (helpful--keymaps-containing #'where-is))
+ ;; This is only defined in `minor-mode-map-alist'.
+ (should
+ (helpful--keymaps-containing #'ido-display-buffer))
+ ;; Create a keybinding that is very unlikely to clobber actually
+ ;; defined keybindings in the current emacs instance.
+ (global-set-key (kbd "C-c M-S-c") #'helpful--dummy-command)
+
+ ;; This command should only be in `global-map' and
+ ;; `mode-specific-map'.
+ (should
+ (equal
+ (length (helpful--keymaps-containing #'helpful--dummy-command))
+ 2))
+
+ ;; Undo keybinding.
+ (global-set-key (kbd "C-c M-S-c") nil))
+
(ert-deftest helpful--source ()
(let* ((source (helpful--source #'helpful--source t)))
(should