I use many[fn:1] custom keybindings.
There are some non-standard control sequences. Anywhere:
C->
expands regionC-.
marks “next like this” usingmultiple-cursors
.
User-reserved combinations are used for (mostly built-in) commands and command maps:
C-c a
fororg-agenda
C-c b
forbookmark
C-c c
fororg-capture
(C-c M-c
captures project tasks)C-c d
is bound tooutline-minor-mode-map
(if mode is enabled)C-c e
foreshell
C-c f
forflymake
C-c `
foreww
C-c g
forsmerge
C-c p
forpuni
C-c q
to do aquick-calc
(inserted if called withC-u
)C-c r
tocompile
(C-c M-r
torecompile
)C-c t
to flop frames (command map oftranspose-frame
withC-c M-t
)C-c w
mimics pressing the hyper key (customizable throughwal-hyper-mock
)C-c x
opens a scratch buffer (can be called with numeric argument)C-c z
for a heavypulse
M-o
switches to most recently used window (C-M-o
switches toother-buffer
)
I rebound my <CAPS>
(caps-lock) key to Hyper_L
to use the hyper bindings below. Therefore, all following keys should be right hand keys.
Most hyper[fn:2] bindings are quick-access actions, some have an alternate action using prefix M
, some bind a command map or transient with prefix C
:
Binding | Context | Action | Alternate | Command Map |
---|---|---|---|---|
H-] | dap-debug | dap-breakpoint-toggle | ✓ | |
H-[ | Buffers with lsp-mode . | wal-lsp-dwim | lsp-execute-code-action | ✓ |
H-' | wal-consult-project | wal-project-switch-to-parent-project | - | |
H-p | org-roam-capture | org-roam-dailies-capture-today | ✓ | |
H-h | project-find=file | wal-project-find-in-here | (use C-x p ) | |
H-k | Goes to headings for org-mode . | wal-consult-place | - | - |
H-k | During compatible consult completion. | Preview selection. | - | - |
H-; | consult-line | wal-consult-line-symbol-at-point | - | |
H-; | During vertico completion. | marginalia-cycle | - | - |
H-j | wal-avy-goto-word | avy-goto-char-timer | - | |
H-j | During vertico completion. | vertico-quick-exit | - | - |
H-j | During corfu completion. | corfu-quick-complete | - | - |
H-y | jump-to-register | wal-point-to-register | (use C-x r ) | |
H-. | embark-act | embark-dwim | - | |
H-l | wal-avy-goto-line | consult-goto-line | - | |
H-l | During vertico completion. | vertico-multiform=vertical | - | - |
H-m | magit-status | magit-find-file | ✓ | |
H-<mouse3> | mc/add-cursor-on-click | - | - | |
H-n | rg-project-literal | rg-literal | ✓ | |
H-o | ace-window | tab-bar-switch-to-tab | - | |
H-, | ship-mate-rerun-command | ship-mate-quick-command-map | ✓ | |
H-<SPC> | wal-consult-clock | wal-consult-agenda-take-note | - | |
H-u | consult-buffer | consult-find-file | ✓ | |
H-{up,down-left,right} | windmove-{up,down,left-right} | - | - | |
H-i | partial-recall-switch-to-buffer | partial-recall-menu | ✓ | |
H-/ | cape-dabbrev | tempel-complete | ✓ | |
H-\ | wal-supernova | wal-consult-display-buffer | - |
Assuming you use Xorg Display server, create an .Xmodmap
file in your home folder containing the following lines.
! Assign Hyper_L to Caps_Lock
keycode 66 = Hyper_L
! Remove caps lock
remove lock = Caps_Lock
! Set hyper to mod3 from mod4
remove mod4 = Hyper_L
add mod3 = Hyper_L
Add a script (also in your home folder) containing the following command and call it during start-up.
[[ -f ~/.Xmodmap ]] && xmodmap ~/.Xmodmap
This assumes that Hyper_L
was assigned to modifier Mod4
that’s already used by Super_L
and modifier Mod3
is an empty group.
A much riskier[fn:1] way, provided the recipe above doesn’t work, would be to edit your /usr/share/X11/xkb/symbols/pc
file like so:
... // key <CAPS> { [ Caps_Lock ] }; key <CAPS> { [ Hyper_L ] }; ... // modifier_map Lock { Caps_Lock }; modifier_map Mod3 { Hyper_L, Hyper_R }; ... // modifier_map Mod4 { <HYPR> }; modifier_map Mod3 { <HYPR> };
There are seven named command map keys (three of them general
leaders), each serving its unique purpose by prefixing (groups of) actions by common context or scope.
The general
leader keys have so-called sinks for additional commands.
Leader key ambassador
deals with the (buffer-, project-)local context.
If the respective buffer-local minor-mode
is active, the following commands and command maps are bound:
0
fordashboard-refresh-buffer
8
forkubernetes
b
fordap-mode
d
fordocker
f
forflycheck
@
formu4e
h
fordiff-hl
v
forverb
.
Leader key major
invokes a dispatch if the underlying major-mode has it defined.
Leader key editor
provides a layer of useful editing actions.
They are:
c
to copy a lined
to duplicate lines (in Emacs 29)h
to kill-save whole bufferj
to go to next spelling error withjinx
k
for to start/stopkmacro
recordingM-.
to go to definition withdumb-jump
m
to move a lines
to insert pair withsurround
.
to mark all “like this”w
to kill-save a linex
to kill a line,<up>
,<down>
,<left>
and<right>
to drag stuff.
The sink for editor
provides alternative version of these calls.
They are:
c
to copy a regionj
to fix spelling withjinx
m
to move a regions
to kill between pair withsurround
.
to mark all ends in a regionw
to kill a regionx
to delete a region.
Binds various custom commands.
Binds various custom commands that relate to finding things.
A command map that binds various administrative Emacs commands.
A command map that binds various completion commands.
;;; wal-key-bindings.el --- Key bindings. -*- lexical-binding: t -*-
;;; Commentary:
;;
;; Key bindings package.
;;; Code:
(eval-when-compile
(require 'wal-useful nil t)
(require 'wal-package nil t))
(declare-function general-define-key "ext:general")
(declare-function transient-args "ext:transient.el")
(declare-function transient-arg-value "ext:transient.el")
(defvar transient-current-command)
(defgroup wal-key-bindings nil
"Change key bindings settings."
:group 'wal
:tag "Key bindings")
;;;; Customization:
(defcustom wal-hyper-mock (kbd "C-c w")
"The key sequence to use to mock hyper modifier."
:type 'key-sequence
:group 'wal-key-bindings)
(defcustom wal-leaders '(("6" . whaler)
("7" . editor)
("8" . ambassador)
("9" . administrator)
("0" . seeker)
("-" . adjunct)
("=" . major))
"Alist mapping prefix keys to leaders."
:type '(alist :key-type string :value-type symbol)
:group 'wal-key-bindings)
(defsubst wal-prefix-user-key (user-key)
"Prefix USER-KEY."
(let ((prefix "H-"))
(concat prefix user-key)))
(defun wal-key-by-leader (leader)
"Get the key for LEADER."
(car-safe (rassoc leader wal-leaders)))
(cl-defun wal-key-combo-for-leader (leader &key key in-sink translate)
"Get the key combination for LEADER.
If KEY is non-nil, append it. If IN-SINK is non-nil, infix leader
key. If TRANSLATE is non-nil, convert using `kbd'."
(when-let* ((leader-key (wal-key-by-leader leader))
(prefix (if (string-prefix-p "<" leader-key)
leader-key
(wal-prefix-user-key leader-key)))
(combo (if key
(if in-sink
(concat prefix " " leader-key " " key)
(concat prefix " " key))
prefix)))
(if translate
(kbd combo)
combo)))
Allows defining custom prefixes. This adds macros to create so-called sinks for leader keys, an additional layer using the same prefix key, as well as to mirror certain commands for the editor leader key.
(defvar wal-general-leaders '(editor seeker administrator adjunct ambassador)
"Leaders that with a `general' definer.
The exceptions bind `transient' maps directly.")
(cl-defmacro wal-create-leader-sink (name &key definer prefix)
"Macro to create a leader sink `NAME-sink'.
NAME is the name of the macro. DEFINER is the definer to create
the sink for and PREFIX is its prefix."
(declare (indent defun))
(let* ((defname (symbol-name definer))
(suffix (substring prefix -1))
(wk (upcase (concat defname "!"))))
(progn
(general-define-key :prefix prefix suffix `(:ignore t :wk ,wk))
`(defmacro ,name (&rest args)
`(, ',definer ,@,`(mapcar (lambda (it)
(if (stringp it)
(concat ,suffix it)
it))
args))))))
(cl-defmacro editors (key fun mfun &rest args)
"Bind FUN to KEY, MFUN in the sink.
All ARGS are passed to both definers."
(declare (indent defun))
`(progn
(editor ,@args ,key ,fun)
(editor-sink ,@args ,key ,mfun)))
(defun wal-general-create-definer (leader)
"Create a definer for LEADER with a sink."
(let* ((key (wal-key-combo-for-leader leader))
(sink (intern (format "%s-sink" leader)))
(name (symbol-name leader)))
;; Queue up `which-key' replacements.
(eval-after-load 'which-key `(which-key-add-key-based-replacements ,key ,name))
;; Create the normal definer.
(eval `(general-create-definer ,leader :prefix ,key))
;; Also create the sink.
(eval `(wal-create-leader-sink ,sink :definer ,leader :prefix ,key))))
(defun major? ()
"Show message when major is not locally bound."
(interactive)
(let ((key (propertize (wal-key-combo-for-leader 'major) 'face 'success))
(mode (propertize (symbol-name major-mode) 'face 'success)))
(message "Major (%s) has no binding in %s" key mode)))
(use-package general
:demand t
:wal-ways t
:config
(seq-do #'wal-general-create-definer wal-general-leaders)
:functions (general-define-key))
(use-package repeat
:custom
(repeat-exit-key (kbd "q"))
(repeat-exit-timeout 5))
Another nice way of grouping keys.
Some transients are bound directly, others are wal-univ
variants (see above).
(defun wal-transient-grab (arg)
"Grab argument ARG from current command."
(transient-arg-value
(format "--%s=" arg)
(transient-args transient-current-command)))
(defun wal-transient-command-or-major ()
"Show only major if command includes it."
(if (string-match "major" mode-line-buffer-identification)
"major"
mode-line-buffer-identification))
(defun wal-with-delayed-transient-popup (fun &rest args)
"Delay the transient FUN before calling it with ARGS."
(defvar transient-show-popup)
(let ((transient-show-popup 0.8))
(apply fun args)))
(use-package transient
:demand t
:custom
(transient-hide-during-minibuffer-read t)
(transient-mode-line-format '("%e"
mode-line-front-space
(:eval (wal-transient-command-or-major)))))
Show the next possible key presses towards a command.
(cl-defmacro that-key (description &key key condition user-key leader)
"Add DESCRIPTION for KEY after loading `which-key'.
If CONDITION is non-nil, surround the replacement with it.
USER-KEY and LEADER can be used to prefix the key."
(let ((key (cond
(user-key
(wal-prefix-user-key user-key))
(leader
(apply 'wal-key-combo-for-leader leader))
(key key)
(t ""))))
`(with-eval-after-load 'which-key
(declare-function which-key-add-key-based-replacements "ext:which-key.el")
,(if condition
`(when ,condition
(which-key-add-key-based-replacements ,key ,description))
`(which-key-add-key-based-replacements ,key ,description)))))
(use-package which-key
:defer 2
:wal-ways t
:config
(which-key-mode 1)
:custom
(which-key-lighter " wk?")
(which-key-idle-delay 0.8)
(which-key-idle-secondary-delay 0.2)
(which-key-sort-uppercase-first nil)
(which-key-sort-order #'which-key-prefix-then-key-order)
(which-key-show-docstrings t)
(which-key-preserve-window-configuration t)
(which-key-show-early-on-C-h t)
:functions (which-key-mode))
(with-no-warnings
(with-eval-after-load 'general
;; Additional `general' bindings.
(administrator
"f" '(:ignore t :wk "find")
"fc" 'wal-find-custom-file
"fi" 'wal-find-init
"fl" 'find-library
"l" '(:ignore t :wk "list")
"lp" 'list-processes
"lt" 'list-timers
"s" '(:ignore t :wk "set")
"st" 'wal-set-transparency
"sc" 'wal-set-cursor-type
"p" '(:ignore t :wk "package")
"pf" 'package-refresh-contents
"pi" 'package-install
"pl" 'list-packages
"pr" 'package-reinstall
"pd" 'package-delete
"pu" 'package-upgrade
"t" '(:ignore t :wk "profiler")
"ts" 'profiler-startd
"to" 'profiler-stop
"tr" 'profiler-report
"h" '(:ignore t :wk "help")
"hw" 'woman)
(general-create-definer completionist :prefix (wal-prefix-user-key "C-/"))
(eval-after-load 'which-key
(which-key-add-key-based-replacements (wal-prefix-user-key "M-/") "completionist"))
(global-set-key (kbd (wal-key-combo-for-leader 'major)) #'major?)
(global-set-key (kbd (wal-key-combo-for-leader 'whaler)) #'whaler)
(when (wal-modern-emacs-p 29)
(editor "d" 'duplicate-dwim))
(editor "h" 'wal-kill-ring-save-whole-buffer)
(adjunct
"b" 'wal-kill-some-file-buffers
"d" 'wal-doppelganger
"f" 'wal-fundamental-mode
"1" 'wal-force-delete-other-windows)
(seeker
"f" 'wal-find-fish-config
"h" 'wal-dired-from-home
"s" 'find-sibling-file))
(global-set-key [remap kill-line] #'wal-kwim)
(global-set-key (kbd "C-c x") #'wal-scratch-buffer)
(global-set-key (kbd "C-c `") #'eww)
(global-set-key (kbd "C-M-i") #'completion-at-point)
(global-set-key (kbd "C-M-s") #'wal-isearch-other-window)
(global-set-key (kbd "C-M-q") #'wal-spill-paragraph)
(global-set-key (kbd "C-z") nil)
(global-set-key (kbd (wal-prefix-user-key "\\")) #'wal-supernova)
(global-set-key [remap zap-to-char] #'zap-up-to-char)
(unless (wal-modern-emacs-p 30)
;; Replaced by `toggle-window-dedicated' in Emacs 30.
(define-key window-prefix-map (kbd "d") #'wal-l)
(define-key window-prefix-map (kbd "q") #'quit-window))
;; Replace `buffer-list'.
(global-set-key [remap list-buffers] #'ibuffer-other-window)
;; Alternate binding for C-c x @ h.
(define-key function-key-map wal-hyper-mock #'event-apply-hyper-modifier)
;; One-handed events.
(define-key function-key-map (kbd "<f5>") #'event-apply-control-modifier)
(define-key function-key-map (kbd "<f6>") #'event-apply-meta-modifier)
(define-key function-key-map (kbd "<f7>") #'event-apply-hyper-modifier)
(define-key function-key-map (kbd "<f8>") #'event-apply-shift-modifier)
;; Add alternative bindings to repeat map.
(define-key undo-repeat-map "/" #'undo)
(define-key undo-repeat-map "?" #'undo-redo)
;; Bind additional `other-window' commands.
(define-key ctl-x-4-map (kbd "M-4") 'wal-swipe-window-prefix)
(global-set-key (kbd "M-o") 'wal-other-window)
(global-set-key (kbd "C-M-o") 'wal-switch-to-other-buffer)
(with-eval-after-load 'window
(when (boundp 'other-window-repeat-map)
(define-key other-window-repeat-map "0" 'delete-window)
(define-key other-window-repeat-map "1" 'delete-other-windows)
(define-key other-window-repeat-map (kbd "C-k") 'wal-force-delete-other-windows)
(define-key other-window-repeat-map "5" 'other-frame))))
(provide 'wal-key-bindings)
;;; wal-key-bindings.el ends here
[fn:1] To get a full overview you’ll have to call describe-personal-keybindings
and general-describe-keybindings
.
[fn:2] Note that C-c w
is bound to apply the hyper modifier as well; so if you don’t have access to the key, you can always use that instead.