James Williams

Emacs as an IDE: Setup

Tags: Emacs Programming

I found myself in emacsland through somewhat unconventional means. It wasn't a parent, mentor, or king of the geeks that turned me on to it, it was grad school. And it was one class in particular, Analysis of Algorithms. To complete the homework assignments for the class, I had to create lots and lots of mathematical equations, graphs, and diagrams. Drawing them by hand was acceptable but no one wants to draw a five or six level diagram by hand.

The one sensible way to do that was LaTeX, a typesetting language created in the 1980s but still in use for print technical books. Emacs org-mode, a likely subject for a subsequent post, has extensive LaTeX support and my time to complete homework was cut by a third.

Application of B-Tree Insert Algorithm

From there I branched out into the other capabilities of emacs, taking advantage of its high level of customization, rich set of plugins, and existence on almost every modern computing platform.

I'll answer the burning question in advance, "why not VS CODE?" I started using emacs before VS Code was a thing. I also like having everything under one roof. Org-mode is an emacs-exclusive feature that I use for document creation and personal productivity. So I'd be spending time here anyways.

Emacs users often share their configurations online in dotfiles repos and there's a culture of learning by example vs formal education. Packages for almost every purpose imaginable are available in various repositories so the joke about users endlessly tweaking their configuration can ring true. This series of posts will not go to far down that path. Mind you, I have been that user but have trimmed down the hand rolled stuff. There will be opportunities to tweak things but we'll be starting from an opinionated base "distro" (a collection of plugins, platforms and processes) called doom-emacs.

Why doom ?

First it's to combat bad ergonomics of a default Emacs config. Long story short, Emacs uses the CTRL and ALT keys a lot for UI navigation. These keys are often at the extreme left and right of the keyboard and a reduced size key as well. There are a lot of other examples where the application shows its age, pre-dating many of the conventions of the last 40 years.

For example, in normal Emacs, you save a file with CTRL+x CTRL+s. In doom-emacs, saving a file is completed with SPC f s. Space is the system wide leader key that brings up a popup window. f is the context for file operations and s saves the file. This organization system makes it easier to discover commands quickly.

Doom emacs menu subsystem

Doom provides a couple different ways to install packages: through normal repositories like MELPA or using its managed module system. Each module will specify packages to install if that module is enabled, associated functions and commands that should be available to the user and binds them to context menus. For example, when you enable the Java module, it knows that most Java builds use Gradle/Groovy so those are installed along side the Java packages.

Doom is also really fast. With a spartan build, it is ready to use in a couple seconds, my full build loads in just under 10s. Much much much faster than I could do with IntelliJ.

Install Emacs and doom-emacs

Doom has a pretty good guide to install Emacs and doom.

It covers the quirks of getting Emacs installed on different platforms and the two lines of code to get doom installed:

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

The doom command is your main way to update and sync your installed packages as well as troubleshoot problems with your install(doctor). After this first instance, you can update, sync, and reload your configuration from inside Emacs.

Understanding the module system

Your doom configuration lives in three files: init.el, config.el, and packages.el. Most of the time, you'll be editing init.el as it controls which modules are installed. Modules are grouped by general function areas. Here is a subset of the module groups, listing only the ones we care about.

  • input, alternate keyboard layouts and Japanese/Chinese input
  • completion, code completion and file/command search manipulation
  • ui, modules that modify the general app user interface
  • editor, modules that make Emacs more IDE like (snippets, text manipulation, cursor niceties, etc)
  • emacs, core emacs functionality
  • term, options to run a terminal in Emacs
  • checkers, syntax and grammar checkers
  • tools, general small to medium scale apps and integrations (git, docker, password managers, etc )
  • lang, programming language support
  • app, large scale apps that tailor emacs to that purpose like social media clients, RSS readers and media players

You can peruse the list by running SPC f P. The capital P is important.

Navigation and working with files

Here are a couple of basic doom emacs commands.

Doom emacs File submenu

  • Open file SPC f f
  • Save file SPC f s
  • Open configuration files SPC f P
  • Change to insert mode i
  • Exit insert mode ESC
  • Run a command M-x (read as Meta, it's usually the Alt key)

Emacs needs to differentiate between when you want to edit the contents of a file and when you want to enter commands. You can tell if you are in insert mode or not based on the cursor shape. | is insert mode, █ is command mode. Press i to start editing files.

Setting up basic IDE features

By default, Doom enables a number of defaults. To make it more like a traditional IDE, we can add a project drawer for easy navigation, tabs to switch between files in a project more easily, github style emoji and terminal support. We can also enable a side minimap to navigate inside a file quickly.

In the :ui section of init.el, uncomment/add [remove the ;;]:

minimap
tabs
treemacs
(emoji +github)

In the :term section, uncomment:

term

Exit insert mode (ESC), press SPC f s to save the file and then SPC h r r or alternatively run M-x doom/reload to reload the config.

An project will look like this:

Treemacs and Minimap in emacs

Pretty cool but we add some language support.

Tweaking fonts and themes

Emacs can use an installed system font as the editor font. The default is perfectly serviceable but I like to use Fira Code.

The font is set in the config.el file, usually commented out at first. The following sets 16pt Fira Code Retina as the default UI font.

(setq doom-font (font-spec :family "Fira Code Retina" :size 16))

Doom has over 70 included themes, you can apply a different one using M-x load-theme or SPC h t.

doom-one is the default, check this repo for descriptions of the included themes.

You may get the prompts Loading a theme can run Lisp code. Really load? and Treat this theme as safe in future sessions?. Answer yes to both.

This will activate the theme for only the current session, after which it will revert back to doom-one. To make it permanent, update the theme in the config.el file. The following assigns doom-dark+ permanently.

(setq doom-theme 'doom-dark+)

Language Server Protocol and Debugging

Some of the key features that separates the text based editors like emacs and vim from more heavy IDEs like Android Studio or XCode are things like refactoring, linting and code completion. In the past, you had to forgo all that if you wanted a lightweight portable solution. That's no longer the case.

The Language Server Protocol allows a server to be notified when changes in a document happen. These changes are analyzed and the client is sent a message noting any errors or warnings that have come with the change. The language server can also include possible linting optimizations the user can perform as well as navigating to signatures/defintions of classes and functions.

LSP started as a Microsoft Visual Studio Code feature but developed as its own specification and can be used in almost any text editor that's capable of sending and consuming JSON-RPC messages.

LangServer.org maintains a list of well supported LSPs. I'll be covering the specifics for setting up support for the languages individually but for now, we need to set up global support. In tools section of init.el, enable:

:tools
;; ...
lsp
(debugger +lsp)

Company-mode, short for complete anything is a text completion framework that you get for free with enabling doom's LSP support.

The Debug Adapter Protocol, modeled somewhat like LSP, is a means to provide language and tooling debugging to users. Tooling only needs the common interface of debug adapters. Each debug adapter then handles the specifics of interacting with the underlying debugging tool. Emacs dap-mode hasn't hit 1.0 so it doesn't have as much language coverage as LSP.

The following blog posts will cover the various programming languages and writing environments I use. First up is Flutter.