Files
dotfiles/shared/.config/emacs/init.el
Rob Harbaugh 7a82a6b8f8 Switch theme from catppuccin to modus-vivendi
modus-vivendi is built into Emacs 29+ so it loads reliably without
a MELPA install — better for WSL where package errors were occurring.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 15:35:44 -04:00

199 lines
5.3 KiB
EmacsLisp

;;; init.el --- Emacs configuration -*- lexical-binding: t -*-
;;; Package management
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
(require 'use-package)
(setq use-package-always-ensure t)
;;; Sane defaults
(setq-default
inhibit-startup-message t
visible-bell t
make-backup-files nil
auto-save-default nil
create-lockfiles nil
vc-follow-symlinks t
find-file-visit-truename t
fill-column 80
tab-width 4
indent-tabs-mode nil
truncate-lines t
sentence-end-double-space nil)
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(tooltip-mode -1)
(setq display-line-numbers-type 'relative)
(global-display-line-numbers-mode 1)
(column-number-mode 1)
(global-hl-line-mode 1)
(show-paren-mode 1)
(electric-pair-mode 1)
(delete-selection-mode 1)
(setq global-auto-revert-non-file-buffers t)
(global-auto-revert-mode 1)
(setq scroll-margin 4
scroll-conservatively 101)
(setq uniquify-buffer-name-style 'forward)
;;; Font — matches ghostty / tmux setup
(set-face-attribute 'default nil
:family "Hack Nerd Font Mono"
:height 110)
;;; Theme
(use-package modus-themes
:ensure nil
:config
(load-theme 'modus-vivendi t))
;;; Discoverability
(use-package which-key
:config
(setq which-key-idle-delay 0.5)
(which-key-mode))
;;; Minibuffer — Vertico + Orderless + Marginalia + Consult
(use-package vertico
:config (vertico-mode))
(use-package orderless
:custom
(completion-styles '(orderless basic))
(completion-category-overrides '((file (styles basic partial-completion)))))
(use-package marginalia
:config (marginalia-mode))
(use-package consult
:bind (("C-x b" . consult-buffer)
("M-y" . consult-yank-pop)
("M-g M-g" . consult-goto-line)
("M-g i" . consult-imenu)
("M-s l" . consult-line)
("M-s r" . consult-ripgrep)))
;; Contextual actions on any minibuffer candidate or thing at point
(use-package embark
:bind (("C-." . embark-act)
("C-;" . embark-dwim))
:custom (embark-indicators '(embark-minimal-indicator)))
(use-package embark-consult
:hook (embark-collect-mode . consult-preview-at-point-mode))
;; Edit consult-ripgrep / grep results as a buffer; save to apply across files
(use-package wgrep
:custom (wgrep-auto-save-buffer t))
;;; In-buffer completion — Corfu + Cape
(use-package corfu
:custom
(corfu-auto t)
(corfu-auto-delay 0.2)
(corfu-auto-prefix 2)
(corfu-quit-no-match 'separator)
:config
(global-corfu-mode))
(use-package cape
:init
(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-dabbrev))
;;; Visual aids
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
;; nerd-icons supplies glyphs for doom-modeline; Hack Nerd Font Mono already
;; includes them, so M-x nerd-icons-install-fonts is only needed on first use
;; if you see boxes instead of icons.
(use-package nerd-icons)
(use-package doom-modeline
:hook (after-init . doom-modeline-mode)
:custom
(doom-modeline-height 28)
(doom-modeline-bar-width 4)
(doom-modeline-icon t)
(doom-modeline-major-mode-icon t)
(doom-modeline-buffer-file-name-style 'truncate-upto-project)
(doom-modeline-vcs-max-length 24))
;;; Tree-sitter — auto-switch to *-ts-mode when grammars are available
(use-package treesit-auto
:custom (treesit-auto-install 'prompt)
:config (global-treesit-auto-mode))
;;; LSP — Eglot (built-in since Emacs 29)
(use-package eglot
:ensure nil
:custom
(eglot-autoshutdown t)
(eglot-confirm-server-initiated-edits nil)
(eglot-events-buffer-size 0))
;;; Navigation
;; Jump to any visible character — type a few chars of the target, pick the label
(use-package avy
:bind (("M-j" . avy-goto-char-timer)
("C-c j" . avy-goto-line)))
;; Pin files to numbered slots per project; jump to them instantly
(use-package harpoon
:bind (("C-c h a" . harpoon-add-file)
("C-c h h" . harpoon-toggle-file)
("C-c h 1" . harpoon-go-to-1)
("C-c h 2" . harpoon-go-to-2)
("C-c h 3" . harpoon-go-to-3)
("C-c h 4" . harpoon-go-to-4)))
;; Expand selection by semantic units: word → symbol → string → block → …
(use-package expand-region
:bind ("C-=" . er/expand-region))
;;; Git
(use-package magit
:bind ("C-x g" . magit-status))
;;; Direnv — respect .envrc per buffer (extends your shell direnv hook into Emacs)
(use-package envrc
:hook (after-init . envrc-global-mode))
;;; Server — enables emacsclient ('e' alias) and AUCTeX inverse search
(use-package server
:ensure nil
:config (unless (server-running-p) (server-start)))
;;; Drop-in language configs (stow'd from laptop/ or work/)
(let ((lang-dir (expand-file-name "lisp/langs" user-emacs-directory)))
(when (file-directory-p lang-dir)
(dolist (f (directory-files lang-dir t "\\.el$"))
(load f nil t))))