]> code.delx.au - gnu-emacs/blobdiff - lisp/diff-mode.el
(hi-lock-line-face-buffer, hi-lock-face-buffer)
[gnu-emacs] / lisp / diff-mode.el
index 88270b2b3f426af98cbe6dca6073ec97ba00fb47..d61f7689ae52e9c121f26562db0dffa20e6f1745 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.
 
 ;;; 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
@@ -229,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))
@@ -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,7 +569,10 @@ If the prefix ARG is given, restrict the view to the current file instead."
       (diff-end-of-hunk)
       (kill-region start (point)))))
 
-(defconst diff-file-junk-re "diff \\|index ") ; "index " is output by git-diff.
+;; "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.
@@ -672,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))))
@@ -745,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))
@@ -832,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))
@@ -845,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
@@ -879,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)
@@ -959,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))
@@ -1050,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]\\|#")
@@ -1119,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))
@@ -1171,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
@@ -1232,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)
@@ -1344,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
@@ -1357,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)
@@ -1426,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)
@@ -1542,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")))
@@ -1696,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
@@ -1752,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))
@@ -1844,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)