Last active
August 3, 2026 10:34
-
-
Save reflechant/c900eb968fc75a16a676428f29ed2138 to your computer and use it in GitHub Desktop.
new Emacs config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ;;; init.el --- Personal Emacs config -*- lexical-binding: t; -*- | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Package Management ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| (require 'package) | |
| (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) | |
| ;; use-package is built-in in Emacs 30 but :ensure needs this module | |
| (require 'use-package-ensure) | |
| (setq use-package-always-ensure t) | |
| ;; Keep Custom's auto-generated stuff out of init.el | |
| (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Sane Defaults ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| (setopt use-short-answers t) ; y/n instead of yes/no | |
| (setq inhibit-startup-message t) ; skip splash screen | |
| (menu-bar-mode -1) ; hide File/Edit/… menu bar | |
| (tool-bar-mode -1) ; hide icon toolbar | |
| (setq column-number-mode t) ; show column in modeline | |
| ;; Fill column indicator at 80 chars | |
| (setopt display-fill-column-indicator-column 80) | |
| (global-display-fill-column-indicator-mode t) | |
| ;; Line numbers in code | |
| (add-hook 'prog-mode-hook #'display-line-numbers-mode) | |
| ;; Remember cursor position in files | |
| (save-place-mode 1) | |
| ;; Remember recent files | |
| (recentf-mode 1) | |
| (setq recentf-max-saved-items 50) | |
| ;; Remember minibuffer history (Vertico sorts by this) | |
| (savehist-mode 1) | |
| ;; Auto-reload files changed by external programs (git, AI tools, etc.) | |
| (global-auto-revert-mode 1) | |
| (setq auto-revert-verbose nil) ; Don't spam "Reverting buffer" messages | |
| ;; Auto-pair brackets (disabled in paredit buffers below) | |
| (electric-pair-mode t) | |
| ;; Smooth pixel scrolling | |
| (pixel-scroll-precision-mode t) | |
| ;; Shift + arrow keys to move between windows | |
| (windmove-default-keybindings) | |
| ;; Store backups in a dedicated directory | |
| (setq backup-directory-alist | |
| `(("." . ,(expand-file-name "backups" user-emacs-directory)))) | |
| ;; Hide commands in M-x that don't apply to current mode | |
| (setq read-extended-command-predicate #'command-completion-default-include-p) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Leader Key (C-c) ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; C-c acts as global leader key (reserved by Emacs standards for user bindings) | |
| (use-package general | |
| :config | |
| (general-create-definer my-leader-def | |
| :prefix "C-c") | |
| (my-leader-def | |
| "" '(nil :which-key "leader") | |
| "SPC" '(execute-extended-command :which-key "M-x") | |
| "u" '(universal-argument :which-key "universal arg") | |
| "TAB" '(mode-line-other-buffer :which-key "last buffer") | |
| "!" '(shell-command :which-key "shell command") | |
| "/" '(consult-ripgrep :which-key "ripgrep") | |
| ";" '(eval-expression :which-key "eval expression") | |
| "'" '(eshell :which-key "eshell") | |
| ;; Files | |
| "f" '(:ignore t :which-key "file") | |
| "f f" '(find-file :which-key "find file") | |
| "f r" '(consult-recent-file :which-key "recent files") | |
| "f s" '(save-buffer :which-key "save") | |
| "f S" '(write-file :which-key "save as") | |
| "f R" '(rename-visited-file :which-key "rename current file") | |
| ;; Buffers | |
| "b" '(:ignore t :which-key "buffer") | |
| "b b" '(consult-buffer :which-key "switch buffer") | |
| "b d" '(kill-current-buffer :which-key "kill buffer") | |
| "b l" '(ibuffer :which-key "list buffers") | |
| "b n" '(next-buffer :which-key "next buffer") | |
| "b p" '(previous-buffer :which-key "previous buffer") | |
| "b r" '(revert-buffer-quick :which-key "revert buffer") | |
| "b s" '(scratch-buffer :which-key "scratch buffer") | |
| "b m" '(bookmark-set :which-key "set bookmark") | |
| "b M" '(consult-bookmark :which-key "jump to bookmark") | |
| ;; Project (built-in project.el) | |
| "p" '(:ignore t :which-key "project") | |
| "p f" '(project-find-file :which-key "find file in project") | |
| "p p" '(project-switch-project :which-key "switch project") | |
| "p s" '(project-find-regexp :which-key "grep in project") | |
| "p b" '(consult-project-buffer :which-key "project buffers") | |
| "p c" '(project-compile :which-key "compile") | |
| "p e" '(project-eshell :which-key "eshell in project root") | |
| "p d" '(project-dired :which-key "dired in project root") | |
| "p k" '(project-kill-buffers :which-key "kill project buffers") | |
| "p r" '(project-query-replace-regexp :which-key "replace in project") | |
| ;; Search | |
| "s" '(:ignore t :which-key "search") | |
| "s s" '(consult-line :which-key "search in buffer") | |
| "s S" '(isearch-forward :which-key "isearch forward") | |
| "s r" '(consult-ripgrep :which-key "ripgrep from here") | |
| "s f" '(consult-find :which-key "find file by name") | |
| "s i" '(consult-imenu :which-key "imenu symbols") | |
| "s I" '(consult-imenu-multi :which-key "imenu all buffers") | |
| "s l" '(consult-line-multi :which-key "search all open buffers") | |
| "s o" '(consult-outline :which-key "outline headings") | |
| "s R" '(query-replace :which-key "search & replace in buffer") | |
| ;; Code / LSP | |
| "c" '(:ignore t :which-key "code") | |
| "c a" '(eglot-code-actions :which-key "code actions") | |
| "c r" '(eglot-rename :which-key "rename symbol") | |
| "c f" '(eglot-format-buffer :which-key "format buffer") | |
| "c d" '(xref-find-definitions :which-key "go to definition") | |
| "c D" '(xref-find-references :which-key "find references") | |
| "c e" '(consult-flymake :which-key "list diagnostics") | |
| "c j" '(flymake-goto-next-error :which-key "next error") | |
| "c k" '(flymake-goto-prev-error :which-key "prev error") | |
| "c o" '(eglot-code-action-organize-imports :which-key "organize imports") | |
| "c x" '(eglot-code-action-quickfix :which-key "quickfix") | |
| "c h" '(eldoc :which-key "docs at point") | |
| ;; Git | |
| "g" '(:ignore t :which-key "git") | |
| "g g" '(magit-status :which-key "status") | |
| "g b" '(magit-blame :which-key "blame") | |
| "g l" '(magit-log-current :which-key "log current branch") | |
| "g L" '(magit-log-all :which-key "log all branches") | |
| "g d" '(diff-hl-show-hunk :which-key "show hunk diff") | |
| "g r" '(diff-hl-revert-hunk :which-key "revert hunk") | |
| "g n" '(diff-hl-next-hunk :which-key "next changed hunk") | |
| "g p" '(diff-hl-previous-hunk :which-key "prev changed hunk") | |
| "g f" '(magit-file-dispatch :which-key "file actions") | |
| "g s" '(magit-stage-file :which-key "stage current file") | |
| ;; Window management | |
| "w" '(:ignore t :which-key "window") | |
| "w v" '(split-window-right :which-key "split vertical") | |
| "w s" '(split-window-below :which-key "split horizontal") | |
| "w d" '(delete-window :which-key "close window") | |
| "w w" '(ace-window :which-key "select window") | |
| "w m" '(delete-other-windows :which-key "maximize") | |
| "w =" '(balance-windows :which-key "balance windows") | |
| "w r" '(window-swap-states :which-key "swap windows") | |
| "w h" '(windmove-left :which-key "go left") | |
| "w j" '(windmove-down :which-key "go down") | |
| "w k" '(windmove-up :which-key "go up") | |
| "w l" '(windmove-right :which-key "go right") | |
| ;; Toggles | |
| "t" '(:ignore t :which-key "toggle") | |
| "t d" '(my/load-dark-theme :which-key "dark theme") | |
| "t l" '(my/load-light-theme :which-key "light theme") | |
| "t T" '(consult-theme :which-key "browse themes") | |
| "t n" '(display-line-numbers-mode :which-key "line numbers") | |
| "t w" '(whitespace-mode :which-key "whitespace") | |
| "t f" '(display-fill-column-indicator-mode :which-key "fill column") | |
| "t v" '(visual-line-mode :which-key "word wrap") | |
| ;; Help | |
| "h" '(:ignore t :which-key "help") | |
| "h k" '(describe-key :which-key "describe key") | |
| "h f" '(describe-function :which-key "describe function") | |
| "h v" '(describe-variable :which-key "describe variable") | |
| "h m" '(describe-mode :which-key "describe mode") | |
| "h p" '(describe-package :which-key "describe package") | |
| "h i" '(info :which-key "info manual") | |
| "h a" '(apropos :which-key "search all help") | |
| "h b" '(embark-bindings :which-key "all keybindings") | |
| ;; Org | |
| "o" '(:ignore t :which-key "org") | |
| "o a" '(org-agenda :which-key "agenda") | |
| "o c" '(org-capture :which-key "capture") | |
| "o l" '(org-store-link :which-key "store link") | |
| "o t" '(org-todo-list :which-key "global todo list") | |
| ;; Evaluate elisp | |
| "e" '(:ignore t :which-key "eval") | |
| "e b" '(eval-buffer :which-key "eval buffer") | |
| "e r" '(eval-region :which-key "eval region") | |
| "e e" '(eval-last-sexp :which-key "eval last sexp") | |
| ;; Quit | |
| "q" '(:ignore t :which-key "quit") | |
| "q q" '(save-buffers-kill-terminal :which-key "quit emacs") | |
| "q r" '(restart-emacs :which-key "restart emacs") | |
| ;; Packages | |
| "P" '(:ignore t :which-key "packages") | |
| "P r" '(package-refresh-contents :which-key "refresh archives") | |
| "P u" '(package-upgrade-all :which-key "upgrade all") | |
| "P l" '(package-list-packages :which-key "list packages"))) | |
| ;; Show available keys after pressing a prefix | |
| (use-package which-key | |
| :ensure nil | |
| :diminish | |
| :config | |
| (which-key-mode)) | |
| (use-package diminish) | |
| ;; Fast window switching with ace-window (M-o) | |
| (use-package ace-window | |
| :bind (("M-o" . ace-window)) | |
| :custom | |
| (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) | |
| (aw-scope 'frame)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Appearance ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Font | |
| (set-face-attribute 'default nil :family "Fira Code" :height 120) | |
| ;; Dark theme (Spacemacs-like) | |
| (use-package doom-themes | |
| :config | |
| (load-theme 'doom-one t) | |
| (doom-themes-visual-bell-config) | |
| (doom-themes-org-config)) | |
| ;; Light themes for daytime | |
| (use-package ef-themes | |
| :defer t) | |
| ;; Quick theme switching | |
| (defun my/load-dark-theme () | |
| "Switch to dark theme." | |
| (interactive) | |
| (mapc #'disable-theme custom-enabled-themes) | |
| (load-theme 'doom-one t)) | |
| (defun my/load-light-theme () | |
| "Switch to light theme." | |
| (interactive) | |
| (mapc #'disable-theme custom-enabled-themes) | |
| (load-theme 'ef-light t)) | |
| ;; Icons (run M-x nerd-icons-install-fonts on first launch) | |
| (use-package nerd-icons) | |
| ;; Modeline | |
| (use-package doom-modeline | |
| :init (doom-modeline-mode 1) | |
| :custom | |
| (doom-modeline-height 35)) | |
| ;; Font ligatures (Fira Code) | |
| (use-package ligature | |
| :config | |
| (ligature-set-ligatures | |
| 'prog-mode | |
| '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\" | |
| "{-" "::" ":::" ":=" "!!" "!=" "!==" "-}" "----" "-->" "->" "->>" | |
| "-<" "-<<" "-~" "#{" "#[" "##" "###" "####" "#(" "#?" "#_" "#_(" | |
| ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*" "/**" | |
| "/=" "/==" "/>" "//" "///" "&&" "||" "||=" "|=" "|>" "^=" "$>" | |
| "++" "+++" "+>" "=:=" "==" "===" "==>" "=>" "=>>" "<=" | |
| "=<<" "=/=" ">-" ">=" ">=>" ">>" ">>-" ">>=" ">>>" "<*" | |
| "<*>" "<|" "<|>" "<$" "<$>" "<!--" "<-" "<--" "<->" "<+" | |
| "<+>" "<=" "<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<" | |
| "<~" "<~~" "</" "</>" "~@" "~-" "~>" "~~" "~~>" "%%")) | |
| (global-ligature-mode t)) | |
| ;; Colorful parentheses | |
| (use-package rainbow-delimiters | |
| :hook (prog-mode . rainbow-delimiters-mode)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Completion Framework ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Vertical completion UI | |
| (use-package vertico | |
| :custom | |
| (vertico-count 20) | |
| (vertico-resize t) | |
| :config | |
| (vertico-mode)) | |
| ;; Fuzzy/orderless matching | |
| (use-package orderless | |
| :custom | |
| (completion-styles '(orderless basic)) | |
| (completion-category-overrides '((file (styles basic partial-completion))))) | |
| ;; Enhanced commands (search, buffer switch, etc.) | |
| (use-package consult | |
| :bind (("C-x b" . consult-buffer) | |
| ("M-y" . consult-yank-pop) | |
| ("M-g g" . consult-goto-line) | |
| ("M-g M-g" . consult-goto-line) | |
| ("M-g i" . consult-imenu)) | |
| :hook (completion-list-mode . consult-preview-at-point-mode) | |
| :init | |
| (setq xref-show-xrefs-function #'consult-xref | |
| xref-show-definitions-function #'consult-xref) | |
| :config | |
| (consult-customize | |
| consult-theme :preview-key '(:debounce 0.2 any) | |
| consult-ripgrep consult-git-grep consult-grep consult-bookmark | |
| consult-recent-file consult-xref | |
| consult-source-bookmark consult-source-file-register | |
| consult-source-recent-file consult-source-project-recent-file | |
| :preview-key '(:debounce 0.4 any)) | |
| (setq consult-narrow-key "<")) | |
| ;; Annotations in minibuffer completions | |
| (use-package marginalia | |
| :init (marginalia-mode)) | |
| ;; Context-dependent action palette (Tier 2) | |
| (use-package embark | |
| :bind (("C-." . embark-act) ; Pick context action on target at point / minibuffer | |
| ("M-." . embark-dwim) ; Do-what-I-mean default action on target | |
| ("C-h B" . embark-bindings)) ; Show keybindings for target / mode | |
| :init | |
| ;; Replace standard prefix help (C-h after a prefix key) with Embark | |
| (setq prefix-help-command #'embark-prefix-help-command) | |
| :config | |
| ;; Hide mode line in Embark collect buffers | |
| (add-to-list 'display-buffer-alist | |
| '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" | |
| nil | |
| (window-parameters (mode-line-format . none))))) | |
| (use-package embark-consult | |
| :ensure t | |
| :after (embark consult) | |
| :hook (embark-collect-mode . consult-preview-at-point-mode)) | |
| ;; Inline completion popup (replaces company-mode) | |
| (use-package corfu | |
| :custom | |
| (corfu-auto t) ; show completions automatically | |
| (corfu-auto-delay 0.1) | |
| (corfu-cycle t) ; cycle through candidates | |
| :init | |
| (global-corfu-mode)) | |
| ;; Extra completion sources for corfu | |
| (use-package cape | |
| :init | |
| (add-hook 'completion-at-point-functions #'cape-dabbrev) | |
| (add-hook 'completion-at-point-functions #'cape-file)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Development Tools ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Import PATH and environment from shell (needed for nvm, etc.) | |
| (use-package exec-path-from-shell | |
| :config | |
| (when (memq window-system '(mac ns x pgtk)) | |
| (exec-path-from-shell-initialize))) | |
| ;; Tree-sitter grammar management | |
| (use-package treesit-auto | |
| :custom | |
| (treesit-auto-install 'prompt) | |
| :config | |
| (treesit-auto-add-to-auto-mode-alist 'all) | |
| (global-treesit-auto-mode)) | |
| ;; LSP via built-in eglot | |
| (use-package eglot | |
| :ensure nil | |
| :hook ((go-ts-mode rust-ts-mode python-ts-mode python-mode | |
| js-ts-mode typescript-ts-mode zig-mode) . eglot-ensure) | |
| :config | |
| (add-to-list 'eglot-server-programs '(odin-mode . ("ols"))) | |
| (setq eglot-autoshutdown t)) ; shut down server when last buffer is closed | |
| ;; Git porcelain | |
| (use-package magit | |
| :defer t) | |
| ;; Git gutter indicators | |
| (use-package diff-hl | |
| :config | |
| (global-diff-hl-mode) | |
| (add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh) | |
| (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Clojure + CIDER ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| (use-package clojure-mode | |
| :mode (("\\.clj\\'" . clojure-mode) | |
| ("\\.edn\\'" . clojure-mode) | |
| ("\\.cljs\\'" . clojurescript-mode) | |
| ("\\.cljc\\'" . clojurec-mode))) | |
| (use-package cider | |
| :defer t | |
| :diminish subword-mode | |
| :custom | |
| (cider-repl-pop-to-buffer-on-connect t) | |
| (cider-show-error-buffer t) | |
| (cider-auto-select-error-buffer t)) | |
| (use-package clj-deps-new | |
| :defer t) | |
| ;; Structural editing for Lisps — paredit handles parens, so disable electric-pair | |
| (use-package paredit | |
| :hook ((clojure-mode . paredit-mode) | |
| (clojurescript-mode . paredit-mode) | |
| (clojurec-mode . paredit-mode) | |
| (cider-repl-mode . paredit-mode) | |
| (emacs-lisp-mode . paredit-mode) | |
| (lisp-interaction-mode . paredit-mode)) | |
| :config | |
| (add-hook 'paredit-mode-hook | |
| (lambda () (electric-pair-local-mode -1)))) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Languages ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Go | |
| (use-package go-ts-mode | |
| :ensure nil | |
| :mode "\\.go\\'") | |
| (use-package flymake-golangci | |
| :after go-ts-mode | |
| :hook (go-ts-mode . flymake-golangci-load)) | |
| ;; Odin | |
| (use-package odin-mode | |
| :vc (:url "https://github.com/mattt-b/odin-mode") | |
| :defer t | |
| :hook (odin-mode . eglot-ensure)) | |
| (with-eval-after-load 'eglot | |
| (add-to-list 'eglot-server-programs '(odin-mode . ("ols")))) | |
| ;; Zig | |
| (use-package zig-mode | |
| :defer t) | |
| ;; Racket | |
| (use-package racket-mode | |
| :defer t) | |
| (use-package geiser-racket | |
| :defer t) | |
| ;; Web (HTML, JSX, TSX) | |
| (use-package web-mode | |
| :mode ("\\.html\\'" "\\.jsx\\'" "\\.tsx\\'")) | |
| ;; Markdown | |
| (use-package markdown-mode | |
| :mode ("\\.md\\'" ("README\\.md\\'" . gfm-mode)) | |
| :custom | |
| (markdown-command "multimarkdown")) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Org Mode ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| (use-package org | |
| :ensure nil ; built-in | |
| :custom | |
| (org-directory "~/org") | |
| (org-agenda-files '("~/org")) | |
| (org-startup-indented t) | |
| (org-hide-emphasis-markers t) | |
| (org-return-follows-link t) | |
| :config | |
| (setq org-capture-templates | |
| '(("t" "Todo" entry (file+headline "~/org/inbox.org" "Tasks") | |
| "* TODO %?\n %i\n %a") | |
| ("n" "Note" entry (file+headline "~/org/inbox.org" "Notes") | |
| "* %?\n %i\n %a")))) | |
| ;; Pretty org headers, bullets, tables | |
| (use-package org-modern | |
| :hook (org-mode . org-modern-mode)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Project Management ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| (use-package project | |
| :ensure nil | |
| :config | |
| ;; Automatically remember the project when you open any file in it | |
| (defun my-project-remember-current () | |
| (when-let ((pr (project-current))) | |
| (project-remember-project pr))) | |
| (add-hook 'find-file-hook #'my-project-remember-current)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Dired ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| (use-package dired | |
| :ensure nil | |
| :custom | |
| ;; Always copy/delete recursively | |
| (dired-recursive-copies 'always) | |
| (dired-recursive-deletes 'top) | |
| ;; Quickly copy/move file in dired to the other split window | |
| (dired-dwim-target t) | |
| :hook | |
| ;; Hide file details by default for a cleaner workspace (press '(' to toggle) | |
| (dired-mode . dired-hide-details-mode)) | |
| ;; Add beautiful icons to dired | |
| (use-package nerd-icons-dired | |
| :hook | |
| (dired-mode . nerd-icons-dired-mode)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; Load custom.el ;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| (when (file-exists-p custom-file) | |
| (load custom-file)) | |
| ;;; init.el ends here |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment