Modules
Jotain’s Elisp configuration is split acrosslisp/init-*.el, one file per concern. Each file is self-contained: a package that only exists to enhance a built-in (e.g. dirvish → dired, magit → vc, corfu → completion-preview) lives in the same file as the built-in it enhances. There is no “builtins.el” / “third-party.el” split.
Conventions
- Every module file is named
init-<concern>.elunderlisp/. - Every file starts with a
lexical-binding: tcookie. - Every file ends with
(provide 'init-<concern>). - Modules are loaded in order from
init.el. use-package-always-ensureist(set inearly-init.el), so everyuse-packageblock defaults to “install if missing”. Built-ins must opt out with:ensure nil. Packages provided by Nix use:ensure nil— Nix puts them onload-path, souse-packagefinds them without touching the network.- User options (
defcustomvariables) are set viasetopt, notsetq, so the:setcallback and:typevalidation run.
Load order
init.el loads modules in this order:
Core modules
init-core
GC, encoding, and the sane-defaults baseline. Restores gc-cons-threshold to 16 MiB after the early-init bump; pauses GC entirely while the minibuffer is open; forces UTF-8 everywhere; enables save-place-mode, recentf, savehist, repeat-mode, uniquify, ibuffer as the default buffer list, global-auto-revert-mode. Sets up no-littering to keep var/ and etc/ under the config directory. Also contains the macOS modifier-key fix (Cmd→Meta, Option→Super, right Option untouched for special characters).
init-keys
Global bindings only — per-package bindings live in the relevant use-package block. Unbinds C-z / C-x C-z (no accidental suspend on GUI), binds M-o to other-window, enables windmove-default-keybindings (Shift + arrows), and defines jotain-toggle-window-split on C-x j for rotating a two-window layout.
init-ui
Appearance. Uses modus-operandi-tinted and modus-vivendi-tinted (user-customisable via jotain-theme-light / jotain-theme-dark), with auto-dark-mode flipping between them based on the system appearance. Installs doom-modeline, picks the first available font from jotain-font-preferences (JetBrains Mono Nerd Font → Iosevka Nerd Font → DejaVu Sans Mono), enables display-line-numbers, pixel-scroll-precision-mode, hl-line-mode, show-paren-mode, built-in which-key, nerd-icons integrations for dired/ibuffer/corfu/marginalia, hl-todo, breadcrumb, pulsar, rainbow-delimiters, indent-bars. Also enables global-kkp-mode (Kitty Keyboard Protocol) and clipetty for OSC 52 clipboard support through SSH + tmux.
init-help
Built-in help tweaks (help-window-select) plus helpful for richer describe-* buffers. See Finding Information in Emacs for day-to-day usage.
init-editing
Baseline editing primitives: electric-pair-mode, delete-selection-mode, whitespace-mode, undo settings, region tools.
init-completion
The vertico stack on the minibuffer side, corfu + cape on the in-buffer side, plus the built-in minibuffer tweaks they rely on. All configured in one file because touching one nearly always means touching the others. C-s is deliberately left as isearch-forward; use M-s l for consult-line when you want the consult UI.
init-navigation
dired and dirvish in the same file, along with project, winner-mode, and directional navigation.
init-vc
vc pinned to Git only (other backends are a slow startup tax), magit with refined hunks and worktrees in magit-status, magit-todos, forge (configured with the built-in sqlite backend, emacsql-sqlite-builtin), diff-hl (including diff-hl-flydiff-mode for pre-save indicators), smerge-mode helpers, and ediff with sane window setup.
init-prog
The shared substrate for programming modes: prog-mode hooks, treesit setup, eglot, flymake, eldoc, apheleia for format-on-save, compile. Per-language eglot-ensure hooks live here so all LSP wiring is in one place; per-language mode settings live in init-lang-*.el.
init-project
Two complementary project command systems: projection (.dir-locals.el-backed commands exposed via C-x P, auto-detected from Makefiles/justfiles/Cargo.toml/etc) and compile-multi (per-major-mode named compile commands like “go test”, “pytest file”, “nix flake check”). They overlap but neither fully covers the other.
init-ai
AI assistants. claude-code-ide on C-c C-' for agentic multi-file edits via the Claude Code CLI. gptel on C-c RET (send) / C-c M-RET (menu) with three backends configured: Anthropic (default, claude-sonnet-4), Google Gemini, and a local Ollama instance (no key needed). mcp is loaded after gptel for Model Context Protocol tool use. API keys resolve from the environment (ANTHROPIC_API_KEY / GEMINI_API_KEY) first, then fall back to auth-source — auth-source-1password (see init-systems) makes that transparent.
init-shell
eshell, vterm, and comint together — shells have their own input handling, history, prompts, and rendering, so grouping them lets the rules be coordinated in one place.
init-systems
Sysadmin tools: SOPS-encrypted file editing, logview for log files, and auth-source-1password as an auth-source backend so magit/forge, gptel, smtpmail, etc. all resolve credentials against the 1Password vault transparently.
init-tracking
Passive usage instrumentation. keyfreq counts command frequency (M-x keyfreq-show), wakatime-mode reports heartbeats to a Wakatime/Wakapi server (guarded by :if on wakatime-cli + WAKATIME_API_KEY, so it is a no-op if either is missing), and activity-watch-mode is installed but off by default — enable with M-x global-activity-watch-mode.
init-writing
Prose editing: jinx for spell-checking, markdown-mode, denote for notes, pdf-tools. Org is big enough to earn its own file.
init-org
org-mode, org-modern, capture templates, agenda, and friends. Binds C-c a → org-agenda, C-c c → org-capture, C-c l → org-store-link.
Language modules
Eachinit-lang-*.el file pins :mode regexes and provides font-lock for a group of related languages. Formatter configuration is centralised through apheleia in init-prog.el; LSP server hooks live in init-prog.el too.
init-lang-nix—nix-ts-mode. Formatting via apheleia (which ships anixfmtentry).init-lang-rust—rust-ts-mode(built-in) plus Cargo command wrappers.init-lang-python—python-ts-mode(built-in), the interpreter set topython3. The LSP server (pyright/basedpyright/ruff-lsp) comes from the project environment, not from this config.init-lang-web—typescript-ts-mode,tsx-ts-mode, CSS, HTML, JSON, and related tree-sitter modes.init-lang-devops— Dockerfile, docker-compose, Terraform (*.tf), gitlab-ci, justfile, Ansible.init-lang-data— YAML, CSV (withcsv-align-mode), SQL (sql-indent), Jinja2, gnuplot.init-lang-systems— Go (go-mode), C/C++ (cc-mode), CMake, Haskell.
Adding a new module
- Create
lisp/init-<concern>.elwith the usual Elisp headers and alexical-binding: tcookie. - End the file with
(provide 'init-<concern>). - Add a
(require 'init-<concern>)line ininit.elat the appropriate point in the load order. - Run
just checkto confirm the parser is happy, thenjust compileto catch warnings.