The Common Lisp Cookbook - Using Emacs as a Lisp IDE

The material on this page was originally presented at the ILC 2003 conference. A paper with more in-depth coverage of some of the material on this page can be found on Bill Clementson's ILC2003 page.

This page is meant to provide an introduction to using Emacs as a Lisp IDE. The key bindings used in the example code snippets assume an Emacs configuration similar to that provided by the .emacs file that is included as part of the Setting up an IDE with Emacs on Windows or Mac OS X page. If you use ILISP, the key bindings reflect the bindings that are present in the current CVS version of ILISP.

Contents

  1. Why Use Emacs?
  2. Emacs Lisp vs Common Lisp
  3. Lisp Modes in Emacs
  4. Lisp Modes in Emacs - Inferior Lisp Mode
  5. Lisp Modes in Emacs - ILISP
  6. Lisp Modes in Emacs - ELI (Emacs-Lisp Interface)
  7. Lisp Modes in Emacs - Which One to Choose?
  8. Working with Lisp Code
  9. Working with Lisp Code - Editing
  10. Working with Lisp Code - Evaluating and Compiling Lisp
  11. Working with Lisp Code - Searching Lisp Code
  12. Lisp Documentation in Emacs - Learning About Lisp Symbols
  13. Lisp Documentation in Emacs - Lisp Documentation
  14. Miscellaneous
  15. Questions/Answers

Why Use Emacs?

Emacs Lisp vs Common Lisp

Lisp Modes in Emacs

Lisp Modes in Emacs - Inferior Lisp Mode

Lisp Modes in Emacs - ILISP

Lisp Modes in Emacs - ELI (Emacs-Lisp Interface)

Lisp Modes in Emacs - Which One to Choose?

Working with Lisp Code

Working with Lisp Code - Editing

;; Put the cursor on the open parens of "(defvar.." and press "C-M-f"
;; and "C-M-b" a few times to see how you move in units of sexps. Put
;; the cursor on the second additon in the "(progn" statement and
;; press "C-M-t" to swap the first addition sexp and the second
;; one. Put the cursor on the open parens of "(+ x" in defun c and
;; press "C-M-@" to highlight the entire sexp. Then press "C-M-u" to
;; expand the selection "upwards" to the enclosing "(let". Pressing
;; "C-M-d" moves to the next enclosed sexp or (if you are at the
;; beginning of a line) to the enclosed sexp on the line: 

(defvar a "a variable")

(defun b ()
  "a function"
  (+ 2 2))

(defun c ()
  "another function"
  (let ((x 42))
    (+ x
       (+ 2 2)
       (+ 3 3)
       (+ 4 4))))

(progn
  (+ 1 1)
  (+ 2 2)
  (+ 3 3))
;; Put the cursor on the open parens of "(progn .." and press "C-M-k"
;; to delete it. Then press "C-M-backspace" to delete the sexp before
;; the cursor:

(defun d ()
  (if t  
      (+ 3 3)
    (progn
      (+ 1 1)
      (if t
	  (+ 2 2)
	(+ 3 3)))
    (+ 4 4)))
;; Put the cursor on the open parens of "(defun ..." and press "C-M-q"
;; to indent the code:

(defun e ()
"A badly indented function."
(let ((x 20))
(loop for i from 0 to x 
do (loop for j from 0 below 10 
do (print j)) 
(if (< i 10)
(let ((z nil) )
(setq z (format t "x=~d" i))
(print z))))))

;; This is the result:

(defun e ()
  "A badly indented function."
  (let ((x 20))
    (loop for i from 0 to x 
	do (loop for j from 0 below 10 
	       do (print j)) 
	   (if (< i 10)
	       (let ((z nil) )
		 (setq z (format t "x=~d" i))
		 (print z))))))
;; Placing the cursor on a "(" or after a ")" highlights the matching
;; parens:

(progn (+ 3 3) (- 2 2))

