Emacs code for indent mk:defsystem and asdf:defsystem

Raymond Toy <[email protected]> Tue, 28 Apr 2026 20:59:19 -0700
Newsgroups gmane.comp.mathematics.maxima.general
Message-ID <[email protected]>
I got tired of looking at the weirdly indented mk:defsystem definition 
that we have. I asked Claude to create some elisp code to indent them in 
a more natural form. Matches the current maxima.asd form, roughly.

For kicks, I asked Claude to add a similar version for indenting 
asdf:defsystem.

I haven’t applied it yet. I’m sure people will have different opinions 
on how to indent, but if someone wants to propose an “official” version, 
I’d be happy to use it.

I’ve attached the elisp file. Just load it in emacs. Put point at the 
opening paren of |(mk:defsystem ...)| and enter |M-x 
mk-defsystem-reformat|. Use |asdf-defsystem-reformat| for asdf.

Claude created some keybindings. Not sure I’ll use those.

&#8203;

_______________________________________________
Maxima-discuss mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/maxima-discuss
defsys-indent.el (text/x-emacs-lisp, 5.5 KB)
;;; mk:defsystem and asdf:defsystem editing support

(require 'cl-indent)

(add-hook 'lisp-mode-hook
          (lambda ()
            (setq-local lisp-indent-function 'common-lisp-indent-function)))

;;; ---------------------------------------------------------------
;;; mk:defsystem
;;; ---------------------------------------------------------------

(defun mk-defsystem-indent (path state indent-point sexp-column normal-indent)
  "Custom indent for mk:defsystem and component forms.
- :depends-on / :serial-depends-on values: data-list alignment
  (continuation at open-paren + 1).
- Otherwise: defun-style body indent at +2."
  (let* ((current-open (elt state 1))
         (enclosing-kw (save-excursion
                         (goto-char current-open)
                         (condition-case nil
                             (progn
                               (backward-sexp 1)
                               (when (looking-at ":[a-zA-Z-]+")
                                 (intern (match-string 0))))
                           (error nil)))))
    (if (memq enclosing-kw '(:depends-on :serial-depends-on))
        (save-excursion
          (goto-char current-open)
          (1+ (current-column)))
      (lisp-indent-259 '(4 &rest 2) path state indent-point
                       sexp-column normal-indent))))

(put 'mk:defsystem 'common-lisp-indent-function 'mk-defsystem-indent)
(put 'defsystem    'common-lisp-indent-function 'mk-defsystem-indent)
(put :module       'common-lisp-indent-function 'mk-defsystem-indent)
(put :file         'common-lisp-indent-function 'mk-defsystem-indent)
(put :system       'common-lisp-indent-function 'mk-defsystem-indent)

(defvar mk-defsystem-break-before-keywords
  '(":depends-on" ":components" ":serial-depends-on"
    ":source-pathname" ":source-extension"
    ":binary-pathname" ":binary-extension"
    ":initially-do" ":finally-do"
    ":compiler-options")
  "Keywords in mk:defsystem that should start on a new line.")

(defun mk-defsystem-reformat ()
  "Reformat the current top-level mk:defsystem form."
  (interactive)
  (defsystem-reformat-1 mk-defsystem-break-before-keywords))

;;; ---------------------------------------------------------------
;;; asdf:defsystem
;;; ---------------------------------------------------------------

(defun asdf-defsystem-indent (path state indent-point sexp-column normal-indent)
  "Custom indent for asdf:defsystem.
- :depends-on values: data-list alignment.
- Otherwise: defun-style body indent at +2."
  (let* ((current-open (elt state 1))
         (enclosing-kw (save-excursion
                         (goto-char current-open)
                         (condition-case nil
                             (progn
                               (backward-sexp 1)
                               (when (looking-at ":[a-zA-Z-]+")
                                 (intern (match-string 0))))
                           (error nil)))))
    (if (memq enclosing-kw '(:depends-on :weakly-depends-on
                             :defsystem-depends-on))
        (save-excursion
          (goto-char current-open)
          (1+ (current-column)))
      (lisp-indent-259 '(4 &rest 2) path state indent-point
                       sexp-column normal-indent))))

(put 'asdf:defsystem 'common-lisp-indent-function 'asdf-defsystem-indent)

(defvar asdf-defsystem-break-before-keywords
  '(":depends-on" ":weakly-depends-on" ":defsystem-depends-on"
    ":components" ":serial"
    ":pathname" ":default-component-class"
    ":author" ":maintainer" ":license" ":licence"
    ":description" ":long-description" ":version"
    ":homepage" ":bug-tracker" ":mailto" ":source-control"
    ":entry-point" ":build-operation" ":build-pathname"
    ":around-compile" ":encoding"
    ":in-order-to" ":perform"
    ":if-feature")
  "Keywords in asdf:defsystem that should start on a new line.")

(defun asdf-defsystem-reformat ()
  "Reformat the current top-level asdf:defsystem form."
  (interactive)
  (defsystem-reformat-1 asdf-defsystem-break-before-keywords))

;;; ---------------------------------------------------------------
;;; Shared reformatting machinery
;;; ---------------------------------------------------------------

(defun defsystem-reformat-1 (break-before-keywords)
  "Break lines before each keyword in BREAK-BEFORE-KEYWORDS, then re-indent."
  (save-excursion
    (when (looking-at "(")
      (forward-char 1))
    (let ((bounds (bounds-of-thing-at-point 'defun)))
      (unless bounds
        (user-error "Not inside a top-level form"))
      (save-restriction
        (narrow-to-region (car bounds) (cdr bounds))
        (goto-char (point-min))
        (let ((re (concat "\\(?:#[-+][^ \t\n]+[ \t]+\\)?"
                          "\\_<"
                          (regexp-opt break-before-keywords)
                          "\\_>")))
          (while (re-search-forward re nil t)
            (let ((kw-start (match-beginning 0))
                  (ppss (syntax-ppss)))
              (unless (or (nth 3 ppss) (nth 4 ppss)
                          (= (nth 0 ppss) 0))
                (save-excursion
                  (goto-char kw-start)
                  (unless (looking-back "^[ \t]*" (line-beginning-position))
                    (delete-horizontal-space)
                    (insert "\n"))))))))
      (indent-region (car bounds) (cdr bounds)))))

;;; ---------------------------------------------------------------
;;; Key bindings
;;; ---------------------------------------------------------------

(with-eval-after-load 'lisp-mode
  (define-key lisp-mode-map (kbd "C-c C-=") #'mk-defsystem-reformat)
  (define-key lisp-mode-map (kbd "C-c C-+") #'asdf-defsystem-reformat))