diff lisp/diff-mode.el @ 88155:d7ddb3e565de

sync with trunk
author Henrik Enberg <henrik.enberg@telia.com>
date Mon, 16 Jan 2006 00:03:54 +0000
parents 37645a051842
children
line wrap: on
line diff
--- a/lisp/diff-mode.el	Sun Jan 15 23:02:10 2006 +0000
+++ b/lisp/diff-mode.el	Mon Jan 16 00:03:54 2006 +0000
@@ -1,6 +1,7 @@
 ;;; diff-mode.el --- a mode for viewing/editing context diffs
 
-;; Copyright (C) 1998, 1999, 2000, 2001, 2002  Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005 Free Software Foundation, Inc.
 
 ;; Author: Stefan Monnier <monnier@cs.yale.edu>
 ;; Keywords: convenience patch diff
@@ -19,8 +20,8 @@
 
 ;; 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., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
@@ -28,7 +29,7 @@
 ;; commands, editing and various conversions as well as jumping
 ;; to the corresponding source file.
 
-;; Inspired by Pavel Machek's patch-mode.el (<pavel@atrey.karlin.mff.cuni.cz>)
+;; Inspired by Pavel Machek's patch-mode.el (<pavel@@atrey.karlin.mff.cuni.cz>)
 ;; Some efforts were spent to have it somewhat compatible with XEmacs'
 ;; diff-mode as well as with compilation-minor-mode
 
@@ -38,32 +39,32 @@
 
 ;; Todo:
 
-;; - Improve narrowed-view support.
-;; - re-enable (conditionally) the `compile' support after improving it to use
-;;   the same code as diff-goto-source.
-;; - Support for # comments in context->unified.
-;; - Allow diff.el to use diff-mode.
-;;   This mostly means ability to jump from half-hunk to half-hunk
-;;   in context (and normal) diffs and to jump to the corresponding
-;;   (i.e. new or old) file.
+;; - Add a `delete-after-apply' so C-c C-a automatically deletes hunks.
+;;   Also allow C-c C-a to delete already-applied hunks.
+;;
+;; - Try `diff <file> <hunk>' to try and fuzzily discover the source location
+;;   of a hunk.  Show then the changes between <file> and <hunk> and make it
+;;   possible to apply them to <file>, <hunk-src>, or <hunk-dst>.
+;;   Or maybe just make it into a ".rej to diff3-markers converter".
+;;
+;; - Refine hunk on a word-by-word basis.
+;;
 ;; - Handle `diff -b' output in context->unified.
 
