Professional Documents
Culture Documents
#+STARTUP: nofold
GNU Emacs is one grand ol' adventure, let alone Doom Emacs. Before you start
you'll need to set up Emacs, Doom, and its packages, then learn how to take care
of your new puppy/operating system. This guide will walk you through installing,
using, configuring and troubleshooting all of these things, to smooth you into
your Emacs journey.
This guide will gloss over many technicalities so you can get up and running as
soon as possible. A more technical user manual is in the works for aspiring
contributors who want a deeper understanding of how Doom Emacs works.
#+begin_quote
If you feel like we've missed something, [[https://discord.gg/qvGgnVx][join us on
our Discord server]] and let
us know!
#+end_quote
* Install
This is what you'll have installed by the end of this section:
- Git 2.23+
- Emacs 27.1+ *(27.2 is recommended, or [[https://www.emacswiki.org/emacs/GccEmacs]
[native-comp]])*
- [[https://github.com/BurntSushi/ripgrep][ripgrep]] 11.0+
- GNU Find
- (Optional) [[https://github.com/sharkdp/fd][fd]] 7.3.0+ (known as ~fd-find~ on
Debian, Ubuntu & derivatives) --
improves performance for many file indexing commands
**** Ubuntu
Emacs 27.x is not available through Ubuntu's package manager out-of-the-box, but
is available through a PPA:
#+BEGIN_SRC bash
add-apt-repository ppa:kelleyk/emacs
apt-get update
apt-get install emacs27
#+END_SRC
Or through snap:
#+BEGIN_SRC bash
snap install emacs --classic
#+END_SRC
In some cases, you may need to delete old version of emacs and it's dependencies
first, before installing emacs27:
#+BEGIN_SRC bash
sudo apt remove emacs
sudo apt autoremove
#+END_SRC
**** Fedora
#+BEGIN_SRC bash
# required dependencies
dnf install emacs git ripgrep
# optional dependencies
dnf install fd-find # is 'fd' in Fedora <28
#+END_SRC
**** NixOS
On NixOS Emacs 27.2 can be installed via ~nix-env -Ai nixos.emacs~, or
permanently with the following added to ~etc/nixos/configuration.nix~:
#+BEGIN_SRC nix
environment.systemPackages = with pkgs; [
# required dependencies
git
emacs # Emacs 27.2
ripgrep
# optional dependencies
coreutils # basic GNU utilities
fd
clang
];
#+END_SRC
environment.systemPackages = [
pkgs.emacsGcc # Installs Emacs 28 + native-comp
];
#+END_SRC
**** openSUSE
***** Emacs 27.1
Emacs can be installed from the [[https://software.opensuse.org/download.html?
project=editors&package=emacs][package list]], or manually via zypper.
If you already have an older version of Emacs installed, you will be prompted to
install the update candidate (Emacs 27.1).
***** ripgrep
Download ripgrep 11.0.2 from [[https://software.opensuse.org/download/package?
package=ripgrep&project=openSUSE%3AFactory][the package list]] or installed
manually (requires
root).
#+BEGIN_SRC bash
zypper addrepo
https://download.opensuse.org/repositories/openSUSE:Factory/standard/
openSUSE:Factory.repo
zypper refresh
zypper install ripgrep
#+END_SRC
Only ripgrep 0.8.1 is officially available on Leap 15.1 and 15.2, so you will
need to install Rust to build ripgrep from source. Rust can be downloaded
[[https://software.opensuse.org/package/rust][from
the package list]] or installed manually via zypper (requires root), e.g.
#+BEGIN_SRC bash
zypper addrepo
https://download.opensuse.org/repositories/openSUSE:Leap:15.1:Update/standard/
openSUSE:Leap:15.1:Update.repo
zypper refresh
zypper install rust
#+END_SRC
***** Emacs
To use Emacs graphically, enable the =gui= USE flag. And enable the =xft= USE flag
to render fonts correctly (see
[[https://github.com/hlissner/doom-emacs/issues/4876][issue #4876]])
#+begin_src sh
echo "app-editors/emacs gui xft" >> /etc/portage/package.use/emacs
#+end_src
Or, for GCCEmacs/Native Compilation, use the live ebuild for version 28.0 with the
=jit= USE flag:
And emerge:
#+begin_src sh
emerge =app-editors/emacs-28.0.9999
#+end_src
*** On macOS
MacOS users have many options for installing Emacs, but not all of them are well
suited to Doom. Before we get to that you'll need either the Homebrew or
MacPorts package manager installed (you only need one):
For Emacs itself, these three formulas are the best options, ordered from most
to least recommended for Doom (based on compatibility).
+ emacsformacosx.com
+ ~brew cask install emacs~ (installs from emacsformacosx.com)
+ AquaMacs
+ XEmacs
Some of these ports do not add an =emacs= binary to your ~PATH~, which is
necessary for Doom's installation process. You'll have to do so yourself by
adding this to your shell config:
#+BEGIN_SRC sh
# Add this to ~/.zshrc or ~/.bash_profile
export PATH="/Applications/MacPorts/Emacs.app/Contents/MacOS:$PATH"
#+END_SRC
*** On Windows
#+begin_quote
*WARNING:* Emacs on Windows is much slower than its Linux or macOS counterparts.
There are some suggestions on how to speed it up later in this section.
#+end_quote
There are three methods for installing Emacs 27.x on Windows, each with their
pros and cons:
+ With chocolatey/scoop
+ With a precompiled binary + Git Bash
+ With WSL2 + Ubuntu
If you don't know which to choose, I highly recommend WSL; it produces the
fastest and most stable environment of the three, but has the most complex
installation process.
Before moving on to installing Emacs et co, a few steps to prepare Windows for
Emacs are necessary:
#+begin_quote
A pre-existing PATH variable should already exist among your system
variables. It contains a string of file paths separated by colons;
~pathA:pathB:pathC~. Prepend the path to bin/doom to that string, like so:
~C:\Users\username\.emacs.d\bin:pathA:pathB:pathC~
#+end_quote
3. Restart your system so your new values for ~HOME~ and ~PATH~ take effect.
Scoop will work too, but because Emacs is a GUI application you'll need to
enable the 'extras' Scoop bucket:
#+BEGIN_SRC sh
scoop bucket add extras
scoop install git emacs ripgrep
# Optional dependencies
scoop install fd llvm
#+END_SRC
And done! Keep git-bash.exe open, you'll need it for the rest of this guide.
#+begin_quote
*IMPORTANT:* you'll need to open git-bash.exe whenever you want to run a
bin/doom command.
#+end_quote
And done! Keep Ubuntu open, you'll need it for the rest of this guide.
** Doom Emacs
With Emacs and Doom's dependencies installed, next is to install Doom Emacs
itself:
#+BEGIN_SRC bash
git clone https://github.com/hlissner/doom-emacs ~/.emacs.d
~/.emacs.d/bin/doom install
#+END_SRC
=doom install= will set up your =DOOMDIR= at =~/.doom.d= (if it doesn't already
exist) and will work you through the first-time setup of Doom Emacs. Carefully
follow any instructions it puts out.
If this is your first time, you should run ~doom doctor~. This will diagnose
common issues with your system or config.
#+BEGIN_QUOTE
If you'd like a more technical break down of ~doom install~, it's been
translated into shell commands below, in the "Install Doom Manually" section.
#+END_QUOTE
It exposes a variety of commands. ~bin/doom help~ will list them all, but here
is a summary of the most important ones:
+ ~doom sync~: This synchronizes your config with Doom Emacs. It ensures that
needed packages are installed, orphaned packages are removed and necessary
metadata correctly generated. Run this whenever you modify your ~doom!~ block
or =packages.el= file. You'll need ~doom sync -u~ if you override the recipe
of package installed by another module.
+ ~doom upgrade~: Updates Doom Emacs (if available) and all its packages.
+ ~doom env~: (Re)generates an "envvar file", which is a snapshot of your
shell environment that Doom loads at startup. If your app launcher or OS
launches Emacs in the wrong environment you will need this. **This is required
for GUI Emacs users on MacOS.**
+ ~doom doctor~: If Doom misbehaves, the doc will diagnose common issues with
your installation, system and environment.
+ ~doom purge~: Over time, the repositories for Doom's plugins will accumulate.
Run this command from time to time to delete old, orphaned packages, and with
the ~-g~ switch to compact existing package repos.
Use ~doom help~ to see an overview of the available commands that =doom=
provides, and ~doom help COMMAND~ to display documentation for a particular
~COMMAND~.
#+begin_quote
I recommend you add =~/.emacs.d/bin= to your ~PATH~ so you can call =doom=
directly and from anywhere. Accomplish this by adding this to your .bashrc or
.zshrc file: ~export PATH="$HOME/.emacs.d/bin:$PATH"~
#+end_quote
#+BEGIN_SRC bash
# So we don't have to write ~/.emacs.d/bin/doom every time
PATH="$HOME/.emacs.d/bin:$PATH"
# The init.example.el file contains an example doom! call, which tells Doom what
# modules to load and in what order.
cp ~/.emacs.d/init.example.el ~/.doom.d/init.el
cp ~/.emacs.d/core/templates/config.example.el ~/.doom.d/config.el
cp ~/.emacs.d/core/templates/packages.example.el ~/.doom.d/packages.el
# You might want to edit ~/.doom.d/init.el here and make sure you only have the
# modules you want enabled.
# If you know Emacs won't be launched from your shell environment (e.g. you're
# on macOS or use an app launcher that doesn't launch programs with the correct
# shell) then create an envvar file to ensure Doom correctly inherits your shell
# environment.
#
# If you don't know whether you need this or not, there's no harm in doing it
# anyway. `doom install` will have prompted you to generate one. If you
# responded no, you can generate it later with the following command:
doom env
2. Move aside any existing config and install Chemacs2 as your new =~/.emacs.d=:
#+BEGIN_SRC bash :eval no
[ -f ~/.emacs ] && mv ~/.emacs ~/.emacs.bak
[ -d ~/.emacs.d ] && mv ~/.emacs.d ~/.emacs.legacy
git clone https://github.com/plexus/chemacs2.git ~/.emacs.d
#+END_SRC
#+BEGIN_SRC bash
emacs --with-profile spacemacs
#+END_SRC
** External/system dependencies
Doom is comprised of approximately 160 modules which provide its features,
language support and integration with external tools. Many of these have
external dependencies that you must install yourself. You'll find what a module
needs and how to install them in that module's README.org file or by running
~bin/doom doctor~.
#+begin_quote
Use ~M-x doom/help-modules~ (bound to =SPC h d m= or =C-h d m=) to jump to a
module's documentation from within Doom, otherwise, place your cursor on a
module in your ~doom!~ block (in =~/.doom.d/init.el=) and press =K= to jump to
its documentation (or =gd= to jump to its source code). =C-c g k= and =C-c g d=
for non-evil users, respectively.
#+end_quote
#+BEGIN_SRC bash
doom upgrade --packages
#+END_SRC
#+begin_quote
To minimize issues while upgrading, avoid modifying Doom's source files in
=~/.emacs.d=. All your customization should be kept in your =DOOMDIR= (e.g.
=~/.doom.d=). Read the [[#Configure][Configure]] section for more on configuring
Doom.
#+end_quote
** TODO Rollback
The =bin/doom= script doesn't currently offer rollback support for Doom or its
packages (yet).
** Up/Downgrading Emacs
*You may encounter errors after up/downgrading Emacs.* Run ~doom sync~ on the
command line after changing the installed version of Emacs. If you've changed
the major version (e.g. 27 -> 28 or vice versa) run ~doom build~ too.
+ ~doom sync~ will re-index any built-in/site loaddef files. This is especially
necessary if paths to built-in libraries have changed.
+ ~doom build~ will recompile all your installed packages, which is necessary
because Emacs bytecode not generally forward compatible across major releases
(e.g. 27 -> 28). Alternatively, reinstall all your packages by deleting
=~/.emacs.d/.local=, then run ~doom sync~.
* TODO Migrate
If you're here from another Emacs distribution (or your own), here are a few
things to be aware of while you convert your old config to Doom:
+ Doom does not use =package.el= to manage its packages, but ~use-package~ does!
You will see errors if you have ~:ensure ...~ properties in your ~use-package~
blocks. Remove these and, instead, add ~package!~ declarations to
=~/.doom.d/packages.el= to install your packages.
* Configure
You can configure Doom by tweaking the files found in your =DOOMDIR=. Doom
expects this directory to be found in one of:
#+begin_quote
Change the =DOOMDIR= environment variable to change where Doom looks for this
directory. Symlinks will work as well.
#+end_quote
When you ran ~doom install~, it deployed a simple Doom configuration to your
=DOOMDIR=, comprised of these three files:
+ init.el :: Where you'll find your ~doom!~ block, which controls what Doom
modules are enabled and in what order they will be loaded.
This file is evaluated early when Emacs is starting up; before any other
module has loaded. You generally shouldn't add code to this file unless you're
targeting Doom's CLI or something that needs to be configured very early in
the startup process.
+ config.el :: Here is where 99.99% of your private configuration should go.
Anything in here is evaluated /after/ all other modules have loaded, when
starting up Emacs.
+ packages.el :: Package management is done from this file; where you'll declare
what packages to install and where from.
#+begin_quote
Note: do not use ~M-x customize~ or the customize API in general. Doom is
designed to be configured programmatically from your config.el, which can
conflict with Customize's way of modifying variables.
If you're concerned about ~defcustom~ setters, Doom has a ~setq!~ macro that
will trigger them.
#+end_quote
** Modules
Doom consists of around 160 modules and growing. A Doom module is a bundle of
packages, configuration and commands, organized into a unit that can be toggled
easily by tweaking your ~doom!~ block (found in =$DOOMDIR/init.el=).
#+begin_quote
If =$DOOMDIR/init.el= doesn't exist, you haven't run ~doom install~ yet. See
[[#install][the
"Install" section]] above.
#+end_quote
#+BEGIN_SRC emacs-lisp
;; To comment something out, you insert at least one semicolon before it and the
;; Emacs Lisp interpreter will ignore everything until the end of the line.
(doom! :lang
python ; this module is not commented, therefore enabled
;;javascript ; this module is commented out, therefore disabled
;;lua ; this module is disabled
ruby ; this module is enabled
php) ; this module is enabled
#+END_SRC
It controls what modules are enabled and in what order they are loaded. Some
modules have *optional features* that can be enabled by passing them flags,
denoted by a plus prefix:
#+BEGIN_SRC emacs-lisp
(doom! :completion
(company +childframe)
:lang
(csharp +unity)
(org +brain +dragndrop +gnuplot +hugo +jupyter)
(sh +fish))
#+END_SRC
#+begin_quote
*IMPORTANT:* any changes to your ~doom!~ block won't take effect until you run
~doom sync~ on the command line.
#+end_quote
#+begin_quote
~doom doctor~ will detect issues with your ~doom!~ block, such as duplicate or
misspelled modules and flags.
#+end_quote
** Package management
**Doom Emacs does not use package.el** (the package manager built into Emacs).
Instead, it uses its own declarative package manager built on top of
[[https://github.com/raxod502/straight.el][straight.el]].
Packages are declared in ~packages.el~ files. You'll find one in your =DOOMDIR=
and in many of Doom's modules. Read on to learn how to use this system to
install your own packages.
#+begin_quote
*WARNING:* Do not install packages directly (with ~M-x package-install~ or ~M-x
straight-use-package~). Without an accompanying ~package!~ declaration somewhere
these packages will be forgotten when you restart Emacs and uninstalled the next
time you run ~doom sync~ or ~doom purge~.
#+end_quote
#+begin_quote
*WARNING:* If you're here from another Emacs distro (or vanilla Emacs), be wary
of the ~:ensure~ property in ~use-package~ blocks, because it will attempt (and
fail) to install packages through package.el. Tutorials will recommend you
install packages this way too!
#+end_quote
If a package could not be found in any known repo you will get an error like:
#+begin_quote
Could not find package X in recipe repositories: (org-elpa melpa gnu-elpa-mirror
emacsmirror-mirror)
#+end_quote
~package!~ will return non-nil if the package is cleared for install and hasn't
been disabled elsewhere. Use this fact to chain package dependencies together.
e.g.
#+BEGIN_SRC elisp
(when (package! example)
(package! plugin-that-example-depends-on))
#+END_SRC
#+begin_quote
*IMPORTANT:* New packages won't be installed until you run ~doom sync~.
#+end_quote
*** Installing packages from external sources
To install a package straight from an external source (like github, gitlab,
etc), you'll need to specify a [[https://github.com/raxod502/straight.el#the-
recipe-format][MELPA-style straight recipe]]:
;; If the source files for a package are in a subdirectory in said repo, use
;; `:files' to target them.
(package! example :recipe
(:host github
:repo "username/my-example-fork"
:files ("*.el" "src/lisp/*.el")))
;; If the repo pulls in many unneeded submodules, you can disable recursive cloning
(package! example :recipe (:nonrecursive t))
;; A package can be installed straight from a git repo by setting :host to nil:
(package! example
:recipe (:host nil :repo "https://some/git/repo"))
#+END_SRC
#+begin_quote
*IMPORTANT:* Run ~bin/doom sync~ whenever you modify packages.el files to
ensure your changes take effect.
#+end_quote
;; This will work too, if you prefer the syntax, but it provides no concise
;; syntax for unpinning multiple packages:
(package! helm :pin nil)
#+END_SRC
Though it is *highly* discouraged, you may unpin all packages and make Doom
Emacs rolling release:
#+BEGIN_SRC elisp
(unpin! t)
#+END_SRC
#+begin_quote
Unpinning all packages is discouraged because Doom's modules are designed
against the pinned versions of its packages. More volatile packages (like
lsp-mode, ein and org) change rapidly, and are likely to cause breakages if
unpinned.
Instead, it's a better to selectively unpin packages, or repin them to the exact
commit you want.
#+end_quote
#+begin_quote
*IMPORTANT:* Run ~bin/doom sync~ whenever you modify packages.el files to
ensure your changes take effect.
#+end_quote
;; in DOOMDIR/packages.el
(package! evil :recipe (:host github :repo "username/my-evil-fork"))
#+END_SRC
#+begin_quote
*IMPORTANT:* Remember to run ~doom sync -u~ after changing recipes for existing
packages. At the time of writing, ~doom sync~ alone will not pick up on recipe
changes.
#+end_quote
(package! my-package
:recipe (:local-repo "/path/to/my/package"
;; By default, the package manager grabs all *.el files at the root
;; of the project and nothing else. To include other files, or
;; accommodate unconventional project structures, specify what :files
;; you want:
:files ("*.el" "src/lisp/*.el")
;; With this you can avoid having to run 'doom sync' every time you
;; change the package.
:build (:not compile)))
#+END_SRC
;; or
(add-load-path! "lisp/package")
#+END_SRC
#+begin_quote
*IMPORTANT:* Remember to run ~doom sync~ to rebuild your package after you've
changed it, and to re-index any autoloads in it.
#+end_quote
** Configuring Doom
*** Configuring packages
If your configuration needs are simple, the ~use-package!~, ~after!~,
~add-hook!~ and ~setq-hook!~ macros are your bread and butter.
#+BEGIN_SRC emacs-lisp
;;; ~/.doom.d/config.el (example)
(setq doom-font (font-spec :family "Fira Mono" :size 12))
(use-package! hl-todo
;; if you omit :defer, :hook, :commands, or :after, then the package is loaded
;; immediately. By using :hook here, the `hl-todo` package won't be loaded
;; until prog-mode-hook is triggered (by activating a major mode derived from
;; it, e.g. python-mode)
:hook (prog-mode . hl-todo-mode)
:init
;; code here will run immediately
:config
;; code here will run after the package is loaded
(setq hl-todo-highlight-punctuation ":"))
#+END_SRC
#+BEGIN_SRC emacs-lisp
;;; ~/.doom.d/init.el (example)
;; If a :pre-init / :pre-config hook returns nil, it overwrites that package's
;; original :init / :config block. Exploit this to overwrite Doom's config.
(use-package-hook! doom-themes
:pre-config
(setq doom-neotree-file-icons t)
nil)
+ Evil users can use the =gr= operator to evaluate a segment of code. The return
value is displayed in the minibuffer or in a popup (if the result is large
enough to warrant one).
=gr= works for most languages, but using it on Elisp is a special case; it's
executed within your current session of Emacs. You can use this to modify
Emacs' state on the fly.
+ Non-evil users can use =C-x C-e= to run ~eval-last-sexp~, as well as ~M-x
+eval/buffer-or-region~ (on =SPC c e=).
+ Another option is to open a scratch buffer with =SPC x=, change its major mode
(~M-x emacs-lisp-mode~), and use the above keys to evaluate your code.
+ An ielm REPL is available by pressing =SPC o r=
(~+eval/open-repl-other-window~).
+ There's also =M-:= or =SPC ;=, which invokes ~eval-expression~, which you can
use to run elisp code inline.
While all this is helpful for reconfiguring your running Emacs session, it can
also be helpful for debugging.
#+begin_quote
In this example, =:abc= is called the category and =xyz= is the name of the
module. Doom refers to modules in one of two formats: =:abc xyz= and =abc/xyz=.
#+end_quote
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:
The remainder of this guide will go over the technical details of a Doom module.
#+begin_example
category/
module/
test/*.el
autoload/*.el
autoload.el
init.el
cli.el
config.el
packages.el
doctor.el
#+end_example
**** =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, when ~after!~ 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!~ or ~after!~ 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 what =cli.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!~ or ~use-package!~ to configure packages.
#+BEGIN_SRC emacs-lisp
;; 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)
[...])
#+END_SRC
+ 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= or ~use-package~'s ~:ensure~ property. Doom
has its own package manager. That's what =packages.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 the ~disable-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!~ or ~after!~ in here!).
This file is read in an isolated environment and will have no lasting effect.
The only exception is configuration targeting =straight.el=.
+ Perform expensive calculations. These files are read often and sometimes
multiple times.
+ Produce any side-effects, for the same reason.
#+begin_quote
The "[[#package-management][Package Management]]" section goes over the ~package!~
macro and how to deal
with packages.
#+end_quote
Doom's autoload file is generated by scanning these files when you execute ~doom
sync~.
For example:
#+BEGIN_SRC emacs-lisp
;; 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>")
[...])
#+END_SRC
**** =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:
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:
#+BEGIN_SRC emacs-lisp
;; 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"))
#+END_SRC
These can be loaded with the ~load!~ macro, which will load an elisp file
relative to the file it's used from. e.g.
#+BEGIN_SRC emacs-lisp
;; Omitting the file extension allows Emacs to load the byte-compiled version,
;; if it is available:
(load! "+git") ; loads ./+git.el
#+END_SRC
This can be useful for splitting up your configuration into multiple files,
saving you the hassle of creating multiple modules.
*** Load order
A module's files have a precise load-order, which differs slightly depending on
what kind of session it is. Doom has three types of sessions:
+ Interactive session :: the typical session you open when you intend to use
Emacs (e.g. for text editing). This loads the most, because you will likely be
using a lot of it.
+ Batch session :: this is a non-interactive session, loaded when you execute
Emacs commands on the command line with no UI, e.g. ~emacs --batch --eval
'(message "Hello world")'~.
The expectation for these sessions is that it should quickly spin up, run the
command then quit, therefore very little is loaded in this session.
+ CLI session :: this is the same as a batch session /except/ it is what starts
up when you run any =bin/doom= command.
With that out of the way, here is the load order of Doom's most important files:
*** Flags
A module's flag is an arbitrary symbol. By convention, these symbols are
prefixed with a ~+~ or a ~-~ to denote the addition or removal of a feature,
respectively. There is no functional significance to this notation.
A module may choose to interpret flags however it wishes, and can be tested for
using the ~featurep!~ macro:
#+BEGIN_SRC elisp
;; Has the current module been enabled with the +my-feature flag?
(when (featurep! +my-feature) ...)
Use this fact to make aspects of a module conditional. e.g. Prevent company
plugins from loading if the =:completion company= module isn't enabled.
**** ~;;;###if~
Any file in a module can have a ~;;;###if FORM~ cookie at or near the top of the
file (must be within the first 256 bytes of the file). =FORM= is evaluated to
determine whether or not to include this file for autoloads scanning (on ~doom
sync~) or byte-compilation (on ~doom compile~).
i.e. if =FORM= returns ~nil~, Doom will neither index its ~;;;###autoload~
cookies nor byte-compile the file.
Use this to prevent errors that would occur if certain conditions aren't met.
For example, say =file.el= is using a certain function that won't be available
if the containing module wasn't enabled with a particular flag. We could safe
guard against this with:
#+BEGIN_SRC emacs-lisp
;;;###if (featurep! +particular-flag)
#+END_SRC
This will prevent errors at compile time or if/when that file is loaded.
#+begin_quote
Keep in mind that =FORM= runs in a limited, non-interactive sub-session. I don't
recommend doing anything expensive or especially complicated in them.
#+end_quote
**** ~;;;###package~
This cookie exists solely to assist the ~doom/help-packages~ command. This
command shows you documentation about packages in the Emacs ecosystem, including
the ones that are installed. It also lists a) all the modules that install said
package and b) all the places it is configured.
+ ~after!~ calls
+ ~use-package!~ or ~use-package~ calls
+ and ~;;;###package X~ cookies, where X is the name of the package
Use it to let ~doom/help-packages~ know where to find config for packages where
no ~after!~ or ~use-package!~ call is involved.
**** ~;;;###autodef~
An autodef is a special kind of autoloaded function (or macro) which Doom
guarantees will /always/ be defined, whether or not its containing module is
enabled (but will no-op if it is disabled).
#+begin_quote
If the containing module is disabled the definition is replaced with a macro
that does not process its arguments, so it is a zero-cost abstraction.
#+end_quote
You can browse the available autodefs in your current session with ~M-x
doom/help-autodefs~ (=SPC h d u= or =C-h d u=).
An autodef cookie is used in exactly the same way as the autoload cookie:
#+BEGIN_SRC elisp
;;;###autodef
(defun set-something! (value)
...)
#+END_SRC
An example would be the ~set-company-backend!~ function that the =:completion
company= module exposes. It lets you register company completion backends with
certain major modes. For instance:
#+BEGIN_SRC emacs-lisp
(set-company-backend! 'python-mode '(company-anaconda))
#+END_SRC
And if =:completion company= is disabled, this call and its arguments are left
unprocessed and ignored.
There may be some special cases, however. Doom tries to handle a couple of them
(e.g. with ob-jupyter, ob-ipython and ob-async). If you are experiencing errors
while trying to use a certain language in org src blocks, check out the
[[file:../modules/lang/org/README.org][:lang
org module documentation]] for details on how to add support for it.
These two lines are a common sight in Emacs configs, but they are unnecessary
for Doom Emacs. We already use the more sophisticated =ws-butler= to manage
extraneous whitespace. However, you might have the impression that it isn't
working. That's because =ws-butler= works in two unusual ways, meant to be less
imposing than its alternatives:
1. It only cleans up trailing whitespace /on lines that you've touched/ (but
always strips newlines at EOF).
Why do this? Because you might have wanted to use that space for something in
your current editing session, and it would be inconvenient for the editor to
delete it before you got to it.
If you use it, it's there. If you don't, it isn't written to the file.
* Troubleshoot
When problems arise, you should be prepared to collect information in order to
solve them, or for the bug report you're about to write. Both Emacs and Doom
provide tools to make this easier. Here are a few things you can try, first:
+ Investigate the =*Messages*= log for warnings or error messages. This log can
be opened with =SPC h e=, =C-h e= or =M-x view-echo-area-messages=.
+ Run ~bin/doom doctor~ on the command line to diagnose common issues with your
environment and config. It will suggest solutions for them as well.
+ ~bin/doom clean~ will ensure the problem isn't stale bytecode in your private
config or Doom core. If you haven't used ~bin/doom compile~, there's no need
to do this.
+ ~bin/doom sync~ will ensure the problem isn't missing packages or outdated
autoloads files
+ ~bin/doom build~ will ensure the problem isn't stale package bytecode or
broken symlinks.
+ ~bin/doom update~ will ensure that your packages are up-to-date, eliminating
issues that originate from upstream.
+ If you happen to know what module(s) are relevant to your issue, check their
documentation (press =<leader> h d m= to jump to a module's documentation). Your
issue may be documented.
If none of these things have helped you, then it's time to open a bug report.
See "[[file:contributing.org::*Reporting issues][Reporting Issues]]" in the
[[file:contributing.org][contributing guidelines]] on how to file an
effective bug report.
You can also evaluate code with ~eval-expression~ (=M-;= or =SPC ;=).
1. Start Emacs with ~emacs --debug-init~. Use this for errors that occur at
startup.
2. Evil users can press =SPC h d d= and non-evil users can press =C-h d d=.
3. If the above don't work, there's always: ~M-x toggle-debug-on-error~
Now that ~debug-on-error~ is on, recreate the error. A window should pop up with
a backtrace.
#+BEGIN_SRC sh
doom -d sync
doom --debug install
DEBUG=1 doom update
#+END_SRC
#+BEGIN_QUOTE
Note: switch order is important. ~-d~ / ~--debug~ /must/ come right after ~doom~
and before the subcommand. This will be fixed eventually.
#+END_QUOTE
If you can recreate a bug in vanilla Emacs then it should be reported to the
developers of the relevant packages or, perhaps, the Emacs devs themselves.
Otherwise, it is best to bring it up on the Doom Emacs issue list, rather than
confusing and inundating the Emacs community with Doom-specific issues.
Doing any of the above will pop up a ~*doom:sandbox*~ window. What you enter
into this buffer will be executed in the new instance of Emacs when you decide
to launch it.
*** Launching the sandbox
You have four options when it comes to launching the sandbox:
- =C-c C-c= :: This launches "vanilla Emacs". Vanilla means nothing is loaded;
purely Emacs and nothing else. If you can reproduce an error here, then the
issue likely lies in the plugin(s) you are testing or in Emacs itself.
- =C-c C-d= :: This launches "vanilla Doom", which is vanilla Emacs plus Doom's
core. This does not load your private config, nor any of Doom's (or your)
modules.
- =C-c C-p= :: This launches "vanilla Doom+". That is, Doom core plus the
modules that you have specified in the ~doom!~ block of your private config
(in =~/.doom.d/init.el=). This *does not* load your private config, however.
- =C-c C-f= :: This launches "full Doom". It loads Doom's core, your enabled
modules, and your private config. This instance should be identical to the
instance you launched it from.
#+BEGIN_QUOTE
All new instances will inherit your ~load-path~ so you can access any packages
you have installed.
#+END_QUOTE
*** Testing packages in the sandbox
Instances of Emacs launched from the sandbox have inherited your ~load-path~.
This means you can load packages -- even in Vanilla Emacs -- without worrying
about installing or setting them up. Just ~(require PACKAGE)~ and launch the
sandbox. e.g.
#+BEGIN_SRC elisp
(require 'magit)
(find-file "~/some/file/in/a/repo")
(call-interactively #'magit-status)
#+END_SRC