To create your own module you need only create a directory for it in ~/.doom.d/modules/abc/xyz
, then add :abc xyz
to your doom!
block in ~/.doom.d/init.el
to enable it.
In this example,
:abc
is called the category andxyz
is the name of the module. Doom refers to modules in one of two formats::abc xyz
andabc/xyz
.
If a private module possesses the same name as a built-in Doom module (say, :lang org
), it replaces the built-in module. Use this fact to rewrite modules you don’t agree with.
Of course, an empty module isn’t terribly useful, but it goes to show that nothing in a module is required. The typical module will have:
- A
packages.el
to declare all the packages it will install, - A
config.el
to configure and load those packages, - And, sometimes, an
autoload.el
to store that module’s functions, to be loaded when they are used.
These are a few exceptional examples of a well-rounded module:
The remainder of this guide will go over the technical details of a Doom module.
File structure
Doom recognizes a handful of special file names, none of which are required for a module to function. They are:
category/
module/
test/*.el
autoload/*.el
autoload.el
init.el
cli.el
config.el
packages.el
doctor.el
init.el
This file is loaded early, before anything else, but after Doom core is loaded. It is loaded in both interactive and non-interactive sessions (it’s the only file, besides cli.el
that is loaded when the bin/doom
starts up).
Do:
- Configure Emacs or perform setup/teardown operations that must be set early; before other modules are (or this module is) loaded.
- Reconfigure packages defined in Doom modules with
use-package-hook!
(as a last resort, whenafter!
and hooks aren’t enough). - Configure behavior of
bin/doom
in a way that must also apply in interactive sessions.
Don’t:
- Configure packages with
use-package!
orafter!
from here - Preform expensive or error-prone operations; these files are evaluated whenever
bin/doom
is used; a fatal error in this file can make Doom unbootable (but not irreversibly). - Define new
bin/doom
commands here. That’s whatcli.el
is for.
config.el
The heart of every module. Code in this file should expect dependencies (in packages.el
) to be installed and available. Use it to load and configure its packages.
Do:
- Use
after!
oruse-package!
to configure packages.
;; from modules/completion/company/config.el
(use-package! company ; `use-package!' is a thin wrapper around `use-package'
; it is required that you use this in Doom's modules,
; but not required to be used in your private config.
:commands (company-mode global-company-mode company-complete
company-complete-common company-manual-begin company-grab-line)
:config
(setq company-idle-delay nil
company-tooltip-limit 10
company-dabbrev-downcase nil
company-dabbrev-ignore-case nil)
[...])
- Lazy load packages with
use-package
’s:defer
property. - Use the
featurep!
macro to make some configuration conditional based on the state of another module or the presence of a flag.
Don’t:
- Use
package!
- Install packages with
package.el
oruse-package
’s:ensure
property. Doom has its own package manager. That’s whatpackages.el
is for.
packages.el
This file is where package declarations belong. It’s also a good place to look if you want to see what packages a module manages (and where they are installed from).
Do:
- Declare packages with the
package!
macro - Disable single packages with
package!
’s:disable
property or multiple packages with thedisable-packages!
macro. - Use the
featurep!
macro to make packages conditional based on the state of another module or the presence of a flag.
Don’t:
- Configure packages here (definitely no
use-package!
orafter!
in here!). This file is read in an isolated environment and will have no lasting effect. The only exception is configuration targetingstraight.el
. - Perform expensive calculations. These files are read often and sometimes multiple times.
- Produce any side-effects, for the same reason.
The ”Package Management” section goes over the
package!
macro and how to deal with packages.
autoload/*.el
OR autoload.el
These files are where you’ll store functions that shouldn’t be loaded until they’re needed and logic that should be autoloaded (evaluated very, very early at startup).
This is all made possible thanks to these autoload cookie: ;;;###autoload
. Placing this on top of a lisp form will do one of two things:
- Add a
autoload
call to Doom’s autoload file (found in~/.emacs.d/.local/autoloads.el
, which is read very early in the startup process). - Or copy that lisp form to Doom’s autoload file verbatim (usually the case for anything other then
def*
forms, likedefun
ordefmacro
).
Doom’s autoload file is generated by scanning these files when you execute doom sync
.
For example:
;; from modules/lang/org/autoload/org.el
;;;###autoload
(defun +org/toggle-checkbox ()
(interactive)
[...])
;; from modules/lang/org/autoload/evil.el
;;;###autoload (autoload '+org:attach "lang/org/autoload/evil" nil t)
(evil-define-command +org:attach (&optional uri)
(interactive "<a>")
[...])
doctor.el
When you execute doom doctor
, this file defines a series of tests for the module. These should perform sanity checks on the environment, such as:
- Check if the module’s dependencies are satisfied,
- Warn if any of the enabled flags are incompatible,
- Check if the system has any issues that may interfere with the operation of this module.
Use the warn!
, error!
and explain!
macros to communicate issues to the user and, ideally, explain how to fix them.
For example, the :lang cc
module’s doctor checks to see if the irony server is installed:
;; from lang/cc/doctor.el
(require 'irony)
(unless (file-directory-p irony-server-install-prefix)
(warn! "Irony server isn't installed. Run M-x irony-install-server"))
cli.el
This file is read when bin/doom
starts up. Use it to define your own CLI commands or reconfigure existing ones.
test/**/test-*.el
Doom’s unit tests go here. More information on them to come…
Additional files
Any files beyond the ones I have already named are not given special treatment. They must be loaded manually to be loaded at all. In this way modules can be organized in any way you wish. Still, there is one convention that has emerged in Doom’s community that you may choose to adopt: extra files in the root of the module are prefixed with a plus, e.g. +extra.el
. There is no syntactical or functional significance to this convention.
These can be loaded with the load!
macro, which will load an elisp file relative to the file it’s used from. e.g.
;; Omitting the file extension allows Emacs to load the byte-compiled version,
;; if it is available:
(load! "+git") ; loads ./+git.el
This can be useful for splitting up your configuration into multiple files, saving you the hassle of creating multiple modules.