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,
:abcis called the category and
xyzis the name of the module. Doom refers to modules in one of two formats:
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:
packages.elto declare all the packages it will install,
config.elto configure and load those packages,
- And, sometimes, an
autoload.elto 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.
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
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).
- 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, when
after!and hooks aren’t enough).
- Configure behavior of
bin/doomin a way that must also apply in interactive sessions.
- Configure packages with
- Preform expensive or error-prone operations; these files are evaluated whenever
bin/doomis used; a fatal error in this file can make Doom unbootable (but not irreversibly).
- Define new
bin/doomcommands here. That’s what
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.
use-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 the
featurep!macro to make some configuration conditional based on the state of another module or the presence of a flag.
- Install packages with
:ensureproperty. Doom has its own package manager. That’s what
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).
- Declare packages with the
- Disable single packages with
:disableproperty or multiple packages with the
- Use the
featurep!macro to make packages conditional based on the state of another module or the presence of a flag.
- Configure packages here (definitely no
after!in here!). This file is read in an isolated environment and will have no lasting effect. The only exception is configuration targeting
- 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.
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
autoloadcall 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
Doom’s autoload file is generated by scanning these files when you execute
doom sync .
;; 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>") [...])
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.
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"))
This file is read when
bin/doom starts up. Use it to define your own CLI commands or reconfigure existing ones.
Doom’s unit tests go here. More information on them to come…
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.