| git.druid.rocks | index | druid520 | radium | radium-lang-scheme.el |
radium-lang-scheme.el
;;; radium-lang-scheme.el, chez scheme repl + xref via geiser -*- lexical-binding: t; -*-
;;
;; geiser-mode registers its own completion-at-point functions
;; (geiser-capf-setup, unconditional the instant geiser-mode turns on
;; for a real scheme buffer) regardless of whether a repl connection
;; actually exists yet - two of the three call straight down into
;; geiser-con--send-string/wait, which throws a real elisp error,
;; "Geiser connection not active", the instant anything calls it
;; before a connection exists (checked directly against geiser's own
;; source: geiser-completion.el's symbol/module-list -> geiser-eval--
;; send/result -> ... -> that throw). corfu-auto's idle timer does
;; exactly that on nearly every keystroke - the same bug shape (and
;; same real "Corfu detected an error" symptom) radium-lang-lisp.el's
;; own slime fix already found and fixed.
;;
;; unlike slime, geiser has no matched connect/disconnect hook pair to
;; toggle registration around (only a connect-side geiser-repl-
;; startup-hook, no real equivalent on the disconnect side, checked
;; directly rather than assumed missing) - so instead of toggling
;; geiser's own capfs in and out of completion-at-point-functions,
;; this replaces them with one wrapper that checks the connection
;; fresh on every actual completion attempt (geiser-repl--connection*
;; is a plain lookup against already-live repl buffers, no side
;; effects - confirmed by reading it, it never creates one), and only
;; delegates to geiser's own real capfs once one exists - correct
;; regardless of when a connection comes and goes, no lifecycle hook
;; needed at all.
(use-package geiser
:defer t)
(with-eval-after-load 'geiser-capf
(defun radium-geiser-completion-at-point ()
(when (geiser-repl--connection*)
(run-hook-with-args-until-success 'geiser-capf--capfs)))
(defun radium-scheme-completion-setup ()
(when (bound-and-true-p geiser-mode)
(dolist (f geiser-capf--capfs)
(remove-hook 'completion-at-point-functions f t))
(add-hook 'completion-at-point-functions #'radium-geiser-completion-at-point nil t)))
(add-hook 'geiser-mode-hook #'radium-scheme-completion-setup))
(use-package geiser-chez
:after geiser
:init (setq geiser-active-implementations '(chez)
geiser-default-implementation 'chez
;; geiser-chez only auto-detects "chez"/"scheme" on PATH -
;; my install's binary is chezscheme (chezscheme-script is
;; the non-interactive script runner, no good for a repl).
geiser-chez-binary "chezscheme"))
(add-hook 'scheme-mode-hook
(lambda ()
(setq indent-tabs-mode t
lisp-indent-offset 8)))
(add-to-list 'auto-mode-alist '("\\.scm\\'" . scheme-mode))
(add-to-list 'auto-mode-alist '("\\.ss\\'" . scheme-mode))
;; emacs ships interpreter-mode-alist entries for "scm" and "guile"
;; out of the box, but not "chez"/"chezscheme"/"chezscheme-script" -
;; the actual binary names this config's own geiser-chez-binary uses
;; (see above) - so a shebang'd chez script with no .scm/.ss extension
;; fell through to fundamental-mode instead of scheme-mode.
(dolist (interp '("chez" "chezscheme" "chezscheme-script"))
(add-to-list 'interpreter-mode-alist (cons interp 'scheme-mode)))
(provide 'radium-lang-scheme)