changeset 41526:f0fb05d40941

(diff-end-of-hunk): Watch out for ambiguities. (diff-hunk-kill): Simplify. (diff-post-command-hook): Only apply to a single hunk. (diff-hunk-text): Make `char-offset' non-optional. (diff-find-text): Return a cons cell. (diff-find-approx-text): New fun. (diff-find-source-location): Use it. (diff-apply-hunk, diff-test-hunk, diff-goto-source): Adapt to new retval of diff-find-source-location.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Mon, 26 Nov 2001 00:20:41 +0000
parents e484ddbc92e1
children b5d7677d0f20
files lisp/diff-mode.el
diffstat 1 files changed, 92 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/diff-mode.el	Mon Nov 26 00:08:20 2001 +0000
+++ b/lisp/diff-mode.el	Mon Nov 26 00:20:41 2001 +0000
@@ -47,7 +47,6 @@
 ;; - re-enable (conditionally) the `compile' support after improving it to use
 ;;   the same code as diff-goto-source.
 ;; - Support for # comments in context->unified.
-;; - Do a fuzzy search in diff-goto-source.
 ;; - 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
@@ -77,7 +76,6 @@
 (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."
-  :group 'diff-mode
   :type '(boolean))
 
 (defcustom diff-update-on-the-fly t
@@ -87,12 +85,10 @@
 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)."
-  :group 'diff-mode
   :type '(boolean))
 
 (defcustom diff-advance-after-apply-hunk t
   "*Non-nil means `diff-apply-hunk' will move to the next hunk after applying."
-  :group 'diff-mode
   :type 'boolean)
 
 
@@ -164,7 +160,6 @@
 
 (defcustom diff-minor-mode-prefix "\C-c="
   "Prefix key for `diff-minor-mode' commands."
-  :group 'diff-mode
   :type '(choice (string "\e") (string "C-c=") string))
 
 (easy-mmode-defmap diff-minor-mode-map
@@ -186,8 +181,7 @@
     (((class color) (background dark))
      (:background "grey45"))
     (t (:bold t)))
-  "`diff-mode' face inherited by hunk and index header faces."
-  :group 'diff-mode)
+  "`diff-mode' face inherited by hunk and index header faces.")
 (defvar diff-header-face 'diff-header-face)
 
 (defface diff-file-header-face
@@ -200,32 +194,27 @@
     (((class color) (background dark))
      (:background "grey60" :bold t))
     (t (:bold t)))			; :height 1.3
-  "`diff-mode' face used to highlight file header lines."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight file header lines.")
 (defvar diff-file-header-face 'diff-file-header-face)
 
 (defface diff-index-face
   '((t (:inherit diff-file-header-face)))
-  "`diff-mode' face used to highlight index header lines."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight index header lines.")
 (defvar diff-index-face 'diff-index-face)
 
 (defface diff-hunk-header-face
   '((t (:inherit diff-header-face)))
-  "`diff-mode' face used to highlight hunk header lines."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight hunk header lines.")
 (defvar diff-hunk-header-face 'diff-hunk-header-face)
 
 (defface diff-removed-face
   '((t (:inherit diff-changed-face)))
-  "`diff-mode' face used to highlight removed lines."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight removed lines.")
 (defvar diff-removed-face 'diff-removed-face)
 
 (defface diff-added-face
   '((t (:inherit diff-changed-face)))
-  "`diff-mode' face used to highlight added lines."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight added lines.")
 (defvar diff-added-face 'diff-added-face)
 
 (defface diff-changed-face
@@ -234,14 +223,12 @@
     (((type tty pc) (class color) (background dark))
      (:foreground "yellow" :bold t :italic t))
     (t ()))
-  "`diff-mode' face used to highlight changed lines."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight changed lines.")
 (defvar diff-changed-face 'diff-changed-face)
 
 (defface diff-function-face
   '((t (:inherit diff-context-face)))
-  "`diff-mode' face used to highlight function names produced by \"diff -p\"."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight function names produced by \"diff -p\".")
 (defvar diff-function-face 'diff-function-face)
 
 (defface diff-context-face
@@ -250,14 +237,12 @@
     (((class color) (background dark))
      (:foreground "grey70"))
     (t ))
-  "`diff-mode' face used to highlight context and other side-information."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight context and other side-information.")
 (defvar diff-context-face 'diff-context-face)
 
 (defface diff-nonexistent-face
   '((t (:inherit diff-file-header-face)))
-  "`diff-mode' face used to highlight nonexistent files in recursive diffs."
-  :group 'diff-mode)
+  "`diff-mode' face used to highlight nonexistent files in recursive diffs.")
 (defvar diff-nonexistent-face 'diff-nonexistent-face)
 
 (defvar diff-font-lock-keywords
@@ -313,7 +298,9 @@
 (defun diff-end-of-hunk (&optional style)
   (if (looking-at diff-hunk-header-re) (goto-char (match-end 0)))
   (let ((end (and (re-search-forward (case style
-				       (unified "^[^-+# \\]")
+				       ;; A `unified' header is ambiguous.
+				       (unified (concat "^[^-+# \\]\\|"
+							diff-file-header-re))
 				       (context "^[^-+#! \\]")
 				       (normal "^[^<>#\\]")
 				       (t "^[^-+#!<> \\]"))
@@ -365,20 +352,17 @@
   "Kill current hunk."
   (interactive)
   (diff-beginning-of-hunk)
-  (let ((start (point))
-	(firsthunk (save-excursion
-		     (ignore-errors
-		       (diff-beginning-of-file) (diff-hunk-next) (point))))
-	(nexthunk  (save-excursion
-		     (ignore-errors
-		       (diff-hunk-next) (point))))
-	(nextfile (save-excursion
-		    (ignore-errors
-		      (diff-file-next) (point)))))
+  (let* ((start (point))
+	 (nexthunk (ignore-errors (diff-hunk-next) (point)))
+	 (firsthunk (ignore-errors
+		      (goto-char start)
+		      (diff-beginning-of-file) (diff-hunk-next) (point)))
+	 (nextfile (ignore-errors (diff-file-next) (point))))
+    (goto-char start)
     (if (and firsthunk (= firsthunk start)
 	     (or (null nexthunk)
 		 (and nextfile (> nexthunk nextfile))))
-	;; we're the only hunk for this file, so kill the file
+	;; It's the only hunk for this file, so kill the file.
 	(diff-file-kill)
       (diff-end-of-hunk)
       (kill-region start (point)))))
@@ -849,15 +833,24 @@
     (ignore-errors
       (save-excursion
 	(goto-char (car diff-unhandled-changes))
-	(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, 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))
+	(diff-beginning-of-hunk)
+	(when (save-excursion
+		(diff-end-of-hunk)
+		(> (point) (cdr diff-unhandled-changes)))
+	  (diff-fixup-modifs (point) (cdr diff-unhandled-changes)))))
     (setq diff-unhandled-changes nil)))
 
 ;;;; 
@@ -941,12 +934,11 @@
 			      nil t)
 	   (equal (match-string 1) (match-string 2)))))
 
-(defun diff-hunk-text (hunk destp &optional char-offset)
-  "Return the literal source text from HUNK.
-if DESTP is nil return the source, otherwise the destination text.
-If CHAR-OFFSET is non-nil, it should be a char-offset in
-HUNK, and instead of a string, a cons cell is returned whose car is the
-appropriate text, and whose cdr is the corresponding char-offset in that text."
+(defun diff-hunk-text (hunk destp char-offset)
+  "Return the literal source text from HUNK as (TEXT . OFFSET).
+if DESTP is nil TEXT is the source, otherwise the destination text.
+CHAR-OFFSET is a char-offset in HUNK, and OFFSET is the corresponding
+char-offset in TEXT."
   (with-temp-buffer
     (insert hunk)
     (goto-char (point-min))
@@ -1025,23 +1017,47 @@
 
 
 (defun diff-find-text (text)
-  "Return the buffer position of the nearest occurrence of TEXT.
+  "Return the buffer position (BEG . END) of the nearest occurrence of TEXT.
 If TEXT isn't found, nil is returned."
   (let* ((orig (point))
 	 (forw (and (search-forward text nil t)
-			  (match-beginning 0)))
+		    (cons (match-beginning 0) (match-end 0))))
 	 (back (and (goto-char (+ orig (length text)))
 		    (search-backward text nil t)
-			  (match-beginning 0))))
-	  ;; Choose the closest match.
+		    (cons (match-beginning 0) (match-end 0)))))
+    ;; Choose the closest match.
     (if (and forw back)
-	(if (> (- forw orig) (- orig back)) back forw)
+	(if (> (- (car forw) orig) (- orig (car back))) back forw)
+      (or back forw))))
+
+(defun diff-find-approx-text (text)
+  "Return the buffer position (BEG . END) of the nearest occurrence of TEXT.
+Whitespace differences are ignored."
+  (let* ((orig (point))
+	 (re (concat "^[ \t\n]*"
+		     (mapconcat 'regexp-quote (split-string text) "[ \t\n]+")
+		     "[ \t\n]*\n"))
+	 (forw (and (re-search-forward re nil t)
+		    (cons (match-beginning 0) (match-end 0))))
+	 (back (and (goto-char (+ orig (length text)))
+		    (re-search-backward re nil t)
+		    (cons (match-beginning 0) (match-end 0)))))
+    ;; Choose the closest match.
+    (if (and forw back)
+	(if (> (- (car forw) orig) (- orig (car back))) back forw)
       (or back forw))))
 
 (defsubst diff-xor (a b) (if a (not b) b))
 
 (defun diff-find-source-location (&optional other-file reverse)
-  "Find out (BUF LINE-OFFSET POS SRC DST SWITCHED)."
+  "Find out (BUF LINE-OFFSET POS SRC DST SWITCHED).
+BUF is the buffer corresponding to the source file.
+LINE-OFFSET is the offset between the expected and actual positions
+  of the text of the hunk or nil if the text was not found.
+POS is a pair (BEG . END) indicating the position of the text in the buffer.
+SRC and DST are the two variants of text as returned by `diff-hunk-text'.
+  SRC is the variant that was found in the buffer.
+SWITCHED is non-nil if the patch is already applied."
   (save-excursion
     (let* ((other (diff-xor other-file diff-jump-to-old-file))
 	   (char-offset (- (point) (progn (diff-beginning-of-hunk) (point))))
@@ -1065,13 +1081,19 @@
       (with-current-buffer buf
 	(goto-line (string-to-number line))
 	(let* ((orig-pos (point))
-	       (pos (diff-find-text (car old)))
-	       (switched nil))
-	  (when (null pos)
-	    (setq pos (diff-find-text (car new)) switched t))
+	       (switched nil)
+	       (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)))
+			(progn (setq switched t)
+			       (diff-find-approx-text (car new)))
+			(progn (setq switched nil) nil))))
 	  (nconc
 	   (list buf)
-	   (if pos (list (count-lines orig-pos pos) pos) (list nil orig-pos))
+	   (if pos
+	       (list (count-lines orig-pos (car pos)) pos)
+	     (list nil (cons orig-pos (+ orig-pos (length (car old))))))
 	   (if switched (list new old t) (list old new))))))))
 
 
@@ -1104,7 +1126,7 @@
 	   ;; A reversed patch was detected, perhaps apply it in reverse.
 	   (not (save-window-excursion
 		  (pop-to-buffer buf)
-		  (goto-char (+ pos (cdr old)))
+		  (goto-char (+ (car pos) (cdr old)))
 		  (y-or-n-p
 		   (if reverse
 		       "Hunk hasn't been applied yet; apply it now? "
@@ -1113,11 +1135,11 @@
      (t
       ;; Apply the hunk
       (with-current-buffer buf
-	(goto-char pos)
-	(delete-char (length (car old)))
+	(goto-char (car pos))
+	(delete-region (car pos) (cdr pos))
 	(insert (car new)))
       ;; Display BUF in a window
-      (set-window-point (display-buffer buf) (+ pos (cdr new)))
+      (set-window-point (display-buffer buf) (+ (car pos) (cdr new)))
       (diff-hunk-status-msg line-offset (diff-xor switched reverse) nil)
       (when diff-advance-after-apply-hunk
 	(diff-hunk-next))))))
@@ -1129,7 +1151,7 @@
   (interactive "P")
   (destructuring-bind (buf line-offset pos src dst &optional switched)
       (diff-find-source-location nil reverse)
-    (set-window-point (display-buffer buf) (+ pos (cdr src)))
+    (set-window-point (display-buffer buf) (+ (car pos) (cdr src)))
     (diff-hunk-status-msg line-offset (diff-xor reverse switched) t)))
 
 
@@ -1147,7 +1169,7 @@
     (destructuring-bind (buf line-offset pos src dst &optional switched)
 	(diff-find-source-location other-file rev)
       (pop-to-buffer buf)
-      (goto-char (+ pos (cdr src)))
+      (goto-char (+ (car pos) (cdr src)))
       (diff-hunk-status-msg line-offset (diff-xor rev switched) t))))
 
 
@@ -1170,7 +1192,7 @@
 		(funcall (with-current-buffer buf major-mode))
 		(add-log-current-defun))))
 	  (with-current-buffer buf
-	    (goto-char (+ pos (cdr src)))
+	    (goto-char (+ (car pos) (cdr src)))
 	    (add-log-current-defun))))))
 
 ;; provide the package