diff options
Diffstat (limited to 'lisp/vc/diff-mode.el')
| -rw-r--r-- | lisp/vc/diff-mode.el | 160 |
1 files changed, 115 insertions, 45 deletions
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index c6a9371ea9a..0c023b0f7f4 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -178,6 +178,8 @@ when editing big diffs)." ["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-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) :help "Show trailing whitespace in modified lines"] @@ -476,11 +478,13 @@ See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html") (let* ((nold (string-to-number (or (match-string 2) "1"))) (nnew (string-to-number (or (match-string 4) "1"))) (endold - (save-excursion - (re-search-forward (if diff-valid-unified-empty-line - "^[- \n]" "^[- ]") + (save-excursion + (re-search-forward (if diff-valid-unified-empty-line + "^[- \n]" "^[- ]") nil t nold) - (line-beginning-position 2))) + (line-beginning-position + ;; Skip potential "\ No newline at end of file". + (if (looking-at ".*\n\\\\") 3 2)))) (endnew ;; The hunk may end with a bunch of "+" lines, so the `end' is ;; then further than computed above. @@ -488,7 +492,9 @@ See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html") (re-search-forward (if diff-valid-unified-empty-line "^[+ \n]" "^[+ ]") nil t nnew) - (line-beginning-position 2)))) + (line-beginning-position + ;; Skip potential "\ No newline at end of file". + (if (looking-at ".*\n\\\\") 3 2))))) (setq end (max endold endnew))))) ;; We may have a first evaluation of `end' thanks to the hunk header. (unless end @@ -563,11 +569,27 @@ next hunk if TRY-HARDER is non-nil; otherwise signal an error." (goto-char (match-beginning 1)) (beginning-of-line))) +(defvar diff--auto-refine-data nil) + ;; Define diff-{hunk,file}-{prev,next} (easy-mmode-define-navigation diff-hunk diff-hunk-header-re "hunk" diff-end-of-hunk diff-restrict-view - (if diff-auto-refine-mode - (condition-case-unless-debug nil (diff-refine-hunk) (error nil)))) + (when diff-auto-refine-mode + (unless (prog1 diff--auto-refine-data + (setq diff--auto-refine-data + (cons (current-buffer) (point-marker)))) + (run-at-time 0.0 nil + (lambda () + (when diff--auto-refine-data + (let ((buffer (car diff--auto-refine-data)) + (point (cdr diff--auto-refine-data))) + (setq diff--auto-refine-data nil) + (with-local-quit + (when (buffer-live-p buffer) + (with-current-buffer buffer + (save-excursion + (goto-char point) + (diff-refine-hunk)))))))))))) (easy-mmode-define-navigation diff-file diff-file-header-re "file" diff-end-of-file) @@ -891,7 +913,7 @@ PREFIX is only used internally: don't use it." "Convert unified diffs to context diffs. START and END are either taken from the region (if a prefix arg is given) or else cover the whole buffer." - (interactive (if (or current-prefix-arg (and transient-mark-mode mark-active)) + (interactive (if (or current-prefix-arg (use-region-p)) (list (region-beginning) (region-end)) (list (point-min) (point-max)))) (unless (markerp end) (setq end (copy-marker end t))) @@ -1015,7 +1037,7 @@ else cover the whole buffer." START and END are either taken from the region \(when it is highlighted) or else cover the whole buffer. With a prefix argument, convert unified format to context format." - (interactive (if (and transient-mark-mode mark-active) + (interactive (if (use-region-p) (list (region-beginning) (region-end) current-prefix-arg) (list (point-min) (point-max) current-prefix-arg))) (if to-context @@ -1025,7 +1047,7 @@ With a prefix argument, convert unified format to context format." (inhibit-read-only t)) (save-excursion (goto-char start) - (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)$" nil t) + (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)\\(?: \\(.*\\)\\|$\\)" nil t) (< (point) end)) (combine-after-change-calls (if (match-beginning 2) @@ -1041,7 +1063,9 @@ With a prefix argument, convert unified format to context format." ;; Variables to use the special undo function. (old-undo buffer-undo-list) (old-end (marker-position end)) - (reversible t)) + ;; We currently throw away the comment that can follow + ;; the hunk header. FIXME: Preserve it instead! + (reversible (not (match-end 6)))) (replace-match "") (unless (re-search-forward diff-context-mid-hunk-header-re nil t) @@ -1111,7 +1135,7 @@ With a prefix argument, convert unified format to context format." "Reverse the direction of the diffs. START and END are either taken from the region (if a prefix arg is given) or else cover the whole buffer." - (interactive (if (or current-prefix-arg (and transient-mark-mode mark-active)) + (interactive (if (or current-prefix-arg (use-region-p)) (list (region-beginning) (region-end)) (list (point-min) (point-max)))) (unless (markerp end) (setq end (copy-marker end t))) @@ -1177,7 +1201,7 @@ else cover the whole buffer." "Fixup the hunk headers (in case the buffer was modified). START and END are either taken from the region (if a prefix arg is given) or else cover the whole buffer." - (interactive (if (or current-prefix-arg (and transient-mark-mode mark-active)) + (interactive (if (or current-prefix-arg (use-region-p)) (list (region-beginning) (region-end)) (list (point-min) (point-max)))) (let ((inhibit-read-only t)) @@ -1315,6 +1339,9 @@ a diff with \\[diff-reverse-direction]. \\{diff-mode-map}" (set (make-local-variable 'font-lock-defaults) diff-font-lock-defaults) + (add-hook 'font-lock-mode-hook + (lambda () (remove-overlays nil nil 'diff-mode 'fine)) + nil 'local) (set (make-local-variable 'outline-regexp) diff-outline-regexp) (set (make-local-variable 'imenu-generic-expression) diff-imenu-generic-expression) @@ -1388,6 +1415,8 @@ modified lines of the diff." (set (make-local-variable 'whitespace-style) '(face trailing)) (let ((style (save-excursion (goto-char (point-min)) + ;; FIXME: For buffers filled from async processes, this search + ;; will simply fail because the buffer is still empty :-( (when (re-search-forward diff-hunk-header-re nil t) (goto-char (match-beginning 0)) (diff-hunk-style))))) @@ -1897,7 +1926,7 @@ For use in `add-log-current-defun-function'." '((default :inherit diff-refine-change) (((class color) (min-colors 88) (background light)) - :background "#ffaaaa") + :background "#ffbbbb") (((class color) (min-colors 88) (background dark)) :background "#aa2222")) "Face used for removed characters shown by `diff-refine-hunk'." @@ -1951,8 +1980,13 @@ For use in `add-log-current-defun-function'." (goto-char beg) (pcase style (`unified - (while (re-search-forward "^\\(?:-.*\n\\)+\\(\\)\\(?:\\+.*\n\\)+" - end t) + (while (re-search-forward + (eval-when-compile + (let ((no-LF-at-eol-re "\\(?:\\\\.*\n\\)?")) + (concat "^\\(?:-.*\n\\)+" no-LF-at-eol-re + "\\(\\)" + "\\(?:\\+.*\n\\)+" no-LF-at-eol-re))) + end t) (smerge-refine-subst (match-beginning 0) (match-end 1) (match-end 1) (match-end 0) nil 'diff-refine-preproc props-r props-a))) @@ -2016,35 +2050,71 @@ I.e. like `add-change-log-entry-other-window' but applied to all hunks." ;; When there's no more hunks, diff-hunk-next signals an error. (error nil)))) -(defun diff-remove-trailing-whitespace () - "When on a buffer that contains a diff, inspects the -differences and removes trailing whitespace (spaces, tabs) from -the lines modified or introduced by this diff. Shows a message -with the name of the altered buffers, which are unsaved. If a -file referenced on the diff has no buffer and needs to be fixed, -a buffer visiting that file is created." - (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) |
