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
foreww
C-c c
fororg-capture
(C-c v
captures project tasks)C-c e
foreshell
C-c f
forflymake
C-c g
forsmerge
C-c i
forcompile
C-c j
is bound tooutline-minor-mode-map
(if mode is enabled)C-c l
to toggledisplay-line-numbers-mode
C-c m
forbookmark
C-c n
to take notes for anorg-agenda
itemC-c o
quits windows showing usually hidden buffersC-c p
for a heavypulse
C-c q
to do aquick-calc
(inserted if called withC-u
)C-c r
torecompile
C-c s
forflyspell
C-c t
fortranspose-frame
C-c x
opens a scratch buffer (can be called with numeric argument).
M-o
to switch to the most recently used window (C-M-o
switch to other 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:
H-]
callsdap-next
commands (H-M-]
binds custom command map)H-[
callswal-lsp-dwim
in LSP buffers (H-M-.
bindslsp-command-map
)- =H-‘= switches projects (=H-M-‘= switches to parent project)
H-\
captures note withorg-roam
(H-M-\
opens customtransient
)H-h
finds file inproject
(H-M-h
finds file in current directory)H-i
callsconsult
to find a place usingimenu
oroutline
H-j
- jumps to word in line (
H-M-j
jumps to char with timer) - during
vertico
andcorfu
completion, it triggers the respective quick completion - when used with
M
it goes to char with a timer
- jumps to word in line (
H-;
jumps to register,H-M-;
stores point in registerH-k
completes at point; whencorfu
is active, it quits the candidate selection (H-M-k
will insert a separator whilecorfu
is active, otherwise activatecompletionist
keymap)H-.
acts on the current point withembark
(immediately withH-M-.
)H-l
jumps to line withavy
(beginning of line if called withC-u
,H-M-l
goes to line usingconsult
)H-m
formagit-status
(H-M-m
runsma-magit
)H-<mouse3>
adds anothermultiple-cursor
at pointH-n
searches project withrg
(C-0
searches using regex,H-M-n
doesrg-menu
)H-o
switches tabs (M-H-o
renames the current tab)H-p
to rerun the lastship-mate
command (H-M-p
bindsship-mate-command-map
)H-<SPC>
clocks in (or out if called with prefix; prefix0
clocks in without continuation)H-,= switches to buffer using =consult
(=H-M-,= opens custom transient)H-{up,down,left,right}
moves withwindmove
(swap with shift, delete with meta, and prep display with control)H-u
switches buffers inpartial-recall
memory (H-M-u
calls the command map)H-y
dispatchesace-window
.
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 lineq
to “spill” a paragraphs
to insert pair withsurround
.
to mark all “like this”w
to kill-save a linex
to kill a line.
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.
Function key <f6>
is bound to administrator
, a command map that
binds various administrative Emacs 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))
(defvar transient-current-command)
(declare-function general-define-key "ext:general")
(declare-function general-simulate-key "ext:general")
(declare-function transient-args "ext:transient.el")
(declare-function transient-arg-value "ext:transient.el")
(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))
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-start
"to" 'profiler-stop
"tr" 'profiler-report
"h" '(:ignore t :wk "help")
"hw" 'woman)
(general-create-definer completionist :prefix (wal-prefix-user-key "M-k"))
(eval-after-load 'which-key
(which-key-add-key-based-replacements "C-c k" "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)
(editor "q" 'wal-spill-paragraph)
(adjunct
"b" 'wal-kill-some-file-buffers
"d" 'wal-doppelganger
"w" 'wal-l
"f" 'wal-fundamental-mode
"1" 'wal-force-delete-other-windows
"4" 'wal-swipe-window-prefix)
(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 [remap move-beginning-of-line] #'wal-mwim-beginning)
(global-set-key (kbd "C-c x") #'wal-scratch-buffer)
(global-set-key (kbd "C-c b") #'eww)
(global-set-key (kbd "C-c l") #'display-line-numbers-mode)
(global-set-key (kbd "C-c o") #'wal-supernova)
(global-set-key (kbd "C-M-i") #'completion-at-point)
(global-set-key (kbd "C-M-s") #'wal-isearch-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.
(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.