Editing Lisp Code with Emacs

Marco's Highly Opinionated Guide to Editing Lisp Code with Emacs

GNU Emacs (and XEmacs) is a very large program, I've been using it as a mail client, shell, note pad and IDE for the past six years and I'm still learning new things. For those of you who've never really sat down and looked at Emacs' LISP editing functions, here's a guide to what I do.

Tips on Hardware

  1. Use Standard US keyboard. The US keyboard's layout (unlike the western European keyboard layouts) makes typing the punctuation characters you need in programming languages easy.
  2. Learn to touch-type.
  3. Don't spend 1500 EUR (or more) for a computer and then skimp on the keyboard.

Key Swapping

Here's a complete keyboard layout suggested by a photo of a Lisp Machine keyboard:

(I'm am using lately the layout: Alt/AltGr -> Control, Win -> Meta+Control, Control -> Meta. It works for Emacs and most applications. But I have problems with Xterm (Control does not work anymore), Rxvt, etc. Outside Gnome and KDE, I must use Eterm (or ansi-term within Emacs).)

Some xmodmap files that describe the Lisp machine keyboard on the picture above reasonably faithfully can be found here.

The xkeycaps program takes care of this on X, MacOS users can use uControl, Windows users can use XKeymacs.

In just about every programming language I've ever used (C, Java, Perl, bash and LISP), and in normal prose, parenthesises occur far more often than the square bracket characters. So it's important to make typing the parenthesis easy:

(keyboard-translate ?\( ?\[)
(keyboard-translate ?\[ ?\()
(keyboard-translate ?\) ?\])
(keyboard-translate ?\] ?\))

This assumes that the [ and ] are easy to reach unshifted chars.

The keyboard-translates above can interfere with pasting text using the mouse in emacs. Another way to accomplish the same thing while limiting the effect to Slime is:

(define-key slime-mode-map (kbd "[") 'insert-parentheses)
(define-key slime-mode-map (kbd "]") 'move-past-close-and-reindent)
(define-key slime-mode-map (kbd "(") (lambda () (interactive) (insert "[")))
(define-key slime-mode-map (kbd ")") (lambda () (interactive) (insert "]")))

Structured editing

What is structured editing?

Structured editing is a way of editing source code which attempts, as much as is possible, to keep the source code consistently valid. This means, among other things, that the inserting of an open parenthesis should insert the closing one as well (similarly for ").

Taylor R. Campbell (Riastradh on #lisp) has written a package of lisp editing code called paredit. The rest of this page uses functions defined in that package.

Start with paredit-open-list it inserts both the open and the closing parenthesis and then leaves the cursor between the two (unless we're in a comment or string, in which case it inserts a lone ( character). After filling in the LISP form, use the paredit-close-list command which moves past the closing parenthesis and reindents the line. Here's my setup:

(define-key slime-mode-map [(?\()] 'paredit-open-list)
(define-key slime-mode-map [(?\))] 'paredit-close-list)
(define-key slime-mode-map [(return)] 'paredit-newline)

Unstructured editing

So how do you insert a simple, standalone "(" or ")" character? I happen to have the "=" character directly below ")" and the "\" character just to the left of that; so here's what I use:

(define-key slime-mode-map [(control ?\=)] (lambda () (interactive) (insert "(")))
(define-key slime-mode-map [(control ?\\)] (lambda () (interactive) (insert ")")))

Choose something that makes sense to use and which requires at most one key press (though it will probably be modified). Even with structured editing you'll still need to insert lone parenthesis every now and then. Note: You can also just as easily do C-q ( and C-q ); personally, I insert lone parenthesises often enough that that's too many keypresses for my tastes.

Splicing, Dicing and Moving Around

When editing LISP code, you shouldn't think of the source code as text, but as program structure (SEXPs). With parenthesis-aware editors, we get the same functionality used in IntelliSense (otherwise a separate IDE component in other languages). The basic lists/program manipulation commands (basic EMACS bindings) are:

All of these functions take prefix args which specify how many sexps to move around. backward-up-list can take a negative arg which moves out of the enclosing parens but forwards, basically simulating up-list.

When editing LISP code you will use these the forward/backward/transpose-sexp functions far more often the the char versions, I'd suggest swapping them so that, for example, C-M-t (transpose-sexp) is C-t and C-t (transpose-char) is C-M-t:

(define-key slime-mode-map (kbd "C-t") 'transpose-sexps)
(define-key slime-mode-map (kbd "C-M-t") 'transpose-chars)
(define-key slime-mode-map (kbd "C-b") 'backward-sexp)
(define-key slime-mode-map (kbd "C-M-b") 'backward-char)
(define-key slime-mode-map (kbd "C-f") 'forward-sexp)
(define-key slime-mode-map (kbd "C-M-f") 'forward-char)

Use the standard keybindings for a bit and after you've figured out how they work and how often you use them, chose some keybindings based on your preferences. For example, I have a Dvorak keyboard where the H, T, and N keys are located directly under the right hand, so I've put the forward functions on then N key, the transpose functions on the T key, and the backward functions on the H key.

In addition, paredit provides the following commands:

For example, leaving point inside the Sexp (b c), slurping (a (b c) d e f) forward would result in (a (b c d) e f), then (a (b c d e) f), and finally (a (b c d e f)). Barfing reverses/undoes slurps. Refer to the paredit.el package for more functions that are similar to text editing functions, but additionally respect parentheses (and even quotes) around SEXPs.

Indentation

A simple, consistent way to indent lisp code is using C-M-q at the begining of an s-exp. Alternatively, use C-M-\ to re-indent the selected region.

Fun and Games

Use keywiz.el to learn new Emacs commands. Keywiz is a game that shows you the name and documentation of a command and challenges you to press the corresponding key sequence. Highly educational.

Caveat Lector

I am lucky enough to spend much of my time programming in Lisp, so I find these changes worth the effort it took to figure them out and get used to them.

Your experience may vary, but if you use Emacs for development, it's a good idea to use the extra mileage it can give you.


Colophon

The idea for this document grew out of a conversation on IRC. It was written by Marco Baringer, contributions were provided by Luke Gorrie, Paolo Amoroso, Matthew Danish, Nikodemus Siivola, David Douthitt and artime?. Code was taken from Andreas Fuchs and Hannu Koivisto?.


This page is linked from: document  

CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively