DOOM Emacs

Configuration is at the same time the most fun and biggest challenge of any Emacs installation. I think it’s a good time to recall Anthony Bourdain’s thoughts about mise en place.

Mise-en-place is the religion of all good line cooks. Do not mess with a line cook’s ‘meez’ — meaning his setup, his carefully arranged supplies of sea salt, rough-cracked pepper, softened butter, cooking oil, wine, backups, and so on. As a cook, your station, and its condition, its state of readiness, is an extension of your nervous system… The universe is in order when your station is set up the way you like it: you know where to find everything with your eyes closed, everything you need during the course of the shift is at the ready at arm’s reach, your defenses are deployed. If you let your mise-en-place run down, get dirty and disorganized, you’ll quickly find yourself spinning in place and calling for backup. I worked with a chef who used to step behind the line to a dirty cook’s station in the middle of a rush to explain why the offending cook was falling behind. He’d press his palm down on the cutting board, which was littered with peppercorns, spattered sauce, bits of parsley, bread crumbs and the usual flotsam and jetsam that accumulates quickly on a station if not constantly wiped away with a moist side towel. “You see this?” he’d inquire, raising his palm so that the cook could see the bits of dirt and scraps sticking to his chef’s palm. “That’s what the inside of your head looks like now.”

— Anthony Bourdain, from Kitchen Confidential.

This an annotated version of my DOOM Emacs configuration. The source files can be found on Github or Sourcehut.


The most basic installation of Doom Emacs consists of:

$ git clone --depth 1 ~/.emacs.d
$ ~/.emacs.d/bin/doom install




In order to use Python’s virtualenv, the virtualenvwrapper.el1 module is used. This is done by adding to packages.el

(package! virtualenvwrapper)

The only configuration I use for this library is setting my virtualenv root location in config.el

(use-package! virtualenvwrapper)
(after! virtualenvwrapper
  (setq venv-location "~/.virtualenvs/")

From a Python project the virtual environment can be selected by using M-x venv-workon.

Black formatter

To allow formatting of Python blocks in org-mode and elsewhere, add python-black 2 to packages.el.

(package! python-black)

Then we configure it with

(use-package! python-black
  :after python
  :hook (python-mode . python-black-on-save-mode-enable-dwim))

We should install black-machiatto 3 to allow formatting of partial regions.


The following modules must be enabled in init.el:

  • (go +lsp) in the lang section
  • lsp in the tools section
  • snippets in the editor section

gopls should be installed.


To enable support of Go templates, install the lang/web packages by adding to init.el.

(web +html)

This will install the web-mode package4 To specify the sepcific template engine as Go M-x web-mode-set-engine to go.

Running doom sync will finish the setup.


Support for Pikchr5 is added via the pikchr-mode package. By adding

(package! pikchr-mode)

It is necessary to install the pikchr binary according to the instructions in Pikchr. That page also contains examples.



To disable completed items to show up in agenda views, the following is set in config.el

(setq org-agenda-skip-scheduled-if-done t)
(setq org-agenda-skip-deadline-if-done t)

Key bindings

For Doom Emacs we add some additional key-bindings to the org-mode specifically. These bindings go under the local org leader key with the u prefix (which in my case is ) SPC m u. They are

  • SPC m u
    • a, archive an org-agenda item using #org-archive-subtree-default
    • r, get a random note using #org-randomnote

Pretty bullets

The default Doom Emacs org-mode bullets are *. To get “pretty” bullets you need to add the +pretty flag to init.el:

(org +pretty ) ; organize your plain life in plain text

Optionally, you can specify the bullet’s hierarchy glyphs with

    org-superstar-headline-bullets-list '("⁖" "◉" "○" "✸" "✿")


Prettify symbols

Since Emacs 24.46 there is a builtin prettify-symbols-mode. It can be customized by changing prettify-symbols-alist. These strings will be replace by our selection, typically an unicode symbol. In this configuration we use the following:

(defun my/pretty-symbols ()
  (setq prettify-symbols-alist
          '(("#+begin_src python" . "🐍")
            ("#+begin_src elisp" . "λ")
            ("#+begin_src jupyter-python" . "🐍")
            ("#+end_src" . "―")
            ("#+results:" . "🔨")
            ("#+RESULTS:" . "🔨"))))

This will, for instance, replace the beginning of Python org-babel blocks with the single symbol. To register the prettify list with each mode we use

(add-hook 'org-mode-hook 'my/pretty-symbols)

Or we can register the prettify-symbols-mode as a global mode

(global-prettify-symbols-mode +1)


company is great, but it can get in the way when using on org-mode buffers. To disable add the following:

(after! org
  ;; disable auto-complete in org-mode buffers
  (remove-hook 'org-mode-hook #'auto-fill-mode)
  ;; disable company too
  (setq company-global-modes '(not org-mode))
  ;; ...


Beacon highlights the current cursor line after major movements. Especially useful for HDPi screens. Add it on packages.el:

  (package! beacon)

And enable the global minor-mode on config.el with:

  ;; global beacon minor-mode
  (use-package! beacon)
  (after! beacon (beacon-mode 1))


This mode relies on the Focus package that dims regions not on … focus. Since the package is on MELPA, it can be installed by adding to packages.el:

  (package! focus)

Next, require it from config.el

  (use-package! focus)

And call it by setting the mode with M-x focus-mode.


treemacs7 is a great file navigation explorer for Emacs. However, since it has its own iternal concept of project, it needs an external helper to be able to synchronise with other project management tools, such as projectile.

To be able to synchronise treemacs and projectile the treemacs-projectile module must be used. It can be activated using

(use-package treemacs-projectile
  :after (treemacs projectile))

(after! (treemacs projectile)
  (treemacs-project-follow-mode 1))

This guarantees that when moving to a buffer of a different projectile project, the treemacs tree will reflect that.


Dirvish offers a suitable replacement/enhancement for dired with features such as improved UI and image preview. The only requirement for dirvish is the ls alternative exa It is available from MELPA which means you can add it to packages.el with

(package! dirvish)

and then enable it on config.el with

(use-package! dirvish)



An interesting talk on the advantages of using vterm as the default Emacs terminal emulator can be found can found at EmacsConf 2021A Tour of vterm”. To use vterm as the Emacs shell, the respective section in init.el should be selected:

 ;;eshell            ; the elisp shell that works everywhere
 ;;shell             ; simple shell REPL for Emacs
 ;;term              ; basic terminal emulator for Emacs
 vterm             ; the best terminal emulation in Emacs

We also need to install libvterm, with in macOS can be done with

brew install libvterm

and in Linux with

$ sudo apt-get install -y libvterm-dev # Ubuntu
$ sudo dnf -y install libvterm # Fedora


For full-text search, deadgrep8 is used, which leverages ripgrep. To install ripgrep on Linux, run

$ sudo apt install ripgrep # Ubuntu
$ sudo dnf install ripgrep # Fedora


Dynamic modules

The best place to put dynamic modules in Doom Emacs is inside the actual .doom.d folder. This allows to load modules from Doom’s “private dir”. For instance, for a module called my_module and sub-directory named modules we would create

mkdir ~/.doom.d/modules

and then add to config.el

(add-to-list 'load-path (expand-file-name "modules/my_module" doom-private-dir))

(def-package! my_module
  :commands (mymodule-foo mymodule-bar))