While SLIME is most of the time able to indent Common Lisp correctly, it will sometimes trip on custom forms. Let us see how we can customize indentation.
In the process of writing my PostgreSQL client in Common Lisp, I wrote a
READ-MESSAGE-CASE
macro which reads a message from a stream and execute code
depending on the type of the message:
(defmacro read-message-case ((message stream) &rest forms)
`(let ((,message (read-message ,stream)))
(case (car ,message)
(:error-response
(backend-error (cdr ,message)))
(:notice-response
nil)
,@forms
(t
(error 'unexpected-message :message ,message)))))
This macro is quite useful: all message loops can use it to automatically handle error responses, notices, and signal unexpected messages.
But SLIME does not know how to indent READ-MESSAGE-CASE
, so by default it
will align all message forms on the first argument:
(read-message-case (message stream)
(:authentication-ok
(return))
(:authentication-cleartext-password
(unless password
(error 'missing-password))
(write-password-message password stream)))
While we want it aligned the same way as HANDLER-CASE
:
(read-message-case (message stream)
(:authentication-ok
(return))
(:authentication-cleartext-password
(unless password
(error 'missing-password))
(write-password-message password stream)))
Good news, SLIME indentation is defined as a list of rules. Each rule
associates an indentation specification (a S-expression describing how to
indent the form) to a symbol and store it as the common-lisp-indent-function
property of the symbol.
You can obtain the indentation rule of a Common Lisp symbol easily. For
example, executing (get 'defun 'common-lisp-indent-function)
(e.g. in IELM
or with eval-expression
) yields (4 &lambda &body)
. This indicates that
DEFUN
forms are to be indented as follows:
- The first argument of
DEFUN
(the function name) is indented by four spaces. - The second argument (the list of function arguments) is indented as a lambda list.
- The rest of the arguments are indented based on the
lisp-body-indent
custom variable, which controls the indentation of the body of a lambda form (two spaces by default).
You can refer to the documentation of the common-lisp-indent-function
Emacs
function (defined in SLIME of course) for a complete description of the
format.
We want READ-MESSAGE-CASE
to be indented the same way as HANDLER-CASE
,
whose indentation specification is (4 &rest (&whole 2 &lambda &body))
(in
short, an argument and a list of lambda lists). Fortunately there is a way to
specify that a form must be indented the same way as another form, using (as <symbol>)
.
Let us first define a function to set the indentation specification of a symbol:
(defun g-common-lisp-indent (symbol indent)
"Set the indentation of SYMBOL to INDENT."
(put symbol 'common-lisp-indent-function indent))
Then use it for READ-MESSAGE-CASE
:
(g-common-lisp-indent 'read-message-case '(as handler-case))
While it is in general best to avoid custom indentation, exceptions are sometimes necessary for readability. And SLIME makes it easy.