Subject: Re: bug#39035: Show key bindings on M-x completion

From: Juri Linkov <[email-masked]>
Date: Tue, 28 Jan 2020 23:39:13 +0200

> Severity: wishlist
>
> In the completion interface for M-x, please add functionality to show
> the key bindings in parenthesis after the command.  This functionality
> is already there in the highly popular helm and swiper completion
> frameworks, which could be useful for reference.
>
> For example, when typing M-x kmacro TAB, one should see:
>
>    kmacro-insert-counter (C-x C-k TAB)
>    kmacro-set-counter (C-x C-k C-c)
>    [...]
>
> Ideally, the keybinding should also use a different color from the command.
>
> This was discussed on emacs-devel, and Stefan Monnier suggested that
> it shouldn't be too hard to do:
> https://lists.gnu.org/archive/html/emacs-devel/2020-01/msg00115.html

Indeed, not hard at all:

diff --git a/lisp/simple.el b/lisp/simple.el
index 00a706848b..b0159df203 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -1783,17 +1783,29 @@ read-extended-command
 	     ;; and it serves as a shorthand for "Extended command: ".
 	     "M-x ")
      (lambda (string pred action)
-       (let ((pred
-              (if (memq action '(nil t))
-                  ;; Exclude obsolete commands from completions.
-                  (lambda (sym)
-                    (and (funcall pred sym)
-                         (or (equal string (symbol-name sym))
-                             (not (get sym 'byte-obsolete-info)))))
-                pred)))
-         (complete-with-action action obarray string pred)))
+       (if (and suggest-key-bindings (eq action 'metadata))
+	   '(metadata
+	     (annotation-function . read-extended-command--annotation)
+	     (category . suggest-key-bindings))
+         (let ((pred
+                (if (memq action '(nil t))
+                    ;; Exclude obsolete commands from completions.
+                    (lambda (sym)
+                      (and (funcall pred sym)
+                           (or (equal string (symbol-name sym))
+                               (not (get sym 'byte-obsolete-info)))))
+                  pred)))
+           (complete-with-action action obarray string pred))))
      #'commandp t nil 'extended-command-history)))
 
+(defun read-extended-command--annotation (command-name)
+  (let* ((function (and (stringp command-name) (intern-soft command-name)))
+         (binding (where-is-internal function overriding-local-map t)))
+    (when binding
+      (format " (%s)" (if (stringp binding)
+                          (concat "M-x " binding " RET")
+                        (key-description binding))))))
+
 (defcustom suggest-key-bindings t
   "Non-nil means show the equivalent key-binding when M-x command has one.
 The value can be a length of time to show the message for.