Skip to content

Instantly share code, notes, and snippets.

@alexforsale
Forked from progfolio/general-spacemacs.org
Created July 10, 2023 13:52
Show Gist options
  • Select an option

  • Save alexforsale/94c9a5f28b988476f675963e8c4d0c42 to your computer and use it in GitHub Desktop.

Select an option

Save alexforsale/94c9a5f28b988476f675963e8c4d0c42 to your computer and use it in GitHub Desktop.
Spacemacs-like menus using general.el
* Spacemacs-like menus using general.el
** Global keybindings
First, we define a global prefix key:
#+begin_src emacs-lisp
(general-create-definer global-definer
:keymaps 'override
:states '(insert emacs normal hybrid motion visual operator)
:prefix "SPC"
:non-normal-prefix "S-SPC")
#+end_src
Bind top level, global commands like so:
#+begin_src emacs-lisp
(global-definer
"!" 'shell-command
":" 'eval-expression)
#+end_src
** Nested menus
To ease the creation of nested menus, we define a macro:
#+begin_src emacs-lisp
(defmacro +general-global-menu! (name infix-key &rest body)
"Create a definer named +general-global-NAME wrapping global-definer.
Create prefix map: +general-global-NAME. Prefix bindings in BODY with INFIX-KEY."
(declare (indent 2))
`(progn
(general-create-definer ,(intern (concat "+general-global-" name))
:wrapping global-definer
:prefix-map (quote ,(intern (concat "+general-global-" name "-map")))
:infix ,infix-key
:wk-full-keys nil
"" '(:ignore t :which-key ,name))
(,(intern (concat "+general-global-" name))
,@body)))
#+end_src
This macro defines top-level nested menus. e.g. a menu for "buffer" bindings:
#+begin_src emacs-lisp
(+general-global-menu! "buffer" "b"
"d" 'kill-current-buffer
"o" '((lambda () (interactive) (switch-to-buffer nil))
:which-key "other-buffer")
"p" 'previous-buffer
"r" 'rename-buffer
"M" '((lambda () (interactive) (switch-to-buffer "*Messages*"))
:which-key "messages-buffer")
"n" 'next-buffer
"s" '((lambda () (interactive) (switch-to-buffer "*scratch*"))
:which-key "scratch-buffer")
"TAB" '((lambda () (interactive) (switch-to-buffer nil))
:which-key "other-buffer"))
#+end_src
You can specify per-package keybindings with the =:general= use-package keyword.
For example, here's a minimal helm configuration that binds =SPC bb= to =helm-mini=:
#+begin_src emacs-lisp
(use-package helm
:init (require 'helm-config)
:defer 1
:config
(helm-mode)
:general
(+general-global-buffer
"b" 'helm-mini))
#+end_src
** Contextual (mode specific) leader key
We define a global-leader definer to access major-mode specific bindings:
#+begin_src emacs-lisp
(general-create-definer global-leader
:keymaps 'override
:states '(emacs normal hybrid motion visual operator)
:prefix "SPC m"
"" '(:ignore t :which-key (lambda (arg) `(,(cadr (split-string (car arg) " ")) . ,(replace-regexp-in-string "-mode$" "" (symbol-name major-mode))))))
#+end_src
Again, we use the =:general= keyword to bind mode-specific bindings.
For example, with the following, we can =eval-buffer= when in =emacs-lisp-mode= or =lisp-interaction-mode=
by pressing =SPC m e b=:
#+begin_src emacs-lisp
(use-package elisp-mode
;;this is a built in package, so we don't want to try and install it
:ensure nil
:general
(global-leader
;;specify the major modes these should apply to:
:major-modes
'(emacs-lisp-mode lisp-interaction-mode t)
;;and the keymaps:
:keymaps
'(emacs-lisp-mode-map lisp-interaction-mode-map)
"e" '(:ignore t :which-key "eval")
"eb" 'eval-buffer
"ed" 'eval-defun
"ee" 'eval-expression
"ep" 'pp-eval-last-sexp
"es" 'eval-last-sexp
"i" 'elisp-index-search))
#+end_src
* Conclusion
Hope this helps.
~ Nicholas Vollmer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment