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 slime-load-failed-fasl
to 'never
:
(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 display-buffer-alist
to
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 q
.
One can look at the slime-compilation-finished
function to see that SLIME
calls the function referenced by the slime-compilation-finished-hook
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
way, 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, *compile-verbose*
and *load-verbose*
,
which control how much information is displayed during compilation and loading
respectively.
My implementation of choice, SBCL, is quite chatty by
default. So I always set both variables to nil
in my $HOME/.sbclrc
file.
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
*compile-verbose*
to t
around the call of a list of functions susceptible
to handle the file. The one we are interested about is
swank::swank-compile-file*
.
First, let us write some Common Lisp code to replace the function with a wrapper
which sets *compile-verbose*
to nil
.
(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.