;;; syd-general.el -*- lexical-binding: t; -*- (use-package general :custom (general-use-package-emit-autoloads t)) (require 'general) (defvar syd-leader-key "SPC" "A prefix key akin to Vim's .") (defvar syd-localleader-key "SPC m" "A prefix key akin to Vim's .") (defvar syd-alt-leader-key "M-SPC" "`syd-leader', but for the states specified in `syd-alt-leader-key-states'. Often, your \"usual\" leader key will be something unavailable in the Insert state. This key exists as a fallback for when you need your Leader, but must remain in the Insert state. Substitute \"Insert state\" for your states of choice with `syd-alt-leader-key-states'.") (defvar syd-alt-localleader-key "M-SPC m" "`syd-localleader', but for the states specified in `syd-alt-leader-key-states'. See `syd-alt-leader-key' for rationale.") (defvar syd-leader-key-states '(normal visual motion) "States for which the Leader keys (`syd-leader-key', `syd-localleader-key') are active.") (defvar syd-alt-leader-key-states '(emacs insert) "States for which the alternative Leader keys are active. See `syd-alt-leader-key' and `syd-alt-localleader-key'.") (defvar-keymap syd-leader-map :doc "Leader-prefixed commands") (defun syd-initialise-leader () "Set up the (empty) keymap associated with `syd-leader-key', `syd-localleader-key', `syd-alt-leader-key', and `syd-alt-localleader-key'." (require 'evil) ;; Define `syd/leader' as a command corresponding to the prefix map ;; `syd-leader-map'. (define-prefix-command 'syd/leader 'syd-leader-map) ;; This should help make the Leader key close to universally available. ;; Ideally, *nothing* takes precedence over Leader — it's an incredibly ;; important key! ;; https://github.com/noctuid/evil-guide?tab=readme-ov-file#undoprevent-overridingintercept-maps ;; See `evil-make-overriding-map'. (define-key syd-leader-map [override-state] 'all) ;; Finally, we shall bind the damned keys. }:) (let ((map general-override-mode-map)) (evil-define-key* syd-leader-key-states map (kbd syd-leader-key) 'syd/leader) (evil-define-key* syd-alt-leader-key-states map (kbd syd-alt-leader-key) 'syd/leader)) (general-override-mode 1)) (defvar syd-escape-hook nil "A hook run when C-g is pressed (or ESC in Evil's normal state). More specifically, when `syd/escape' is pressed. If any hook returns non-nil, all hooks after it are ignored.") ;; ;;; Universal, non-nuclear escape ;; `keyboard-quit' is too much of a nuclear option. I want ESC/C-g to ;; do-what-I-mean. It serves four purposes (in order): ;; ;; 1. Quit active states; e.g. highlights, searches, snippets, iedit, ;; multiple-cursors, recording macros, etc. ;; 2. Close popup windows remotely (if it is allowed to) ;; 3. Refresh buffer indicators, like diff-hl and flycheck ;; 4. Or fall back to `keyboard-quit' ;; ;; And it should do these things incrementally, rather than all at once. And it ;; shouldn't interfere with recording macros or the minibuffer. This may ;; require you press ESC/C-g two or three times on some occasions to reach ;; `keyboard-quit', but this is much more intuitive. (defun syd/escape (&optional interactive) "Run `syd-escape-hook'." (interactive (list 'interactive)) (let ((inhibit-quit t)) (cond ((minibuffer-window-active-p (minibuffer-window)) ;; quit the minibuffer if open. (when interactive (setq this-command 'abort-recursive-edit)) (abort-recursive-edit)) ;; Run all escape hooks. If any returns non-nil, then stop there. ((run-hook-with-args-until-success 'syd-escape-hook)) ;; don't abort macros ((or defining-kbd-macro executing-kbd-macro) nil) ;; Back to the default ((unwind-protect (keyboard-quit) (when interactive (setq this-command 'keyboard-quit))))))) (with-eval-after-load 'eldoc (eldoc-add-command 'syd/escape)) ;; In normal state, pressing escape should run `syd-escape-hook'. (with-eval-after-load 'evil (defun evil-syd/escape-a (&rest _) "Call `syd/escape' if `evil-force-normal-state' is called interactively." (when (called-interactively-p 'any) (call-interactively #'syd/escape))) (advice-add #'evil-force-normal-state :after #'evil-syd/escape-a)) (provide 'syd-general)