;;; 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 catppuccin-theme :config (setq catppuccin-flavor 'mocha) (load-theme 'catppuccin 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))))