changeset 30873:7f824fa01e10

(diff-mode-map): Bind diff-apply-hunk. (diff-find-source-location): New fun, extracted from diff-goto-source. (diff-goto-source): Use it. (diff-next-complex-hunk, diff-filter-lines): New function. (diff-apply-hunk): New command.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Wed, 16 Aug 2000 19:56:10 +0000
parents ddf00eb5fb12
children 79e9f560240d
files lisp/diff-mode.el
diffstat 1 files changed, 130 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/diff-mode.el	Wed Aug 16 19:55:48 2000 +0000
+++ b/lisp/diff-mode.el	Wed Aug 16 19:56:10 2000 +0000
@@ -4,7 +4,7 @@
 
 ;; Author: Stefan Monnier <monnier@cs.yale.edu>
 ;; Keywords: patch diff
-;; Revision: $Id: diff-mode.el,v 1.7 2000/05/10 22:12:46 monnier Exp $
+;; Revision: $Id: diff-mode.el,v 1.8 2000/06/05 07:30:09 monnier Exp $
 
 ;; This file is part of GNU Emacs.
 
@@ -40,21 +40,25 @@
 
 ;; Bugs:
 
-;; - reverse doesn't work with normal diffs.
-;; - (nitpick) the mark is not always quite right in diff-goto-source.
+;; - Reverse doesn't work with normal diffs.
+;; - (nitpick) The mark is not always quite right in diff-goto-source.
+;; - diff-apply-hunk only works on unified diffs.
 
 ;; Todo:
 
-;; - spice up the minor-mode with font-lock support
-;; - improve narrowed-view support
-;; - improve the `compile' support (?)
-;; - recognize pcl-cvs' special string for `cvs-execute-single'
-;; - support for # comments in context->unified
-;; - diff-apply-hunk
-;; - do a fuzzy search in diff-goto-source
-;; - allow diff.el to use diff-mode
-;; - imenu support
-;; - handle `diff -b' output in context->unified
+;; - Add change-log support.
+;; - Spice up the minor-mode with font-lock support.
+;; - Improve narrowed-view support.
+;; - Improve the `compile' support (?).
+;; - Recognize pcl-cvs' special string for `cvs-execute-single'.
+;; - 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
+;;   (i.e. new or old) file.
+;; - imenu support.
+;; - Handle `diff -b' output in context->unified.
 
 ;;; Code:
 
@@ -93,18 +97,18 @@
 ;;;; 
 
 (easy-mmode-defmap diff-mode-shared-map
-  '(;; from Pavel Machek's patch-mode
+  '(;; From Pavel Machek's patch-mode.
     ("n" . diff-hunk-next)
     ("N" . diff-file-next)
     ("p" . diff-hunk-prev)
     ("P" . diff-file-prev)
     ("k" . diff-hunk-kill)
     ("K" . diff-file-kill)
-    ;; from compilation-minor-mode
+    ;; From compilation-minor-mode.
     ("}" . diff-file-next)
     ("{" . diff-file-prev)
     ("\C-m" . diff-goto-source)
-    ;; from XEmacs' diff-mode
+    ;; From XEmacs' diff-mode.
     ("W" . widen)
     ;;("." . diff-goto-source)		;display-buffer
     ;;("f" . diff-goto-source)		;find-file
@@ -116,7 +120,7 @@
     ;;("q" . diff-quit)
     (" " . scroll-up)
     ("\177" . scroll-down)
-    ;; our very own bindings
+    ;; Our very own bindings.
     ("A" . diff-ediff-patch)
     ("r" . diff-restrict-view)
     ("R" . diff-reverse-direction)
@@ -126,8 +130,10 @@
 
 (easy-mmode-defmap diff-mode-map
   `(("\e" . ,diff-mode-shared-map)
-    ;; from compilation-minor-mode
-    ("\C-c\C-c" . diff-goto-source))
+    ;; From compilation-minor-mode.
+    ("\C-c\C-c" . diff-goto-source)
+    ;; Misc operations.
+    ("\C-cda" . diff-apply-hunk))
   "Keymap for `diff-mode'.  See also `diff-mode-shared-map'.")
 
 (easy-menu-define diff-mode-menu diff-mode-map
@@ -420,42 +426,51 @@
 	      (cons (cons fs file) diff-remembered-files-alist))
 	 file)))))
 
