jerrypnz / major-mode-hydra.el

Spacemacs-esque major mode leader key powered by Hydra
297 stars 9 forks source link

Hydra Generator Helper Function #54

Open axelknock opened 11 months ago

axelknock commented 11 months ago

Hello! Great package. I've written a function to generate major mode keybindings lists where the functions are prefixed with C-c C-KEY where KEY is used for the activation key in the hydra. The first line of the function's documentation is pulled in. It serves as a good starting point for defining a major mode hydra.

Example usage:

(flat-hydra-commands 'emacs-lisp-mode-map) =>
(("b" elisp-byte-compile-buffer
  "Byte compile the current buffer, but don’t write a file.")
 ("f" elisp-byte-compile-file
  "Byte compile the file the current buffer is visiting.")
 ("e" elisp-eval-region-or-buffer
  "Evaluate the forms in the active region or the whole current buffer."))

It can also be used interactively with which-key keymap completion.

It's left to the user to group the functions and change the descriptions as they like. I was thinking of a way to group similar functions using their broken up command names, but this is probably good enough. It seems like the whole idea of hydras are bespoke menus anyway.

Relies on functions from embark and which-key, reproduced below.

Code:

;; Source: embark
;; https://github.com/oantolin/embark
(defun embark--all-bindings (keymap &optional nested)
  "Return an alist of all bindings in KEYMAP.
If NESTED is non-nil subkeymaps are not flattened."
  (let (bindings maps)
    (map-keymap
     (lambda (key def)
       (cond
        ((keymapp def)
         (if nested
             (push (cons (vector key) def) maps)
           (dolist (bind (embark--all-bindings def))
             (push (cons (vconcat (vector key) (car bind)) (cdr bind))
                   maps))))
        (def (push (cons (vector key) def) bindings))))
     (keymap-canonicalize keymap))
    (nconc (nreverse bindings) (nreverse maps))))

;; Source: which-key
;; https://github.com/justbur/emacs-which-key
(defvar which-key-keymap-history nil
  "History of keymap selections in functions like
`which-key-show-keymap'.")
(defun which-key--read-keymap ()
  "Read keymap symbol from minibuffer."
  (intern
   (completing-read "Keymap: " obarray
                    (lambda (m)
                      (and (boundp m)
                           (keymapp (symbol-value m))
                           (not (equal (symbol-value m)
                                       (make-sparse-keymap)))))
                    t
                    (let ((sym (symbol-at-point)))
                      (and (boundp sym)
                           (keymapp (symbol-value sym))
                           (symbol-name sym)))
                    'which-key-keymap-history)))

(defun index-to-string (index)
  "Convert positive integers less than 27 to corresponding letters."
  (when (< index 27)
    (char-to-string (+ index 96))))

(defun c-bindings (keymap)
  "List of binding letters prepended with C-c and their functions from KEYMAP."
  (delq nil (mapcar (lambda (binding)
                      (when (eq 3 (aref (car binding) 0))
                        (list (index-to-string (aref (car binding) 1))
                              (cdr binding))))
                    (embark--all-bindings keymap))))

(defun flat-hydra-commands (keymap)
  "Insert c-prefixed bindings of KEYMAP into buffer."
  (interactive (list (which-key--read-keymap)))
  (cl-prettyprint
   (cl-remove-duplicates
    (mapcar (lambda (binding)
              (append binding (list (car (s-lines (documentation (car (cdr binding))))))))
            (c-bindings (symbol-value keymap)))
    :test (lambda (x y) (equal (car x) (car y))))))

This might be useful in the wiki, or for anyone who stumbles on this issue.