I recently went back to Common Lisp to solve the daily problems of the Advent of Code. Of course it started with installing and configuring SLIME, the main major mode used for Common Lisp development in Emacs.
The most useful feature of SLIME is the ability to load sections of code into
the Common Lisp implementation currently running. One can use
C-c C-c to
evaluate the current top-level form, and
C-c C-k to reload the entire file,
making incremental development incredibly convenient.
However I found the default configuration frustrating. Here are a few tips which made my life easier.
Removing the compilation error prompt
If the Common Lisp implementation fails to compile the file, SLIME will ask the user if they want to load the fasl file (i.e. the compiled form of the file) anyway.
I cannot find a reason why one would want to load the ouput of a file that failed to compile, and having to decline every time is quite annoying.
Disable the prompt by setting
(setq slime-load-failed-fasl 'never)
Removing the SLIME compilation buffer on success
When compilation fails, SLIME creates a new window containing the diagnostic
reported by the Common Lisp implementation. I use
make sure the window is displayed on the right side of my three-column split,
and fix my code in the middle column.
However if the next compilation succeeds, SLIME updates the buffer to indicate
the absence of error, but keeps the window open even though it is not useful
anymore, meaning that I have to switch to it and close it with
One can look at the
slime-compilation-finished function to see that SLIME
calls the function referenced by the
variable right after the creation or update of the compilation buffer. The
default value is
slime-maybe-show-compilation-log which does not open a new
window if there is no error, but does not close an existing one.
Let us write our own function and use it:
(defun g-slime-maybe-show-compilation-log (notes) (with-struct (slime-compilation-result. notes successp) slime-last-compilation-result (when successp (let ((name (slime-buffer-name :compilation))) (when (get-buffer name) (kill-buffer name)))) (slime-maybe-show-compilation-log notes))) (setq slime-compilation-finished-hook 'g-slime-maybe-show-compilation-log)`
Nothing crazy here, we obtain the compilation status (in a very SLIME-specific
with-struct is not a standard Emacs Lisp macro) and kill the
compilation buffer if there is one while compilation succeeded.
Making compilation less verbose
Common Lisp specifies two variables,
which control how much information is displayed during compilation and loading
My implementation of choice, SBCL, is quite chatty by
default. So I always set both variables to
nil in my
However SLIME forces
*compile-verbose*; this is done in SWANK, the Common
Lisp part of SLIME. When compiling a file, SLIME instructs the running Common
Lisp implementation to execute
swank:compile-file-for-emacs which forces
t around the call of a list of functions susceptible
to handle the file. The one we are interested about is
First, let us write some Common Lisp code to replace the function with a wrapper
(let ((old-function #'swank::swank-compile-file*)) (setf (fdefinition 'swank::swank-compile-file*) (lambda (pathname load-p &rest options &key policy &allow-other-keys) (declare (ignore policy)) (let ((*compile-verbose* nil)) (apply old-function pathname load-p options)))))
We save it to a file in the Emacs directory.
In Emacs, we use the
slime-connected-hook hook to load the code into the
Common Lisp implementation as soon as Slime is connected to it:
(defun g-slime-patch-swank-compilation-function () (let* ((path (expand-file-name "swank-patch-compilation-function.lisp" user-emacs-directory)) (lisp-path (slime-to-lisp-filename path))) (slime-eval-async `(swank:load-file ,lisp-path)))) (add-hook 'slime-connected-hook 'g-slime-patch-swank-compilation-function)
Quite a hack, but it works.