UPDATE: since the original publication of this blog entry, Nikodemus Siivola and I have done some more work on various other aspects of Common Lisp indentation, and I must say that the result is pretty satisfactory. Nikodemus has merged all the changes into Slime, and I have done so for XEmacs. If you're an XEmacs user, you don't need to use the slime-indentation contrib to get these improvements. Simply upgrade the "prog-modes" package and load cl-indent.el.

I have just modified XEmacs to improve the indentation of Common Lisp code. This change involves two things: the support for multiple method qualifiers in a call to defmethod and, much more importantly, a cleaner and more flexible scheme for indenting lambda lists. The patch has also been submitted to the GNU Emacs developers. Below is a more detailed description of what you get with these changes.

Method qualifiers

Until now, only one method qualifier was understood. Below are some examples demonstrating that the one method qualifier and the argument list are indented by 4 characters, and the method's body only by 2:

(defmethod foo :around ()        (defmethod foo :around
  do-this)                           ()
                                   do-this)
(defmethod foo
    :around ()                   (defmethod foo
  do-this)                           :around
                                     ()
                                   do-this)

Now let's add a second method qualifier:

(defmethod foo comb :around ()        (defmethod foo	    
	   do-this)			  comb :around () 
					  do-this)

Woops. Neither is correct. But now, you get this instead:

(defmethod foo comb :around ()        (defmethod foo	    
  do-this)				  comb :around () 
					do-this)

Three more examples to show how confused we were:

(defmethod foo comb :around     (defmethod foo comb     (defmethod foo 
  ()				    :around		    comb       
  do-this)			  ()			    :around    
				  do-this)		  ()	       
							  do-this)

And how better we just got:

(defmethod foo comb :around     (defmethod foo comb     (defmethod foo
    ()				    :around		    comb      
  do-this)			    ()			    :around   
				  do-this)		    ()	      
							  do-this)

Indeed, you can see that everything between the method's name and its body is now correctly indented by 4.

Lambda Lists

The next round of changes deals with the formatting of lambda-lists. As such, this will apply everywhere a lambda-list is expected, such as defun, defgeneric, defmethod etc. First of all, here are two examples showing that we were not very clever before:

(defun foo (mand1 mand2
	    &optional opt1
	    opt2
	    &rest args
	    &key key1 key2
	    (key3 val3) &aux aux1
	    aux2)
  do-this)

(defun foo (mand1 mand2 &optional opt1
	    opt2
	    &rest args
	    &key key1 key2
	    (key3 val3) &aux aux1
	    aux2)
  do-this)

Basically, everything was blindly aligned at the beginning of the lambda-list. Here is what you get now:

(defun foo (mand1 mand2
	    &optional opt1
	      opt2
	    &rest args
	    &key key1 key2
	      (key3 val3) &aux aux1
			    aux2)
  do-this)

(defun foo (mand1 mand2 &optional opt1
			  opt2
	    &rest args
	    &key key1 key2
	      (key3 val3) &aux aux1
			    aux2)
  do-this)

The difference is that keyword parameters are indented with respect to their corresponding keyword. The amount of indentation is provided by a new customizable user option named lisp-lambda-list-keyword-parameter-indentation (oh boy, what a mouthful). If you prefer, you can also have the parameters vertically aligned with each other. Set the new customizable user option named lisp-lambda-list-keyword-parameter-alignment to t and you will get this instead:

(defun foo (mand1 mand2
	    &optional opt1
		      opt2
	    &rest args
	    &key key1 key2
		 (key3 val3) &aux aux1
				  aux2)
  do-this)

(defun foo (mand1 mand2 &optional opt1
				  opt2
	    &rest args
	    &key key1 key2
		 (key3 val3) &aux aux1
				  aux2)
  do-this)

Finally, just as you could align keyword parameters together, you can also align the keywords together. Set the new customizable user option named lisp-lambda-list-keyword-alignment to t, and you will get this (only the second example differs):

(defun foo (mand1 mand2 &optional opt1
				  opt2
			&rest args
			&key key1 key2
			     (key3 val3) &aux aux1
					      aux2)
  do-this)

These are in fact my preferred settings, although both alignment options default to nil. Here is a final example demonstrating how you could format a long lambda-list with plenty of arguments:

(defun foo (mand1 mand2
	    &optional
	      opt1 opt2 opt3
	    &key
	      key1 key2 key3
	    &aux
	      aux1 aux2 aux3)
  do-this)

These indentation problems have been a huge peeve of mine for quite a long time. I hope you will find the changes useful!