Ross Hagan

Weekend with Doom Emacs as a Clojure IDE

June 1, 2021

This is pretty much a set of notes from what I went through trying to get a good clojure development environment setup and have a change of view from JetBrains Intellij.

Where you might be starting from

Where I started from in my knowledge and you might already be for this to be useful:

  • User of VIM keybindings in JetBrains products
  • Past experience of intellij clojure plugin cursive and clojure
  • User of MacOS Big Sur, with homebrew already installed
  • Java installed via asdf
  • Clojure build tool leiningen installed with brew install leiningen

Starting at install

Installing doom emacs is pretty well documented.

Visit the Doom Emacs getting started documentation

Follow the MacOS thread. I went with the installation of emacs-mac for no stronger reason than it was top of the list.

By the end of the linked install guide you should have Emacs.app symlinked to your Applications folder and doom emacs installed and opening with Doom emacs.

Running from the CLI

Being able to launch the Application from the CLI doesn't come for free.

Railwaycat has thankfully written a script in a gist, see the first comment for a shell script so get this script into a file in your path, ahead of the existing emacs command.

I went with a new command entirely, simply e, so:

vim /usr/local/bin/e
# paste the script after reviewing for anything nefarious
# write and quit
chmod +x /usr/local/bin/e

The script needs some further attention as right now it won't work if you don't pass a parameter.

# this does not work:
e
# any of these work:
e .
e file.txt

General Doom Emacs configuration

So now we're actually in doom emacs, and it's looking pretty but... what now? This section is going to give a whirlwind tour of the things I immediately wanted to tweak.

Key notation guide for mac users:

  • SPC is equal to pressing the spacebar
  • C is equal to Ctrl, so C-h is Ctrl+h
  • M is the Option key

Access the doom emacs config files

You can get to them from your terminal easily, they're all in ~/.doom.d/.

Any custom configuration you need can live in the config.el file.

If you've already got emacs open, but want to get to your doom config quickly then hit:

SPC f p - This lists the doom config files, and you can use C-j or C-k to move up and down the list, hit Enter to open one.

Reloading config

Once you've made changes in your doom config and written the file, at the terminal you can run:

~/.emacs.d/bin/doom sync

Alternatively, you can reload from within emacs for changes that don't need a full restart:

; sync (reload) doom
SPC h r r

Start Doom Emacs maximised

Launching emacs will likely be opening it in a small window.

So how do I make emacs start maximised?

;; in your config.el, add:
(add-to-list 'default-frame-alist '(fullscreen . maximized))

Adjusting text - increase font size

There's already a handy guide for how to increase font size and the doom config.el comes with example configuration in the comments.

In config.el:

(setq doom-font (font-spec :family "JetBrainsMono Nerd Font Mono" :size 20))</code></pre>

Use whatever family you like, but I like the jetbrains mono font, so give that a go from a brew cask if you like:

brew tap homebrew/cask-fonts
brew install --cask font-jetbrains-mono-nerd-font

See all the other fonts available in the cask and look for the nerd font variants for icon support.

Finding keybindings for commands

A generally useful tip is how to find the thing that you want to do, and the vim, or evil-mode, keybindings to do it.

Hitting SPC, that is the spacebar, and waiting a second will bring up a minibuffer at the bottom of the screen with a handy guide for where you can go next in your command chain.

Bring up the command prompt with SPC : (often seen as M-x in standard emacs keybindings). From here you can type what you're trying to do and poke around package commands.

For example, enter doom, and you'll see the list start filtering down, and any commands with keybindings will have a convenient format like:

doom/reload (SPC h r r)       Reloads your private config.

Some panels will popup help when you enter a ?. Also worth exploring is the which-key mode and related commands but I didn't get to that yet.

With so many options built in, and some occasionally remapped from the package's defaults, it can be hard to find the best practice with this method but spending some time exploring those command options is worth some of your time.

IDE alternative patterns

In my day to day work I reach for Intellij products, so looking for the same conveniences is one of my first stops.

Working with projects

Doom emacs comes with a few conveniences for project work available. projectile is the underlying project tool here.

Hitting SPC p will bring up the projectile options

Project visual tree, or file viewer

I'm used to the project panel in Intellij, an easy overview of the project structure.

To get a visual project tree, I also enabled the treemacs option in the doom init.el - uncomment the :ui > treemacs option and sync your settings.

