| name | doom-emacs |
| description | Guide for reading, writing, and modifying the user's Doom Emacs configuration. Use this skill whenever the user asks about Emacs, Doom Emacs, elisp config, keybindings, packages, modules, or any change to files under .config/doom/. Also trigger when the user mentions editor configuration, text editing workflows, or anything that would naturally live in their Emacs setup — even if they don't say "Emacs" explicitly but the context makes it clear (e.g., "add a snippet", "configure LSP for language X", "add a keybinding for…").
|
Doom Emacs Configuration
Configuration layout
The Doom Emacs config lives at .config/doom/ (the DOOMDIR). Here's how it's organized:
Top-level files
| File | Purpose |
|---|
config.el | Main config — UI, themes, fonts, global keybindings, general settings, and load! calls to split-out files. This is the entry point. |
packages.el | Package declarations (package! forms with pins and recipes). No config logic goes here — only dependency declarations. |
dd/ — Topic-specific config files
Split-out config loaded from config.el via (load! "dd/<name>"). Each file covers a language or feature area:
| File | Topic |
|---|
common-lisp.el | Common Lisp / Sly |
dired.el | Dired customizations |
dunst.el | Dunst notification integration |
elisp.el | Emacs Lisp editing |
host-dogdot.el | Host-specific config for the dogdot machine |
host-lapdog.el | Host-specific config for the lapdog machine |
javascript.el | JavaScript/TypeScript |
lsp.el | LSP client settings |
nix.el | Nix language support |
terminal.el | Terminal emulator config |
tree-sitter.el | Tree-sitter text objects and helpers |
When adding config for a new language or feature area, create a new file here and add a (load! "dd/<name>") call in config.el.
modules/ — Private Doom modules
Full-blown private Doom modules (with their own config.el and packages.el). These are enabled in the Doom init.el macro (generated by Nix — see below).
| Module | Description |
|---|
lang/elixir-extra | Extra Elixir tooling |
tools/ai | AI/LLM integration (gptel, copilot, MCP, prompts) |
Private modules follow standard Doom module conventions: config.el for configuration, packages.el for package declarations, and optional sub-files loaded from config.el.
snippets/ — YASnippet snippets
Organized by major mode:
envrc-file-mode/
+javascript-npm-mode/
makefile-gmake-mode/
nix-mode/
nix-ts-mode/
Nix integration
The list of enabled Doom modules and Emacs system-level configuration is managed through Nix at:
modules/home-manager/programs/emacs.nix
This file:
- Declares which Doom modules are enabled (with flags) via a Nix attrset that gets rendered into
init.el
- Installs system packages needed by Emacs (language servers, formatters, tools)
- Sets up the Emacs package itself and the Doom Emacs framework
When the user wants to enable or disable a Doom module, or change module flags, edit emacs.nix. When they want to configure an already-enabled module's behavior, edit the elisp files under .config/doom/.
Reference sources
When you need to understand how a package or Doom module works:
-
Installed package source code: ~/.local/share/doomemacs/straight/repos/
Each package has its own directory. Read the source to understand available functions, variables, hooks, and faces.
-
Doom Emacs framework source: /etc/nix/inputs/doomemacs/
Contains all built-in module definitions (modules/), core libraries (lisp/), and the module system itself. Check here to understand what a Doom module provides, what variables it exposes, and how it configures packages.
Use these paths liberally — reading source is far more reliable than guessing at API surfaces.
Writing conventions
Follow the patterns already established in the config:
- Every
.el file starts with ;;; <path> -*- lexical-binding: t; -*-
- Files in
dd/ that are substantial include a copyright header (check existing files for the format)
- Use
map! for keybindings (Doom's binding macro)
- Use
with-eval-after-load or use-package with :after / :defer for lazy loading
- Use
(load! "dd/<name>") in config.el to pull in topic files
- Pin packages in
packages.el with :pin when declaring them
- Host-specific behavior is gated with
(pcase (system-name) ...) or (when (string= "hostname" (system-name)) ...)
Common tasks
Adding a new package
- Add the
package! declaration to packages.el (with :pin for the commit hash)
- Add configuration in the appropriate
dd/<topic>.el file (or config.el if it's general)
- If the package needs system dependencies (binaries, libraries), also update
emacs.nix
Adding keybindings
Use map! — the Doom keybinding macro. Common patterns from the config:
;; Normal mode under leader
(map! :leader :desc "Description" :n "key" #'command)
;; Mode-local under localleader
(map! :localleader :map some-mode-map
:desc "Action" "key" #'command)
;; Multiple bindings in one form
(map! :n "key1" #'cmd1
:n "key2" #'cmd2)
Adding a new language config
- Create
.config/doom/dd/<language>.el
- Add
(load! "dd/<language>") to config.el in the "Extra config files" section
- If a new Doom module needs enabling, edit
emacs.nix
- If extra packages are needed, add them to
packages.el
Creating a private Doom module
- Create the directory under
.config/doom/modules/<category>/<name>/
- Add
config.el (and packages.el if it needs packages)
- Enable it in
emacs.nix by adding the module to the appropriate category in the Doom init attrset