You can edit a Diff mode buffer like any other buffer. (If it is
read-only, you need to make it writable first. @xref{Misc Buffer}.)
Whenever you change a hunk, Diff mode attempts to automatically
-correct the line numbers in the hunk headers, to ensure that the diff
+correct the line numbers in the hunk headers, to ensure that the patch
remains ``correct''. To disable automatic line number correction,
change the variable @code{diff-update-on-the-fly} to @code{nil}.
@c Trailing whitespace is NOT shown by default.
@c Emacs's dir-locals file enables this (for some reason).
-@cindex trailing whitespace, in diffs
-@findex diff-remove-trailing-whitespace
- Diff mode has various features for dealing with trailing whitespace
-on modified lines, since this is often an unintentional and unwanted
-change. If you enable Whitespace mode in a Diff buffer, trailing
-whitespace is highlighted (@pxref{Useless Whitespace}). The command
-@kbd{M-x diff-remove-trailing-whitespace} searches for trailing
-whitespace in the lines modified or added by a diff. If it finds any,
-it tries to visit the associated file(s) and remove it. It does not
-save the modifications, rather it lists any buffers that were modified
-so you can decide for yourself what to do.
+@cindex trailing whitespace, in patches
+@findex diff-delete-trailing-whitespace
+ Patches sometimes include trailing whitespace on modified lines, as
+an unintentional and undesired change. There are two ways to deal
+with this problem. Firstly, if you enable Whitespace mode in a Diff
+buffer (@pxref{Useless Whitespace}), it automatically highlights
+trailing whitespace in modified lines. Secondly, you can use the
+command @kbd{M-x diff-delete-trailing-whitespace}, which searches for
+trailing whitespace in the lines modified by the patch, and removes
+that whitespace in both the patch and the patched source file(s).
+This command does not save the modifications that it makes, so you can
+decide whether to save the changes (the list of modified files is
+displayed in the echo area). With a prefix argument, it tries to
+modify the original source files rather than the patched source files.
@node Misc File Ops
@section Miscellaneous File Operations
["Unified -> Context" diff-unified->context
:help "Convert unified diffs to context diffs"]
;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)]
- ["Remove trailing whitespace" diff-remove-trailing-whitespace
+ ["Remove trailing whitespace" diff-delete-trailing-whitespace
:help "Remove trailing whitespace problems introduced by the diff"]
["Show trailing whitespace" whitespace-mode
:style toggle :selected (bound-and-true-p whitespace-mode)
;; When there's no more hunks, diff-hunk-next signals an error.
(error nil))))
-(defun diff-remove-trailing-whitespace ()
- "Remove trailing whitespace from the lines modified/added by a diff.
-Called from a buffer containing a diff, this searches for trailing
-whitespace (spaces, tabs) in the modified/added lines. If the
-file that such a line refers to can be found, it visits it and
-removes the associated whitespace, if it is present. It does not
-save any changed buffers, it just gives a message naming them."
- (interactive)
- ;; We assume that the diff header has no trailing whitespace.
- (let ((modified-buffers nil))
- (save-excursion
- (goto-char (point-min))
- (while (re-search-forward "^[+!>].*[ \t]+$" (point-max) t)
- (pcase-let ((`(,buf ,line-offset ,pos ,src ,_dst ,_switched)
- (diff-find-source-location t t)))
- (when line-offset
- (with-current-buffer buf
- (save-excursion
- (goto-char (+ (car pos) (cdr src)))
- (beginning-of-line)
- (when (re-search-forward "\\([ \t]+\\)$" (line-end-position) t)
- (unless (memq buf modified-buffers)
- (push buf modified-buffers))
- (replace-match ""))))))))
- (if modified-buffers
- (message "Deleted new trailing whitespace from: %s"
- (mapconcat (lambda (buf) (concat "`" (buffer-name buf) "'"))
- modified-buffers " "))
- (message "No trailing whitespace fixes needed."))))
+(defun diff-delete-trailing-whitespace (&optional other-file)
+ "Remove trailing whitespace from lines modified in this diff.
+This edits both the current Diff mode buffer and the patched
+source file(s). If `diff-jump-to-old-file' is non-nil, edit the
+original (unpatched) source file instead. With a prefix argument
+OTHER-FILE, flip the choice of which source file to edit.
+
+If a file referenced in the diff has no buffer and needs to be
+fixed, visit it in a buffer."
+ (interactive "P")
+ (save-excursion
+ (goto-char (point-min))
+ (let* ((other (diff-xor other-file diff-jump-to-old-file))
+ (modified-buffers nil)
+ (style (save-excursion
+ (when (re-search-forward diff-hunk-header-re nil t)
+ (goto-char (match-beginning 0))
+ (diff-hunk-style))))
+ (regexp (concat "^[" (if other "-<" "+>") "!]"
+ (if (eq style 'context) " " "")
+ ".*?\\([ \t]+\\)$"))
+ (inhibit-read-only t)
+ (end-marker (make-marker))
+ hunk-end)
+ ;; Move to the first hunk.
+ (re-search-forward diff-hunk-header-re nil 1)
+ (while (progn (save-excursion
+ (re-search-forward diff-hunk-header-re nil 1)
+ (setq hunk-end (point)))
+ (< (point) hunk-end))
+ ;; For context diffs, search only in the appropriate half of
+ ;; the hunk. For other diffs, search within the entire hunk.
+ (if (not (eq style 'context))
+ (set-marker end-marker hunk-end)
+ (let ((mid-hunk
+ (save-excursion
+ (re-search-forward diff-context-mid-hunk-header-re hunk-end)
+ (point))))
+ (if other
+ (set-marker end-marker mid-hunk)
+ (goto-char mid-hunk)
+ (set-marker end-marker hunk-end))))
+ (while (re-search-forward regexp end-marker t)
+ (let ((match-data (match-data)))
+ (pcase-let ((`(,buf ,line-offset ,pos ,src ,_dst ,_switched)
+ (diff-find-source-location other-file)))
+ (when line-offset
+ ;; Remove the whitespace in the Diff mode buffer.
+ (set-match-data match-data)
+ (replace-match "" t t nil 1)
+ ;; Remove the whitespace in the source buffer.
+ (with-current-buffer buf
+ (save-excursion
+ (goto-char (+ (car pos) (cdr src)))
+ (beginning-of-line)
+ (when (re-search-forward "\\([ \t]+\\)$" (line-end-position) t)
+ (unless (memq buf modified-buffers)
+ (push buf modified-buffers))
+ (replace-match ""))))))))
+ (goto-char hunk-end))
+ (if modified-buffers
+ (message "Deleted trailing whitespace from %s."
+ (mapconcat (lambda (buf) (concat "`" (buffer-name buf) "'"))
+ modified-buffers ", "))
+ (message "No trailing whitespace to delete.")))))
;; provide the package
(provide 'diff-mode)