-;; Low priority:
-;; - Spice up the minor-mode with font-lock support.
-;; - Recognize pcl-cvs' special string for `cvs-execute-single'.
-
 ;;; Code:
 
 (eval-when-compile (require 'cl))
 
+(defvar add-log-buffer-file-name-function)
+
 
 (defgroup diff-mode ()
-  "Major mode for viewing/editing diffs"
+  "Major mode for viewing/editing diffs."
   :version "21.1"
   :group 'tools
   :group 'diff)
 
-(defcustom diff-default-read-only t
+(defcustom diff-default-read-only nil
   "If non-nil, `diff-mode' buffers default to being read-only."
   :type 'boolean
   :group 'diff-mode)
@@ -71,7 +72,8 @@
 (defcustom diff-jump-to-old-file nil
   "*Non-nil means `diff-goto-source' jumps to the old file.
 Else, it jumps to the new file."
-  :type '(boolean))
+  :type 'boolean
+  :group 'diff-mode)
 
 (defcustom diff-update-on-the-fly t
   "*Non-nil means hunk headers are kept up-to-date on-the-fly.
@@ -80,15 +82,20 @@
 either be done on the fly (but this sometimes interacts poorly with the
 undo mechanism) or whenever the file is written (can be slow
 when editing big diffs)."
-  :type '(boolean))
+  :type 'boolean
+  :group 'diff-mode)
 
 (defcustom diff-advance-after-apply-hunk t
   "*Non-nil means `diff-apply-hunk' will move to the next hunk after applying."
-  :type 'boolean)
+  :type 'boolean
+  :group 'diff-mode)
 
 
-(defvar diff-mode-hook nil
-  "Run after setting up the `diff-mode' major mode.")
+(defcustom diff-mode-hook nil
+  "Run after setting up the `diff-mode' major mode."
+  :type 'hook
+  :options '(diff-delete-empty-files diff-make-unified)
+  :group 'diff-mode)
 
 (defvar diff-outline-regexp
   "\\([*+][*+][*+] [^0-9]\\|@@ ...\\|\\*\\*\\* [0-9].\\|--- [0-9]..\\)")
@@ -109,7 +116,7 @@
     ("}" . diff-file-next)
     ("{" . diff-file-prev)
     ("\C-m" . diff-goto-source)
-    ([mouse-2] . diff-mouse-goto-source)
+    ([mouse-2] . diff-goto-source)
     ;; From XEmacs' diff-mode.
     ("W" . widen)
     ;;("." . diff-goto-source)		;display-buffer
@@ -127,7 +134,8 @@
     ("r" . diff-restrict-view)
     ("R" . diff-reverse-direction)
     ("U" . diff-context->unified)
-    ("C" . diff-unified->context))
+    ("C" . diff-unified->context)
+    ("q" . quit-window))
   "Basic keymap for `diff-mode', bound to various prefix keys.")
 
 (easy-mmode-defmap diff-mode-map
@@ -135,9 +143,11 @@
     ;; From compilation-minor-mode.
     ("\C-c\C-c" . diff-goto-source)
     ;; Misc operations.
+    ("\C-c\C-r" . diff-refine-hunk)
     ("\C-c\C-s" . diff-split-hunk)
     ("\C-c\C-a" . diff-apply-hunk)
-    ("\C-c\C-t" . diff-test-hunk))
+    ("\C-c\C-t" . diff-test-hunk)
+    ("\C-c\C-f" . next-error-follow-minor-mode))
   "Keymap for `diff-mode'.  See also `diff-mode-shared-map'.")
 
 (easy-menu-define diff-mode-menu diff-mode-map
@@ -155,7 +165,8 @@
 
 (defcustom diff-minor-mode-prefix "\C-c="
   "Prefix key for `diff-minor-mode' commands."
-  :type '(choice (string "\e") (string "C-c=") string))
+  :type '(choice (string "\e") (string "C-c=") string)
+  :group 'diff-mode)
 
 (easy-mmode-defmap diff-minor-mode-map
   `((,diff-minor-mode-prefix . ,diff-mode-shared-map))
@@ -166,99 +177,173 @@
 ;;;; font-lock support
 ;;;;
 
-(defface diff-header-face
-  '((((type tty pc) (class color) (background light))
-     (:foreground "blue1" :weight bold))
-    (((type tty pc) (class color) (background dark))
-     (:foreground "green" :weight bold))
+(defface diff-header
+  '((((class color) (min-colors 88) (background light))
+     :background "grey85")
+    (((class color) (min-colors 88) (background dark))
+     :background "grey45")
     (((class color) (background light))
-     (:background "grey85"))
+     :foreground "blue1" :weight bold)
     (((class color) (background dark))
-     (:background "grey45"))
-    (t (:weight bold)))
-  "`diff-mode' face inherited by hunk and index header faces.")
-(defvar diff-header-face 'diff-header-face)
+     :foreground "green" :weight bold)
+    (t :weight bold))
+  "`diff-mode' face inherited by hunk and index header faces."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-header-face 'face-alias 'diff-header)
+(defvar diff-header-face 'diff-header)
+
+(defface diff-file-header
+  '((((class color) (min-colors 88) (background light))
+     :background "grey70" :weight bold)
+    (((class color) (min-colors 88) (background dark))
+     :background "grey60" :weight bold)
+    (((class color) (background light))
+     :foreground "green" :weight bold)
+    (((class color) (background dark))
+     :foreground "cyan" :weight bold)
+    (t :weight bold))			; :height 1.3
+  "`diff-mode' face used to highlight file header lines."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-file-header-face 'face-alias 'diff-file-header)
+(defvar diff-file-header-face 'diff-file-header)
 
-(defface diff-file-header-face
-  '((((type tty pc) (class color) (background light))
-     (:foreground "yellow" :weight bold))
-    (((type tty pc) (class color) (background dark))
-     (:foreground "cyan" :weight bold))
-    (((class color) (background light))
-     (:background "grey70" :weight bold))
-    (((class color) (background dark))
-     (:background "grey60" :weight bold))
-    (t (:weight bold)))			; :height 1.3
-  "`diff-mode' face used to highlight file header lines.")
-(defvar diff-file-header-face 'diff-file-header-face)
+(defface diff-index
+  '((t :inherit diff-file-header))
+  "`diff-mode' face used to highlight index header lines."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-index-face 'face-alias 'diff-index)
+(defvar diff-index-face 'diff-index)
+
+(defface diff-hunk-header
+  '((t :inherit diff-header))
+  "`diff-mode' face used to highlight hunk header lines."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-hunk-header-face 'face-alias 'diff-hunk-header)
+(defvar diff-hunk-header-face 'diff-hunk-header)
 
-(defface diff-index-face
-  '((t (:inherit diff-file-header-face)))
-  "`diff-mode' face used to highlight index header lines.")
-(defvar diff-index-face 'diff-index-face)
+(defface diff-removed
+  '((t :inherit diff-changed))
+  "`diff-mode' face used to highlight removed lines."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-removed-face 'face-alias 'diff-removed)
+(defvar diff-removed-face 'diff-removed)
 
-(defface diff-hunk-header-face
-  '((t (:inherit diff-header-face)))
-  "`diff-mode' face used to highlight hunk header lines.")
-(defvar diff-hunk-header-face 'diff-hunk-header-face)
+(defface diff-added
+  '((t :inherit diff-changed))
+  "`diff-mode' face used to highlight added lines."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-added-face 'face-alias 'diff-added)
+(defvar diff-added-face 'diff-added)
 
-(defface diff-removed-face
-  '((t (:inherit diff-changed-face)))
-  "`diff-mode' face used to highlight removed lines.")
-(defvar diff-removed-face 'diff-removed-face)
+(defface diff-changed
+  '((((type tty pc) (class color) (background light))
+     :foreground "magenta" :weight bold :slant italic)
+    (((type tty pc) (class color) (background dark))
+     :foreground "yellow" :weight bold :slant italic))
+  "`diff-mode' face used to highlight changed lines."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-changed-face 'face-alias 'diff-changed)
+(defvar diff-changed-face 'diff-changed)
 
-(defface diff-added-face
-  '((t (:inherit diff-changed-face)))
-  "`diff-mode' face used to highlight added lines.")
-(defvar diff-added-face 'diff-added-face)
+(defface diff-indicator-removed
+  '((t :inherit diff-removed))
+  "`diff-mode' face used to highlight indicator of removed lines (-, <)."
+  :group 'diff-mode
+  :version "22.1")
+(defvar diff-indicator-removed-face 'diff-indicator-removed)
 
-(defface diff-changed-face
-  '((((type tty pc) (class color) (background light))
-     (:foreground "magenta" :weight bold :slant italic))
-    (((type tty pc) (class color) (background dark))
-     (:foreground "yellow" :weight bold :slant italic))
-    (t ()))
-  "`diff-mode' face used to highlight changed lines.")
-(defvar diff-changed-face 'diff-changed-face)
+(defface diff-indicator-added
+  '((t :inherit diff-added))
+  "`diff-mode' face used to highlight indicator of added lines (+, >)."
+  :group 'diff-mode
+  :version "22.1")
+(defvar diff-indicator-added-face 'diff-indicator-added)
+
+(defface diff-indicator-changed
+  '((t :inherit diff-changed))
+  "`diff-mode' face used to highlight indicator of changed lines."
+  :group 'diff-mode
+  :version "22.1")
+(defvar diff-indicator-changed-face 'diff-indicator-changed)
 
-(defface diff-function-face
-  '((t (:inherit diff-context-face)))
-  "`diff-mode' face used to highlight function names produced by \"diff -p\".")
-(defvar diff-function-face 'diff-function-face)
+(defface diff-function
+  '((t :inherit diff-context))
+  "`diff-mode' face used to highlight function names produced by \"diff -p\"."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-function-face 'face-alias 'diff-function)
+(defvar diff-function-face 'diff-function)
+
+(defface diff-context
+  '((((class color grayscale) (min-colors 88)) :inherit shadow))
+  "`diff-mode' face used to highlight context and other side-information."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-context-face 'face-alias 'diff-context)
+(defvar diff-context-face 'diff-context)
 
-(defface diff-context-face
-  '((((class color) (background light))
-     (:foreground "grey50"))
-    (((class color) (background dark))
-     (:foreground "grey70"))
-    (t ))
-  "`diff-mode' face used to highlight context and other side-information.")
-(defvar diff-context-face 'diff-context-face)
+(defface diff-nonexistent
+  '((t :inherit diff-file-header))
+  "`diff-mode' face used to highlight nonexistent files in recursive diffs."
+  :group 'diff-mode)
+;; backward-compatibility alias
+(put 'diff-nonexistent-face 'face-alias 'diff-nonexistent)
+(defvar diff-nonexistent-face 'diff-nonexistent)
 
-(defface diff-nonexistent-face
-  '((t (:inherit diff-file-header-face)))
-  "`diff-mode' face used to highlight nonexistent files in recursive diffs.")
-(defvar diff-nonexistent-face 'diff-nonexistent-face)
+(defconst diff-yank-handler '(diff-yank-function))
+(defun diff-yank-function (text)
+  ;; FIXME: the yank-handler is now called separately on each piece of text
+  ;; with a yank-handler property, so the next-single-property-change call
+  ;; below will always return nil :-(   --stef
+  (let ((mixed (next-single-property-change 0 'yank-handler text))
+	(start (point)))
+    ;; First insert the text.
+    (insert text)
+    ;; If the text does not include any diff markers and if we're not
+    ;; yanking back into a diff-mode buffer, get rid of the prefixes.
+    (unless (or mixed (derived-mode-p 'diff-mode))
+      (undo-boundary)		; Just in case the user wanted the prefixes.
+      (let ((re (save-excursion
+		  (if (re-search-backward "^[><!][ \t]" start t)
+		      (if (eq (char-after) ?!)
+			  "^[!+- ][ \t]" "^[<>][ \t]")
+		    "^[ <>!+-]"))))
+	(save-excursion
+	  (while (re-search-backward re start t)
+	    (replace-match "" t t)))))))
+
 
 (defvar diff-font-lock-keywords
-  '(("^\\(@@ -[0-9,]+ \\+[0-9,]+ @@\\)\\(.*\\)$" ;unified
-     (1 diff-hunk-header-face)
-     (2 diff-function-face))
-    ("^--- .+ ----$" . diff-hunk-header-face) ;context
-    ("^\\(\\*\\{15\\}\\)\\(.*\\)$"	;context
-     (1 diff-hunk-header-face)
-     (2 diff-function-face))
+  `(("^\\(@@ -[0-9,]+ \\+[0-9,]+ @@\\)\\(.*\\)$"          ;unified
+     (1 diff-hunk-header-face) (2 diff-function-face))
+    ("^\\(\\*\\{15\\}\\)\\(.*\\)$"                        ;context
+     (1 diff-hunk-header-face) (2 diff-function-face))
     ("^\\*\\*\\* .+ \\*\\*\\*\\*". diff-hunk-header-face) ;context
+    ("^--- .+ ----$"             . diff-hunk-header-face) ;context
+    ("^[0-9,]+[acd][0-9,]+$"     . diff-hunk-header-face) ;normal
+    ("^---$"                     . diff-hunk-header-face) ;normal
     ("^\\(---\\|\\+\\+\\+\\|\\*\\*\\*\\) \\(\\S-+\\)\\(.*[^*-]\\)?\n"
      (0 diff-header-face) (2 diff-file-header-face prepend))
-    ("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face)
-    ("^!.*\n" . diff-changed-face)	;context
-    ("^[+>].*\n" . diff-added-face)
-    ("^[-<].*\n" . diff-removed-face)
-    ("^Index: \\(.+\\).*\n" (0 diff-header-face) (1 diff-index-face prepend))
+    ("^\\([-<]\\)\\(.*\n\\)"
+     (1 diff-indicator-removed-face) (2 diff-removed-face))
+    ("^\\([+>]\\)\\(.*\n\\)"
+     (1 diff-indicator-added-face) (2 diff-added-face))
+    ("^\\(!\\)\\(.*\n\\)"
+     (1 diff-indicator-changed-face) (2 diff-changed-face))
+    ("^Index: \\(.+\\).*\n"
+     (0 diff-header-face) (1 diff-index-face prepend))
     ("^Only in .*\n" . diff-nonexistent-face)
-    ("^#.*" . font-lock-string-face)
-    ("^[^-=+*!<>].*\n" . diff-context-face)))
+    ("^\\(#\\)\\(.*\\)"
+     (1 font-lock-comment-delimiter-face)
+     (2 font-lock-comment-face))
+    ("^[^-=+*!<>#].*\n" (0 diff-context-face))))
 
 (defconst diff-font-lock-defaults
   '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil)))
@@ -271,27 +356,19 @@
     (nil "^--- \\([^\t\n]+\\)\t.*\n\\*" 1))) ; context diffs
 
 ;;;;
-;;;; Compile support
-;;;;
-
-(defvar diff-file-regexp-alist
-  '(("Index: \\(.+\\)" 1)))
-
-(defvar diff-error-regexp-alist
-  '(("@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@" nil 2)
-    ("--- \\([0-9]+\\),[0-9]+ ----" nil 1)
-    ("\\([0-9]+\\)\\(,[0-9]+\\)?[adc]\\([0-9]+\\)" nil 3)))
-
-;;;;
 ;;;; 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)))
+(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1)))
 (defvar diff-narrowed-to nil)
 
 (defun diff-end-of-hunk (&optional style)
-  (if (looking-at diff-hunk-header-re) (goto-char (match-end 0)))
+  (when (looking-at diff-hunk-header-re)
+    (unless style
+      ;; Especially important for unified (because headers are ambiguous).
+      (setq style (cdr (assq (char-after) '((?@ . unified) (?* . context))))))
+    (goto-char (match-end 0)))
   (let ((end (and (re-search-forward (case style
 				       ;; A `unified' header is ambiguous.
 				       (unified (concat "^[^-+# \\]\\|"
@@ -322,12 +399,15 @@
 
 (defun diff-end-of-file ()
   (re-search-forward "^[-+#!<>0-9@* \\]" nil t)
-  (re-search-forward "^[^-+#!<>0-9@* \\]" nil 'move)
-  (beginning-of-line))
+  (re-search-forward (concat "^[^-+#!<>0-9@* \\]\\|" diff-file-header-re)
+		     nil 'move)
+  (if (match-beginning 1)
+      (goto-char (match-beginning 1))
+    (beginning-of-line)))
 
 ;; Define diff-{hunk,file}-{prev,next}
 (easy-mmode-define-navigation
- diff-hunk diff-hunk-header-re "hunk" diff-end-of-hunk)
+ diff-hunk diff-hunk-header-re "hunk" diff-end-of-hunk diff-restrict-view)
 (easy-mmode-define-navigation
  diff-file diff-file-header-re "file" diff-end-of-hunk)
 
@@ -348,7 +428,8 @@
   (interactive)
   (diff-beginning-of-hunk)
   (let* ((start (point))
-	 (nexthunk (ignore-errors (diff-hunk-next) (point)))
+	 (nexthunk (when (re-search-forward diff-hunk-header-re nil t)
+		     (match-beginning 0)))
 	 (firsthunk (ignore-errors
 		      (goto-char start)
 		      (diff-beginning-of-file) (diff-hunk-next) (point)))
@@ -374,6 +455,7 @@
 		  (re-search-backward "^Index: " prevhunk t))))
     (when index (setq start index))
     (diff-end-of-file)
+    (if (looking-at "^\n") (forward-char 1)) ;`tla' generates such diffs.
     (kill-region start (point))))
 
 (defun diff-kill-junk ()
@@ -450,6 +532,47 @@
 	      (match-string 4 str)
 	      (substring str (match-end 6) (match-end 5))))))
 
+(defun diff-tell-file-name (old name)
+  "Tell Emacs where the find the source file of the current hunk.
+If the OLD prefix arg is passed, tell the file NAME of the old file."
+  (interactive
+   (let* ((old current-prefix-arg)
+	  (fs (diff-hunk-file-names current-prefix-arg)))
+     (unless fs (error "No file name to look for"))
+     (list old (read-file-name (format "File for %s: " (car fs))
+			       nil (diff-find-file-name old) t))))
+  (let ((fs (diff-hunk-file-names old)))
+    (unless fs (error "No file name to look for"))
+    (push (cons fs name) diff-remembered-files-alist)))
+
+(defun diff-hunk-file-names (&optional old)
+  "Give the list of file names textually mentioned for the current hunk."
+  (save-excursion
+    (unless (looking-at diff-file-header-re)
+      (or (ignore-errors (diff-beginning-of-file))
+	  (re-search-forward diff-file-header-re nil t)))
+    (let ((limit (save-excursion
+		   (condition-case ()
+		       (progn (diff-hunk-prev) (point))
+		     (error (point-min)))))
+	  (header-files
+	   (if (looking-at "[-*][-*][-*] \\(\\S-+\\)\\(\\s-.*\\)?\n[-+][-+][-+] \\(\\S-+\\)")
+	       (list (if old (match-string 1) (match-string 3))
+		     (if old (match-string 3) (match-string 1)))
+	     (forward-line 1) nil)))
+      (delq nil
+	    (append
+	     (when (and (not old)
+			(save-excursion
+			  (re-search-backward "^Index: \\(.+\\)" limit t)))
+	       (list (match-string 1)))
+	     header-files
+	     (when (re-search-backward
+		    "^diff \\(-\\S-+ +\\)*\\(\\S-+\\)\\( +\\(\\S-+\\)\\)?"
+		    nil t)
+	       (list (if old (match-string 2) (match-string 4))
+		     (if old (match-string 4) (match-string 2)))))))))
+
 (defun diff-find-file-name (&optional old)
   "Return the file corresponding to the current patch.
 Non-nil OLD means that we want the old file."
@@ -457,24 +580,7 @@
     (unless (looking-at diff-file-header-re)
       (or (ignore-errors (diff-beginning-of-file))
 	  (re-search-forward diff-file-header-re nil t)))
-    (let* ((limit (save-excursion
-		   (condition-case ()
-		       (progn (diff-hunk-prev) (point))
-		     (error (point-min)))))
-	   (header-files
-	    (if (looking-at "[-*][-*][-*] \\(\\S-+\\)\\(\\s-.*\\)?\n[-+][-+][-+] \\(\\S-+\\)")
-		(list (if old (match-string 1) (match-string 3))
-		      (if old (match-string 3) (match-string 1)))
-	      (forward-line 1) nil))
-	   (fs (append
-		(when (save-excursion
-			(re-search-backward "^Index: \\(.+\\)" limit t))
-		  (list (match-string 1)))
-		header-files
-		(when (re-search-backward "^diff \\(-\\S-+ +\\)*\\(\\S-+\\)\\( +\\(\\S-+\\)\\)?" nil t)
-		  (list (if old (match-string 2) (match-string 4))
-			(if old (match-string 4) (match-string 2))))))
-	   (fs (delq nil fs)))
+    (let ((fs (diff-hunk-file-names old)))
       (or
        ;; use any previously used preference
        (cdr (assoc fs diff-remembered-files-alist))
@@ -504,14 +610,6 @@
 	 file)))))
 
 
-(defun diff-mouse-goto-source (event)
-  "Run `diff-goto-source' for the diff at a mouse click."
-  (interactive "e")
-  (save-excursion
-    (mouse-set-point event)
-    (diff-goto-source)))
-
-
 (defun diff-ediff-patch ()
   "Call `ediff-patch-file' on the current buffer."
   (interactive)
@@ -570,7 +668,7 @@
 		      (while (progn (setq last-pt (point))
 				    (= (forward-line -1) 0))
 			(case (char-after)
-			  (?  (insert " ") (setq modif nil) (backward-char 1))
+			  (?\s (insert " ") (setq modif nil) (backward-char 1))
 			  (?+ (delete-region (point) last-pt) (setq modif t))
 			  (?- (if (not modif)
 				  (progn (forward-char 1)
@@ -595,7 +693,7 @@
 		    (let ((modif nil) (delete nil))
 		      (while (not (eobp))
 			(case (char-after)
-			  (?  (insert " ") (setq modif nil) (backward-char 1))
+			  (?\s (insert " ") (setq modif nil) (backward-char 1))
 			  (?- (setq delete t) (setq modif t))
 			  (?+ (if (not modif)
 				  (progn (forward-char 1)
@@ -653,7 +751,7 @@
 		(while (< (point) pt2)
 		  (case (char-after)
 		    ((?! ?-) (delete-char 2) (insert "-") (forward-line 1))
-		    (?\ 		;merge with the other half of the chunk
+		    (?\s		;merge with the other half of the chunk
 		     (let* ((endline2
 			     (save-excursion
 			       (goto-char pt2) (forward-line 1) (point)))
@@ -663,7 +761,7 @@
 			  (insert "+"
 				  (prog1 (buffer-substring (+ pt2 2) endline2)
 				    (delete-region pt2 endline2))))
-			 (?\ 		;FIXME: check consistency
+			 (?\s		;FIXME: check consistency
 			  (delete-region pt2 endline2)
 			  (delete-char 1)
 			  (forward-line 1))
@@ -744,7 +842,7 @@
 		       (t (when (and first last (< first last))
 			    (insert (delete-and-extract-region first last)))
 			  (setq first nil last nil)
-			  (equal ?\  c)))
+			  (equal ?\s c)))
 		(forward-line 1))))))))))
 
 (defun diff-fixup-modifs (start end)
@@ -759,9 +857,12 @@
       (goto-char end) (diff-end-of-hunk)
       (let ((plus 0) (minus 0) (space 0) (bang 0))
 	(while (and (= (forward-line -1) 0) (<= start (point)))
-	  (if (not (looking-at "\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|[-*][-*][-*] .+ [-*][-*][-*][-*]\\)$"))
+	  (if (not (looking-at
+		    (concat "@@ -[0-9,]+ \\+[0-9,]+ @@"
+			    "\\|[-*][-*][-*] [0-9,]+ [-*][-*][-*][-*]$"
+			    "\\|--- .+\n\\+\\+\\+ ")))
 	      (case (char-after)
-		(?\  (incf space))
+		(?\s (incf space))
 		(?+ (incf plus))
 		(?- (incf minus))
 		(?! (incf bang))
@@ -847,9 +948,14 @@
 	  (diff-fixup-modifs (point) (cdr diff-unhandled-changes)))))
     (setq diff-unhandled-changes nil)))
 
-;;;;
-;;;; The main function
-;;;;
+(defun diff-next-error (arg reset)
+  ;; Select a window that displays the current buffer so that point
+  ;; movements are reflected in that window.  Otherwise, the user might
+  ;; never see the hunk corresponding to the source she's jumping to.
+  (pop-to-buffer (current-buffer))
+  (if reset (goto-char (point-min)))
+  (diff-hunk-next arg)
+  (diff-goto-source))
 
 ;;;###autoload
 (define-derived-mode diff-mode fundamental-mode "Diff"
@@ -857,11 +963,11 @@
 Supports unified and context diffs as well as (to a lesser extent)
 normal diffs.
 When the buffer is read-only, the ESC prefix is not necessary.
-IF you edit the buffer manually, diff-mode will try to update the hunk
+If you edit the buffer manually, diff-mode will try to update the hunk
 headers for you on-the-fly.
 
 You can also switch between context diff and unified diff with \\[diff-context->unified],
-or vice versa with \\[diff-unified->context] and you can also revert the direction of
+or vice versa with \\[diff-unified->context] and you can also reverse the direction of
 a diff with \\[diff-reverse-direction]."
   (set (make-local-variable 'font-lock-defaults) diff-font-lock-defaults)
   (set (make-local-variable 'outline-regexp) diff-outline-regexp)
@@ -877,38 +983,25 @@
   ;;   (set (make-local-variable 'paragraph-separate) paragraph-start)
   ;;   (set (make-local-variable 'page-delimiter) "--- [^\t]+\t")
   ;; compile support
-
-  ;;;; compile support is not good enough yet.  It should be merged
-  ;;;; with diff.el's support.
-  (set (make-local-variable 'compilation-file-regexp-alist)
-       diff-file-regexp-alist)
-  (set (make-local-variable 'compilation-error-regexp-alist)
-       diff-error-regexp-alist)
-  (when (string-match "\\.rej\\'" (or buffer-file-name ""))
-    (set (make-local-variable 'compilation-current-file)
-  	 (substring buffer-file-name 0 (match-beginning 0))))
-  ;; Be careful not to change compilation-last-buffer when we're just
-  ;; doing a C-x v = (for example).
-  (if (boundp 'compilation-last-buffer)
-      (let ((compilation-last-buffer compilation-last-buffer))
-	(compilation-minor-mode 1))
-    (compilation-minor-mode 1))
-  ;; M-RET and RET should be done by diff-mode because the `compile'
-  ;; support is significantly less good.
-  (add-to-list 'minor-mode-overriding-map-alist
-  	       (cons 'compilation-minor-mode (make-sparse-keymap)))
+  (set (make-local-variable 'next-error-function) 'diff-next-error)
 
   (when (and (> (point-max) (point-min)) diff-default-read-only)
     (toggle-read-only t))
   ;; setup change hooks
   (if (not diff-update-on-the-fly)
-      (add-hook 'write-contents-hooks 'diff-write-contents-hooks)
+      (add-hook 'write-contents-functions 'diff-write-contents-hooks nil t)
     (make-local-variable 'diff-unhandled-changes)
     (add-hook 'after-change-functions 'diff-after-change-function nil t)
     (add-hook 'post-command-hook 'diff-post-command-hook nil t))
   ;; Neat trick from Dave Love to add more bindings in read-only mode:
-  (add-to-list 'minor-mode-overriding-map-alist
-  	       (cons 'buffer-read-only diff-mode-shared-map))
+  (let ((ro-bind (cons 'buffer-read-only diff-mode-shared-map)))
+    (add-to-list 'minor-mode-overriding-map-alist ro-bind)
+    ;; Turn off this little trick in case the buffer is put in view-mode.
+    (add-hook 'view-mode-hook
+	      `(lambda ()
+		 (setq minor-mode-overriding-map-alist
+		       (delq ',ro-bind minor-mode-overriding-map-alist)))
+	      nil t))
   ;; add-log support
   (set (make-local-variable 'add-log-current-defun-function)
        'diff-current-defun)
@@ -919,15 +1012,38 @@
 (define-minor-mode diff-minor-mode
   "Minor mode for viewing/editing context diffs.
 \\{diff-minor-mode-map}"
-  nil " Diff" nil
+  :group 'diff-mode :lighter " Diff"
   ;; FIXME: setup font-lock
   ;; setup change hooks
   (if (not diff-update-on-the-fly)
-      (add-hook 'write-contents-hooks 'diff-write-contents-hooks)
+      (add-hook 'write-contents-functions 'diff-write-contents-hooks nil t)
     (make-local-variable 'diff-unhandled-changes)
     (add-hook 'after-change-functions 'diff-after-change-function nil t)
     (add-hook 'post-command-hook 'diff-post-command-hook nil t)))
 
+;;; Handy hook functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun diff-delete-if-empty ()
+  ;; An empty diff file means there's no more diffs to integrate, so we
+  ;; can just remove the file altogether.  Very handy for .rej files if we
+  ;; remove hunks as we apply them.
+  (when (and buffer-file-name
+	     (eq 0 (nth 7 (file-attributes buffer-file-name))))
+    (delete-file buffer-file-name)))
+
+(defun diff-delete-empty-files ()
+  "Arrange for empty diff files to be removed."
+  (add-hook 'after-save-hook 'diff-delete-if-empty nil t))
+
+(defun diff-make-unified ()
+  "Turn context diffs into unified diffs if applicable."
+  (if (save-excursion
+	(goto-char (point-min))
+	(and (looking-at diff-hunk-header-re) (eq (char-after) ?*)))
+      (let ((mod (buffer-modified-p)))
+	(unwind-protect
+	    (diff-context->unified (point-min) (point-max))
+	  (restore-buffer-modified-p mod)))))
 
 ;;;
 ;;; Misc operations that have proved useful at some point.
@@ -1091,12 +1207,17 @@
 	(goto-line (string-to-number line))
 	(let* ((orig-pos (point))
 	       (switched nil)
+	       ;; FIXME: Check for case where both OLD and NEW are found.
 	       (pos (or (diff-find-text (car old))
 			(progn (setq switched t) (diff-find-text (car new)))
 			(progn (setq switched nil)
-			       (diff-find-approx-text (car old)))
+			       (condition-case nil
+				   (diff-find-approx-text (car old))
+				 (invalid-regexp nil)))	;Regex too big.
 			(progn (setq switched t)
-			       (diff-find-approx-text (car new)))
+			       (condition-case nil
+				   (diff-find-approx-text (car new))
+				 (invalid-regexp nil)))	;Regex too big.
 			(progn (setq switched nil) nil))))
 	  (nconc
 	   (list buf)
@@ -1127,7 +1248,8 @@
 With a prefix argument, REVERSE the hunk."
   (interactive "P")
   (destructuring-bind (buf line-offset pos old new &optional switched)
-      (diff-find-source-location nil reverse)
+      ;; If REVERSE go to the new file, otherwise go to the old.
+      (diff-find-source-location (not reverse) reverse)
     (cond
      ((null line-offset)
       (error "Can't find the text to patch"))
@@ -1159,21 +1281,25 @@
 With a prefix argument, try to REVERSE the hunk."
   (interactive "P")
   (destructuring-bind (buf line-offset pos src dst &optional switched)
-      (diff-find-source-location nil reverse)
+      ;; If REVERSE go to the new file, otherwise go to the old.
+      (diff-find-source-location (not reverse) reverse)
     (set-window-point (display-buffer buf) (+ (car pos) (cdr src)))
     (diff-hunk-status-msg line-offset (diff-xor reverse switched) t)))
 
 
-(defun diff-goto-source (&optional other-file)
+(defalias 'diff-mouse-goto-source 'diff-goto-source)
+
+(defun diff-goto-source (&optional other-file event)
   "Jump to the corresponding source line.
 `diff-jump-to-old-file' (or its opposite if the OTHER-FILE prefix arg
 is given) determines whether to jump to the old or the new file.
 If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument])
   then `diff-jump-to-old-file' is also set, for the next invocations."
-  (interactive "P")
+  (interactive (list current-prefix-arg last-input-event))
   ;; When pointing at a removal line, we probably want to jump to
   ;; the old location, and else to the new (i.e. as if reverting).
   ;; This is a convenient detail when using smerge-diff.
+  (if event (posn-set-point (event-end event)))
   (let ((rev (not (save-excursion (beginning-of-line) (looking-at "[-<]")))))
     (destructuring-bind (buf line-offset pos src dst &optional switched)
 	(diff-find-source-location other-file rev)
@@ -1185,9 +1311,12 @@
 (defun diff-current-defun ()
   "Find the name of function at point.
 For use in `add-log-current-defun-function'."
-  (destructuring-bind (buf line-offset pos src dst &optional switched)
-      (diff-find-source-location)
-    (save-excursion
+  (save-excursion
+    (when (looking-at diff-hunk-header-re)
+      (forward-line 1)
+      (re-search-forward "^[^ ]" nil t))
+    (destructuring-bind (buf line-offset pos src dst &optional switched)
+	(diff-find-source-location)
       (beginning-of-line)
       (or (when (memq (char-after) '(?< ?-))
 	    ;; Cursor is pointing at removed text.  This could be a removed
@@ -1204,6 +1333,49 @@
 	    (goto-char (+ (car pos) (cdr src)))
 	    (add-log-current-defun))))))
 
+(defun diff-refine-hunk ()
+  "Refine the current hunk by ignoring space differences."
+  (interactive)
+  (let* ((char-offset (- (point) (progn (diff-beginning-of-hunk) (point))))
+	 (opts (case (char-after) (?@ "-bu") (?* "-bc") (t "-b")))
+	 (line-nb (and (or (looking-at "[^0-9]+\\([0-9]+\\)")
+			   (error "Can't find line number"))
+		       (string-to-number (match-string 1))))
+	 (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)
+	 old new)
+    (unwind-protect
+	(save-excursion
+	  (setq old (diff-hunk-text hunk nil char-offset))
+	  (setq new (diff-hunk-text hunk t char-offset))
+	  (write-region (concat lead (car old)) nil file1 nil 'nomessage)
+	  (write-region (concat lead (car new)) nil file2 nil 'nomessage)
+	  (with-temp-buffer
+	    (let ((status
+		   (call-process diff-command nil t nil
+				 opts file1 file2)))
+	      (case status
+		(0 nil)			;Nothing to reformat.
+		(1 (goto-char (point-min))
+		   ;; Remove the file-header.
+		   (when (re-search-forward diff-hunk-header-re nil t)
+		     (delete-region (point-min) (match-beginning 0))))
+		(t (goto-char (point-max))
+		   (unless (bolp) (insert "\n"))
+		   (insert hunk)))
+	      (setq hunk (buffer-string))
+	      (unless (memq status '(0 1))
+		(error "Diff returned: %s" status)))))
+      ;; Whatever happens, put back some equivalent text: either the new
+      ;; one or the original one in case some error happened.
+      (insert hunk)
+      (delete-file file1)
+      (delete-file file2))))
+
 ;; provide the package
 (provide 'diff-mode)
 
@@ -1242,4 +1414,5 @@
 ;; use `combine-after-change-calls' to minimize the slowdown of font-lock.
 ;;
 
+;; arch-tag: 2571d7ff-bc28-4cf9-8585-42e21890be66
 ;;; diff-mode.el ends here