commit b42bd5adcbf910bffec44022d1c99df7322d98f7
parent c9a3b0428dcbda6c8f3c3a0926307f2a52cdfd14
Author: Wilfred Hughes <me@wilfred.me.uk>
Date: Fri, 16 Mar 2018 21:21:25 +0000
Fix keymap rendering with prefix keys
See projectile-mode for an example.
Diffstat:
3 files changed, 53 insertions(+), 18 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
@@ -5,6 +5,9 @@ underlying symbol, show the `defalias` call in the source code, don't
show duplicates in the Aliases list, and cross-reference the Elisp
manual.
+Fixed an issue where keymaps with prefix keys were not rendered
+correctly.
+
Fixed an issue when looking at variable docs where we would visit the
definition buffer without the user requesting it.
diff --git a/helpful.el b/helpful.el
@@ -669,9 +669,21 @@ Flattens nested keymaps and follows remapped commands.
Returns a list of pairs (KEYCODES COMMAND), where KEYCODES is a
vector suitable for `key-description', and COMMAND is a smbol."
- (let (result)
- (if (symbolp keymap)
- (push (list (vector) keymap) result)
+ (cond
+ ;; Prefix keys.
+ ((and
+ (symbolp keymap)
+ (fboundp keymap)
+ ;; Prefix keys use a keymap in the function slot of a symbol.
+ (keymapp (symbol-function keymap)))
+ (helpful--keymap-keys (symbol-function keymap)))
+ ;; Other symbols mean we've reached a leaf, so this is a command
+ ;; we can call.
+ ((symbolp keymap)
+ `(([] ,keymap)))
+ ;; Otherwise, recurse on the keys at this level of the keymap.
+ (t
+ (let (result)
(dolist (item (cdr keymap))
(cond
((and (consp item)
@@ -693,21 +705,21 @@ vector suitable for `key-description', and COMMAND is a smbol."
(-lambda ((keycodes command))
(push (list (vconcat (vector keycode) keycodes) command)
result))))
- item)))))
- ;; For every command `foo' mapped to a command `bar', show `foo' with
- ;; the key sequence for `bar'.
- (setq result
- (-map-when
- (-lambda ((keycodes _))
- (and (> (length keycodes) 1)
- (eq (elt keycodes 0) 'remap)))
- (-lambda ((keycodes command))
- (list
- (where-is-internal (elt keycodes 1) global-map t)
- command))
- result))
- ;; Preserve the original order of the keymap.
- (nreverse result)))
+ item))))
+ ;; For every command `new-func' mapped to a command `orig-func', show `new-func' with
+ ;; the key sequence for `orig-func'.
+ (setq result
+ (-map-when
+ (-lambda ((keycodes _))
+ (and (> (length keycodes) 1)
+ (eq (elt keycodes 0) 'remap)))
+ (-lambda ((keycodes command))
+ (list
+ (where-is-internal (elt keycodes 1) global-map t)
+ command))
+ result))
+ ;; Preserve the original order of the keymap.
+ (nreverse result)))))
;; TODO: unlike `substitute-command-keys', this shows keybindings
;; which are currently shadowed (e.g. a global minor mode map).
@@ -830,6 +842,8 @@ This is used in `helpful--syntax-highlight' to support extra
highlighting that the user may have configured in their mode
hooks.")
+;; TODO: crashes on `backtrace-frame' on a recent checkout.
+
(defun helpful--syntax-highlight (source &optional mode)
"Return a propertized version of SOURCE in MODE."
(unless mode
diff --git a/test/unit-test.el b/test/unit-test.el
@@ -357,6 +357,24 @@ associated a lambda with a keybinding."
([3 27 1] backward-char)
([97] forward-char))))))
+(defvar helpful--dummy-keymap
+ (let ((keymap (make-sparse-keymap)))
+ (define-key keymap (kbd "a") #'forward-char)
+ keymap))
+
+;; `fset' is necessary for keymaps as prefixes. This is a quirky Emacs
+;; API: https://emacs.stackexchange.com/q/28576/304
+(fset 'helpful--dummy-keymap helpful--dummy-keymap)
+
+(ert-deftest helpful--keymap-keys--prefix ()
+ "Test we flatten keymaps with prefix keys."
+ (let* ((keymap (make-sparse-keymap)))
+ (define-key keymap (kbd "C-c") 'helpful--dummy-keymap)
+ (should
+ (equal
+ (helpful--keymap-keys keymap)
+ '(([3 97] forward-char))))))
+
(ert-deftest helpful--keymap-keys ()
(let* ((parent-keymap (make-keymap))
(keymap (make-keymap)))