Emacs provides a fair few commands (delete-trailing-whitespace, for instance) that can be used to clean up buffers, but sometimes we cannot use them and risk compromising the version history of unmodified lines. I just spent time on a particularly cumbersome merge made much harder because of whitespace changes and thought of ways to overcome this.

I'd like to implement a before-save-hook that runs delete-trailing-whitespace on every modified line in a buffer. How can I get hold of a list of all the modified lines in a buffer?

An alternative solution using Basil's approach:

(require 'diff)
(defun delete-trailing-whitespace-at-modified-lines ()
  (when buffer-file-name
    (when (derived-mode-p 'prog-mode)
            (diff-no-select buffer-file-name (current-buffer) nil 'noasync)

(when (eq system-type 'gnu/linux)
  (add-hook 'before-save-hook 'delete-trailing-whitespace-at-modified-lines))
  • 1
    Not sure if this is helpful, but I just noticed the command diff-delete-trailing-whitespace which works in diff-mode buffers. – Basil Feb 26 '18 at 16:00
  • @Drew Good catch, Drew. I've explored both options, but I think modified lines in the buffer, as in unsaved changes, makes the most sense. – Caterpillar Feb 26 '18 at 16:02
  • Please update the question to make that clear. Thx. – Drew Feb 26 '18 at 16:04
  • @Basil Thanks! I'll look into it :) – Caterpillar Feb 26 '18 at 16:24
  • 1
    The small package prevent-trailing-whitespace.el is an alternative. It prevents trailing whitespace only in the edited lines of the buffer. Note that I've written it as answer to your actual problem (but not to your question how to list modified lines). Therefore, expect teething problems. – Tobias Feb 26 '18 at 21:36
(defun ar-delete-trailing-whitespace-changed-lines ()
  (interactive "*")
  (let ((erg (cdr buffer-undo-list)))
    (dolist (ele erg)
      (when (and (numberp (car-safe ele)) (numberp (cdr ele)))
    (delete-trailing-whitespace (car ele) (cdr ele))))))

First draft below messaged a list of changed lines. As delete-trailing-whitespace doesn't need the lines, but the region only, command above is enough.

(defun ar-update-buffer-undo-list-intern (pos lines)
  (let (l)
    (setq l (save-excursion (goto-char pos)
                (count-lines (point-min) (point))))
    (unless (member l lines)
      (push l lines))

(defun ar-buffer-changed-lines ()
  (let ((erg (cdr buffer-undo-list))
    lines l beg end)
    (dolist (ele erg)
      (when (numberp (car-safe ele))
    (setq beg (car ele))
    (setq end (cdr ele))
    (while (< beg end)
      (setq lines (ar-update-buffer-undo-list-intern beg lines))
      (setq beg (1+ beg)))
    (setq lines (ar-update-buffer-undo-list-intern end lines))))
    (message "%s" (nreverse lines))))
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.