212 lines
6.9 KiB
Org Mode
212 lines
6.9 KiB
Org Mode
|
#+title: Emacs — Keybinding Managers
|
|||
|
#+setupfile: ../headers
|
|||
|
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
|||
|
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/keybinding-managemers.el
|
|||
|
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
|||
|
|
|||
|
* Keybinding Managers
|
|||
|
** Which-key
|
|||
|
Which key is, I think, one of my favorite quality of life package.
|
|||
|
When you begin a keybind, Emacs will show you all keybinds you can
|
|||
|
follow the first one with in order to form a full keychord. Very
|
|||
|
useful when you have a lot of keybinds and don’t remember exactly what
|
|||
|
is what.
|
|||
|
#+begin_src emacs-lisp
|
|||
|
(use-package which-key
|
|||
|
:straight (:build t)
|
|||
|
:defer t
|
|||
|
:init (which-key-mode)
|
|||
|
:diminish which-key-mode
|
|||
|
:config
|
|||
|
(setq which-key-idle-delay 1))
|
|||
|
#+end_src
|
|||
|
|
|||
|
** General
|
|||
|
General is an awesome package for managing keybindings. Not only is it
|
|||
|
oriented towards keychords by default (which I love), but it also
|
|||
|
provides some integration with evil so that we can declare keybindings
|
|||
|
for certain states only! This is a perfect replacement for ~define-key~,
|
|||
|
~evil-define-key~, and any other function for defining keychords. And it
|
|||
|
is also possible to declare a prefix for my keybindings! By default,
|
|||
|
all keybinds will be prefixed with ~SPC~ and keybinds related to a
|
|||
|
specific mode (often major modes) will be prefixed by a comma ~,~ (and
|
|||
|
by ~C-SPC~ and ~M-m~ respectively when in ~insert-mode~ or ~emacs-mode~). You
|
|||
|
can still feel some influence from my Spacemacs years here.
|
|||
|
#+begin_src emacs-lisp
|
|||
|
(use-package general
|
|||
|
:straight (:build t)
|
|||
|
:init
|
|||
|
(general-auto-unbind-keys)
|
|||
|
:config
|
|||
|
(general-create-definer phundrak/undefine
|
|||
|
:keymaps 'override
|
|||
|
:states '(normal emacs))
|
|||
|
(general-create-definer phundrak/evil
|
|||
|
:states '(normal))
|
|||
|
(general-create-definer phundrak/leader-key
|
|||
|
:states '(normal insert visual emacs)
|
|||
|
:keymaps 'override
|
|||
|
:prefix "SPC"
|
|||
|
:global-prefix "C-SPC")
|
|||
|
(general-create-definer phundrak/major-leader-key
|
|||
|
:states '(normal insert visual emacs)
|
|||
|
:keymaps 'override
|
|||
|
:prefix ","
|
|||
|
:global-prefix "M-m"))
|
|||
|
#+end_src
|
|||
|
|
|||
|
** Evil
|
|||
|
Evil emulates most of vim’s keybinds, because let’s be honest here,
|
|||
|
they are much more comfortable than Emacs’.
|
|||
|
#+begin_src emacs-lisp
|
|||
|
(use-package evil
|
|||
|
:straight (:build t)
|
|||
|
:after (general)
|
|||
|
:init
|
|||
|
(setq evil-want-integration t
|
|||
|
evil-want-keybinding nil
|
|||
|
evil-want-C-u-scroll t
|
|||
|
evil-want-C-i-jump nil)
|
|||
|
(require 'evil-vars)
|
|||
|
(evil-set-undo-system 'undo-tree)
|
|||
|
:config
|
|||
|
<<evil-undefine-keys>>
|
|||
|
<<evil-bepo>>
|
|||
|
(evil-mode 1)
|
|||
|
(setq evil-want-fine-undo t) ; more granular undo with evil
|
|||
|
(evil-set-initial-state 'messages-buffer-mode 'normal)
|
|||
|
(evil-set-initial-state 'dashboard-mode 'normal))
|
|||
|
#+end_src
|
|||
|
|
|||
|
I want to undefine some default keybinds of Evil because it does not
|
|||
|
match my workflow. Namely, I use the space key and the comma as
|
|||
|
leaders for my keybinds, and I’m way too used to Emacs’ ~C-t~, ~C-a~, ~C-e~,
|
|||
|
and ~C-y~.
|
|||
|
#+name: evil-undefine-keys
|
|||
|
#+begin_src emacs-lisp :tangle no
|
|||
|
(evil-global-set-key 'motion "t" 'evil-next-visual-line)
|
|||
|
(evil-global-set-key 'motion "s" 'evil-previous-visual-line)
|
|||
|
|
|||
|
(general-define-key
|
|||
|
:keymaps 'evil-motion-state-map
|
|||
|
"SPC" nil
|
|||
|
"," nil)
|
|||
|
(general-define-key
|
|||
|
:keymaps 'evil-insert-state-map
|
|||
|
"C-t" nil)
|
|||
|
(general-define-key
|
|||
|
:keymaps 'evil-insert-state-map
|
|||
|
"U" nil
|
|||
|
"C-a" nil
|
|||
|
"C-y" nil
|
|||
|
"C-e" nil)
|
|||
|
#+end_src
|
|||
|
|
|||
|
Something else that really bugs me is I use the bépo layout, which is
|
|||
|
not at all like the qwerty layout. For instance, ~hjkl~ becomes ~ctsr~.
|
|||
|
Thus, I need some bépo-specific changes.
|
|||
|
#+name: evil-bepo
|
|||
|
#+begin_src emacs-lisp :tangle no
|
|||
|
(dolist (key '("c" "C" "t" "T" "s" "S" "r" "R" "h" "H" "j" "J" "k" "K" "l" "L"))
|
|||
|
(general-define-key :states 'normal key nil))
|
|||
|
|
|||
|
(general-define-key
|
|||
|
:states 'motion
|
|||
|
"h" 'evil-replace
|
|||
|
"H" 'evil-replace-state
|
|||
|
"j" 'evil-find-char-to
|
|||
|
"J" 'evil-find-char-to-backward
|
|||
|
"k" 'evil-substitute
|
|||
|
"K" 'evil-smart-doc-lookup
|
|||
|
"l" 'evil-change
|
|||
|
"L" 'evil-change-line
|
|||
|
|
|||
|
"c" 'evil-backward-char
|
|||
|
"C" 'evil-window-top
|
|||
|
"t" 'evil-next-visual-line
|
|||
|
"T" 'evil-join
|
|||
|
"s" 'evil-previous-visual-line
|
|||
|
"S" 'evil-lookup
|
|||
|
"r" 'evil-forward-char
|
|||
|
"R" 'evil-window-bottom)
|
|||
|
#+end_src
|
|||
|
|
|||
|
This package enables and integrates Evil into a lot of different
|
|||
|
modes, such as org-mode, dired, mu4e, etc. Again, I need some
|
|||
|
additional code compared to most people due to the bépo layout.
|
|||
|
#+begin_src emacs-lisp
|
|||
|
(use-package evil-collection
|
|||
|
:after evil
|
|||
|
:straight (:build t)
|
|||
|
:config
|
|||
|
;; bépo conversion
|
|||
|
(defun my/bépo-rotate-evil-collection (_mode mode-keymaps &rest _rest)
|
|||
|
(evil-collection-translate-key 'normal mode-keymaps
|
|||
|
;; bépo ctsr is qwerty hjkl
|
|||
|
"c" "h"
|
|||
|
"t" "j"
|
|||
|
"s" "k"
|
|||
|
"r" "l"
|
|||
|
;; add back ctsr
|
|||
|
"h" "c"
|
|||
|
"j" "t"
|
|||
|
"k" "s"
|
|||
|
"l" "r"))
|
|||
|
(add-hook 'evil-collection-setup-hook #'my/bépo-rotate-evil-collection)
|
|||
|
(evil-collection-init))
|
|||
|
#+end_src
|
|||
|
|
|||
|
~undo-tree~ is my preferred way of undoing and redoing stuff. The main
|
|||
|
reason is it doesn’t create a linear undo/redo history, but rather a
|
|||
|
complete tree you can navigate to see your complete editing history.
|
|||
|
One of the two obvious things to do are to tell Emacs to save all its
|
|||
|
undo history fies in a dedicated directory, otherwise we’d risk
|
|||
|
littering all of our directories. The second thing is to simply
|
|||
|
globally enable its mode.
|
|||
|
#+begin_src emacs-lisp
|
|||
|
(use-package undo-tree
|
|||
|
:defer t
|
|||
|
:straight (:build t)
|
|||
|
:custom
|
|||
|
(undo-tree-history-directory-alist
|
|||
|
`(("." . ,(expand-file-name (file-name-as-directory "undo-tree-hist")
|
|||
|
user-emacs-directory))))
|
|||
|
:init
|
|||
|
(global-undo-tree-mode)
|
|||
|
:config
|
|||
|
<<undo-tree-ignore-text-properties>>
|
|||
|
<<undo-tree-compress-files>>
|
|||
|
(setq undo-tree-visualizer-diff t
|
|||
|
undo-tree-visualizer-timestamps t
|
|||
|
undo-tree-auto-save-history t
|
|||
|
undo-tree-enable-undo-in-region t
|
|||
|
undo-limit (* 800 1024)
|
|||
|
undo-strong-limit (* 12 1024 1024)
|
|||
|
undo-outer-limit (* 128 1024 1024)))
|
|||
|
#+end_src
|
|||
|
|
|||
|
An interesting behavior from DoomEmacs is to compress the history
|
|||
|
files with ~zstd~ when it is present on the system. Not only do we enjoy
|
|||
|
much smaller files (according to DoomEmacs, we get something like 80%
|
|||
|
file savings), Emacs can load them much faster than the regular files.
|
|||
|
Sure, it uses more CPU time uncompressing these files, but it’s
|
|||
|
insignificant, and it’s still faster than loading a heavier file.
|
|||
|
#+name: undo-tree-compress-files
|
|||
|
#+begin_src emacs-lisp :tangle no
|
|||
|
(when (executable-find "zstd")
|
|||
|
(defun my/undo-tree-append-zst-to-filename (filename)
|
|||
|
"Append .zst to the FILENAME in order to compress it."
|
|||
|
(concat filename ".zst"))
|
|||
|
(advice-add 'undo-tree-make-history-save-file-name
|
|||
|
:filter-return
|
|||
|
#'my/undo-tree-append-zst-to-filename))
|
|||
|
#+end_src
|
|||
|
|
|||
|
** Hydra
|
|||
|
[[https://github.com/abo-abo/hydra][Hydra]] is a simple menu creator for keybindings.
|
|||
|
#+begin_src emacs-lisp
|
|||
|
(use-package hydra
|
|||
|
:straight (:build t)
|
|||
|
:defer t)
|
|||
|
#+end_src
|