;;; init-evil.el --- Evil, scoped to buffer editing -*- lexical-binding: t; -*- ;;; Commentary: ;; Evil is deliberately narrow: modal editing in text buffers only, no ;; evil-collection. Dired/Magit/etc. keep native bindings on purpose. ;; The nav keys below use `my/project-or': project-scoped command inside a ;; project, consult fallback everywhere else. ;;; Code: (require 'project) (defmacro my/project-or (project-cmd fallback-cmd) "Call PROJECT-CMD inside a known project, else FALLBACK-CMD. Both are unquoted command symbols." `(lambda () (interactive) (call-interactively (if (project-current) #',project-cmd #',fallback-cmd)))) (defun my/insert-tab () "Insert a literal tab. Bound to both TAB and to override major-mode indent in insert state; `self-insert-command' can't handle the event (its arg is the symbol `tab', not a character)." (interactive) (insert "\t")) (use-package evil :init (setq evil-want-keybinding nil) :config (evil-mode 1) ;; Vim commands (evil-ex-define-cmd "w" #'save-buffer) (evil-ex-define-cmd "q" #'kill-current-buffer) (evil-ex-define-cmd "wq" #'save-buffers-kill-terminal) ;; Half-page movement (define-key evil-normal-state-map (kbd "C-d") #'evil-scroll-down) (define-key evil-normal-state-map (kbd "C-u") #'evil-scroll-up) ;; Editor shortcuts (dolist (map '(evil-normal-state-map evil-motion-state-map)) (define-key (symbol-value map) (kbd "C-e") (my/project-or project-dired dired-jump)) (define-key (symbol-value map) (kbd "C-s") #'consult-line) ;; C-m is \r (= RET). Bind [return] to evil-ret so GUI Enter never falls ;; back to this C-m binding, leaving C-m free as its own key (GUI only). (define-key (symbol-value map) (kbd "C-m") (my/project-or project-switch-to-buffer consult-buffer)) (define-key (symbol-value map) [return] #'evil-ret) ;; consult-ripgrep everywhere: it already scopes to the project root when ;; there is one, and shows hits in the posframe (RET jumps) like the other ;; finders — unlike project-find-regexp's separate xref pane. (define-key (symbol-value map) (kbd "C-f") #'consult-ripgrep) (define-key (symbol-value map) (kbd "C-p") (my/project-or project-find-file consult-fd)))) ;; Literal tab on TAB/ in insert state, overriding major-mode indent. (with-eval-after-load 'evil (evil-define-key 'insert 'global (kbd "TAB") #'my/insert-tab) (evil-define-key 'insert 'global [tab] #'my/insert-tab)) (provide 'init-evil) ;;; init-evil.el ends here