88 lines
3.5 KiB
EmacsLisp
88 lines
3.5 KiB
EmacsLisp
|
|
;;; 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
|