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 place1.
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.
Installation
The most basic installation of Doom Emacs consists of:
$ git clone --depth 1 https://github.com/doomemacs/doomemacs ~/.emacs.d
$ ~/.emacs.d/bin/doom install
After any configuration changes you should run the sync
command with
$ ~/.emacs.d/bin/doom sync
Any upgrade to the doom Emacs packages can be done using the upgrade
command.
$ ~/.emacs.d/bin/doom upgrade
Languages
Python
virtualenv
In order to use Python’s virtualenv
, the virtualenvwrapper.el
2 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
3 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
4 to allow formatting of partial regions.
Go
The following modules must be enabled in init.el
:
(go +lsp)
in thelang
sectionlsp
in thetools
sectionsnippets
in theeditor
section
gopls
should be installed.
Templates
To enable support of Go templates, install the lang/web
packages by adding to init.el
.
(web +html)
This will install the web-mode
package5
To specify the sepcific template engine as Go M-x web-mode-set-engine
to go
.
Running doom sync
will finish the setup.
Pikchr
Support for Pikchr6 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.
xonsh
I also use a basic mode7 to support xonsh8 scripts. The package can be installed with
;; packages.el
(package! xonsh-mode)
and invoked with
;; config.el
(use-package! xonsh-mode)
org-mode
org-agenda
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
- a, archive an
For more information on how to set doom Emacs keybings, check the elevant section.
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
(setq
org-superstar-headline-bullets-list '("⁖" "◉" "○" "✸" "✿")
)
UI
Resolution dependent fonts
I have to work with a variety of monitor resolutions, from a 720p pocket book to a 2160p iMac.
The fonts usually look tiny in my larger screen so I’ve added support for resolution dependent font sizes in my config.el
.
For instance
(when (window-system) ;; only apply to GUI frames
;; Adjust font size dependent of the screen resolution
(when (> (x-display-pixel-width) 3000)
(setq doom-font (font-spec :family "Ubuntu Mono" :size 26 :weight 'regular)))
(when (< (x-display-pixel-width) 3000)
(setq doom-font (font-spec :family "Ubuntu Mono" :size 14 :weight 'regular)))
)
Would set a larger font size for monitors with a horizontal resolution higher than 3000 pixels.
Prettify symbols
Since Emacs 24.49 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
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
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))
Focus
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
.
Navigation
Treemacs
treemacs
10 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.
Personally, I’m not a fan of doom Emacs’ and treemacs
’ default behaviour of use variable pitch fonts. If you want, for consistency, to set treemacs
to use a monospaced font, set the following on your config.el
:
(setq doom-themes-treemacs-enable-variable-pitch nil)
Opening on project
To open Treemacs automatically in Doom Emacs when you open a project, regardless of whether it’s recognized by Projectile or another project management system Doom integrates with, you can leverage Doom’s project detection mechanisms and hooks. Since SPC p p uses Doom’s project management system which is built on top of Projectile but also accommodates other forms of project recognition, you can write a function that checks if you’re in a recognized project directory at startup and opens Treemacs accordingly.
Doom Emacs doesn’t have a direct hook that fires after opening a project with SPC p p (or +projectile/find-file
), since this action is more interactive and manual. However, you can achieve automatic Treemacs opening on startup if you’re in a project directory by using the doom-first-file-hook
. This hook runs after the first buffer is opened, which can be used to check if the opened buffer is part of a recognized project.
Here’s how you can do it. Add a Custom Function**: Insert the following Elisp code into your config.el
:
(defun my/open-treemacs-in-project ()
"Automatically open Treemacs in a project."
(when (doom-project-root) ; Checks if the current buffer is in a Doom-recognized project
(treemacs))) ; Opens Treemacs
(add-hook 'doom-first-file-hook 'my/open-treemacs-in-project)
This function my/open-treemacs-in-project
checks if the current buffer belongs to a project (Doom’s doom-project-root
will return non-nil if so) and opens Treemacs if it does. By adding this function to doom-first-file-hook
, it ensures Treemacs opens automatically when you start Doom Emacs in a project directory or open a file that is part of a project.
Dirvish
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)
Tools
vterm
An interesting talk on the advantages of using vterm
as the default Emacs terminal emulator can be found
can found at EmacsConf 2021 “A Tour of vterm”.
To use vterm
as the Emacs shell, the respective section in init.el
should be selected:
(doom!
:term
;;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
deadgrep
For full-text search, deadgrep
11 is used, which leverages ripgrep.
To install ripgrep
on Linux, run
$ sudo apt install ripgrep # Ubuntu
$ sudo dnf install ripgrep # Fedora
Defining key bindings
(map! :leader
(:prefix-map ("f" . "foo")
(:prefix ("b" . "bar")
:desc "Baz" "b" #'foo-bar-baz
:desc "Qux" "q" #'foo-bar-qux)))
This will define SPC-f-b-b to invoke foo-bar-baz
and SPC-f-b-q to invoke foo-bar-qux
.
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))
mise
is, by no coincidence, the name of an excellent SDK management tool: https://github.com/jdx/mise ↩︎