doomemacs/docs/getting_started.org
Giorgos Logiotatidis 5751f8c5a5
Typo
2019-11-24 13:21:49 +02:00

45 KiB
Raw Blame History

Getting Started Guide

Table of Contents   TOC_4

Install

To embark on this grand Emacs adventure, you'll need a couple things installed, including Emacs (shocking, I know), Doom Emacs, the plugins Doom depends on, and any external tools they depend on as well.

In summary, you'll be installing:

  • git
  • Emacs 26.1+
  • ripgrep
  • all-the-icons fonts unnecessary for exclusive use of terminal Emacs

And then some optional dependencies that you will likely want, as the will optimize Doom's performance and stability.

  • fd
  • GNU ls (BSD ls on macOS/BSD Linux has some limitations)
  • clang with which to compile certain external dependencies, like the emacsqlite binary, irony server (requires clang), or vterm module

The following sections will cover how to install Emacs and these dependencies across various operating systems.

If any of these install instructions are outdated, or your OS is missing, please help us by letting us know (or correcting it yourself; pull requests are welcome).

Emacs & dependencies

On Linux

Emacs should be available through your distribution's package manager. Otherwise, it can be built from source.

Arch Linux:
pacman -S git tar clang emacs ripgrep fd

Emacs 27 (HEAD) can be installed through emacs-git, available on the AUR.

Ubuntu:
apt-get install git tar clang ripgrep fd-find

On Ubuntu 18.04, the latest version of Emacs available is 25.3 (and 24.3 on Ubuntu 16 or 14). Therefore, we have a few extra steps to install 26.1+:

add-apt-repository ppa:kelleyk/emacs
apt-get update
apt-get install emacs26
NixOS

On NixOS Emacs 26.x can be installed via nix-env --install emacs, or more permanently by adding the following entry to etc/nixos/configuration.nix:

environment.systemPackages = with pkgs; [
  coreutils # basic GNU utilities
  git
  clang
  emacs
  ripgrep
  fd
];

On macOS

