| git.druid.rocks | index | druid520 | radium | radium-lang-lisp.el |
radium-lang-lisp.el
;;; radium-lang-lisp.el, common lisp via slime + sbcl -*- lexical-binding: t; -*-
;;
;; slime, not sly: this whole config already leans on the older,
;; established package over the newer alternative wherever both exist
;; (vc over magit, per radium-git.el's own header) - same call here.
;;
;; slime-setup itself (not deferred) is what wires slime-mode into
;; every lisp-mode buffer automatically (it does its own `(add-hook
;; 'lisp-mode-hook 'slime-lisp-mode-hook)' - confirmed directly by
;; reading slime.el, not assumed): the elisp side loads eagerly since
;; it's cheap (just hook/autoload registration), the real cost - an
;; actual sbcl process - only happens on M-x slime or the first eval,
;; same lazy-runtime/eager-elisp split geiser already has for scheme.
;;
;; no gtags, no checkonsave, no radium-kona-send-region/-line/-run
;; take-back here, on purpose, checked directly rather than assumed
;; safe by analogy: slime-mode's own minor-mode map already binds
;; C-c C-c (slime-compile-defun) and C-c C-l (slime-load-file) - both
;; outrank kona's global bindings on ordinary keymap precedence
;; (minor-mode map, then major-mode map, then global map) with zero
;; code needed here, exactly like scheme's own "geiser keeps its own
;; keys" situation and unlike c/sh/ada/forth (where the existing
;; binding really was dead weight worth reclaiming). gtags has no real
;; common-lisp backend the way it does c/perl/scheme's token index,
;; and slime's own M-. (slime-edit-definition, image-aware and
;; macro-aware) is a strictly better answer than a text index would be
;; anyway - the same reasoning scheme's own M-. override already used.
;; sbcl has no meaningful one-shot "syntax check" the way perl -c/
;; sh -n do (compiling real cl code runs it, with real side effects),
;; so it isn't in checkonsave's alist either, same category as scheme/
;; forth/kona/cron.
;;
;; C-c C-z is NOT "start a lisp, like run-lisp" - checked directly
;; against slime's own source rather than assumed from the scheme
;; parity above (that assumption was wrong, and shipped wrong for a
;; while): it's slime-switch-to-output-buffer, which only switches to
;; an *already-connected* repl and calls slime-connection same as
;; completion below - `M-x slime' is the actual command that starts
;; sbcl and connects (its own docstring says exactly that). unlike
;; geiser, slime has no on-demand auto-start wired into C-c C-z by
;; default (there's a real slime-auto-start defcustom that can make
;; slime-connection start one instead of erroring, 'always or 'ask -
;; left at its own upstream default, 'never, here: 'ask re-prompts on
;; every single corfu-auto tick below while disconnected, and 'always
;; means opening any .lisp file at all can silently spawn a real sbcl
;; process the moment corfu's own idle timer fires, neither of which
;; is a good enough trade for a bit of typing to invoke M-x slime
;; instead. worth knowing this exists if that trade ever looks better).
;;
;; slime-mode itself unconditionally registers its own completion-at-
;; point function (slime--completion-at-point) into every buffer the
;; moment slime-mode turns on - i.e. the moment any .lisp file is
;; opened at all, connected or not (checked directly: slime-mode's own
;; :init body is slime--on, which is nothing but a plain, unconditional
;; (slime-setup-completion) call). that function's real completion
;; path (slime-c-p-c-completion-at-point -> ... -> slime-current-
;; package -> slime-connection) throws a real, uncaught-by-slime elisp
;; error, "Not connected.", the instant anything calls it before a
;; connection exists - corfu-auto's own idle timer does exactly that
;; on nearly every keystroke, which is what corfu's "Corfu detected an
;; error" backtraces were. slime-autodoc (eldoc) was checked too and
;; already guards itself correctly via the non-throwing slime-current-
;; connection/slime-background-activities-enabled-p, so only
;; completion needed this.
;;
;; fix: keep slime--completion-at-point registered only while a real
;; connection actually exists, so corfu falls through to plain cape-
;; dabbrev/cape-file/etc (radium-completion.el's own defaults) instead
;; of erroring while disconnected, and gets real slime completion the
;; moment one connects - verified live against a real daemon: while
;; disconnected, slime--completion-at-point is absent from
;; completion-at-point-functions and corfu-auto produces zero errors;
;; M-x slime connecting adds it back (in every already-open lisp
;; buffer, not just whichever one is current); disconnecting removes
;; it again everywhere.
(use-package slime
:init
(setq inferior-lisp-program "sbcl")
:config
(slime-setup '(slime-fancy)))
(defun radium-slime-completion-sync (&rest _)
"add/remove slime's own completion-at-point-function in this buffer
to match whether a real connection actually exists right now - slime-
mode itself registers it unconditionally regardless of connection
state (see the file's own header comment for why that's a real
problem), so this is what actually keeps corfu from calling into it
before there's a connection for it to talk to."
(when (bound-and-true-p slime-mode)
(if (slime-connected-p)
(add-hook 'completion-at-point-functions #'slime--completion-at-point nil t)
(remove-hook 'completion-at-point-functions #'slime--completion-at-point t))))
(defun radium-slime-completion-sync-all-buffers (&rest _)
"radium-slime-completion-sync, in every live buffer - slime-
connected-hook/slime-net-process-close-hooks each fire once per
connection event, not once per buffer, so every already-open lisp
buffer needs re-checking here, not just whichever one is current."
(dolist (buf (buffer-list))
(with-current-buffer buf
(radium-slime-completion-sync))))
(add-hook 'slime-mode-hook #'radium-slime-completion-sync)
(add-hook 'slime-connected-hook #'radium-slime-completion-sync-all-buffers)
(add-hook 'slime-net-process-close-hooks #'radium-slime-completion-sync-all-buffers)
(add-hook 'lisp-mode-hook
(lambda ()
(setq indent-tabs-mode t
lisp-indent-offset 8)))
(add-to-list 'auto-mode-alist '("\\.lisp\\'" . lisp-mode))
(add-to-list 'auto-mode-alist '("\\.lsp\\'" . lisp-mode))
(add-to-list 'auto-mode-alist '("\\.cl\\'" . lisp-mode))
(add-to-list 'auto-mode-alist '("\\.asd\\'" . lisp-mode))
(provide 'radium-lang-lisp)