When you're in a file in a source code repository, toggle open treemacs with SPC o p automatically find this and present it as the root folder.

Search for a file in project

SPC p f is one way to find a file in a project context. See also SPC SPC for navigating to a file by name. One less keypress...!

Project search for text in file

SPC s p will search in a project for an arbitrary string.

Using Doom Emacs as a Clojure IDE

Getting started with clojure in doom emacs is fairly well documented.

Doom emacs comes with its own set of configuration files in the form of a language module.

Enabling the clojure language module

The place to enable this is in ~/.doom-emacs.d/init.el

Change a couple of things in init.el:

  1. Uncomment :tools > lsp to get language server support
  2. Under :ui, replace clojure with (clojure +lsp) to enable the language server.

Save your file :w and sync doom SPC h r r.

When you first open a clojure file, you'll get a prompt about installing (clojure-lsp), go ahead!

It'll take a minute for the language server to start on first access but the interface does a good job of indicating what's going on.

You might get a prompt about setting the project root for import but it's well documented on how to proceed.

What's included in the clojure module

Your clojure module should now be up and running. If you want to see what you're actually getting as part of the clojure language module then you can view the module readme in ~/.emacs.d/modules/lang/clojure/README.org

Core keybindings

The README for the module gives you a list of keybindings, but they don't always map to the doom emacs setup. While you can use the tip from earlier to lookup by command name SPC : here are some quick places to start:

  • See SPC m, wait for the options to appear. These are the module actions.
  • Launch (jack in to) the cider reply SPC m '
  • Load buffer into repl SPC m r l
  • Load/refresh namespace SPC m n r
  • Run the test at the cursor SPC m t t
  • Rerun test SPC m t a
  • Rerun failed test SPC m t r
  • Copy code into repl - grab the nearest top level function with SPC m e D
  • Copy code into repl - copy the last S-expression SPC m e E
  • View docs for item under cursor SPC m h d

I have a feeling I'm going to want to find a way to auto load the current namespace into the repl as part of the test.

Finding the repl when it's lost

Switch to the repl buffer in the module shortcuts: SPC m r b

I found it easy to lose my repl buffer while learning commands, the buffer menu is also a reasonable place to start looking for lost buffers. While SPC b b navigates workspace buffers, the repl won't appear in this list.

SPC b B, note the capital letter B at the end, will show you a list of all the buffers floating around including your repl.

The project buffer is pretty handy too as a wide view of what's going on, bring it up with SPC p b.

Wrapping text in your repl

If you like your repl window vertical, you might find it's not got the space it needs to stop text running off the screen.

SPC t w will toggle soft wrapping.

What are the lightbulbs?

With the lsp language server enabled, you're probably seeing one or more lightbulbs hovering around at the end of each line as you move around.

These are... maybe useful... quick actions. To execute them, you're looking for code actions under SPC c a.

Code navigation

  • Go to definition - g d
  • Show usages - g D

Refactoring

Refactoring clojure code is supported by clj-refactor.

This is a section I'm looking to improve on myself in terms of giving myself quicker access. You can find the refactorings available under the SPC m R refactoring menu. I've not found the faster way to access these yet.

Create a new file

One option if you've gone with treemacs is to pop open the treemacs window SPC o p and use the cf shortcut from there.

Where's my #/hash/pound key?

Normally under Option+3, this gets lost in emacs for UK keyboards at least.

The shortest path to getting this back is to edit your doom config.el with:

(define-key key-translation-map (kbd "M-3") (kbd "#"))

Qualitative experience

I've not completely arrived at an opinion here. It'll take longer than a weekend for this to be muscle memory. The options above are just a tiny fraction of the differences that you don't realise are missing until you reach for them.

I'm really liking being back on the keyboard in the more natural hand positioning pure vim gives. Some of the refactoring conveniences aren't quite there in terms of quick key bindings, but that's something I can easily adapt.

The power of other clojure specific refactoring is yet to be hit for me, and I've really not actually started coding yet while I've been trying to get a decent starting point. For that I've been using the luminus lein profile to help me get up and running with a ClojureScript environment.

Now to get into actual development. The learning curve is looking pretty steep, not just for emacs but the wider clojure ecosystem. Yet this feels like it's given me a good starting point for a test driven way of developing with the repl. Exciting!