+(defun diff-find-source-location (&optional other-file)
+  "Find out (FILE LINE SPAN)."
+  (save-excursion
+    (diff-beginning-of-hunk)
+    (let* ((old (if (not other-file) diff-jump-to-old-file-flag
+		  (not diff-jump-to-old-file-flag)))
+	   ;; Find the location specification.
+	   (loc (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?"))
+		    (error "Can't find the hunk header")
+		  (if old (match-string 1)
+		    (if (match-end 3) (match-string 3)
+		      (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t)
+			(error "Can't find the hunk separator"))
+		      (match-string 1)))))
+	   ;; Extract the actual line number.
+	   (lines (if (string-match "^\\([0-9]*\\),\\([0-9]*\\)" loc)
+		      (cons (string-to-number (match-string 1 loc))
+			    (string-to-number (match-string 2 loc)))
+		    (cons (string-to-number loc) nil)))
+	   (file (diff-find-file-name old))
+	   (line (car lines))
+	   (span (if (or (null (cdr lines)) (< (cdr lines) 0)) 0
+		   ;; Bad hack.
+		   (if (< (cdr lines) line) (cdr lines)
+		     (- (cdr lines) line)))))
+      ;; Update the user preference if he so wished.
+      (when (> (prefix-numeric-value other-file) 8)
+	(setq diff-jump-to-old-file-flag old))
+      (if (null file) (error "Can't find the file")
+	(list file line span)))))
+
 (defun diff-goto-source (&optional other-file)
   "Jump to the corresponding source line.
 `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg
 is give) 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-flag' is also set, for the next invokations."
+  then `diff-jump-to-old-file-flag' is also set, for the next invocations."
   (interactive "P")
   (save-excursion
-    (let ((old (if (not other-file) diff-jump-to-old-file-flag
-		 (not diff-jump-to-old-file-flag))))
-      (when (> (prefix-numeric-value other-file) 8)
-	(setq diff-jump-to-old-file-flag old))
-      (diff-beginning-of-hunk)
-      (let* ((loc (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?"))
-		      (error "Can't find the hunk header")
-		    (if old (match-string 1)
-		      (if (match-end 3) (match-string 3)
-			(unless (re-search-forward "^--- \\([0-9,]+\\)" nil t)
-			  (error "Can't find the hunk separator"))
-			(match-string 1)))))
-	     (lines (if (string-match "^\\([0-9]*\\),\\([0-9]*\\)" loc)
-			(cons (string-to-number (match-string 1 loc))
-			      (string-to-number (match-string 2 loc)))
-		      (cons (string-to-number loc) nil)))
-	     (file (diff-find-file-name old)))
-	(unless file (error "Can't find the file"))
-	(pop-to-buffer (find-file-noselect file))
-	(let* ((line (car lines))
-	       (span (if (or (null (cdr lines)) (< (cdr lines) 0)) 0
-		       (if (< (cdr lines) line) (cdr lines)
-			 (- (cdr lines) line)))))
-	  (ignore-errors
-	    (goto-line line)
-	    (forward-line span)
-	    (push-mark (point) t t)
-	    (goto-line line)))))))
+    (let ((loc (diff-find-source-location other-file)))
+      (pop-to-buffer (find-file-noselect (car loc)))
+      (ignore-errors
+	(goto-line (+ (cadr loc) (caddr loc)))
+	(push-mark (point) t t)
+	(goto-line (cadr loc))))))
 
 
 (defun diff-ediff-patch ()
@@ -836,50 +851,76 @@
 	      'diff-post-command-hook nil t)))
 
 
+;;;
+;;; Misc operations that have proved useful at some point.
+;;;
+
+(defun diff-next-complex-hunk ()
+  "Jump to the next \"complex\" hunk.
+\"Complex\" is approximated by \"the hunk changes the number of lines\".
+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)))))
+
+
+(defun diff-filter-lines (char)
+  (goto-char (point-min))
+  (while (not (eobp))
+    (if (eq (char-after) char)
+	(delete-region (point) (progn (forward-line 1) (point)))
+      (delete-char 1)
+      (forward-line 1))))
+
+(defun diff-apply-hunk (&optional reverse)
+  "Apply the current hunk.
+With a prefix argument, REVERSE the hunk.
+FIXME: Only works for unified diffs."
+  (interactive "P")
+  (save-excursion
+    (let ((loc (diff-find-source-location nil)))
+      (diff-beginning-of-hunk)
+      (unless (looking-at diff-hunk-header-re) (error "Help! Mom!"))
+      (goto-char (1+ (match-end 0)))
+      ;; Extract the SRC and DEST strings.
+      (let ((text (buffer-substring (point) (progn (diff-end-of-hunk) (point))))
+	    src dest)
+	(with-temp-buffer
+	  (insert text)
+	  (diff-filter-lines ?+)
+	  (setq src (buffer-string))
+	  (erase-buffer)
+	  (insert text)
+	  (diff-filter-lines ?-)
+	  (setq dest (buffer-string)))
+	;; Exchange the two strings if we're reversing the patch.
+	(if reverse (let ((tmp src)) (setq src dest) (setq dest tmp)))
+	;; Look for SRC in the file.
+	(pop-to-buffer (find-file-noselect (car loc)))
+	(goto-line (cadr loc))
+	(let* ((pos (point))
+	       (forw (and (search-forward src nil t)
+			  (match-beginning 0)))
+	       (back (and (goto-char (+ pos (length src)))
+			  (search-backward src nil t)
+			  (match-beginning 0))))
+	  ;; Choose the closest match.
+	  (setq pos (if (and forw back)
+			(if (> (- forw pos) (- pos back)) back forw)
+		      (or back forw)))
+	  (unless pos (error "Can't find the text to patch"))
+	  ;; Do it!
+	  (goto-char pos)
+	  (delete-char (length src))
+	  (insert dest))))))
+      
+
 ;; provide the package
 (provide 'diff-mode)
 
-;;; Change Log:
-;; $Log: diff-mode.el,v $
-;; Revision 1.7  2000/05/10 22:12:46  monnier
-;; (diff-font-lock-keywords): Recognize comments.
-;; (diff-font-lock-defaults): Explicitly turn off multiline.
-;; (diff-end-of-hunk): Handle comments and fix end-of-buffer bug.
-;; (diff-ediff-patch): Fix call to ediff-patch-file.
-;; (diff-end-of-file, diff-reverse-direction, diff-fixup-modifs):
-;; Handle comments.
-;;
-;; Revision 1.6  2000/03/21 16:59:17  monnier
-;; (diff-mode-*-map): use `easy-mmode-defmap'.
-;; (diff-end-of-hunk): Return the end position for use in
-;; `easy-mmode-define-navigation'.
-;; (diff-recenter): Remove.
-;; (diff-(next|prev)-*): Rename `diff-*-(prev|next)' and defined in terms
-;; of `easy-mmode-define-navigation'.
-;; (diff-kill-*): Rename `diff-*-kill' (for consistency with the
-;; previous renaming) and fix to use new names.
-;; (diff-merge-strings): Use \n as separator: simpler, faster.
-;; (diff-mode): Use `define-derived-mode'.
-;;
-;; Revision 1.5  2000/02/07 02:01:07  monnier
-;; (diff-kill-junk): New interactive function.
-;; (diff-reverse-direction): Use delete-and-extract-region.
-;; (diff-post-command-hook): Restrict the area so that the hook also works
-;; outside of any diff hunk.  This is necessary for the minor-mode.
-;; (diff-mode): Use toggle-read-only and minor-mode-overriding-map-alist.
-;; (diff-minor-mode): Setup the hooks for header-hunk rewriting.
-;;
-;; Revision 1.4  1999/12/07 07:04:03  monnier
-;; * diff-mode.el (diff-mode-shared-map): fset'd and doc change.
-;; (diff-minor-mode, diff-minor-mode-prefix, diff-minor-mode-map):
-;; New code to support the minor mode version.
-;; (diff-recenter): New function.
-;; (diff-next-hunk, diff-next-file): Use it.
-;; (diff-remembered-files-alist): New var.
-;; (diff-merge-strings): New function.
-;; (diff-find-file-name): Make it smarter and use the user's input more.
-;; (diff-mode): Cosmetic changes.
-;;
+;;; Old Change Log from when diff-mode wasn't part of Emacs:
 ;; Revision 1.11  1999/10/09 23:38:29  monnier
 ;; (diff-mode-load-hook): dropped.
 ;; (auto-mode-alist): also catch *.diffs.