Last active
April 18, 2026 05:20
-
-
Save ianthetechie/535e68e39f00f8f77f1bbb27959c35ef to your computer and use it in GitHub Desktop.
My 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
| ;;; early-init.el --- Pre-GUI initialization -*- lexical-binding: t -*- | |
| ;; Performance: maximize GC threshold during init (reset in init.el after startup) | |
| (setq gc-cons-threshold most-positive-fixnum) | |
| (setq gc-cons-percentage 0.6) | |
| ;; Performance: read up to 1MB from subprocesses at a time (default 4KB) | |
| ;; Prevents pipe buffer backpressure that blocks child processes like cargo nextest | |
| (setq read-process-output-max (* 1024 1024)) | |
| ;; Suppress UI chrome before the frame is drawn (prevents flash) | |
| (setq default-frame-alist | |
| '((menu-bar-lines . 0) | |
| (tool-bar-lines . 0) | |
| (vertical-scroll-bars . nil))) |
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 --- ianthetechie's configuration -*- lexical-binding: t -*- | |
| ;; | |
| ;; System dependencies: | |
| ;; Required: | |
| ;; - git | |
| ;; - ripgrep | |
| ;; - nushell | |
| ;; - nuspell/hunspell/aspell | |
| ;; - A C compiler (e.g. clang) | |
| ;; - CMake and gmake (for vterm) | |
| ;; - libenchant (brew install enchant on macOS; enchant2 package on FreeBSD/OpenBSD) | |
| ;; | |
| ;; Optional: | |
| ;; - Berkeley Mono font | |
| ;; - sourcekit-lsp | |
| ;; | |
| ;; macOS only: | |
| ;; - Homebrew | |
| ;; ============================================================ | |
| ;; Bootstrap & Package System | |
| ;; ============================================================ | |
| ;; Reset GC to a reasonable runtime value after init | |
| ;; (early-init.el sets it to most-positive-fixnum for fastest startup) | |
| (add-hook 'emacs-startup-hook | |
| (lambda () | |
| (setq gc-cons-threshold (* 20 1024 1024) | |
| gc-cons-percentage 0.1))) | |
| ;; Package archives | |
| (require 'package) | |
| (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/")) | |
| (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) | |
| (package-initialize) | |
| ;; Allow MELPA to upgrade built-in packages (needed for eglot semantic tokens) | |
| (setq package-install-upgrade-built-in t) | |
| ;; All use-package blocks auto-install their packages | |
| (require 'use-package-ensure) | |
| (setq use-package-always-ensure t) | |
| ;; ============================================================ | |
| ;; Platform & Environment | |
| ;; ============================================================ | |
| ;; Enable basic mouse support over SSH connections | |
| (unless (display-graphic-p) | |
| (xterm-mouse-mode 1)) | |
| ;; macOS-specific configuration | |
| (when (eq system-type 'darwin) | |
| ;; Homebrew bin path (needed for some tools like spelling backends for Jin) | |
| (add-to-list 'exec-path "/opt/homebrew/bin") | |
| ;; Use clang (otherwise strange defaults and some packages won't compile) | |
| (setenv "CC" "/usr/bin/clang") | |
| (setenv "CXX" "/usr/bin/clang++")) | |
| ;; Ensure package archive index is available on fresh installs | |
| (unless package-archive-contents | |
| (package-refresh-contents)) | |
| ;; Preferred shell (nushell s not POSIX complient so I normally don't set it as my login shell) | |
| (setq explicit-shell-file-name "nu") | |
| (setq vterm-shell "nu") | |
| ;; SSH config already sets ControlMaster options globally; | |
| ;; tell TRAMP not to add its own ControlMaster options (Emacs 30+) | |
| (setopt tramp-use-connection-share nil) | |
| ;; ============================================================ | |
| ;; UI & Appearance | |
| ;; ============================================================ | |
| ;; Font (with availability check) | |
| (when (display-graphic-p) | |
| (load-theme 'leuven-dark t) | |
| (tool-bar-mode -1) | |
| (scroll-bar-mode -1) | |
| (let ((preferred-font "Berkeley Mono")) | |
| (if (member preferred-font (font-family-list)) | |
| (set-face-attribute 'default nil :family preferred-font :height 150) | |
| (message "WARNING: Font '%s' not found; using Emacs default" preferred-font)))) | |
| ;; Configure bars to my liking | |
| (menu-bar-mode -1) | |
| (setq column-number-mode t) | |
| (setq window-resize-pixelwise t) | |
| (setq frame-resize-pixelwise t) | |
| (tab-bar-mode t) | |
| ;; ============================================================ | |
| ;; Core Editor Behavior | |
| ;; ============================================================ | |
| ; guess major mode from the file name | |
| (setq-default major-mode | |
| (lambda () | |
| (unless buffer-file-name | |
| (let ((buffer-file-name (buffer-name))) | |
| (set-auto-mode))))) | |
| ;; Confirm quit | |
| (setq confirm-kill-emacs #'yes-or-no-p) | |
| (setq auto-save-default nil) | |
| (setq make-backup-files nil) | |
| (save-place-mode t) | |
| (savehist-mode t) | |
| (recentf-mode t) | |
| ;; Automatically revert buffers when files change on disk | |
| (global-auto-revert-mode 1) | |
| (setq auto-revert-check-vc-info nil) | |
| (defalias 'yes-or-no #'y-or-n-p) | |
| ;; Store automatic customisation options elsewhere | |
| (setq custom-file (locate-user-emacs-file "custom.el")) | |
| (when (file-exists-p custom-file) | |
| (load custom-file)) | |
| ;; Use editorconfig | |
| (use-package editorconfig | |
| :config | |
| (editorconfig-mode t)) | |
| ;; Use ripgrep instead of grep | |
| (setq xref-search-program 'ripgrep) | |
| ;; Easy layout swapping | |
| (use-package transpose-frame | |
| :bind ("C-x |" . transpose-frame)) | |
| ;; ============================================================ | |
| ;; Completion Framework (Vertico + Orderless + Consult) | |
| ;; ============================================================ | |
| (use-package vertico | |
| :init | |
| (vertico-mode t) | |
| :bind (:map vertico-map | |
| ;; improve directory navigation | |
| ("RET" . vertico-directory-enter) | |
| ("DEL" . vertico-directory-delete-word) | |
| ("M-d" . vertico-directory-delete-char))) | |
| ;; Orderless completion style (space-separated fuzzy matching) | |
| (use-package orderless | |
| :demand t | |
| :custom | |
| (completion-styles '(orderless basic))) | |
| ;; Extended completion framework | |
| (use-package consult | |
| :bind (([rebind switch-to-buffer] . consult-buffer) | |
| ("C-c s r" . consult-ripgrep) ; project-wide text search with live preview | |
| ("C-c s l" . consult-line) ; in-buffer line search | |
| ("C-c s i" . consult-imenu)) ; in-buffer symbol navigation | |
| :custom | |
| (read-buffer-completion-ignore-case t) | |
| (read-file-name-completion-ignore-case t) | |
| (completion-ignore-case t)) | |
| ;; ============================================================ | |
| ;; Version Control | |
| ;; ============================================================ | |
| (use-package magit | |
| :custom | |
| ;; Massive speedup on macOS, and shouldn't affect sane platforms like FreeBSD | |
| ;; https://indieweb.social/@xenodium/114793849088519458 | |
| (magit-git-executable (executable-find "git"))) | |
| (use-package diff-hl | |
| :config | |
| (global-diff-hl-mode)) | |
| ;; Ediff: side-by-side view | |
| (setq ediff-split-window-function 'split-window-horizontally) | |
| (setq magit-ediff-dwim-show-on-hunks t) | |
| ;; ============================================================ | |
| ;; LSP (Eglot) | |
| ;; ============================================================ | |
| ;; Face for mutable variables via semantic tokens (like RustRover/JetBrains) | |
| (defface eglot-semantic-mutable | |
| '((t :underline t)) | |
| "Face for mutable variables via semantic tokens.") | |
| ;; Built-in eglot (1.17) lacks semantic token support; install MELPA version | |
| (unless (package-installed-p 'eglot '(1 21)) | |
| (package-install 'eglot)) | |
| (use-package eglot | |
| :hook (prog-mode . eglot-ensure) | |
| :config | |
| ;; Swift LSP | |
| (add-to-list 'eglot-server-programs '(swift-mode . ("sourcekit-lsp"))) | |
| ;; Highlight mutable variables | |
| (add-to-list 'eglot-semantic-token-modifiers "mutable") | |
| ;; Re-enable workspace refresh handlers (disabled/missing in eglot 1.21+ due to | |
| ;; clangd/lean issues, but needed for rust-analyzer which sends incomplete | |
| ;; results before project analysis finishes). | |
| ;; Also refresh inlay hints — eglot doesn't advertise inlayHint refreshSupport | |
| ;; so there's no separate refresh request; piggyback on semantic tokens instead. | |
| (cl-defmethod eglot-handle-request | |
| (_server (_method (eql workspace/semanticTokens/refresh))) | |
| (dolist (buffer (eglot--managed-buffers _server)) | |
| (eglot--when-live-buffer buffer | |
| (setq eglot--semtok-state nil) | |
| (font-lock-flush) | |
| (when (bound-and-true-p eglot-inlay-hints-mode) | |
| (eglot-inlay-hints-mode -1) | |
| (eglot-inlay-hints-mode 1)))))) | |
| ;; Consult + eglot interop | |
| (use-package consult-eglot | |
| :bind ("C-c s s" . consult-eglot-symbols)) | |
| (use-package eldoc-box | |
| :custom | |
| (eldoc-box-clear-with-C-g t) | |
| :bind | |
| (:map help-map ("D" . eldoc-box-help-at-point))) | |
| ;; ============================================================ | |
| ;; Programming language Modes & Behavior | |
| ;; ============================================================ | |
| ;; Parens pairing | |
| (electric-pair-mode t) | |
| ;; Indentation | |
| (setq-default indent-tabs-mode nil) | |
| (setq-default tab-width 4) | |
| ;; Trailing newlines | |
| (setq-default require-final-newline t) | |
| (use-package auctex | |
| :defer t | |
| :custom | |
| (TeX-auto-save t) | |
| (TeX-parse-self t) | |
| :config | |
| (setq-default TeX-master nil) | |
| :hook (LaTeX-mode . LaTeX-math-mode)) | |
| (use-package caddyfile-mode :defer t) | |
| (use-package dockerfile-mode :defer t) | |
| (use-package haskell-mode :defer t) | |
| (use-package just-mode :defer t) | |
| (use-package json-mode | |
| :hook (json-mode . (lambda () (setq-local js-indent-level 2)))) | |
| (use-package kdl-mode :defer t) | |
| (use-package kotlin-mode :defer t) | |
| (use-package lua-mode :defer t) | |
| (use-package markdown-mode :defer t) | |
| (use-package nushell-mode :defer t) | |
| (use-package php-mode :defer t) | |
| (use-package rust-mode :defer t) | |
| (use-package swift-mode :defer t) | |
| (use-package terraform-mode :defer t) | |
| (use-package typescript-mode :defer t) | |
| (use-package uiua-ts-mode :defer t) | |
| (use-package vcl-mode :defer t) | |
| (use-package yaml-mode :defer t) | |
| ;; Line numbers in programming buffers | |
| (add-hook 'prog-mode-hook #'display-line-numbers-mode) | |
| ;; Styling of TODOs and FIXMEs | |
| (use-package hl-todo | |
| :config | |
| (global-hl-todo-mode)) | |
| ;; ============================================================ | |
| ;; Tools | |
| ;; ============================================================ | |
| ;; Spell checking (requires a backend like nuspell or hunspell) | |
| (use-package jinx | |
| :hook ((text-mode prog-mode conf-mode markdown-mode) . jinx-mode)) | |
| ;; Terminal emulators | |
| (use-package eat :defer t) | |
| (use-package vterm | |
| :defer t | |
| :commands (vterm vterm-mode)) | |
| (defun project-term (&optional arg) | |
| "Open a project-aware terminal in a bottom side window. | |
| Reuses an existing buffer if one exists for the project. | |
| With a prefix argument, always create a new terminal buffer." | |
| (interactive "P") | |
| (let* ((project (project-current t)) | |
| (default-directory (project-root project)) | |
| (name (file-name-nondirectory (directory-file-name default-directory))) | |
| (base-name (format "*term[%s]*" name)) | |
| (buf-name (if arg (generate-new-buffer-name base-name) base-name)) | |
| (buf (get-buffer buf-name))) | |
| (unless (and buf (buffer-live-p buf)) | |
| (setq buf (get-buffer-create buf-name)) | |
| (with-current-buffer buf | |
| (vterm-mode))) | |
| (select-window | |
| (display-buffer-in-side-window | |
| buf '((side . bottom) | |
| (window-height . 0.3) | |
| (window-parameters . ((no-delete-other-windows . t)))))))) | |
| (define-key project-prefix-map (kbd "t") #'project-term) | |
| ;; ============================================================ | |
| ;; AI / Assistants | |
| ;; ============================================================ | |
| (use-package claude-code-ide | |
| :vc (:url "https://github.com/manzaltu/claude-code-ide.el" :rev :newest) | |
| :bind ("C-c C-'" . claude-code-ide-menu) | |
| :config | |
| (claude-code-ide-emacs-tools-setup) | |
| (setq claude-code-ide-terminal-backend 'eat)) | |
| ;; ============================================================ | |
| ;; Org Mode | |
| ;; ============================================================ | |
| (setq org-directory "~/org") | |
| (setq org-todo-keywords | |
| '((sequence "TODO" "IN-PROGRESS" "IN-REVIEW" "DONE"))) | |
| ;; ============================================================ | |
| ;; Global Keybindings | |
| ;; ============================================================ | |
| (global-set-key (kbd "s-/") #'comment-line) | |
| (global-set-key (kbd "M-RET") #'eglot-code-actions) | |
| (global-set-key (kbd "S-<f6>") #'eglot-rename) | |
| ;; Mouse button navigation for buffer history (GUI only) | |
| (when (display-graphic-p) | |
| (global-set-key (kbd "<mouse-3>") #'previous-buffer) | |
| (global-set-key (kbd "<mouse-4>") #'next-buffer)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment