Whitespace Cleanup on Diff Lines in Emacs
Some of us are particular about whitespace, especially in files under
version control (PS: you should be using version control,
git). If you’re like me and you use
emacs, then you can often use
whitespace.el) to keep your
source files pristine.
The trouble comes if a collaborator doesn’t respect your whitespace
wishes. If you just run
whitespace-cleanup on your entire buffer,
your revision history and
git blame will be useless.
So far, my solution is to sanitize the whitespace only on the lines
that I’m editing. I was doing this by hand, but since I use
it was time to automate things and learn some rudimentary
elisp in the process.
Without further ado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 (defun buffer-file-git-diff-regions () "" (or (magit-git-dir) (error "Dir NOT in a git repo: %s" default-directory)) (let ((file (buffer-file-name))) (or file (error "Buffer \"%s\" does not have a file associated to it" file)) (let* ((command (concat "git diff -U0 " (shell-quote-argument file) "| grep \"^@@\" | cut -d' ' -f3 | tr +, ' '")) (output (shell-command-to-string command)) (lines (split-string output "\n" t)) (line-pair-helper (lambda (x) (if (not (cadr x)) (list (car x) 1) x))) (line-maybe-pairs (mapcar (lambda (x) (mapcar 'string-to-number (split-string x " " t))) lines))) (mapcar line-pair-helper line-maybe-pairs)))) (defun buffer-file-git-diff-regions-apply (func) "" (interactive "aFunction to apply to dirty regions: ") (save-excursion (dolist (line-len (buffer-file-git-diff-regions)) (goto-char (point-min)) (forward-line (1- (car line-len))) (push-mark) (forward-line (cadr line-len)) (funcall func (region-beginning) (region-end)) (pop-mark)))) (defun whitespace-cleanup-git-diff-regions () "" (interactive) (buffer-file-git-diff-regions-apply 'whitespace-cleanup-region))
The end-user command is
you can run interactively from a file that’s under
git control. Though you can also
use the general function
buffer-file-git-diff-regions-apply to run
any function of the type
(function region-beginning region-end) on
all the “dirty” regions.
magit, but just to test if we are in a
git control. You could swap this out if desired.
The magic command-line incantation
git diff -U0 <filename> | grep "^@@" | cut -d' ' -f3 | tr +, ' '
will generate a newline-delimited list of “dirty” regions of the form
" lineno numlines". I then run
whitespace-cleanup-region on each
of these regions, and we’re done!
So I’ve learned a bit of
elisp, and potentially saved my future self
entire minutes of editing. I hope somebody else benefits, too!
Sorry about the lack of documentation. Use at your own risk (which is
basically zilch, because you use
git, right?). Ping me if you have