From 8fa53a582c95aba33bb011476efc63e8f50a3a89 Mon Sep 17 00:00:00 2001 From: Jason Hilder Date: Wed, 14 Oct 2026 07:44:06 +0200 Subject: [PATCH] Added the old emacs config. --- .gitignore | 12 ++ config/emacs/development.el | 40 ++++++ config/emacs/early-init.el | 44 ++++++ config/emacs/git.el | 58 ++++++++ config/emacs/init.el | 280 ++++++++++++++++++++++++++++++++++++ 5 files changed, 434 insertions(+) create mode 100644 config/emacs/development.el create mode 100644 config/emacs/early-init.el create mode 100644 config/emacs/git.el create mode 100644 config/emacs/init.el diff --git a/.gitignore b/.gitignore index ffe6e0d..633c4ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ +config/emacs/backups/ +config/emacs/eln-cache/ +config/emacs/elpa/ +config/emacs/auto-save/ +config/emacs/auto-save-list/ +config/emacs/custom.el +config/emacs/places.eld +config/emacs/projects.eld +config/emacs/recentf.eld +config/emacs/.ignore +config/emacs/session* +config/emacs/history config/fish/conf.d config/fish/functions config/fish/fish_variables diff --git a/config/emacs/development.el b/config/emacs/development.el new file mode 100644 index 0000000..00864da --- /dev/null +++ b/config/emacs/development.el @@ -0,0 +1,40 @@ +;;;; Markdown +(use-package markdown-mode) + +;;;; Odin +(use-package odin-mode + :vc (:url "https://github.com/mattt-b/odin-mode" + :rev :newest) + :mode "\\.odin\\'" + :hook (odin-mode . eglot-ensure)) + +(use-package eglot + :ensure nil + :config + (setq eglot-autoshutdown t) + (add-to-list 'eglot-ignored-server-capabilities :signatureHelpProvider) + + ;; Explicit OLS configuration. + (add-to-list + 'eglot-server-programs + '(odin-mode + . ("/home/jason/.local/bin/ols" + :initializationOptions + (:odin_command "/home/jason/.odin/odin" + :enable_document_symbols t + :enable_format t + :collections + [(:name "core" + :path "/home/jason/.odin/core") + (:name "vendor" + :path "/home/jason/.odin/vendor")]))))) + +;; OLS built-in definitions. +(setenv "OLS_BUILTIN_FOLDER" "/home/jason/ThirdParty/ols/builtin") + +;; Vim-style code navigation. +(evil-define-key '(normal motion) 'global + "gd" #'xref-find-definitions + "gD" #'xref-find-references + "K" #'eldoc + (kbd "C-t") #'xref-go-back) diff --git a/config/emacs/early-init.el b/config/emacs/early-init.el new file mode 100644 index 0000000..79c96a3 --- /dev/null +++ b/config/emacs/early-init.el @@ -0,0 +1,44 @@ +;;; early-init.el --- Pre-init: GC ceiling and frame chrome -*- lexical-binding: t; -*- + +;;; Commentary: +;; Runs before package.el initialises and before the first frame is drawn, so +;; it is the only place two things can take effect: +;; 1. Raising the GC ceiling, so the whole startup (package load + init.el) +;; runs without garbage-collection pauses. +;; 2. Removing the menu/tool/scroll bars via `default-frame-alist', so they +;; are never drawn — toggling the minor modes in init.el instead draws +;; them and then removes them, a visible flicker on launch. +;; +;; Nothing here may depend on a package: none are loaded yet. +;; +;; IMPORTANT: the GC bump below is only half the pair. Restore a working value +;; once startup is done by adding this to init.el: +;; +;; (add-hook 'emacs-startup-hook +;; (lambda () (setq gc-cons-threshold (* 16 1024 1024)))) +;; +;; Without it, GC never runs again and Emacs slowly eats memory. + +;;; Code: + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Garbage Collection +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Hold GC off for the whole startup; init.el lowers it back on +;; `emacs-startup-hook' (see the Commentary above). + +(setq gc-cons-threshold most-positive-fixnum) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Frame Chrome (no flicker) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Disable the bars before the first frame exists. + +(push '(menu-bar-lines . 0) default-frame-alist) +(push '(tool-bar-lines . 0) default-frame-alist) +(push '(vertical-scroll-bars) default-frame-alist) + +;; Don't resize the frame when the UI/fonts change during startup. +(setq frame-inhibit-implied-resize t) + +;;; early-init.el ends here diff --git a/config/emacs/git.el b/config/emacs/git.el new file mode 100644 index 0000000..c868ecf --- /dev/null +++ b/config/emacs/git.el @@ -0,0 +1,58 @@ +;;; git.el --- Magit, set up for Evil -*- lexical-binding: t; -*- +;;; Commentary: +;; Loaded from init.el, which must set `evil-want-keybinding' to nil before +;; Evil loads (Evil Collection needs it). +;; +;; :git Magit status for the current repo, full frame. +;; q puts your previous window layout back. +;; :git ARGS Run `git ARGS' from the repo root, e.g. :git log --oneline +;; +;; In Magit (Evil Collection layout): +;; j/k move, TAB folds, s stage, u unstage, x discard, c c commit, +;; p push, F pull, l log, d diff, b branch, z stash, gr refresh, q quit, +;; ? lists every command. +;; Commit message: opens in insert state; ZZ or C-c C-c commits, +;; ZQ or C-c C-k cancels. +;; Ctrl+hjkl, C-f and C-b keep their global meaning in Magit buffers. +;;; Code: + +(use-package magit + :defer t + :custom + ;; Status takes the whole frame; q restores the previous window layout. + (magit-display-buffer-function #'magit-display-buffer-fullframe-status-v1) + (magit-bury-buffer-function #'magit-restore-window-configuration)) + +;; Vim-style keys for Magit, its section folding, and the commit message buffer. +;; (magit magit-submodule) is the form Evil Collection's own default list uses. +(with-eval-after-load 'evil-collection + ;; Evil Collection puts section jumps on C-j/C-k and page scrolling on C-f/C-b. + ;; Unbind them so your window and Consult keys show through. Sibling sections + ;; are still on ] and [, and scrolling on C-d/C-u. + (add-hook 'evil-collection-setup-hook + (lambda (mode &rest _) + (when (eq mode 'magit) + (evil-define-key 'normal magit-mode-map + (kbd "C-j") nil + (kbd "C-k") nil + (kbd "C-f") nil + (kbd "C-b") nil)))) + + (evil-collection-init '((magit magit-submodule) magit-section with-editor))) + +;; Commit messages start in insert state. Added after git-commit loads so its +;; default hook functions are kept. +(with-eval-after-load 'git-commit + (add-hook 'git-commit-setup-hook #'evil-insert-state)) + +;; :git opens Magit; :git ARGS runs a git command. +(evil-define-command my/git (args) + "Open Magit status, or run `git ARGS' from the repository root." + (interactive "") + (if (and args (string-match-p "[^[:space:]]" args)) + (magit-git-command-topdir (concat "git " args)) + (magit-status))) + +(evil-ex-define-cmd "git" #'my/git) + +;;; git.el ends here diff --git a/config/emacs/init.el b/config/emacs/init.el new file mode 100644 index 0000000..5f4b0b0 --- /dev/null +++ b/config/emacs/init.el @@ -0,0 +1,280 @@ +;;; init.el --- Minimal Emacs: built-ins + evil + consult -*- lexical-binding: t; -*- +;; Learning: F1 k KEY, F1 f FUNCTION, F1 v VARIABLE, +;; F1 m (modes), F1 b (bindings). +;; C-h works too, except in normal/motion state where it moves +;; to the window on the left. +;;; Code: + +;;;; 1. Basics +(let ((bin (expand-file-name "~/.local/bin"))) + (add-to-list 'exec-path bin) + (setenv "PATH" (concat bin path-separator (getenv "PATH")))) + +;; Custom's output goes to its own file, not here. +(setq custom-file (expand-file-name "custom.el" user-emacs-directory)) +(load custom-file 'noerror 'nomessage) + +;; Package archives: GNU and NonGNU are built in, MELPA is added at lowest priority. +(require 'package) +(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) +(setq package-archive-priorities '(("gnu" . 3) ("nongnu" . 2) ("melpa" . 1))) +(package-initialize) + +(require 'use-package) +(setq use-package-always-ensure t) + +;;;; 2. UI +(menu-bar-mode -1) +(tool-bar-mode -1) +(scroll-bar-mode -1) +(blink-cursor-mode -1) + +(setq inhibit-startup-screen t + initial-scratch-message nil + ring-bell-function #'ignore + use-short-answers t + scroll-conservatively 101 + scroll-margin 15) + +(setq-default indent-tabs-mode nil + tab-width 4) + +(global-hl-line-mode 1) +(global-display-line-numbers-mode 1) +(pixel-scroll-precision-mode 1) +(column-number-mode 1) +(electric-pair-mode 1) +(which-key-mode 1) + +;; Jellybeans theme. +(add-to-list 'warning-suppress-types '(files)) +(use-package jbeans-theme + :config + (load-theme 'jbeans t)) + +;; Font, on a darker background than the theme's. +(set-face-attribute 'default nil + :family "JetBrainsMono Nerd Font" + :height 120 + :background "#131313") + +;; Mode line at the top of each window: the header line takes over its +;; format and look, on the editor background. +(setq-default header-line-format mode-line-format mode-line-format nil) +(set-face-attribute 'header-line nil :inherit 'mode-line :background "#131313") + +(setq-default line-spacing '(4 . 4)) + +;; Command launcher. +(keymap-global-set "C-S-p" #'execute-extended-command) + +;; Escape cancels prompts. keyboard-quit can't leave the minibuffer, so there +;; Escape runs abort-minibuffers, as C-g does. +(keymap-global-set "" #'keyboard-quit) +(keymap-set minibuffer-local-map "" #'abort-minibuffers) + +(add-to-list 'default-frame-alist '(fullscreen . maximized)) + +;;;; 3. Files +(dolist (d '("backups/" "auto-save/")) + (make-directory (locate-user-emacs-file d) t)) + +(setq backup-directory-alist + `(("." . ,(locate-user-emacs-file "backups/"))) + auto-save-file-name-transforms + `((".*" ,(locate-user-emacs-file "auto-save/") t)) + create-lockfiles nil + confirm-kill-processes nil + vc-follow-symlinks t) + +(save-place-mode 1) +(savehist-mode 1) +(recentf-mode 1) +(global-auto-revert-mode 1) + +;;;; 4. Completion +;; C-n/C-p move through candidates: Vertico remaps next-line/previous-line, +;; and section 5 stops Evil from taking those keys in the minibuffer. +(use-package vertico + :init + (vertico-mode 1) + :custom + (vertico-count 15)) + +(setq completion-styles '(basic partial-completion flex) + completion-ignore-case t + read-buffer-completion-ignore-case t + read-file-name-completion-ignore-case t + completions-format 'one-column + isearch-lazy-count t + xref-search-program 'ripgrep) + +(use-package vertico-posframe + :after vertico + :custom + (vertico-posframe-poshandler #'posframe-poshandler-frame-center) + :config + (vertico-posframe-mode 1)) + +;;;; 5. Evil +;; Evil 1.15.0 on Emacs 31 reads this legacy variable, which Emacs no longer defines. +(defvar evil-mode-buffers nil) + +(defun my/insert-tab () + "Insert a literal tab character." + (interactive) + (insert "\t")) + +(use-package evil + :init + ;; evil-want-keybinding: Evil Collection (below) supplies mode bindings instead + ;; of Evil's small built-in set. It has to be set before Evil loads. + (setq evil-want-C-u-scroll t + evil-undo-system 'undo-redo + evil-want-keybinding nil) + + :config + (evil-mode 1) + + (evil-define-key 'insert 'global + ;; Literal tab. as well, or a mode's own binding wins in GUI frames. + (kbd "TAB") #'my/insert-tab + (kbd "") #'my/insert-tab + + ;; C-SPC: complete using the buffer's completion sources. + (kbd "C-SPC") #'completion-at-point + (kbd "C-@") #'completion-at-point) + + ;; Global navigation. + ;; C-p, C-f and C-b are assigned to Consult below. + (evil-define-key '(normal motion) 'global + (kbd "C-e") #'dired-jump)) + +;; Evil Collection: vim-style bindings for other packages, one mode at a time. +;; Info here (F1 i); Magit, Magit sections and commit messages in git.el. +(use-package evil-collection + :after evil + :custom + ;; Don't add its global [b ]b [e ]e ... bindings. + (evil-collection-want-unimpaired-p nil) + :config + (evil-collection-init '(info))) + +;; Minibuffer. Vertico +(with-eval-after-load 'vertico + (evil-define-key '(insert normal) vertico-map + (kbd "C-n") #'vertico-next + (kbd "C-p") #'vertico-previous + (kbd "TAB") #'vertico-insert + (kbd "") #'vertico-insert)) + +(evil-define-key '(insert normal) minibuffer-local-map + (kbd "") #'abort-minibuffers) + +;; Window navigation (normal/motion state): Ctrl+hjkl move, Ctrl+` closes. +;; GUI only for C-`: terminals send it as C-@. +(evil-define-key '(normal motion) 'global + (kbd "C-h") #'windmove-left + (kbd "C-j") #'windmove-down + (kbd "C-k") #'windmove-up + (kbd "C-l") #'windmove-right + (kbd "C-`") #'delete-window) + +;; Result buffers: hjkl motions and RET to jump to a result. +(dolist (m '(occur-mode grep-mode xref--xref-buffer-mode)) + (evil-set-initial-state m 'motion)) + +(with-eval-after-load 'compile + (evil-define-key 'motion compilation-mode-map + (kbd "RET") #'compile-goto-error)) + +(with-eval-after-load 'grep + (evil-define-key 'motion grep-mode-map + (kbd "RET") #'compile-goto-error)) + +(with-eval-after-load 'replace + (evil-define-key 'motion occur-mode-map + (kbd "RET") #'occur-mode-goto-occurrence)) + +(with-eval-after-load 'xref + (evil-define-key 'motion xref--xref-buffer-mode-map + (kbd "RET") #'xref-goto-xref)) + +;;;; 6. Consult +;; Requires the external `fd` and `rg` executables. Both honour .ignore files, +;; so keep excludes (node_modules, elpa, ...) in ~/.ignore or per-project .ignore. +;; Searches start at the project root when there is one, else the current directory. +(use-package consult + :config + ;; Hide noise from the buffer picker. + (dolist (re '("\\`\\*scratch\\*\\'" + "\\`\\*Messages\\*\\'" + "\\`\\*Warnings\\*\\'" + "\\`\\*xref\\*\\'")) + (add-to-list 'consult-buffer-filter re)) + + ;; Don't re-run the preview on every keystroke while typing a search. + (consult-customize consult-ripgrep + :preview-key '(:debounce 0.4 any))) + +;; Bind immediately, without waiting for consult to load. +(evil-define-key '(normal motion) 'global + (kbd "C-p") #'consult-fd + (kbd "C-f") #'consult-ripgrep + (kbd "C-b") #'consult-buffer) + +;;;; 7. Dired +(setq dired-dwim-target t + dired-kill-when-opening-new-dired-buffer t + dired-listing-switches "-agho --group-directories-first" + dired-recursive-copies 'always + delete-by-moving-to-trash t) + +(with-eval-after-load 'dired + (evil-set-initial-state 'dired-mode 'motion) + + (evil-define-key 'motion dired-mode-map + "h" #'dired-up-directory + "l" #'dired-find-file + (kbd "RET") #'dired-find-file)) + +;;;; 8. Build +(setq compile-command "./build.sh" + compilation-read-command nil + compilation-ask-about-save nil + compilation-scroll-output t) + +(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter) + +;; Compilation buffer always at the bottom, full frame width. +(add-to-list 'display-buffer-alist + '((derived-mode . compilation-mode) + (display-buffer-reuse-window display-buffer-at-bottom) + (window-height . 0.45))) + +;; C-c C-c builds from the project root. +;; C-c b asks first: the prompt is pre-filled with ./build.sh, so type the args after it. +(defun my/project-compile-prompt () + "Run `project-compile', prompting to edit the command." + (interactive) + (let ((compilation-read-command t)) + (call-interactively #'project-compile))) + +(keymap-global-set "C-c C-c" #'project-compile) +(keymap-global-set "C-c b" #'my/project-compile-prompt) + +;; Focus the compilation window when a build starts. +(add-hook 'compilation-start-hook + (lambda (proc) + (when-let* ((win (get-buffer-window (process-buffer proc)))) + (select-window win)))) + +;;;; 9. Languages +;; Setup for dev languages, modes, lsp and lsp keybinds. +(load (locate-user-emacs-file "development.el") nil 'nomessage) + +;;;; 10. Git +;; Magit and the :git command live in git.el, next to this file. +(load (locate-user-emacs-file "git.el") nil 'nomessage) + +;;; init.el ends here