;; A mismatched parens is highlighted in a different color (put cursor
;; after last parens and enter a ")" to see this:

(- 2 2)

;; You can also type "M-x check-parens" to locate mismatched parens in
;; a buffer

;; Press "M-(" and you will get:

() 
;; with the cursor placed on the closing parens, ready to enter the
;; function name. 

;; Put the cursor on the open parens of the "(+ 2 2)" sexp below and
;; press "C-u 2 M-(" to enclose the next 2 sexps with parens - then
;; type "+ 1" to add "1" to the result of the following 2 sexps:

(progn (+ 2 2) (+ 3 3))

;; To delete the enclosing "progn" below, put the cursor on the open
;; parens of the "(+ 1 1)" and press the following sequence of keys:
;; "C-M-k C-M-k C-M-k C-M-u C-M-k C-y M-y C-M-a C-M-q":

(defun a ()
  (progn 
    (+ 1 1)
    (+ 2 2)
    (+ 3 3)))
;; Indentation is automatic for Lisp forms. Example: Put the cursor
;; after the first addition form and press Enter:

(progn
  (+ 3 3)
  (- 2 2))

;; Pressing TAB will indent incorrectly indented code. Example: Put
;; the cursor at the beginning of the "(+ 3 3)" form and press TAB:

(progn
(+ 3 3))

;; CL indentation rules are different from Emacs Lisp indentation
;; rules. Make certain you have the following code in a lisp mode hook
;; in your .emacs file:

(set (make-local-variable lisp-indent-function)
		 'common-lisp-indent-function)
;; Press "C-c ]" (in ELI) or "C-C C-v C-]" (in ILISP) to close all
;; parens. Example: Put cursor at end of following form and press the
;; appropriate key sequence:

(progn (if nil (progn (+ 3 (- 2 1
;; Type the following and press "C-c TAB" (both ELI & ILISP) to get an
;; automatic completion for defvar:

(defv

;; Typing in the following and pressing "C-c TAB" results in a list of
;; altermatives:

(def

;; The Emacs hippie-expand command will expand both lisp symbols
;; (however, it only works off of lisp symbol information that is
;; either available in the buffers or a standard CL symbol) and
;; directories. For example, type in the following and press "C-c /"
;; to get a directory completion:

(setq x "c:/pro
;; Highlight the middle "(if ..." block and press "C-x n n" to hide
;; everything but that block ("C-x n w" restores the other code):

(if a 
    (+ 1 1))
(if b
    (+ 2 2))
(if c
    (+ 3 3))

;; Put the cursor on "(defun b ..." and press "C-x n d" to narrow to
;; just defun b (("C-x n w" restores the other code):

(defun a ()
  (+ 1 1))

(defun b ()
  (+ 2 2))

(defun c ()
  (+ 3 3))
;; Put the cursor on the following sexp and press "M-;" to get a
;; code line comment (right-hand comment):

(setq x 1)

;; Highlight the 2nd & 3rd lines and press "M-;" to comment out those
;; lines (highlighting them a 2nd time and pressing "M-;" removes the
;; comment):

(+ 1 1)
(+ 2 2)
(+ 3 3)

;; Using Paul Foley's comment functions allows you to selectively
;; comment out embedded sexps. Example: Put the cursor on the "(+ 4
;; 4)" sexp and press "C-c ;" to comment out that sexp. Pressing "C-c
;; ;" comments out the enclosing sexp (and on upwards). Pressing "C-c
;; :" removes the comment:

(+ 1 (+ 2 (+ 3 (+ 4 4))))

;; Emacs knows how to wrap comment text intelligently. For example, this comment line spans
;; muliple lines but is not aligned consitently
;; with the rest of the comments/code in the file (everything else
;; ends at
;; column 68. Pressing "M-q" adjusts the comments appropriately.

Working with Lisp Code - Evaluating and Compiling Lisp

;; Compile the entire buffer by pressing "C-c C-b" (ELI) or "C-c C-k
;; C-b" (ILISP).

;; Compile a region by selecting the first 2 forms in test-all and
;; pressing "C-c C-r" (ELI) or "C-c C-k C-r" (ILISP).

;; Compile a defun by putting the cursor inside the "test-format"
;; defun and pressing "C-c C-x" (ELI) or "C-c C-k C-d" (ILISP).

;; Compile the sexp before the point by putting the cursor after the
;; closing paren of "(test-format)" and pressing "C-c C-s" (ELI). 

;; As a general rule, to evaluate rather than compile, press "C-u"
;; before the ELI command (e.g. -- "C-u C-c C-s" to evaluate the sexp
;; before the point instead of "C-c C-s" to compile it) or enter the
;; ILISP key sequence with a "C-c C-j" prefix rather than a "C-c C-k"
;; prefix (e.g. -- use "C-c C-j C-d" to evaluate a defun instead of
;; "C-c C-k C-d" to compile the defun)

;; The "Do What I Mean" evaluation/compilation functions work on the
;; following basis: If a region is selected, process the region.  If
;; the cursor is on or immediately after a ')', process the last sexp.
;; If the cursor is on or immediately before a '(', process the next
;; sexp. If the cursor is inside a defun, process the defun. If the
;; cursor is inside a top-level sexp, process the top-level
;; sexp. Tests are done in the order specified, so (if there is any
;; ambiguity), make certain that the cursor is either on a parenthesis
;; (for the last/next commands or not directly before/after/on a
;; parenthesis for the defun/top-level commands.  Press "C-c d" (ELI)
;; or "C-c C-j C-j" (ILISP).

(defun test (n)
  (loop for i from 0 below n
	do (print i)))

(defun test-format ()
  (format t "This is a test.~%"))

(defun test-format-loop (n)
  (loop for i from 0 below n
	do (test-format)
	(sleep 1)))

(defun test-all ()
  (test 5)
  (test-format)
  (test-format-loop 5))

Working with Lisp Code - Searching Lisp Code

;; "C-s" does an incremental search forward (e.g. - as each key is
;; the search string is entered, the source file is searched for the
;; first match. This can make finding specific text much quicker as
;; you only need to type in the unique characters. Repeat searches
;; (using the same search characters) can be done by repeatedly
;; pressing "C-s"

;; "C-r" does an incremental search backward

;; "C-s RET" and "C-r RET" both do conventional string searches
;; (forward and backward respectively)

;; "C-M-s" and "C-M-r" both do regular expression searches (forward
;; and backward respectively)

;; "M-%" does a search/replace while "C-M-%" does a regular
;; expression search/replace
;; Use the Emacs "occur" function to find all occurances of a string
;; (or regexp) in a buffer. Example: Enter "M-x occur" and enter the
;; string "defun" to get a list of all the occurances of the
;; characters "defun" in the current buffer.

(defvar aa "a" "a variable")

(defun b ()
  "a function"
  (+ 2 2))

(defun c ()
  "another function"
  (+ 3 3))

(defmacro d (x)
  "a macro"
  `(list ,x))

;; Use the Emacs "grep" function to find all occurances of a string
;; (or regexp) in a multiple files. Example: Enter "M-x grep" and
;; enter the string "defun *.lisp" to get a list of all the function
;; definitions in lisp files in the current directory.
;; Open a lisp source file and press the middle mouse button for a
;; pop-up window listing all variables and functions in the lisp
;; source file.
;; Use the source location maintained by the CL implementation to
;; locate source definitions.  First load this file and the s13.lisp
;; file.  Then put the cursor on the "aa" variable in the following
;; form and press "C-c ."  (ELI) or "M-." (ILISP) to locate the
;; definition.

(setq x aa)
;; Enter "M-x shell" to bring up a shell window in the current
;; directory. Then run "etags *.lisp" to create a TAG file containing
;; all symbols in lisp files in the current directory. Then run "etags
;; -a subdir/*.lisp" to add to the TAG file symbols from all lisp
;; files in another directory. Locate the definition of the "aa"
;; variable is the s13.lisp file by putting the cursor on the "aa" in
;; the following form and pressing "M-.".

(setq x aa)
;; Press "M-x ecb-activate" to start up ECB. Load a lisp file by
;; pressing the middle mouse button (or the Enter key) on any file
;; name in the ECB mini-window. Pressing the middle mouse button (or
;; the Enter key) on any definition in the ECB symbol mini-window
;; takes you to that definition. Press "C-F7" to toggle between full
;; screen and ECB views.

Lisp Documentation in Emacs - Learning About Lisp Symbols

;; For ILISP/ELI, type in "(set" and press SPACE to get the arglist in
;; the minibuffer.  For ILISP, type in "(set" and press "C-c C-q C-a"
;; to get the arglist in the listener buffer.  For ILISP, type in
;; "(set " and press "C-u C-c C-q C-a" to get the arguments for "set"
;; pasted into the current buffer.  
;; Enter and evaluate the following definitions, then put the cursor
;; on "(xx)" and press "C-c C-f" (ELI) or "C-c C-q C-o" (ILISP) to get
;; the function documentation.

(defun xx ()
  "A do-nothing function"
  nil)

(setf (documentation 'xx 'function) "A function that does nil.")

(xx)
;; Enter and evaluate the following definitions, then put the cursor
;; on "(xx)" and press "C-c C-q C-d" (ILISP) to get a description of
;; the function.

(defun xx ()
  "A do-nothing function"
  nil)

(setf (documentation 'xx 'function) "A function that does nil.")

(xx)
;; Enter and evaluate the following definitions, then put the cursor
;; on "(xx)" and press "C-c C-q C-i" (ILISP) to execute "inspect" on
;; the "xx" function. Entering ":h" gives a list of help commands and
;; ":q" exits "inspect". 

(defun xx ()
  "A do-nothing function"
  nil)

(setf (documentation 'xx 'function) "A function that does nil.")

(xx)
;; Enter the following function definition, then put the cursor on the
;; open parens of "(defun ..." and press "C-c RET" (ELI) or "C-c C-b
;; k" (ILISP) to get a macroexpand-1. Then press "C-c (" (ELI) or "C-c
;; C-b C-k" (ILISP) to get a recursive macroexpansion.

(defun test (n)
  (loop for i from 0 below n
	do (print i)))

Lisp Documentation in Emacs - Lisp Documentation

;; Put the cursor on the "(format" below and press "F1" to get the
;; Hyperspec page for "format". Put the cursor on the "d" in the
;; format string and press "C-u F1" to get the Hyperspec page
;; describing the "Tilde D: Decimal" format character.

(format t "One = ~d" 1)

;; Note: Depending on the documentation packages that have been
;; loaded, and the browser that you wish to use, the following keys
;; may be used:
;;            Default    W3
;; Package    Browser  Browser  Format-Dft  Format-W3  Info
;; =========  =======  =======  ==========  =========  ====
;; Hyperspec    F1      S-F1    C-u F1      C-u S-F1
;; CLtL2       M-F1    M-S-F1   
;; ACL docs    C-F1    C-S-F1
;; Info docs                                           C-M-F1

Miscellaneous

;; With the cursor on the "let", press "C-c x" (ELI or ILISP) to
;; evaluate a lisp form by copying it into the listener.

(let ((n 20))
  (loop for i from 0 below n
      do (print i)))

;; In ILISP, most of the eval & compile functions have an "and go"
;; equivalent that transfers the focus to the listener after
;; execution. Their key bindings are the same as the normal
;; eval/compile functions except that the final key does not have a
;; "Ctrl" prefix. For example, put the cursor at the open paren of the
;; "(let ..." sexp above and press "C-c C-j C-n" to evaluate in the
;; source buffer. Then, press "C-c C-j n" to evaluate "and go" to the
;; listener.
;; Start the ediff utility by entering "M-x ediff". Enter s10a.lisp as
;; the first file name and s10b.lisp as the second file name. Press
;; the space bar to step through the changes. When finished, press "q"
;; to exit.

Questions/Answers

Q1 I get irritated by ELI's switching to an output buffer when I evaluate a sexp in a Lisp source buffer.  

A1 You can control where ELI output goes to by setting the fi:pop-up-temp-window-behavior variable. Alternatively, 
you can use my copy-eval-dwim-lisp function (bound to "C-c x"). It copies Lisp code from the source buffer to the 
listener buffer and evaluates it there. Both buffers stay visible and focus remains in the source buffer. The code 
works for ILISP, ELI and Emacs Lisp.
Q2 I like having access to the HyperSpec when I'm in Emacs, but why does it have to use an external browser? Why 
can't I just see the HyperSpec in Emacs?

A2 If you use the Emacs add-on package W3 (or W3M which provides similar functionality), you can display HTML pages 
inside of Emacs. Once you have W3 and the HyperSpec both installed, use code similar to the following to access the 
HyperSpec from the Shift-F1 key:

(global-set-key [(shift f1)]
		'(lambda ()
		   (interactive)
		   (let ((browse-url-browser-function 
                            'browse-url-w3)
		           (common-lisp-hyperspec-root            
                            "file://c:/home/docs/Hyperspec/")
	                           (common-lisp-hyperspec-symbol-table 
                             (concat common-lisp-hyperspec-root 
                                         "Data/Map_Sym.txt"))
		            (hyperspec-prog 
                             "c:/home/site/ilisp/extra/hyperspec"))
		     (load-library hyperspec-prog)
		     (common-lisp-hyperspec 
                        (thing-at-point 'symbol)))))

Note that the "let" in the above code sets the browse-url-browser-function to W3 for just the HyperSpec. You can
either set the variable globally (if you want to always use W3 or some other specific browser) or locally (if you 
want to use a specific browser and not the default one).
Q3 I switch between UNIX® and Windows environments and, although Emacs makes this switch a lot easier, I find it 
inconvenient having to use different Shell environments on different operating systems.

A3 On Windows, the Cygwin tools provide a lot of the same tools that are available under UNIX® as well as a BASH 
shell. Alternatively, you might want to consider using eshell, a shell written in Emacs Lisp that comes as a 
standard feature in later releases of Emacs. You can access eshell by pressing "F12".
Q4 I would like to use Emacs with Franz's ACL but find that I use the Franz tools so much that I can't afford to not 
load their IDE.

A4 It doesn't have to be an either/or decision. On Windows, Franz allows you to specify (under Options) that Emacs 
is to be the default editor in place of their built-in editor. On UNIX®, Emacs also works very well together with 
the Franz tools.
Q5 I want to use Emacs on a Windows machine. Unfortunately, I have the Windows cut/copy/paste key bindings burned 
into my fingertips and would find it very difficult to switch back and forth between the Windows standard for these 
shortcut keys and the Emacs standard.

A5 Luckily, you don't have to! Download cua.el and you can continue to use the Windows defaults. In fact, you may 
find that the following commands in your .emacs file will make Emacs more Windows-like:

;; Windows-like mouse/arrow movement & selection
(pc-selection-mode)                 
(delete-selection-mode t)           
;; C-z=Undo, C-c=Copy, C-x=Cut, C-v=Paste (needs cua.el)
(require 'cua)
(CUA-mode t)
Q6 There was a lot of Emacs Lisp code presented in this paper. Do I really have to type in all this stuff to get 
started with Emacs and Lisp?

A6 No, there is a sample .emacs file that can be used to get started. It contains all of the configurations that 
have been described in this page and (hopefully) should work with some minor tweaking. See the CL-Cookbook page on 
"Setting up an IDE with Emacs on Windows or Mac OS X".
Q7 I've tried out Emacs and I just can't get used to it. What other Lisp-friendly alternative are there?

A7 The Franz, LispWorks, Corman, and Digitool commercial Lisp offerings all have Lisp-aware editors.  

CMUCL has Hemlock, which is also being adapted for other Lisps.

XEmacs is an alternative to GNU Emacs that works with many of the same Elisp libraries. Some people prefer it to 
GNU Emacs.  

Vim can be used to edit Lisp code. An article by Larry Clapp gives some pointers on how to use Vim with Lisp.  

Jabberwocky is a Lisp editor/debugger written in Java.  

Lastly, for true masochists, notepad on Windows or ed on UNIX® can also be used. ;-)