X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/02cbe062bee38a6705bafb1699d77e3c44cfafcf..24deb97dd68cd102ba6698b2bd1d5a3dd70998ec:/lisp/diff-mode.el diff --git a/lisp/diff-mode.el b/lisp/diff-mode.el index b8b6a009e2..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: @@ -39,6 +37,9 @@ ;; Todo: +;; - Improve `diff-add-change-log-entries-other-window', +;; it is very simplistic now. +;; ;; - Add a `delete-after-apply' so C-c C-a automatically deletes hunks. ;; Also allow C-c C-a to delete already-applied hunks. ;; @@ -90,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 @@ -149,6 +145,8 @@ when editing big diffs)." `(("\e" . ,diff-mode-shared-map) ;; From compilation-minor-mode. ("\C-c\C-c" . diff-goto-source) + ;; By analogy with the global C-x 4 a binding. + ("\C-x4A" . diff-add-change-log-entries-other-window) ;; Misc operations. ("\C-c\C-a" . diff-apply-hunk) ("\C-c\C-e" . diff-ediff-patch) @@ -167,26 +165,45 @@ when editing big diffs)." (easy-menu-define diff-mode-menu diff-mode-map "Menu for `diff-mode'." '("Diff" - ["Jump to Source" diff-goto-source t] - ["Apply hunk" diff-apply-hunk t] - ["Test applying hunk" diff-test-hunk t] - ["Apply diff with Ediff" diff-ediff-patch t] + ["Jump to Source" diff-goto-source + :help "Jump to the corresponding source line"] + ["Apply hunk" diff-apply-hunk + :help "Apply the current hunk to the source file and go to the next"] + ["Test applying hunk" diff-test-hunk + :help "See whether it's possible to apply the current hunk"] + ["Apply diff with Ediff" diff-ediff-patch + :help "Call `ediff-patch-file' on the current buffer"] + ["Create Change Log entries" diff-add-change-log-entries-other-window + :help "Create ChangeLog entries for the changes in the diff buffer"] "-----" - ["Reverse direction" diff-reverse-direction t] - ["Context -> Unified" diff-context->unified t] - ["Unified -> Context" diff-unified->context t] + ["Reverse direction" diff-reverse-direction + :help "Reverse the direction of the diffs"] + ["Context -> Unified" diff-context->unified + :help "Convert context diffs to unified diffs"] + ["Unified -> Context" diff-unified->context + :help "Convert unified diffs to context diffs"] ;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)] "-----" - ["Split hunk" diff-split-hunk (diff-splittable-p)] - ["Ignore whitespace changes" diff-ignore-whitespace-hunk t] - ["Highlight fine changes" diff-refine-hunk t] - ["Kill current hunk" diff-hunk-kill t] - ["Kill current file's hunks" diff-file-kill t] + ["Split hunk" diff-split-hunk + :active (diff-splittable-p) + :help "Split the current (unified diff) hunk at point into two hunks"] + ["Ignore whitespace changes" diff-ignore-whitespace-hunk + :help "Re-diff the current hunk, ignoring whitespace differences"] + ["Highlight fine changes" diff-refine-hunk + :help "Highlight changes of hunk at point at a finer granularity"] + ["Kill current hunk" diff-hunk-kill + :help "Kill current hunk"] + ["Kill current file's hunks" diff-file-kill + :help "Kill all current file's hunks"] "-----" - ["Previous Hunk" diff-hunk-prev t] - ["Next Hunk" diff-hunk-next t] - ["Previous File" diff-file-prev t] - ["Next File" diff-file-next t] + ["Previous Hunk" diff-hunk-prev + :help "Go to the previous count'th hunk"] + ["Next Hunk" diff-hunk-next + :help "Go to the next count'th hunk"] + ["Previous File" diff-file-prev + :help "Go to the previous count'th file"] + ["Next File" diff-file-next + :help "Go to the next count'th file"] )) (defcustom diff-minor-mode-prefix "\C-c=" @@ -198,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 @@ -205,7 +229,7 @@ when editing big diffs)." (defface diff-header '((((class color) (min-colors 88) (background light)) - :background "grey85") + :background "grey80") (((class color) (min-colors 88) (background dark)) :background "grey45") (((class color) (background light)) @@ -345,14 +369,18 @@ when editing big diffs)." (while (re-search-backward re start t) (replace-match "" t t))))))) +(defconst diff-hunk-header-re-unified + "^@@ -\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? \\+\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? @@") +(defconst diff-context-mid-hunk-header-re + "--- \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? ----$") (defvar diff-font-lock-keywords - `(("^\\(@@ -[0-9,]+ \\+[0-9,]+ @@\\)\\(.*\\)$" ;unified - (1 diff-hunk-header-face) (2 diff-function-face)) + `((,(concat "\\(" diff-hunk-header-re-unified "\\)\\(.*\\)$") + (1 diff-hunk-header-face) (6 diff-function-face)) ("^\\(\\*\\{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 @@ -388,8 +416,15 @@ when editing big diffs)." ;;;; Movement ;;;; -(defconst diff-hunk-header-re "^\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|\\*\\{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))) +(defvar diff-valid-unified-empty-line t + "If non-nil, empty lines are valid in unified diffs. +Some versions of diff replace all-blank context lines in unified format with +empty lines. This makes the format less robust, but is tolerated. +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]\\).+\n" (substring diff-hunk-header-re 1))) (defvar diff-narrowed-to nil) (defun diff-hunk-style (&optional style) @@ -398,18 +433,50 @@ when editing big diffs)." (goto-char (match-end 0))) style) -(defun diff-end-of-hunk (&optional style) - ;; Especially important for unified (because headers are ambiguous). - (setq style (diff-hunk-style style)) - (let ((end (and (re-search-forward (case style - ;; A `unified' header is ambiguous. - (unified (concat "^[^-+# \\]\\|" - diff-file-header-re)) - (context "^[^-+#! \\]") - (normal "^[^<>#\\]") - (t "^[^-+#!<> \\]")) - nil t) - (match-beginning 0)))) +(defun diff-end-of-hunk (&optional style donttrustheader) + (let (end) + (when (looking-at diff-hunk-header-re) + ;; Especially important for unified (because headers are ambiguous). + (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 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 + (case style + (unified (concat (if diff-valid-unified-empty-line + "^[^-+# \\\n]\\|" "^[^-+# \\]\\|") + ;; A `unified' header is ambiguous. + diff-file-header-re)) + (context "^[^-+#! \\]") + (normal "^[^<>#\\]") + (t "^[^-+#!<> \\]")) + nil t) + (match-beginning 0))) + (when diff-valid-unified-empty-line + ;; While empty lines may be valid inside hunks, they are also likely + ;; to be unrelated to the hunk. + (goto-char (or end (point-max))) + (while (eq ?\n (char-before (1- (point)))) + (forward-char -1) + (setq end (point))))) ;; The return value is used by easy-mmode-define-navigation. (goto-char (or end (point-max))))) @@ -463,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 @@ -502,11 +569,22 @@ If the prefix ARG is given, restrict the view to the current file instead." (diff-end-of-hunk) (kill-region start (point))))) +;; "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. This is like `diff-beginning-of-file' except it tries to skip back over leading data such as \"Index: ...\" and such." - (let* ((start (point)) + (let* ((orig (point)) + ;; Skip forward over what might be "leading junk" so as to get + ;; closer to the actual diff. + (_ (progn (beginning-of-line) + (while (looking-at diff-file-junk-re) + (forward-line 1)))) + (start (point)) (prevfile (condition-case err (save-excursion (diff-beginning-of-file) (point)) (error err))) @@ -520,6 +598,7 @@ data such as \"Index: ...\" and such." (if (numberp prevfile) (goto-char prevfile)) (diff-hunk-prev) (point)))) (previndex (save-excursion + (forward-line 1) ;In case we're looking at "Index:". (re-search-backward "^Index: " prevhunk t)))) ;; If we're in the junk, we should use nextfile instead of prevfile. (if (and (numberp nextfile) @@ -529,20 +608,28 @@ data such as \"Index: ...\" and such." (if (and previndex (numberp prevfile) (< previndex prevfile)) (setq prevfile previndex)) (if (and (numberp prevfile) (<= prevfile start)) - (goto-char prevfile) + (progn + (goto-char prevfile) + ;; Now skip backward over the leading junk we may have before the + ;; diff itself. + (while (save-excursion + (and (zerop (forward-line -1)) + (looking-at diff-file-junk-re))) + (forward-line -1))) ;; File starts *after* the starting point: we really weren't in ;; a file diff but elsewhere. - (goto-char start) + (goto-char orig) (signal (car err) (cdr err))))) (defun diff-file-kill () "Kill current file's hunks." (interactive) - (diff-beginning-of-file-and-junk) - (let* ((start (point)) + (let ((orig (point)) + (start (progn (diff-beginning-of-file-and-junk) (point))) (inhibit-read-only t)) (diff-end-of-file) (if (looking-at "^\n") (forward-char 1)) ;`tla' generates such diffs. + (if (> orig (point)) (error "Not inside a file diff")) (kill-region start (point)))) (defun diff-kill-junk () @@ -580,11 +667,11 @@ data such as \"Index: ...\" and such." (beginning-of-line) (let ((pos (point)) (start (progn (diff-beginning-of-hunk) (point)))) - (unless (looking-at "@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@") + (unless (looking-at diff-hunk-header-re-unified) (error "diff-split-hunk only works on unified context diffs")) (forward-line 1) (let* ((start1 (string-to-number (match-string 1))) - (start2 (string-to-number (match-string 2))) + (start2 (string-to-number (match-string 3))) (newstart1 (+ start1 (diff-count-matches "^[- \t]" (point) pos))) (newstart2 (+ start2 (diff-count-matches "^[+ \t]" (point) pos))) (inhibit-read-only t)) @@ -601,6 +688,7 @@ data such as \"Index: ...\" and such." ;;;; (defvar diff-remembered-files-alist nil) +(defvar diff-remembered-defdir nil) (defun diff-filename-drop-dir (file) (when (string-match "/" file) (substring file (match-end 0)))) @@ -674,6 +762,10 @@ Non-nil OLD means that we want the old file. Non-nil BATCH means to prefer returning an incorrect answer than to prompt the user. PREFIX is only used internally: don't use it." + (unless (equal diff-remembered-defdir default-directory) + ;; Flush diff-remembered-files-alist if the default-directory is changed. + (set (make-local-variable 'diff-remembered-defdir) default-directory) + (set (make-local-variable 'diff-remembered-files-alist) nil)) (save-excursion (unless (looking-at diff-file-header-re) (or (ignore-errors (diff-beginning-of-file)) @@ -747,7 +839,10 @@ else cover the whole buffer." (inhibit-read-only t)) (save-excursion (goto-char start) - (while (and (re-search-forward "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|@@ -\\([0-9]+\\),\\([0-9]+\\) \\+\\([0-9]+\\),\\([0-9]+\\) @@.*\\)$" nil t) + (while (and (re-search-forward + (concat "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|" + diff-hunk-header-re-unified ".*\\)$") + nil t) (< (point) end)) (combine-after-change-calls (if (match-beginning 2) @@ -758,9 +853,9 @@ else cover the whole buffer." (replace-match "***" t t nil 2)) ;; we matched a hunk header (let ((line1 (match-string 4)) - (lines1 (match-string 5)) + (lines1 (or (match-string 5) "1")) (line2 (match-string 6)) - (lines2 (match-string 7)) + (lines2 (or (match-string 7) "1")) ;; Variables to use the special undo function. (old-undo buffer-undo-list) (old-end (marker-position end)) @@ -770,10 +865,13 @@ else cover the whole buffer." (concat "***************\n*** " line1 "," (number-to-string (+ (string-to-number line1) (string-to-number lines1) - -1)) " ****")) - (forward-line 1) + -1)) + " ****")) (save-restriction - (narrow-to-region (point) + (narrow-to-region (line-beginning-position 2) + ;; Call diff-end-of-hunk from just before + ;; the hunk header so it can use the hunk + ;; header info. (progn (diff-end-of-hunk 'unified) (point))) (let ((hunk (buffer-string))) (goto-char (point-min)) @@ -795,13 +893,16 @@ else cover the whole buffer." (?\\ (when (save-excursion (forward-line -1) (= (char-after) ?+)) (delete-region (point) last-pt) (setq modif t))) + ;; diff-valid-unified-empty-line. + (?\n (insert " ") (setq modif nil) (backward-char 2)) (t (setq modif nil)))))) (goto-char (point-max)) (save-excursion (insert "--- " line2 "," (number-to-string (+ (string-to-number line2) (string-to-number lines2) - -1)) " ----\n" hunk)) + -1)) + " ----\n" hunk)) ;;(goto-char (point-min)) (forward-line 1) (if (not (save-excursion (re-search-forward "^+" nil t))) @@ -828,6 +929,9 @@ else cover the whole buffer." (?\\ (when (save-excursion (forward-line 1) (not (eobp))) (setq delete t) (setq modif t))) + ;; diff-valid-unified-empty-line. + (?\n (insert " ") (setq modif nil) (backward-char 2) + (setq reversible nil)) (t (setq modif nil))) (let ((last-pt (point))) (forward-line 1) @@ -877,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)) @@ -968,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]\\|#") @@ -998,7 +1105,8 @@ else cover the whole buffer." (t (when (and first last (< first last)) (insert (delete-and-extract-region first last))) (setq first nil last nil) - (equal ?\s c))) + (memq c (if diff-valid-unified-empty-line + '(?\s ?\n) '(?\s))))) (forward-line 1)))))))))) (defun diff-fixup-modifs (start end) @@ -1010,11 +1118,11 @@ else cover the whole buffer." (list (point-min) (point-max)))) (let ((inhibit-read-only t)) (save-excursion - (goto-char end) (diff-end-of-hunk) + (goto-char end) (diff-end-of-hunk nil 'donttrustheader) (let ((plus 0) (minus 0) (space 0) (bang 0)) (while (and (= (forward-line -1) 0) (<= start (point))) (if (not (looking-at - (concat "@@ -[0-9,]+ \\+[0-9,]+ @@" + (concat diff-hunk-header-re-unified "\\|[-*][-*][-*] [0-9,]+ [-*][-*][-*][-*]$" "\\|--- .+\n\\+\\+\\+ "))) (case (char-after) @@ -1025,14 +1133,18 @@ else cover the whole buffer." ((?\\ ?#) nil) (t (setq space 0 plus 0 minus 0 bang 0))) (cond - ((looking-at "@@ -[0-9]+,\\([0-9]*\\) \\+[0-9]+,\\([0-9]*\\) @@.*$") - (let* ((old1 (match-string 1)) - (old2 (match-string 2)) + ((looking-at diff-hunk-header-re-unified) + (let* ((old1 (match-string 2)) + (old2 (match-string 4)) (new1 (number-to-string (+ space minus))) (new2 (number-to-string (+ space plus)))) - (unless (string= new2 old2) (replace-match new2 t t nil 2)) - (unless (string= new1 old1) (replace-match new1 t t nil 1)))) - ((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$") + (if old2 + (unless (string= new2 old2) (replace-match new2 t t nil 4)) + (goto-char (match-end 4)) (insert "," new2)) + (if old1 + (unless (string= new1 old1) (replace-match new1 t t nil 2)) + (goto-char (match-end 2)) (insert "," new1)))) + ((looking-at diff-context-mid-hunk-header-re) (when (> (+ space bang plus) 0) (let* ((old1 (match-string 1)) (old2 (match-string 2)) @@ -1084,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 - (diff-end-of-hunk) - (>= (point) (cdr diff-unhandled-changes))) + (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) + ;; 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 @@ -1145,6 +1261,11 @@ a diff with \\[diff-reverse-direction]. ;; compile support (set (make-local-variable 'next-error-function) 'diff-next-error) + (set (make-local-variable 'beginning-of-defun-function) + 'diff-beginning-of-file-and-junk) + (set (make-local-variable 'end-of-defun-function) + 'diff-end-of-file) + (setq buffer-read-only diff-default-read-only) ;; setup change hooks (if (not diff-update-on-the-fly) @@ -1214,9 +1335,8 @@ a diff with \\[diff-reverse-direction]. Only works for unified diffs." (interactive) (while - (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@" - nil t) - (equal (match-string 1) (match-string 2))))) + (and (re-search-forward diff-hunk-header-re-unified nil t) + (equal (match-string 2) (match-string 4))))) (defun diff-sanity-check-context-hunk-half (lines) (let ((count lines)) @@ -1254,26 +1374,25 @@ Only works for unified diffs." (error "Unrecognized context diff first hunk header format") (forward-line 2) (diff-sanity-check-context-hunk-half - (if (match-string 2) + (if (match-end 2) (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 - (if (match-string 2) + (if (match-end 2) (1+ (- (string-to-number (match-string 2)) (string-to-number (match-string 1)))) 1))))) ;; A unified diff. ((eq (char-after) ?@) - (if (not (looking-at - "@@ -[0-9]+\\(?:,\\([0-9]+\\)\\)? \\+[0-9]+\\(?:,\\([0-9]+\\)\\)? @@")) + (if (not (looking-at diff-hunk-header-re-unified)) (error "Unrecognized unified diff hunk header format") - (let ((before (if (match-string 1) (string-to-number (match-string 1)) 1)) - (after (if (match-string 2) (string-to-number (match-string 2)) 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) @@ -1291,12 +1410,17 @@ Only works for unified diffs." (?+ (decf after) t) (t (cond + ((and diff-valid-unified-empty-line + ;; Not just (eolp) so we don't infloop at eob. + (eq (char-after) ?\n) + (> before 0) (> after 0)) + (decf before) (decf after) t) ((and (zerop before) (zerop after)) nil) ((or (< before 0) (< after 0)) (error (if (or (zerop before) (zerop after)) "End of hunk ambiguously marked" "Hunk seriously messed up"))) - ((not (y-or-n-p "Try to auto-fix whitespace loss and word-wrap damage? ")) + ((not (y-or-n-p (concat "Try to auto-fix " (if (eolp) "whitespace loss" "word-wrap damage") "? "))) (error "Abort!")) ((eolp) (insert " ") (forward-line -1) t) (t (insert " ") @@ -1334,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) @@ -1450,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"))) @@ -1604,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 @@ -1620,13 +1745,13 @@ For use in `add-log-current-defun-function'." (line-nb (and (or (looking-at "[^0-9]+\\([0-9]+\\)") (error "Can't find line number")) (string-to-number (match-string 1)))) + (inhibit-read-only t) (hunk (delete-and-extract-region (point) (save-excursion (diff-end-of-hunk) (point)))) (lead (make-string (1- line-nb) ?\n)) ;Line nums start at 1. (file1 (make-temp-file "diff1")) (file2 (make-temp-file "diff2")) (coding-system-for-read buffer-file-coding-system) - (inhibit-read-only t) old new) (unwind-protect (save-excursion @@ -1660,9 +1785,9 @@ For use in `add-log-current-defun-function'." (defface diff-refine-change '((((class color) (min-colors 88) (background light)) - :background "grey90") + :background "grey85") (((class color) (min-colors 88) (background dark)) - :background "grey40") + :background "grey60") (((class color) (background light)) :background "yellow") (((class color) (background dark)) @@ -1725,6 +1850,44 @@ For use in `add-log-current-defun-function'." props 'diff-refine-preproc)))))))) +(defun diff-add-change-log-entries-other-window () + "Iterate through the current diff and create ChangeLog entries. +I.e. like `add-change-log-entry-other-window' but applied to all hunks." + (interactive) + ;; XXX: Currently add-change-log-entry-other-window is only called + ;; once per hunk. Some hunks have multiple changes, it would be + ;; good to call it for each change. + (save-excursion + (goto-char (point-min)) + (let ((orig-buffer (current-buffer))) + (condition-case nil + ;; Call add-change-log-entry-other-window for each hunk in + ;; the diff buffer. + (while (progn + (diff-hunk-next) + ;; Move to where the changes are, + ;; `add-change-log-entry-other-window' works better in + ;; that case. + (re-search-forward + (concat "\n[!+-<>]" + ;; If the hunk is a context hunk with an empty first + ;; half, recognize the "--- NNN,MMM ----" line + "\\(-- [0-9]+\\(,[0-9]+\\)? ----\n" + ;; and skip to the next non-context line. + "\\( .*\n\\)*[+]\\)?") + nil t)) + (save-excursion + (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)