;;; keybindings.el --- Centralizing my keybindings -*- lexical-binding: t -*- ;; Author: William Carroll ;;; Commentary: ;; Attempting to centralize my keybindings to simplify my configuration. ;; ;; I have some expectations about my keybindings. Here are some of those ;; defined: ;; - In insert mode: ;; - C-a: beginning-of-line ;; - C-e: end-of-line ;; - C-b: backwards-char ;; - C-f: forwards-char ;;; Code: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Dependencies ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require 'clipboard) (require 'screen-brightness) (require 'scrot) (require 'ivy-clipmenu) (require 'general) (require 'exwm) (require 'vterm-mgt) (require 'buffer) (require 'display) (require 'device) ;; Note: The following lines must be sorted this way. (setq evil-want-integration t) (setq evil-want-keybinding nil) (general-evil-setup) (require 'evil) (require 'evil-collection) (require 'evil-magit) (require 'evil-commentary) (require 'evil-surround) (require 'key-chord) ;; Ensure that evil's command mode behaves with readline bindings. (general-define-key :keymaps 'evil-ex-completion-map "C-a" #'move-beginning-of-line "C-e" #'move-end-of-line "C-k" #'kill-line "C-u" #'evil-delete-whole-line "C-v" #'evil-paste-after "C-d" #'delete-char "C-f" #'forward-char "M-b" #'backward-word "M-f" #'forward-word "M-d" #'kill-word "M-DEL" #'backward-kill-word "C-b" #'backward-char) (general-mmap :keymaps 'override "RET" #'evil-goto-line "H" #'evil-first-non-blank "L" #'evil-end-of-line "_" #'ranger "-" #'dired-jump "sl" #'wpc/evil-window-vsplit-right "sh" #'evil-window-vsplit "sk" #'evil-window-split "sj" #'wpc/evil-window-split-down) (general-nmap :keymaps 'override "gd" #'xref-find-definitions ;; Wrapping `xref-find-references' in the `let' binding to prevent xref from ;; prompting. There are other ways to handle this variable, such as setting ;; it globally with `setq' or buffer-locally with `setq-local'. For now, I ;; prefer setting it with `let', which should bind it in the dynamic scope ;; for the duration of the `xref-find-references' function call. "gx" (lambda () (interactive) (let ((xref-prompt-for-identifier nil)) (call-interactively #'xref-find-references)))) (general-unbind 'motion "M-." "C-p" "") (general-unbind 'normal "s" "M-." "C-p" "C-n") (general-unbind 'insert "C-v" "C-d" "C-a" "C-e" "C-n" "C-p" "C-k") (setq evil-symbol-word-search t) (evil-mode 1) (evil-collection-init) (evil-commentary-mode) (global-evil-surround-mode 1) ;; Ensure the Evil search results get centered vertically. (progn (defadvice isearch-update (before advice-for-isearch-update activate) (evil-scroll-line-to-center (line-number-at-pos))) (defadvice evil-search-next (after advice-for-evil-search-next activate) (evil-scroll-line-to-center (line-number-at-pos))) (defadvice evil-search-previous (after advice-for-evil-search-previous activate) (evil-scroll-line-to-center (line-number-at-pos)))) (key-chord-mode 1) (key-chord-define evil-insert-state-map "jk" 'evil-normal-state) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; General KBDs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; This may be contraversial, but I never use the prefix key, and I'd prefer to ;; have to bound to the readline function that deletes the entire line. (general-unbind "C-u") (defmacro keybinding/exwm (c fn) "Bind C to FN using `exwm-input-set-key' with `kbd' applied to C." `(exwm-input-set-key (kbd ,c) ,fn)) (keybinding/exwm "C-M-v" #'ivy-clipmenu/copy) (keybinding/exwm "" #'screen-brightness/increase) (keybinding/exwm "" #'screen-brightness/decrease) (keybinding/exwm "" #'pulse-audio/toggle-mute) (keybinding/exwm "" #'pulse-audio/decrease-volume) (keybinding/exwm "" #'pulse-audio/increase-volume) (keybinding/exwm "" #'pulse-audio/toggle-microphone) (keybinding/exwm (kbd/raw 'x11 "s") #'scrot/select) (keybinding/exwm "" #'window-manager-switch-to-exwm-buffer) (general-define-key :keymaps 'override "M-q" #'delete-window "" #'toggle-frame-fullscreen "M-h" #'windmove-left "M-l" #'windmove-right "M-k" #'windmove-up "M-j" #'windmove-down "M-q" #'delete-window) ;; Support pasting in M-:. (general-define-key :keymaps 'read-expression-map "C-v" #'clipboard-yank "C-S-v" #'clipboard-yank) (general-define-key :prefix "" :states '(normal) "." #'ffap "gn" #'notmuch "i" #'counsel-semantic-or-imenu "I" #'ibuffer "hk" #'helpful-callable "hf" #'helpful-function "hm" #'helpful-macro "hc" #'helpful-command "hk" #'helpful-key "hv" #'helpful-variable "hp" #'helpful-at-point "s" #'flyspell-mode "S" #'sort-lines "=" #'align "p" #'flycheck-previous-error "f" #'project-find-file "n" #'flycheck-next-error "N" #'smerge-next "W" #'balance-windows "gs" #'magit-status "E" #'refine "es" #'wpc/create-snippet "l" #'linum-mode "B" #'magit-blame "w" #'save-buffer "r" #'wpc/evil-replace-under-point "R" #'deadgrep) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Vterm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Show or hide a vterm buffer. I'm intentionally not defining this in ;; vterm-mgt.el because it consumes `buffer/show-previous', and I'd like to ;; avoid bloating vterm-mgt.el with dependencies that others may not want. (general-define-key (kbd/raw 'x11 "t") (lambda () (interactive) (if (vterm-mgt--instance? (current-buffer)) (switch-to-buffer (first (buffer/source-code-buffers))) (call-interactively #'vterm-mgt-find-or-create)))) (general-define-key :keymaps '(vterm-mode-map) "C-S-n" #'vterm-mgt-instantiate "C-S-w" #'vterm-mgt-kill "" #'vterm-mgt-next "" #'vterm-mgt-prev "" #'vterm-mgt-rename-buffer) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Displays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (when (device/work-laptop?) (keybinding/exwm "" #'display/cycle-display-states) (general-define-key :prefix "" :states '(normal) "d0" #'display/disable-laptop "d1" #'display/enable-laptop "D0" #'display/disable-4k "D1" #'display/enable-4k)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; notmuch ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; evil-collection adds many KBDs to notmuch modes. Some of these I find ;; disruptive. (general-define-key :states '(normal) :keymaps '(notmuch-show-mode-map) "M-j" nil "M-k" nil "" #'notmuch-show-previous-thread-show "" #'notmuch-show-next-thread-show "e" #'notmuch-show-archive-message-then-next-or-next-thread) ;; TODO(wpcarro): Consider moving this to a separate module (defun evil-ex-define-cmd-local (cmd f) "Define CMD to F locally to a buffer." (unless (local-variable-p 'evil-ex-commands) (setq-local evil-ex-commands (copy-alist evil-ex-commands))) (evil-ex-define-cmd cmd f)) ;; TODO(wpcarro): Support a macro that can easily define evil-ex commands for a ;; particular mode. ;; Consumption: ;; (evil-ex-for-mode 'notmuch-message-mode ;; "x" #'notmuch-mua-send-and-exit) (add-hook 'notmuch-message-mode-hook (lambda () (evil-ex-define-cmd-local "x" #'notmuch-mua-send-and-exit))) ;; For now, I'm mimmicking Gmail KBDs that I have memorized and enjoy (general-define-key :states '(normal visual) :keymaps '(notmuch-search-mode-map) "M" (lambda () (interactive) (notmuch-search-tag '("-inbox" "+muted"))) "mi" (lambda () (interactive) (notmuch-search-tag '("+inbox" "-action" "-review" "-waiting" "-muted"))) "ma" (lambda () (interactive) (notmuch-search-tag '("-inbox" "+action" "-review" "-waiting"))) "mr" (lambda () (interactive) (notmuch-search-tag '("-inbox" "-action" "+review" "-waiting"))) "mw" (lambda () (interactive) (notmuch-search-tag '("-inbox" "-action" "-review" "+waiting"))) "e" #'notmuch-search-archive-thread) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; magit ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (general-define-key :states '(normal) :keymaps '(magit-status-mode-map) "l" #'evil-forward-char "L" #'magit-log) (provide 'keybindings) ;;; keybindings.el ends here