GETTING STARTED W/DOOM EMACS & LISP
Learning Common Lisp is complicated by the fact that Emacs is the defacto standard Common Lisp code editor. Emacs is good, but making use of its capabilities requires adopting a different mental model for code editing, making it difficult and confusing to learn. Finding out that you need to learn an old, weird editor before you can even begin learning Common Lisp in earnest is enough to turn newcomers off–especially now in the age of Agentic AI workflows and IDEs specialized for such workflows.
This chapter is a quick references for getting started with Doom Emacs, a distribution of Emacs that packs a lot of useful extensions (in Emacs, we call them packages) along with a very good default configuration. I don't like taking a lot of time configuring my environment when I first get started with a new tool–I just want to start using it. Doom Emacs enables us to focus our attention on using Emacs rather than diddling with config files (feel free to diddle by yourself with the door closed).
This chapter is intended to help bootstrap an understanding of Emacs concepts. You should experience a smooth transition to learning Common Lisp after completing this chapter.
First, we'll begin with installation and setup (in MacOS and Linux), then I'll teach you the basic vocabulary to get around Emacs, and then finally provide a guide for using Emacs without committing too much to the task (what I call Survival Mode).
INSTALL & SETUP ON MACOS
Installation and configuration in MacOS is easy. The only complicated part right now is deciding which version of Emacs to install on MacOS. There's about 10 different "Emacs for MacOS" binaries floating around–some of which don't play nice with Doom. We're going to use Homebrew for everything to make it easy.
Install Homebrew
Homebrew is a package manager for MacOS.
Run the following command in the terminal and follow instructions:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Install SBCL
SBCL is the most popular Common Lisp implementation.
brew install sbcl
Test that it's installed and working.
$ sbcl --version
Install Emacs
Install dependencies:
brew install git ripgrep coreutils fd
Install Emacs Plus
brew tap d12frosted/emacs-plus
brew install emacs-plus
Test that Emacs is installed and working.
$ emacs
Or open the Emacs.app file in your Application folder.
After confirming that Emacs works, you can move on to installing Doom.
Install Doom
Doom is an Emacs distribution. It provides everything Emacs-related for integration with Common Lisp, and generally makes Emacs more ergonomic.
git clone --depth 1 https://github.com/doomemacs/doomemacs ~/.config/emacs
~/.config/emacs/bin/doom install
After installing, add ~/.config/emacs/bin/ to PATH.
Open either .bash_profile or .zshrc, add this line and save.
export PATH="$HOME/.config/emacs/bin:$PATH"
export PATH="$PATH:/User/yourhomedirectoryhere/.config/emacs/bin"
Restart your terminal for the change to take effect.
Test that Doom is installed and that the changes to your PATH are working.
$ doom sync
$ doom emacs
Configure Doom for Common Lisp
Now that Doom is running, we need to set it up for Common Lisp development.
We're going to edit our first file, so we'll need to learn a wee bit of survival
Emacs right now. I promise this is the only configuration you have to do (you
may want to configure a different default theme later in the config.el
file).
In the menu bar, go to File -> Open File and then navigate to
~/.doom.d/init.el. That will open the init.el file. The below is an
abbreviated version of the file with only the changes (uncommenting) made. Don't
copy and paste this.
If you're wondering how the heck to uncomment, you can simply highlight the ;;
in front of the settings with your mouse and type the x key.
;;; init.el -*- lexical-binding: t; -*-
;; This file controls what Doom modules are enabled and what order they load
;; in. Remember to run 'doom sync' after modifying it!
;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
;; documentation. There you'll find a link to Doom's Module Index where all
;; of our modules are listed, including what flags they support.
;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or
;; 'C-c c k' for non-vim users) to view its documentation. This works on
;; flags as well (those symbols that start with a plus).
;;
;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
;; directory (for easy access to its source code).
;; NOTE I have abbreviated the full init.el file.
;; The below settings are the only ones I've changed.
(doom! :input
:completion
company ; the ultimate code completion backend
:ui
treemacs ; a project drawer, like neotree but cooler
:editor
lispy ; vim for lisp, for people who don't like vim
:emacs
:term
vterm ; the best terminal emulation in Emacs
:checkers
:tools
:os
:lang
common-lisp ; if you've seen one lisp, you've seen them all
;; The rest is unchanged.
Save the init.el file with the menu: File -> Save.
Reload Doom with SPC h r r (space key then h then r then r). Alternative:
Close Emacs. In the terminal, run doom sync. Reopen doom emacs.
Remap Capslock to Control
Because the Control key is used so heavily in Emacs keybindings, most Emacs users will remap the Caps Lock key to Control. That improves comfort and usability of Emacs keybindings.
- Open Settings.
- Go to Keyboard.
- Click on Keyboard Shortcuts.
- Go to Modifier Keys (bottom left).
- Click on dropdown menu right of where it says Caps Lock Key.
- Change it to Control.
- Click Done.
EMACS DICTIONARY
Emacs is an old piece of software. Using it is a little like time traveling to a time when men were men, women were women, and computers were the size of your average Costco. There is a distinct vocabulary you need to know in order to survive and get help.
Buffer
An Emacs buffer displays the contents of a file, terminal, repl, message/log,
etc.
Modeline
The modeline is the area at the bottom of nearly every buffer that shows
information about that buffer–including the file path for file buffers, the
current major mode, and the current Evil state.
You can interact with the modeline. If you hover over the modeline with the mouse, it will tell you what clicking on that area will do.
The modeline, and the toolbar, are both invaluable sources of information for beginners.
Window
An Emacs window is closer to the modern concept of a pane. A window can only
display one buffer at a time.
Frame
An Emacs frame is the equivalent of the modern concept of a window. It's the
box that displays the Emacs application. A frame can hold multiple Emacs windows
and minibuffers simultaneously.
Minibuffer
A minibuffer is a buffer that is intended to be temporary. Usually they
display some information while you are in the middle of a command or other
action, and close automatically when you finish or abort the action/command.
Mode
Buffers have both major and minor modes. If you open a .lisp file, the
file will automatically open in lisp-mode. lisp-mode is a major-mode.
auto-save-mode, evil-mode, company-mode, hl-mode, etc. are minor
modes. Only one major mode can be active in one buffer. Multiple minor modes
can be active in a buffer.
Modes provide syntax highlighting, specialized commands, and various features intended to improve the editing experience.
State
This is more specific to the evil-mode package. States describe the keyboard
cursor. In normal state
Workspaces & Projects
Doom includes packages that provide workspace and project functionality, allowing you to more effectively separate groups of buffers and give you the ability to quickly search multiple projects. The functionality idiomatic of workspaces and projects aren't native to Emacs, but they are conveniently provided by Doom Emacs through several packages.
Packages
Emacs packages are equivalent of "extensions" or "plugins". Not to be confused with Common Lisp packages which are an entirely separate idiom and functionality.
Keybinding Basics
Doom Emacs have vanilla Emacs keybindings and vim-style keybindings (see: evil
in the init.el file) enabled by default.
C is the control key. M is the meta/option/alt key. s is the
super/command/windows key. SPC is the space key ESC is the escape key.
C-x means "Hold control then press x". C-c C-c means "Hold control then
press c, then hold control then press c". SPC f f means "Press the space key
then f then f".
SURVIVAL EMACS
When you first begin with this book, I assume you at least don't know Emacs keybindings. If you know Vim/Neovim style bindings, then you'll know how to do basic text navigation and editing, search and replace, etc. I'll teach you the basics of how to get around and get started following along with the next Lisp chapter. Importantly, you won't need to learn any keybindings at all; you can mostly pretend Emacs is a plain text editor. Thus, in this chapter I'll teach you how to:
- Create & Open a file buffer using the menu.
- Read the modeline.
- Insert text.
- Save a file buffer using the menu.
- View a list of open buffers in the menu.
Additionally, there are some behaviors in Emacs that may be confusing while doing basic text editing. I'll tell you about these behaviors ahead of time so it's less frustrating when you first begin.
Creating & Opening File Buffers
In Emacs, creating a new file buffer and opening an existing file into a buffer are nearly the same action, making it somewhat confusing.
Let's say you want to create a new file. In the menu, navigate to File -> Visit
New File. A new minibuffer will open. In the top left of the minibuffer it will
say "Find File" and show the path to the current directory. To make a new file
there, type in the name of the file you want to create, like "abc.txt" or
"main.lisp", then press Enter. A new, empty buffer with that name will open.
The file doesn't exist until you save it.
To navigate to other directories from that minibuffer, you can type the name of
a subdirectory, or you can press Backspace to navigate to a parent directory
instead.
Now let's say you want to open an existing file. In the menu, navigate to File
-> Open File. The identical minibuffer will open. Navigate to your file, type
in its name, and press Enter to open the file.
Reading The Modeline
With a file buffer open, take a look at the modeline. It contains important information. It's contents, from left to right:
- An icon showing the current
evil-modestate. - An integer showing the buffer size.
- The path to the current file/buffer.
- Two numbers separated by a colon. These show the current line and column of
the cursor. Example:
50:20meansline 50, column 20 - "All", meaning you see the entire contents of the buffer. It will show a percentage when the buffer contents don't fit in the window (requiring scrolling).
- On the right, you'll see one the current major mode is. For example, if you
have a
.lispfile open, it will sayLisp, indicating that the buffer is inlisp-mode. - If you are in a git repo file, it will show the current open branch.
Inserting Text
By default, you are in evil-mode's Normal State. We want to initially avoid
learning any complicated systems of text editing. To do that, we can switch to
Emacs State. Type C-z. In the modeline in the bottom left, you should see
the icon change, probably to E, indicating that you are in Emacs State. In
this state:
- You have access to all of Emacs' keybindings.
- You can insert, highlight, cut, copy, paste, and delete text as you usually would.
You will need to switch to Emacs State every time you open a new buffer.
To switch out of Emacs State and back into Normal State, type C-z again.
Saving Files
You can save a file using your standard keybinding s-s, or you can save with
the menu. Navigate to File -> Save.
View A List Of Open Buffers
You can view a list of open buffers and switch to one in the menu. Navigate to
Buffers. Buffers that have names like *Messages* are special Emacs buffers.
All others should be file buffers.
If you click on one of the buffers, the window with the keyboard cursor active will switch to that buffer.
Kill/Close A Buffer
To close a buffer, navigate to File -> Close.
Strange Behavior
That'll get you started with the basics. There are few strange behaviors to look out for, though.
"I typed something once but it got repeated several times."
In Emacs State, if you type C-u and then a number n, the next thing you
type will be repeated n times. If you type C-u 8 d you will type 8 d's.
This is useful behavior when you know what's happening, confusing and
frustrating when you don't. It's usually more of a problem in Evil Normal
State because you don't even have to type C-u, and if you type 8, go into
Insert State and type in a bunch of code and then type ESC to go back to
Normal State, all of the code will be repeated 8 times.
"I can't type anything; the window seems locked."
Look at the beginning of the buffer name. Does it have a Lock icon? You may have
accidentally typed C-x C-q or SPC t r, putting the buffer into read-only
mode. Type C-x C-q or SPC t r again to make the buffer editable again.
"I typed something and the buffer changed."
You may have accidentally closed or switched the buffer. You can try switching
back using the method above, or you can type C-w C-u to undo whatever the heck
you did. We'll see later that the C-w C-u (winner-undo) command has some
other uses.
And if you accidentally undid something that you want to redo, you can type
C-w C-r.
"I typed something and some minibuffer or prompt came up and I want to just get rid of it."
Typically this happens to me when I accidently type C-x k to kill/close a
buffer. Whatever you did, you can cancel whatever action you are in the middle
of by typing C-g a few times. C-g is a universal command for canceling some
action. If you are in the middle of doing something that you don't want to do,
type C-g to hit the eject button.
"My buffer has a file open but I can only see part of it (or perhaps none of it at all)."
If you're sure you have a file buffer open to a file that exists and has data in it, then you may be a victim of narrowing. You can confirm by looking at the modeline: At the beginning of the buffer/file path in the modeline, if you see two arrows, one above the other, pointing to each other, then the buffer has been narrowed.
To unnarrow or widen, switch to Normal State and type SPC b -
(doom/toggle-narrow-buffer).
"I cut some text, but when I try to paste, some other text appears."
Did you cut some text, delete a word with C-backspace, and then try to paste
the text that you cut? Is that word you deleted with C-backspace the one being
pasted?
You are experiencing expected behavior of the kill ring. You can undo everything
the normal way you undo any text operation using s-z.
To avoid this behavior, you need to use backspace or delete alone (erasing a
character at a time). I'll cover killing later.
SURVIVAL LISP COMMANDS
Beyond simple file/buffer operations and text editing are Common Lisp-specific concepts and commands. This chapter will quickly run through the essentials for getting started.
Compiling/Evaluating Lisp Code From the Buffer
To compile a form (basically anything between two parentheses), place the
cursor anywhere in a form then in the menu navigate to Sly -> Compilation ->
Compile Defun. This works for other forms, not just defun (which we'll learn
about later).
To evaluate the form place the cursor anywhere in the form then navigate to
Sly -> Evaluation -> Eval Defun.
To compile a whole file, navigate to Sly -> Compilation -> Compile and Load File.
If a global variable is set, you can get the value of the variable anywhere it
is in a Lisp file. Set the cursor at the end of the variable then navigate to
Sly -> Evaluation -> Eval last expression. This works for individual symbols and
other forms, too.
Lisp Images
An important concept that has deep implications for Lisp development is the Lisp Image.
When you first open a Common Lisp file in Emacs, a new REPL session and Lisp image is created. Code is then loaded, compiled, and executed in the image. It is sometimes likened to an operating system.
An image can be saved and restored. In fact, you can save the image to an executable and then "restore" the image by running the executable. We'll learn how to do that in the chapter on deploying.
Using the REPL
Since a REPL was opened when you opened a Common Lisp file, you can switch to
it using the standard menu navigation for switching buffers. However, the entire
window will be taken up with the REPL, which may not be what you want. The same
is true if you go to Sly -> mREPL -> Go to Default REPL. New REPL below it
will give you the better behavior, but it creates a new REPL (it's true),
which is usually not what you want.
If you would like a REPL opened in a minibuffer under your Lisp buffer, then
do the following: With a Lisp file open and the cursor in the buffer, type M-x
(meaning the Meta/Option key and the x key) to open the command minibuffer and
type sly and then Enter. A Lisp REPL will be opened. The REPL/image is
"attached" to the Emacs instance. If you open a different Lisp file and compile
the code, it will be loaded into the same image.
Structural Editing
Editing Lisp code can be tricky at first.
If you delete a closing-parenthesis, all of the code between and including the opening and closing parentheses will be deleted.
Deleting an end-quote will do the same.
This is call "structural editing". It prevents unbalanced parentheses or quotes. However, there are some circumstances where it actually makes it difficult to fix unbalanced parentheses (usually because of copy/pasting code).
There can be a lot of confusing behavior with structural editing. To turn it
off, type M-x lispy-mode to toggle lispy-mode off. With lispy-mode off, you
will have more freedom, but less help from the editor. As the spirit moves, you
can toggle it on and play around with it. We'll cover some useful features of
structural editing later.
The Lisp Debugger
We will take a closer look at the debugger and errors in the chapter on errors and conditions, but before you get to that section you'll probably cause the debugger to open and display a message like this:
:AWS fell through ECASE expression.
Wanted one of (:DEVELOPMENT :PRODUCTION).
[Condition of type SB-KERNEL:CASE-FAILURE]
Restarts:
0: [RETRY] Retry SLY evaluation request.
1: [*ABORT] Return to SLY's top level.
2: [ABORT] abort thread (#<THREAD tid=11923 "slynk-worker" RUNNING {70071BE463}>)
In this situation, you can do two things:
- Navigate the cursor to one of the
restartsand pressEnter. - Type the key corresponding to the
restart(0 for [RETRY], 1 for [*ABORT], etc.) to select it. - To just abort no matter what, just type
a.
In the beginning, it's safest to just abort. If your list of restarts is too
long, it will be truncated and you might need to highlight and type Enter on a
Show More option.
However, if you want to, you can give Common Lisp's debugger a test drive. If
you cause an error, try leaving the debugger open, fixing the bug in your code
(make sure to compile it), and then selecting a RETRY restart if one is
available. This is one of Common Lisp's most powerful built-in features.
SPEEDING UP W/KEYBINDINGS
If during the next chapter you find yourself limited by using the menu for doing
things, notice that in the menu there are keybindings printed next to nearly
every command available in there. Try using some of the keybindings for your
most frequently used commands (like Eval Defun or Compile Defun).
NEED HELP?
If you are confused after reading this chapter, ask me for help on X (@almightylisp). Consider me your Emacs and Common Lisp tech support.

