X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/19446c41b170a489d00d551914b781b9e74c54bb..8677dea3af74e8253bb85a00beb9dd4975946d63:/lisp/diff-mode.el diff --git a/lisp/diff-mode.el b/lisp/diff-mode.el index 48b8f18a13..d61f7689ae 100644 --- a/lisp/diff-mode.el +++ b/lisp/diff-mode.el @@ -8,10 +8,10 @@ ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,9 +19,7 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -93,11 +91,6 @@ when editing big diffs)." :type 'boolean :group 'diff-mode) -(defcustom diff-auto-refine t - "Automatically highlight changes in detail as the user visits hunks." - :type 'boolean - :group 'diff-mode) - (defcustom diff-mode-hook nil "Run after setting up the `diff-mode' major mode." :type 'hook @@ -222,6 +215,13 @@ when editing big diffs)." `((,diff-minor-mode-prefix . ,diff-mode-shared-map)) "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'.") +(define-minor-mode diff-auto-refine-mode + "Automatically highlight changes in detail as the user visits hunks. +When transitioning from disabled to enabled, +try to refine the current hunk, as well." + :group 'diff-mode :init-value t :lighter nil ;; " Auto-Refine" + (when diff-auto-refine-mode + (condition-case-no-debug nil (diff-refine-hunk) (error nil)))) ;;;; ;;;; font-lock support @@ -370,7 +370,9 @@ when editing big diffs)." (replace-match "" t t))))))) (defconst diff-hunk-header-re-unified - "^@@ -\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? \\+\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\) @@") + "^@@ -\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? \\+\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? @@") +(defconst diff-context-mid-hunk-header-re + "--- \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? ----$") (defvar diff-font-lock-keywords `((,(concat "\\(" diff-hunk-header-re-unified "\\)\\(.*\\)$") @@ -378,7 +380,7 @@ when editing big diffs)." ("^\\(\\*\\{15\\}\\)\\(.*\\)$" ;context (1 diff-hunk-header-face) (2 diff-function-face)) ("^\\*\\*\\* .+ \\*\\*\\*\\*". diff-hunk-header-face) ;context - ("^--- .+ ----$" . diff-hunk-header-face) ;context + (,diff-context-mid-hunk-header-re . diff-hunk-header-face) ;context ("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face) ;normal ("^---$" . diff-hunk-header-face) ;normal ;; For file headers, accept files with spaces, but be careful to rule @@ -422,7 +424,7 @@ See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html") (defconst diff-hunk-header-re (concat "^\\(?:" diff-hunk-header-re-unified ".*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$")) -(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1))) +(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* \n]\\).+\n" (substring diff-hunk-header-re 1))) (defvar diff-narrowed-to nil) (defun diff-hunk-style (&optional style) @@ -438,12 +440,23 @@ See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html") (setq style (diff-hunk-style style)) (goto-char (match-end 0)) (when (and (not donttrustheader) (match-end 2)) + (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]" "^[- ]") - nil t - (string-to-number (match-string 2))) - (setq end (line-beginning-position 2))))) + nil t nold) + (line-beginning-position 2))) + (endnew + ;; The hunk may end with a bunch of "+" lines, so the `end' is + ;; then further than computed above. + (save-excursion + (re-search-forward (if diff-valid-unified-empty-line + "^[+ \n]" "^[+ ]") + nil t nnew) + (line-beginning-position 2)))) + (setq end (max endold endnew))))) ;; We may have a first evaluation of `end' thanks to the hunk header. (unless end (setq end (and (re-search-forward @@ -517,7 +530,7 @@ but in the file header instead, in which case move forward to the first hunk." ;; 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 + (if diff-auto-refine-mode (condition-case-no-debug nil (diff-refine-hunk) (error nil)))) (easy-mmode-define-navigation @@ -556,8 +569,10 @@ If the prefix ARG is given, restrict the view to the current file instead." (diff-end-of-hunk) (kill-region start (point))))) -;; "index " and "new file mode" are output by git-diff. -(defconst diff-file-junk-re "diff \\|index \\|new file mode") +;; "index ", "old mode", "new mode", "new file mode" and +;; "deleted file mode" are output by git-diff. +(defconst diff-file-junk-re + "diff \\|index \\|\\(?:deleted file\\|new\\(?: file\\)?\\|old\\) mode") (defun diff-beginning-of-file-and-junk () "Go to the beginning of file-related diff-info. @@ -838,11 +853,9 @@ else cover the whole buffer." (replace-match "***" t t nil 2)) ;; we matched a hunk header (let ((line1 (match-string 4)) - (lines1 (if (match-end 5) - (string-to-number (match-string 5)) 1)) + (lines1 (or (match-string 5) "1")) (line2 (match-string 6)) - (lines2 (if (match-end 7) - (string-to-number (match-string 7)) 1)) + (lines2 (or (match-string 7) "1")) ;; Variables to use the special undo function. (old-undo buffer-undo-list) (old-end (marker-position end)) @@ -851,7 +864,9 @@ else cover the whole buffer." (replace-match (concat "***************\n*** " line1 "," (number-to-string (+ (string-to-number line1) - lines1 -1)) " ****")) + (string-to-number lines1) + -1)) + " ****")) (save-restriction (narrow-to-region (line-beginning-position 2) ;; Call diff-end-of-hunk from just before @@ -885,7 +900,8 @@ else cover the whole buffer." (save-excursion (insert "--- " line2 "," (number-to-string (+ (string-to-number line2) - lines2 -1)) + (string-to-number lines2) + -1)) " ----\n" hunk)) ;;(goto-char (point-min)) (forward-line 1) @@ -965,7 +981,7 @@ With a prefix argument, convert unified format to context format." (reversible t)) (replace-match "") (unless (re-search-forward - "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t) + diff-context-mid-hunk-header-re nil t) (error "Can't find matching `--- n1,n2 ----' line")) (let ((line2s (match-string 1)) (line2e (match-string 2)) @@ -1056,11 +1072,14 @@ else cover the whole buffer." (when (= (char-after) ?-) (delete-char 1) (insert "+")) (forward-line 1)) (let ((half1 (delete-and-extract-region half1s (point)))) - (unless (looking-at "^--- \\([0-9]+,-?[0-9]+\\) ----$") + (unless (looking-at diff-context-mid-hunk-header-re) (insert half1) (error "Can't find matching `--- n1,n2 ----' line")) - (let ((str1 (match-string 1))) - (replace-match lines1 nil nil nil 1) + (let* ((str1end (or (match-end 2) (match-end 1))) + (str1 (buffer-substring (match-beginning 1) str1end))) + (goto-char str1end) + (insert lines1) + (delete-region (match-beginning 1) str1end) (forward-line 1) (let ((half2s (point))) (while (looking-at "[!+ \\][ \t]\\|#") @@ -1125,7 +1144,7 @@ else cover the whole buffer." (if old1 (unless (string= new1 old1) (replace-match new1 t t nil 2)) (goto-char (match-end 2)) (insert "," new1)))) - ((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$") + ((looking-at diff-context-mid-hunk-header-re) (when (> (+ space bang plus) 0) (let* ((old1 (match-string 1)) (old2 (match-string 2)) @@ -1177,25 +1196,29 @@ See `after-change-functions' for the meaning of BEG, END and LEN." (goto-char (car diff-unhandled-changes)) ;; Maybe we've cut the end of the hunk before point. (if (and (bolp) (not (bobp))) (backward-char 1)) - ;; We used to fixup modifs on all the changes, but it turns out - ;; that it's safer not to do it on big changes, for example - ;; when yanking a big diff, since we might then screw up perfectly - ;; correct values. -stef - ;; (unless (ignore-errors - ;; (diff-beginning-of-hunk) - ;; (save-excursion - ;; (diff-end-of-hunk) - ;; (> (point) (car diff-unhandled-changes)))) - ;; (goto-char (car diff-unhandled-changes)) - ;; (re-search-forward diff-hunk-header-re (cdr diff-unhandled-changes)) - ;; (diff-beginning-of-hunk)) - ;; (diff-fixup-modifs (point) (cdr diff-unhandled-changes)) + ;; We used to fixup modifs on all the changes, but it turns out that + ;; it's safer not to do it on big changes, e.g. when yanking a big + ;; diff, or when the user edits the header, since we might then + ;; screw up perfectly correct values. --Stef (diff-beginning-of-hunk) - (when (save-excursion + (let* ((style (if (looking-at "\\*\\*\\*") 'context)) + (start (line-beginning-position (if (eq style 'context) 3 2))) + (mid (if (eq style 'context) + (save-excursion + (re-search-forward diff-context-mid-hunk-header-re + nil t))))) + (when (and ;; Don't try to fixup changes in the hunk header. + (> (car diff-unhandled-changes) start) + ;; Don't try to fixup changes in the mid-hunk header either. + (or (not mid) + (< (cdr diff-unhandled-changes) (match-beginning 0)) + (> (car diff-unhandled-changes) (match-end 0))) + (save-excursion (diff-end-of-hunk nil 'donttrustheader) - (>= (point) (cdr diff-unhandled-changes))) + ;; Don't try to fixup changes past the end of the hunk. + (>= (point) (cdr diff-unhandled-changes)))) (diff-fixup-modifs (point) (cdr diff-unhandled-changes))))) - (setq diff-unhandled-changes nil))) + (setq diff-unhandled-changes nil)))) (defun diff-next-error (arg reset) ;; Select a window that displays the current buffer so that point @@ -1355,7 +1378,7 @@ Only works for unified diffs." (1+ (- (string-to-number (match-string 2)) (string-to-number (match-string 1)))) 1)) - (if (not (looking-at "--- \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? ----$")) + (if (not (looking-at diff-context-mid-hunk-header-re)) (error "Unrecognized context diff second hunk header format") (forward-line) (diff-sanity-check-context-hunk-half @@ -1368,10 +1391,8 @@ Only works for unified diffs." ((eq (char-after) ?@) (if (not (looking-at diff-hunk-header-re-unified)) (error "Unrecognized unified diff hunk header format") - (let ((before (if (match-end 2) - (string-to-number (match-string 2)) 1)) - (after (if (match-end 4) - (string-to-number (match-string 4)) 1))) + (let ((before (string-to-number (or (match-string 2) "1"))) + (after (string-to-number (or (match-string 4) "1")))) (forward-line) (while (case (char-after) @@ -1437,7 +1458,7 @@ char-offset in TEXT." ;; context diff (forward-line 2) (setq src-pos (point)) - (re-search-forward "^--- " nil t) + (re-search-forward diff-context-mid-hunk-header-re nil t) (forward-line 0) (setq divider-pos (point)) (forward-line 1) @@ -1553,7 +1574,8 @@ SWITCHED is non-nil if the patch is already applied." (error "Can't find the hunk header") (if other (match-string 1) (if (match-end 3) (match-string 3) - (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t) + (unless (re-search-forward + diff-context-mid-hunk-header-re nil t) (error "Can't find the hunk separator")) (match-string 1))))) (file (or (diff-find-file-name other) (error "Can't find the file"))) @@ -1707,7 +1729,7 @@ For use in `add-log-current-defun-function'." (let ((old (if switched dst src))) (with-temp-buffer (insert (car old)) - (funcall (with-current-buffer buf major-mode)) + (funcall (buffer-local-value 'major-mode buf)) (goto-char (+ (point-min) (cdr old))) (add-log-current-defun)))) (with-current-buffer buf @@ -1855,10 +1877,17 @@ I.e. like `add-change-log-entry-other-window' but applied to all hunks." "\\( .*\n\\)*[+]\\)?") nil t)) (save-excursion - (add-change-log-entry nil nil t t))) + (add-change-log-entry nil nil t nil t))) ;; When there's no more hunks, diff-hunk-next signals an error. (error nil))))) +(defun diff-show-trailing-whitespaces () + "Show trailing whitespaces in modified lines for diff-mode." + (interactive) + (let ((whitespace-style '(trailing)) + (whitespace-trailing-regexp "^[-\+!<>].*?\\([\t ]+\\)$")) + (whitespace-mode 1))) ; display trailing blanks in diff buffer + ;; provide the package (provide 'diff-mode)