- Jul 16th, 2024
- Jul 17th, 2024
- Programming, Emacs
Moving from Spacemacs to Doom Emacs
As I've mentioned previously, I've been experimenting with Lume to replace Hugo as the static site generator for this site. I quickly ran into an issue because Lume is written in TypeScript, and I simply could not get my editor to work with TypeScript.
TypeScript is JavaScript with types. Like JavaScript, it can be used for both web programming and server-side (or just regular) programming. For the latter, there are a number of current runtimes that are in use, with Node.js probably the most popular. I decided to use Deno because it supports TypeScript directly (without a separate compilation step to JavaScript) and has a number of other features that make for saner software development. My editor of choice for a while now has been Spacemacs, an Emacs "distribution" that adds a bunch of packages on top of standard Emacs. Though Spacemacs provides a layer for Typescript, that ostensibly supports integration with Deno's language server protocol implementation, I could not for the life of me get it to actually work.
Truth be told, even though I've been mostly happy with Spacemacs, I was starting to get a little tired of it. It is fairly heavyweight, and opinionated (which isn't always a bad thing). It is also a rolling distribution, meaning that whenever I update it, I get the latest version of all packages. That in turn leads to a bunch of instability. There's been more than one occasion when I sat down to write some code in some language I hadn't touched in my while, and instead I had to spend a couple of hours repairing my setup for said language. Much like Hugo, Spacemacs was starting to not spark joy anymore.
I looked at a couple of options, using my current need for TypeScriipt integration as a litmus test. I wanted to stay within Emacs, but didn't want to revert to setting everything up from scratch again. The first option I considered was Emacs Bedrock a very minimal starter kit for Emacs. It was easy to set up, and I got it working with TypeScript pretty easily, but it was a little too barebones for my taste. Then I tried out Doom Emacs. If Spacemacs is too much and Bedrock is too little, Doom Emacs sits in the Goldilocks zone of just right.
Like Spacemacs, Doom supports Vim-emulation (via Evil) out-of-the-box. It has a system (called modules) for organizing extensions and customizations around particular functionality (like programming language support). It even adopts some of Spacemacs keybinding conventions which make for a very fast editing experience once you get used to it. However it uses a leaner package management system and seems optimized for fast startups. Unlike Spacemacs, there's less abstraction over the underlying Emacs functionality, except in a few areas (like package management and keybindings). Overall, it just feels like a much leaner experience, while replicating a lot of the same functionality that I've become used to. Here are some salient features and choices that I made while moving from Spacemacs to Doom:
Sane Defaults
To my pleasant surprise, I realized I could leave out most of my
existing custom configuration because Doom comes with sane default
for a bunch of things. For example, it uses
ibuffer-projectile
to
organize buffers by project, and loads your PATH
correctly so that any programs launched from Emacs are found
correctly. It also comes with my favored Zenburn color theme. Things like
customizations for particular languages I'll port over as and when I
need them.
Font Support
Doom has suprisingly easy font setup. There are couple fonts you can
specify for different purposes (like symbols, serifs, and the
default). Fira Code has been font of choice for a while now, and all
I had to do was set it as my
doom-font
. Once I enabled the ligatures
module, I also got Fira Code's ligatures working, which previously
I've always had to set up separately. The only minor stumbling block
was getting symbols working, for which I had to install Nerd Fonts
by doing M-x nerd-icons-install-font
.
Programming Language Support
Like most modern editors, Doom supports a wide range of programming
languages, many of which via the language server protocol. For
TypeScript, that meant adding (javascript +lsp)
to my
module list to enable LSP and then (lsp +eglot)
to use
Eglot instead of
lsp-mode
. Then I could use Deno's LSP instructions so
that Eglot automatically launches Deno as the language server for
Typescript files. For languages where the Doom module doesn't
include LSP support, you can easily roll your own by telling Doom to
start using an LSP mode. For example, I wanted to use
Marksman to provide a language for Markdown
and all I had to do was add (add-hook
'markdown-mode-local-vars-hook #'lsp!)
since Eglot already
knows to use Marksman for Markdown files.
Hitting Escape Easily
Once small change I made from my Spacemacs setup was how I moved
from Evil's
insert
to normal
mode. Usually this is
done by hitting the Esc
key, but I've always found that
annoying that key is so far away from where my fingers usually are.
A lot of people map Esc
to the Caps Lock
,
but personally I've always mapped Ctrl
to Caps
Lock
instead. While using Spacemacs, I used the included evil-escape
functionality that let me hit fd
in rapid sequence to
go into normal
mode. Doom doesn't enable this by
default, and though I could enable it, I decided to do something
different. I used an app called HyperKey that lets me map both
Ctrl
and Esc
to the Caps Lock
key. If I just tap the key, it sends an Esc
, but if I
hold it down, it sends a Ctrl
.
There a couple more tweaks I want to do. For example, I think I like powerline over Doom's mode line. I'm also adding custom keybindings as I go, mainly for functionality I've gotten used to in Spacemacs. By and large, I'm very happy with the way my Doom configuration has turned out. As for negatives, the biggest issue is that documentation for the project is quite incomplete. Doom provides a number of custom macros to augment package management and configuring keybindings, but it took me quite a bit of searching around and poking and prodding to figure out how they work in even the most basic case. I'm hoping this is something that improves over time.
My configuration is on Github, for all the five people in the world who are interested.
Now I can get back to what I wanted to do and write some TypeScript.