;;; syd-evil.el -*- lexical-binding: t; -*- ;; Vim emulation. (use-package evil :preface (setq evil-want-minibuffer t evil-move-beyond-eol t evil-vsplit-window-right t evil-ex-search-vim-style-regexp t evil-want-Y-yank-to-eol t evil-want-C-w-in-emacs-state t ;; - If nil: When using ex commands on a visual selection, pass the ;; precise region selected to the command. ;; - If non-nil: Pass the region of /lines/ spanned by the visual ;; selection. evil-ex-visual-char-range nil ;; Don't display the state in the mode line. evil-mode-line-format nil evil-normal-state-cursor 'box evil-emacs-state-cursor 'hbar evil-operator-state-cursor 'evil-half-cursor evil-insert-state-cursor 'bar evil-visual-state-cursor 'hollow ;; Only do highlighting in selected window so that Emacs has less work ;; to do highlighting them all. evil-ex-interactive-search-highlight 'selected-window ;; It's infuriating that innocuous "beginning of line" or "end of line" ;; errors will abort macros, so suppress them: evil-kbd-macro-suppress-motion-error t evil-undo-system (cond ((featurep 'undo-tree) 'undo-tree) ((featurep 'undo-fu) 'undo-fu))) ;; These two are required for evil-collection. (setq evil-want-keybinding nil evil-want-integration t) :config ;; 'M-:' starts off in insert mode, yet initialises with the normal mode ;; cursor. Quick fix! }:P (add-hook 'minibuffer-setup-hook #'evil-refresh-cursor) ;; Unbind 'C-k'. Normally, it inserts digraphs; I have a compose key. It ;; often gets in the way of buffers with navigation, e.g. scrolling through ;; shell/REPL history, navigating Vertico completions, etc. (keymap-set evil-insert-state-map "C-k" nil) (evil-mode 1)) ;; A large, community-sourced collection of preconfigured Evil-mode ;; integrations. (use-package evil-collection :after evil :custom (evil-collection-setup-minibuffer t) :config (evil-collection-init)) ;; Tim Pope's famous `surround.vim' for Evil. (use-package evil-surround :commands (global-evil-surround-mode evil-surround-edit evil-Surround-edit evil-surround-region) :hook (on-first-input . global-evil-surround-mode) :config ;; In `emacs-lisp-mode', `' is a much more common pair than ``. (add-hook 'emacs-lisp-mode-hook (lambda () (push '(?` . ("`" . "'")) evil-surround-pairs-alist)))) ;; TODO: I'd like JK to escape visual state. evil-escape only allows defining a ;; single key sequence. Perhaps key-chord is capable of this? (use-package evil-escape :hook (on-first-input . evil-escape-mode) :custom ((evil-escape-key-sequence "jk") (evil-escape-delay 0.15))) ;; `evil-nerd-commenter' has a bunch of cool functions[1]. Here, only the Evil ;; operator is used. }:3 ;; [1]: https://github.com/redguardtoo/evil-nerd-commenter?tab=readme-ov-file#commands-and-hotkeys (use-package evil-nerd-commenter :commands (evilnc-comment-operator evilnc-inner-comment evilnc-outer-commenter) :bind (:map evil-normal-state-map ("#" . evilnc-comment-operator) :map evil-visual-state-map ("#" . evilnc-comment-operator) :map evil-inner-text-objects-map ("c" . evilnc-inner-comment) :map evil-outer-text-objects-map ("c" . evilnc-outer-comment))) ;; Enhance `evil-surround' with integration with `embrace'. (use-package evil-embrace :disabled :after evil-surround :config (evil-embrace-enable-evil-surround-integration)) ;; Provides an Evil operator to swap two spans of text. (use-package evil-exchange :bind (:map evil-normal-state-map ("gX" . evil-exchange) :map evil-visual-state-map ("gX" . evil-exchange))) ;; Evil doesn't ship with support for Vim's 'g-'/'g+'. `evil-numbers' ;; implements this. (use-package evil-numbers ;; 'g=' is a bit more comfortable than 'g+', whilst preserving the analogy. ;; ('=' is '+' modulo shift) :bind (:map evil-normal-state-map ("g=" . 'evil-numbers/inc-at-pt) :map evil-normal-state-map ("g-" . 'evil-numbers/dec-at-pt))) ;; Tree-sitter queries → Evil text objects. (use-package evil-textobj-tree-sitter :defer t) ;; Visually "flash" the region acted upon by Evil-mode operations. (use-package evil-goggles :hook (on-first-input . evil-goggles-mode) ;; The flash animation will delay actions, which can be very annoying for some ;; operations. Disable `evil-goggles' for those ones. :custom ((evil-goggles-enable-delete nil) (evil-goggles-enable-change nil) (evil-goggles-duration 0.1))) ;; Change cursor shape and color by evil state in terminal. (use-package evil-terminal-cursor-changer ;; This package is only useful in the terminal. :if (not (display-graphic-p)) :hook (on-first-input . evil-terminal-cursor-changer-activate)) ;; Automatic alignment in region, by regexp. (use-package evil-lion :hook (on-first-input . evil-lion-mode)) ;; 'g' text object selecting the entire buffer. (with-eval-after-load 'evil (evil-define-text-object evil-entire-buffer (count &optional beg end type) "Select entire buffer" (evil-range (point-min) (point-max) type)) (define-key evil-inner-text-objects-map "g" #'evil-entire-buffer) (define-key evil-outer-text-objects-map "g" #'evil-entire-buffer)) ;; 2-character search. (use-package evil-snipe :commands (evil-snipe-local-mode evil-snipe-override-local-mode) :hook ((on-first-input . evil-snipe-override-mode) (on-first-input . evil-snipe-mode)) :custom ((evil-snipe-smart-case t) (evil-snipe-scope 'visible) (evil-snipe-repeat-scope 'visible) (evil-snipe-char-fold t))) ;; Evil's default behaviour for '#'/'*' in visual state will remain in visual ;; mode, and jump to the next occurence of the symbol under point. That is, the ;; movement is exactly the same as it is in normal state; if the region is over ;; the text `two words`, but the point is over `two`, Evil will search for ;; `two`. `evil-visualstar' will instead search for `two words`. (use-package evil-visualstar :bind (:map evil-visual-state-map ("*" . evil-visualstar/begin-search-forward))) (provide 'syd-evil)