;;; reindent.el --- batch reindent to cc-mode "stroustrup" -*- lexical-binding: t -*- ;; Reindents each file named on the command line, in place, to the project's ;; canonical style: cc-mode "stroustrup", 4 columns per level, tabs 8 columns ;; wide. See AGENTS.md -> "Coding Style". ;; ;; The style is set explicitly here rather than read from .dir-locals.el so that ;; the result does not depend on where the file lives -- the pre-commit hook ;; runs this over temporary copies outside the project tree. ;; ;; Usage: emacs --batch -Q -l scripts/reindent.el -- FILE... ;;; Code: (require 'cc-mode) (setq make-backup-files nil create-lockfiles nil auto-save-default nil vc-handled-backends nil inhibit-message t) (defun akgl-retab-leading-whitespace () "Rewrite every line's leading whitespace as tabs-then-spaces at `tab-width'. `indent-region' fixes the column a line starts at, but `indent-line-to' leaves a line alone when it is already at the right column even if the bytes are wrong -- eight spaces where the canonical form is one tab. This pass closes that gap. Deliberately not `tabify': that function's `tabify-regexp' is \" [ \\t]+\", which is NOT anchored to the line start, so it rewrites runs of spaces anywhere on the line and destroys the hand-aligned columns in the bit-flag tables in actor.h and iterator.h. Only leading whitespace is touched here, and lines beginning inside a string literal are skipped outright." (goto-char (point-min)) (while (not (eobp)) (let ((bol (point))) (unless (nth 3 (syntax-ppss bol)) (skip-chars-forward " \t" (line-end-position)) (let ((col (current-column))) (unless (or (zerop col) (eolp)) (let ((want (concat (make-string (/ col tab-width) ?\t) (make-string (% col tab-width) ?\s)))) (unless (string= want (buffer-substring bol (point))) (delete-region bol (point)) (insert want))))))) (forward-line 1))) (defun akgl-reindent-file (path) "Reindent PATH in place. Returns t if the file changed on disk." (let ((before (with-temp-buffer (insert-file-contents path) (buffer-string)))) (with-current-buffer (find-file-noselect path t) (c-mode) (c-set-style "stroustrup") (setq indent-tabs-mode t tab-width 8 c-basic-offset 4 require-final-newline t) ;; 1. Put every line at its correct column. (indent-region (point-min) (point-max)) ;; 2. Convert leading whitespace to the canonical tab/space mix. (akgl-retab-leading-whitespace) ;; 3. Trailing whitespace and a single final newline. (delete-trailing-whitespace) (goto-char (point-max)) (unless (bolp) (insert "\n")) (let ((changed (not (string= before (buffer-string))))) (when changed (save-buffer)) (kill-buffer) changed)))) ;; Emacs leaves the "--" separator in `command-line-args-left'; drop it, along ;; with any empty argument, so the remainder is exactly the file list. (dolist (path (seq-remove (lambda (a) (or (string= a "--") (string= a ""))) command-line-args-left)) (when (akgl-reindent-file path) (princ (format "reindented %s\n" path)))) ;; Exit 0 on success whether or not anything changed, so that any non-zero ;; status from this script means a real failure. Callers detect "something ;; changed" from stdout, or by comparing files themselves. (kill-emacs 0) ;;; reindent.el ends here