Mac users several options to install Emacs, but only a few of them are recommended for Doom Emacs (you'll need to install Homebrew first). To start with:

brew install git clang ripgrep fd coreutils

As for Emacs, there are several formulas to choose from. There are the best options, in order from most to least recommended for Doom.

  • emacs-plus (the safest option):

    brew tap d12frosted/emacs-plus
    brew install emacs-plus
    ln -s /usr/local/opt/emacs-plus/Emacs.app /Applications/Emacs.app
  • emacs is another acceptable option.

    brew install emacs
  • emacs-mac is also acceptable. It offers slightly better integration into macOS, with native emojis and better childframe support. However, at the time of writing, it lacks multi-tty support (which impacts daemon usage). Use it if you experience crashing or performance issues with emacs-plus.

    brew tap railwaycat/emacsmacport
    brew install emacs-mac
    ln -s /usr/local/opt/emacs-mac/Emacs.app /Applications/Emacs.app
Where not to install Emacs from

These builds/forks have known compatibility issues with Doom and are likely to cause you issues later on. Do not use them:

  • emacsformacosx.com
  • brew cask install emacs (installs from emacsformacosx.com)
  • AquaMacs
  • XEmacs

On Windows

Support for Windows is immature, so your mileage will vary. Some have reported success with installing Doom via WSL, chocolatey on git-bash or cygwin.

If you manage to get Doom on Windows and found this wasn't enough, or could be improved, please help us expand this section!

chocolatey / scoop

Chocolatey is the simplest to get Doom up and running with:

choco install git llvm emacs ripgrep fd

You can also use scoop by simply replacing choco with scoop in the above snippet to achieve the same result. This hasn't been tested, however.

You will also need to add a HOME system variable, pointing to C:\Users\USERNAME\, otherwise Emacs will treat C:\Users\USERNAME\AppData\Roaming is your HOME, which causes issues.

It's also a good idea to add C:\Users\USERNAME\.emacs.d\bin to your PATH.

TODO WSL
TODO WSL2

Doom Emacs

The quickest way to get Doom up and running is:

git clone https://github.com/hlissner/doom-emacs ~/.emacs.d
~/.emacs.d/bin/doom install

doom install performs the following for you:

  1. It creates your DOOMDIR at ~/.doom.d, if it (or ~/.config/doom) don't already exist.
  2. Copies ~/.emacs.d/init.example.el to $DOOMDIR/init.el, which contains a doom! statement that controls what modules to enable and in what order they are loaded.
  3. Creates dummy config.el and packages.el files in $DOOMDIR.
  4. Optionally generates an envvar file (equivalent to using doom env), which stores your shell environment in an env file that Doom will load at startup. This is essential for macOS users!
  5. Installs all dependencies for enabled modules (specified by $DOOMDIR/init.el),
  6. And prompts to install the icon fonts required by the all-the-icons package.

You'll find a break down of doom install into shell commands in the next section.

Consider the ~/.emacs.d/bin/doom script your new best friend. It performs a variety of essential functions to help you manage your Doom Emacs configuration, not least of which is installing or updating it or its plugins. If nothing else, get to know these four commands:

  • doom refresh: Ensures that Doom is in a proper state to be used (i.e. needed packages are installed, orphaned packages are removed and necessary metadata correctly generated).
  • doom upgrade: Updates Doom Emacs (if available) and its packages.
  • doom env: Generates an "envvar file", which scrapes your shell environment into a file that is loaded by Doom Emacs at startup. This is especially necessary for macOS users who open Emacs through an Emacs.app bundle.
  • doom doctor: If Doom misbehaves, the doc will diagnose common issues with your installation and environment. If all else fails, you'll find help on Doom's Discord server and issue tracker.

Run doom help <COMMAND> for documentation on these commands, or doom help for an overview of what the bin/doom script is capable of.

I recommend you add ~/.emacs.d/bin to your PATH so you can call doom directly, from anywhere. You don't need to be CDed into ~/.emacs.d/bin to use it. A quick way to do so is to add this to your .bashrc or .zshrc file:

export PATH="$HOME/.emacs.d/bin:$PATH"

Install Doom Manually

If you'd rather install Doom yourself, without the magic of bin/doom install, here is its equivalent in bash shell commands:

git clone https://github.com/hlissner/doom-emacs ~/.emacs.d

# So we don't have to write ~/.emacs.d/bin/doom every time
export PATH="$HOME/.emacs.d/bin:$PATH"

# Create a directory for our private config
mkdir ~/.doom.d  # or ~/.config/doom

# 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

# If your ISP or proxy doesn't allow you to install from
# raw.githubusercontent.com, then you'll have to install straight (our package
# manager) manually:
mkdir -p ~/.emacs.d/.local/straight/repos
git clone -b develop https://github.com/raxod502/straight.el ~/.emacs.d/.local/straight/repos/straight.el

# Edit ~/.doom.d/init.el and adjust the modules list to your liking before
# running this:
doom install

# 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 creating an envvar file is necessary to ensure Doom inherits your
# shell environment.
#
# If you don't know whether you need this or not, no harm in doing it anyway.
# `doom install` will prompt you to generate an envvar file. If you responded
# no, you can generate it later with the following command:
doom env

# Install the icon fonts Doom uses
emacs --batch -f all-the-icons-install-fonts

To understand the purpose of the ~/.doom.d directory and ~/.doom.d/init.el file, see the Customize section further below.

Alongside other Emacs configs (with Chemacs)

Chemacs is a bootloader for Emacs. It makes it easy to switch between multiple Emacs configurations. Here is a quick guide for setting it up with Doom Emacs as the default config.

After you've followed the installation instructions for Doom and Emacs, outlined above, deploy the Chemacs' startup script to ~/.emacs:

wget -O ~/.emacs https://raw.githubusercontent.com/plexus/chemacs/master/.emacs

Warning: the ~/.emacs.d directory must not exist for this to work.

Then create ~/.emacs-profiles.el with a list of your Emacs profiles. This file is structured like a .dir-locals.el file. Here is an example with Doom (as the default), Spacemacs, and Prelude:

(("default"   . ((user-emacs-directory . "~/doom-emacs")))
 ("spacemacs" . ((user-emacs-directory . "~/spacemacs")))
 ("prelude"   . ((user-emacs-directory . "~/prelude"))))

To start Emacs with a specific config, use the --with-profile option:

emacs --with-profile spacemacs

If no profile is specified, the default profile is used.

External/system dependencies

Your system, your rules. There are as many ways to set up a programming environment as there are dislikes on Youtube Rewind 2018, so Doom entrusts this task to you, dear user.

Doom is comprised of modules which provide most of its features, including language support and integration with external tools. However, some of these have external dependencies that you must install yourself. You'll find what modules need what and how to install them in that module's README.org file. If you find a module without a README file, helps us out by creating one for us!

doom doctor will provide an overview of missing dependencies (only for the modules you have enabled) by reporting which ones haven't been installed yet. Once you know what's missing, have a look at the documentation for that module.

Use M-x doom/help-modules (bound to SPC h d m) to quickly jump to a module's documentation from inside Doom. Otherwise, check out the Module Index.

Update

Doom is an active project and many of its 300+ plugins are in active development as well. It is wise to occasionally update them. The following section will go over how to do so.

Important: you may encounter errors after up/downgrading Emacs. Emacs bytecode is not forward compatible, so you must recompile or reinstall your plugins to fix this, i.e.

  • doom build, to rebuild all your installed plugins,
  • Or delete ~/.emacs.d/.local then doom refresh to reinstall them

Doom

The bin/doom script provides a simple command for upgrading Doom (which will also update your plugins):

doom upgrade   # short version: doom up

If you want to update Doom manually, doom upgrade is equivalent to:

cd ~/.emacs.d
git pull        # updates Doom
doom refresh    # refreshes plugins & autoloads
doom update     # updates installed plugins

To minimize issues while upgrading, avoid modifying Doom's source files. All your customization should be kept in your DOOMDIR (typically, ~/.doom.d). Read the Customize section for more on configuring Doom.

Plugins

To update only your plugins (i.e. not Doom), run doom update (short version: doom u).

Rollback

The bin/doom script doesn't currently offer rollback support for Doom or its plugins (yet).

Customize

Your private configuration is located in ~/.doom.d, by default (if ~/.config.d/doom exists, that will be used instead). This directory is referred to as your $DOOMDIR or your "private module".

doom install will create three files in your DOOMDIR to start you off:

init.el
This is where you'll find your doom! block, which controls what modules are enabled and in what order they are loaded. This is copied from ~/.emacs.d/init.example.el.
config.el
This is where the bulk of your private configuration will go.
packages.el
This is where you tell Doom what packages you want to install and where from.

How to enable or disable modules

Every private config starts with a doom! block, found in $DOOMDIR/init.el. If you followed the Doom installation instructions and ran doom install, this file should exist and will contain one.

This block controls what modules are enabled and in what order they are loaded. To enable a module, add it to this list. To disable it, either remove it or comment it out (in Emacs Lisp, anything following a semicolon is ignored by the Elisp interpreter; i.e. it's "commented out").

;; To comment something out, you insert at least one semicolon before it. The
;; Emacs Lisp interpreter will ignore whatever follows.
(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

Some modules have optional features that can be enabled by passing them flags like so:

(doom! :completion
       (company +auto)
       :lang
       (csharp +unity)
       (org +attach +babel +capture +export +present +protocol)
       (sh +fish))

Different modules support different flags. To see a quick list of what modules support what flags in the Module Index.

WARNING: when changing your doom! block you must run ~/.emacs.d/bin/doom refresh and restart Emacs for the changes to take effect. This ensures the needed packages are installed, orphaned packages are removed, and necessary metadata for your Doom Emacs config has been generated.

Package management

Doom's package manager is declarative. Your DOOMDIR is a module, and modules may optionally possess a packages.el file, where you may declare what packages you want to install (and where from) using the package! macro. It can be used to:

  1. Install packages (conditionally, even),
  2. Disable packages (uninstalling them and disabling their configuration),
  3. Or change where a package is installed from.

If a package is installed via ELPA and does not have a package! declaration, Doom will assume the package is unwanted and uninstall it for you next time doom refresh is executed.

Remember to run doom refresh after modifying your packages, to ensure they are installed and properly integrated into Doom.

Installing packages

To install a package, add a package! declaration for it to DOOMDIR/packages.el:

;; Install a package named "example" from ELPA or MELPA
(package! example)

;; Tell Doom to install it from a particular archive (e.g. elpa). By default, it
;; will search orgmode.org and melpa.org before searching elpa.gnu.org. See
;; `package-archives' to adjust this order (or to see what values :pin will
;; accept).
(package! example :pin "elpa")

;; Instruct Doom to install this package once, but never update it when you run
;; `doom update` or `doom upgrade`:
(package! example :freeze t)

;; Or tell Doom to not manage a particular package at all.
(package! example :ignore t)

package! will return non-nil if the package isn't disabled and is cleared for install. Use this fact to conditionally install other packages, e.g.

(when (package! example)
  (package! plugin-that-example-depends-on))

Installing packages from external sources

To install a package straight from an external source (like github, gitlab, etc), you'll need to specify a MELPA-style straight recipe:

Here are a few examples:

;; Install it directly from a github repository. For this to work, the package
;; must have an appropriate .el and must have at least a Package-Version 
;; or Version line in its header.
(package! example :recipe (:host github :repo "username/my-example-fork"))

;; If the source files for a package are in a subdirectory in said repo, you'll
;; need to specify what files to pull in.
(package! example :recipe
  (:host github
   :repo "username/my-example-fork"
   :files ("*.el" "src/lisp/*.el")))

;; To grab a particular commit:
(package! example :recipe
  (:host gitlab
   :repo "username/my-example-fork"
   :branch "develop"))

;; If a package has a default recipe on MELPA or emacsmirror, you may omit
;; keywords and the recipe will inherit from their original.
(package! example :recipe (:branch "develop"))

;; If the repo pulls in many unneeded submodules, you can disable recursive cloning
(package! example :recipe (:nonrecursive t))

Disabling packages

The package! macro possesses a :disable property.

(package! irony :disable t)
(package! rtags :disable t)

Once a package is disabled, use-packages! and after! blocks for it will be ignored, and the package will be removed the next time you run doom refresh. Use this to disable undesirable packages included with the built-in modules.

Alternatively, the disable-packages! macro exists for more concisely disabling multiple packages:

(disable-packages! irony rtags)

Changing a built-in recipe for a package

If a module installs package X, but you'd like to install it from somewhere else (say, a superior fork or a fork with a bugfix), simple add a package! declaration for it in your DOOMDIR/packages.el. Your private declarations always have precedence over modules (even your own modules).

;; modules/editor/evil/packages.el
(package! evil) ; installs from MELPA

;; DOOMDIR/packages.el
(package! evil :recipe (:host github :repo "username/my-evil-fork"))

You will need to run doom refresh for this change to take effect.

TODO Using/loading local packages

Configuring Doom

Configuring packages

If your configuration needs are simple, the use-package!, after!, add-hook! and setq-hook! emacros can help you reconfigure packages:

;;; ~/.doom.d/config.el (example)
(setq doom-font (font-spec :family "Fira Mono" :size 12))

;; Takes a feature symbol or a library name (string)
(after! evil
  (setq evil-magic nil))

;; Takes a major-mode, a quoted hook function or a list of either
(add-hook! python-mode
  (setq python-shell-interpreter "bpython"))

;; These are equivalent
(setq-hook! 'python-mode-hook python-indent-offset 2)
(setq-hook! python-mode python-indent-offset 2)

(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 ":"))

For more flexibility, the use-package-hook! is another option, but should be considered a last resort (because there is usually a better way). It allows you to disable, append/prepend to and/or overwrite Doom's use-package! blocks. These are powered by use-package's inject-hooks under the hood.

use-package-hook! must be used before that package's use-package! block. Therefore it must be used from your private init.el file.

;;; ~/.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)

;; ...otherwise, make sure they always return non-nil!
(use-package-hook! evil
  :pre-init
  (setq evil-magic nil)
  t)

;; `use-package-hook' also has :post-init and :post-config hooks

Reloading your config

You may find it helpful to have your changes take effect immediately. For things that don't require a complete restart of Doom Emacs (like changing your enabled modules or installed packages), you can evaluate Emacs Lisp code on-the-fly.

  • 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.

TODO Binding keys

  • define-key
  • global-set-key
  • map!
  • unmap!
  • define-key!

TODO DOOMDIR file structure

Writing your own modules

Modules are made up of several files, all of which are optional. This is a comprehensive list of what they are:

modules/
  category/
    module/
      test/*.el
      autoload/*.el
      autoload.el
      init.el
      config.el
      packages.el
      doctor.el

By default, doom looks for modules in two places: .emacs.d/modules/ where doom's own modules are located and $DOOMDIR/modules/ where you can define your own private modules.

Structure of a module

init.el

This file is loaded first, before anything else, but after Doom core is loaded.

Use this file to:

  • Configure Emacs or perform setup/teardown operations that must be set before other modules are (or this module is) loaded. Tampering with load-path, for instance.
  • Reconfigure packages defined in Doom modules with use-package-hook! (as a last resort, when after! and hooks aren't enough).
  • To change the behavior of bin/doom.

Do not use this file to:

  • Configure packages with use-package! or after!
  • Preform expensive or error-prone operations; these files are evaluated whenever bin/doom is used.
config.el

This file is the heart of every module.

Code in this file should expect that dependencies (in packages.el) are installed and available, but shouldn't make assumptions about what modules are activated (use featurep! for this).

Packages should be configured using after! or use-package!.

;; from modules/completion/company/config.el
(use-package! company
  :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)
   [...])

For anyone already familiar with use-package, use-package! is merely a thin wrapper around it. It supports all the same keywords and can be used in much the same way.

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).

A packages.el file shouldn't contain complex logic. Mostly conditional statements and package!, disable-packages! or depend-on! calls. It shouldn't produce side effects and should be deterministic. Because this file gets evaluated in an environment isolated from your interactive session, code within should make no assumptions about the current session.

The package! macro is the star of the show in packages.el files:

;; from modules/lang/org/packages.el
(package! org-bullets)

;; from modules/tools/rotate-text/packages.el
(package! rotate-text :recipe (:host github :repo "debug-ito/rotate-text.el"))

Its :recipe property accepts a MELPA recipe, which provides a lot of control over where to fetch a package, including specific commit, tags or branches:

(package! rotate-text
  :recipe (:host github
           :repo "debug-ito/rotate-text.el"
           :commit "1a2b3c4d"))

You can also use this package! to disable other packages:

;; Uninstalls evil, keeps it uninstalled, and tells Doom to ignore any
;; use-package! and after! blocks for it
(package! evil :disable t)

;; disable-packages! can be used to disable multiple packages in one statement
(disable-packages! evil evil-snipe evil-escape)
autoload/*.el OR autoload.el

Functions marked with an autoload cookie (;;;###autoload) in these files will be lazy loaded.

When you run bin/doom autoloads, Doom scans these files to populate autoload file in ~/.emacs.d/.local/autoloads.el, which will tell Emacs where to find these functions when they are called.

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

This file is used by make doctor, and should test for all that module's dependencies. If it is missing one, it should use the warn!, error! and explain! macros to inform the user why it's a problem and, ideally, a way to fix it.

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"))
Additional files

Sometimes, it is preferable that a module's config.el file be split up into multiple files. The convention is to name these additional files with a leading +, e.g. modules/feature/version-control/+git.el.

There is no syntactical or functional significance to this convention. Directories do not have to follow this convention, nor do files within those directories.

These additional files are not loaded automatically. You will need to use the load! macro to do so:

;; from modules/feature/version-control/config.el
(load! +git)

The load! macro will try to load a +git.el relative to the current file.

Load order

Module files are loaded in a precise order:

~/.emacs.d/early-init.el     # in Emacs 27+ only
~/.emacs.d/init.el
$DOOMDIR/init.el
{~/.emacs.d,$DOOMDIR}/modules/*/*/init.el
{~/.emacs.d,$DOOMDIR}/modules/*/*/config.el
$DOOMDIR/config.el

Module flags

In the code examples of the previous section, you may have noticed something odd about that haskell entry: (haskell +intero). +intero is a module flag. You may specify these for any module that supports them. Unsupported flags are ignored.

You can find out what flags a module supports by looking at its documentation (a README.org in the module's directory; which can be jumped to quickly with M-x doom/describe-module).

For example, the haskell module supports the +intero and +dante flags, which represent the two Haskell backends available to Emacs. You may choose one or the other (or neither, or both) by specifying the appropriate flags in you doom! block:

(doom! :lang (haskell +dante))

You may specify as many flags are you like:

(doom! :lang (org +attach +babel +capture +export +present))

+flagname is simply a naming convention and has no syntactical or functional significance.

Testing for flags

Modules are free to interpret flags however they like. If you are writing your own module(s), you can test for flags using the featurep! MODULE SUBMODULE &optional FLAG macro:

(when (featurep! :lang haskell +dante)
  [...])

The first two arguments if featurep! may be skipped if it is used from inside a module. For example:

;; In modules/lang/haskell/config.el
(when (featurep! +dante)  ; same as (featurep! :lang haskell +dante)
  [...])

Module settings

Some modules expose settings that can be configured from other modules. Use M-x doom/help-autdefs (SPC h d a or C-h d a) to see what is available and how to use them.

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:

(set-company-backend! 'python-mode '(company-anaconda))

You'll find what settings a module exposes in its documentation (remember to use M-x doom/help-modules on SPC h d m or C-h d m).

Module cookies

There is a special syntax available to module files called module cookies. Like autoload cookies (;;;###autoload), module files may have ;;;###if FORM at or near the top of the file. FORM is read by doom refresh and doom compile to determine whether or not to ignore this file.

If FORM returns nil, the file won't be scanned for autoloads nor will it be byte-compiled. Use this to prevent errors that may occur if that file contains (for example) calls to functions that won't exist if a certain feature isn't available to that module, e.g.

;;;###if (featurep! +intero)
;;;###if (not (featurep 'evil-mode))

Remember that these run in a limited, non-interactive sub-session, so do not call anything that wouldn't be available in a Doom session without any modules enabled.

Common mistakes when configuring Doom Emacs

Having helped many users configure Doom, I've spotted a few recurring oversights that I will list here, in the hopes that it will help you avoid the same mistakes:

Packages are eagerly loaded

Using use-package! without a deferring keyword (one of: :defer :after :commands :defer-incrementally :after-call) will load the package immediately. This can cause other packages to be pulled in and loaded, which will compromise many of Doom's startup optimizations.

This is usually by accident. Choosing which keyword to use depends on the needs of the package, so there is no simple answer to this.

Manual package management

A lot of Emacs documentation and help will contain advice to install packages with package.el's API (e.g. package-install) or with use-package's :ensure keyword). You are free to do this, if it is your preference, but otherwise, Doom has its own package management system.

Migrating use-package code to Doom is usually a case of removing the :ensure keyword and adding a (package! PACKAGENAME) to ~/.doom.d/packages.el (and running doom refresh to sync your config).

Using org-babel-do-load-languages to load your babel plugins

You don't need org-babel-do-load-languages. Doom lazy loads babel plugins based on the language name in #+BEGIN_SRC blocks needed. As long as the babel plugin is installed and the plugin is named after its language (e.g. #+BEGIN_SRC rust will load ob-rust), you don't need to do anything else.

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 :lang org module documentation for details on how to add support for it.

Using delete-trailing-whitespaces or whitespace-cleanup to manage leftover whitespace

(add-hook 'after-save-hook #'delete-trailing-whitespace)
;; or
(add-hook 'after-save-hook #'whitespace-cleanup)

These two lines are a common sight in Emacs configs, but they are unnecessary for Doom Emacs. We already use the more sophisticated wsbutler to manage extraneous whitespace. However, you might have the impression that it isn't working. That's because wsbutler 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 I believe file-wide reformatting should be a deliberate act (and not blindly automated). If it is necessary, chances are you're working on somebody else's project or with other people, but here, large scale whitespace changes could cause problems or simply be rude. We don't endorse PRs that are 1% contribution and 99% whitespace! However, if it's truly deliberate, M-x delete-trailing-whitespaces and M-x whitespace-cleanup are available to be called deliberately, instead.
  2. wsbutler replaces trailing whitespace and newlines with virtual whitespace. This is whitespace that only exists in the Emacs buffer, but isn't actually written to the file. 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, and they will, you will need to debug them. Fortunately, Emacs (and Doom) provide you with tools to make this easier. I recommend becoming acquainted with them. They will be yours (and our) best tool for understanding the problem.

I've run into an issue, where do I start?

Before you file a bug report, there are a number of things you should try first:

  • You'll find a list of common issues & errors in the FAQ. That is a good place to start. You can access and search this FAQ from inside Doom with SPC h d f (or C-h d f for non-evil users).
  • Run doom doctor to diagnose any common issues with your environment or config.
  • Run doom refresh to ensure the problem isn't caused by missing packages or outdated autoloads files.
  • See if your issue is mentioned in the Common Issues section below.
  • Search Doom's issue tracker to see if your issue is mentioned there.
  • Ask for help on our Discord server. This may not be immediately available to everyone, so I won't fault you for skipping this step, but you'll sometimes find help there quicker. In many cases, Henrik fixes issues.

Looking up documentation and state from within Emacs

Variables, functions, faces, etc.

Emacs is a Lisp interpreter whose state you can access on-the-fly with tools provided to you by Emacs itself. They're available on the SPC h prefix by default. Use them to debug your sessions.

Here are some of the more important ones:

  • describe-variable (SPC h v)
  • describe-function (SPC h f)
  • describe-face (SPC h F)
  • describe-bindings (SPC h b)
  • describe-key (SPC h k)
  • describe-char (SPC h ')
  • find-library (SPC h P)

You can also evaluate code with eval-expression (M-; or SPC ;).

TODO For Doom Modules, packages, autodefs, etc.

doom/open-news (SPC h n)
doom/open-manual (SPC h D)
doom/describe-module (SPC h d)
Jumps to a module's documentation.
doom/describe-autodefs (SPC h A)
Jumps to the documentation for an autodef function/macro. These are special functions that are always defined, whether or not their containing modules are enabled.
doom/describe-package (SPC h p)
Look up packages that are installed, by whom (what modules) and where jump to all the places it is being configured.
doom/info

How to extract a backtrace from an error

If you encounter an error while using Doom Emacs, you're probably about to head off and file a bug report (or request help on our Discord server). Before you do, please generate a backtrace to include with it.

To do so you must enable debug-on-error then recreate the error.

Enabling debug-on-error

There are three ways to enable debug-on-error:

  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.

A backtrace from bin/doom

If the error you've encountered is emitted from bin/doom, you can re-run the same command with the -d or --debug switches to force it to emit a backtrace when an error occurs. The DEBUG environment variable will work to.

doom -d refresh
doom --debug install
DEBUG=1 doom update

Note: switch order is important. -d / --debug must come right after doom and before the subcommand. This will be fixed eventually.

Evaluating Elisp on-the-fly

Often, you may find it helpful for debugging to evaluate some Emacs Lisp. Here are couple things you can do:

  • Use M-; (bound to eval-expression),
  • SPC x will open a scratch buffer. M-x emacs-lisp-mode will change it to the appropriate major mode, then use +eval:region (gr) and +eval:buffer (gR) to evaluate code,

How to determine the origin of a bug

Testing in Doom's sandbox

"The sandbox" is one of Doom Emacs' features; it is a test bed for running elisp in a fresh instance of Emacs with varying amounts of Doom loaded (none at all, all of it, or somewhere in between). This can be helpful for isolating bugs to determine who you should report a bug to.

If you can recreate a bug in vanilla Emacs than it should be reported to the developers of the relevant plugins 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.

Opening the sandbox

There are three common ways to access the sandbox:

  • SPC h E (for evil users)
  • C-h E (for non-evil users)
  • M-x doom/sandbox

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.

All new instances will inherit your load-path so you can access any packages you have installed.

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.

(require 'magit)
(find-file "~/some/file/in/a/repo")
(call-interactively #'magit-status)

TODO Bisecting your private config

TODO Bisecting Doom Emacs