changeset 26748:8b8438e9ac12

* 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.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Tue, 07 Dec 1999 07:04:03 +0000
parents a65919d40cd0
children 5bc04426fb75
files lisp/ChangeLog lisp/diff-mode.el
diffstat 2 files changed, 103 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Tue Dec 07 06:39:33 1999 +0000
+++ b/lisp/ChangeLog	Tue Dec 07 07:04:03 1999 +0000
@@ -1,5 +1,15 @@
 1999-12-07  Stefan Monnier  <monnier@cs.yale.edu>
 
+	* 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.
+
 	* files.el (save-some-buffers): Turn EXITING into the more general
 	PRED argument to allow specifying a subset of buffers.
 
--- a/lisp/diff-mode.el	Tue Dec 07 06:39:33 1999 +0000
+++ b/lisp/diff-mode.el	Tue Dec 07 07:04:03 1999 +0000
@@ -4,8 +4,7 @@
 
 ;; Author: Stefan Monnier <monnier@cs.yale.edu>
 ;; Keywords: patch diff
-;; Version: v1_8
-;; Revision: diff-mode.el,v 1.11 1999/10/09 23:38:29 monnier Exp
+;; Revision: $Id$
 
 ;; This file is part of GNU Emacs.
 
@@ -30,8 +29,6 @@
 ;; commands, editing and various conversions as well as jumping
 ;; to the corresponding source file.
 
-;; History:
-
 ;; 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
@@ -48,9 +45,10 @@
 
 ;; Todo:
 
+;; - spice up the minor-mode with editing and font-lock support.
 ;; - improve narrowed-view support.
-;; - improve diff-find-file-name.
-;; - improve the `compile' support.
+;; - improve the `compile' support (?).
+;; - recognize pcl-cvs' special string for `cvs-execute-single'.
 
 ;;; Code:
 
@@ -110,6 +108,7 @@
     ("\C-m" . diff-goto-source)
     ;; from XEmacs' diff-mode
     ("W" . widen)
+    ;;("\C-l" . diff-recenter)
     ;;("." . diff-goto-source)		;display-buffer
     ;;("f" . diff-goto-source)		;find-file
     ("o" . diff-goto-source)		;other-window
@@ -126,7 +125,8 @@
     ("R" . diff-reverse-direction)
     ("U" . diff-context->unified)
     ("C" . diff-unified->context))
-  "Keymap for read-only `diff-mode'. Only active in read-only mode.")
+  "Basic keymap for `diff-mode', bound to various prefix keys.")
+(fset 'diff-mode-shared-map diff-mode-shared-map)
 
 (diff-defmap diff-mode-map
   `(("\e" . ,diff-mode-shared-map)
@@ -146,6 +146,15 @@
     ;;["Fixup Headers"		diff-fixup-modifs	(not buffer-read-only)]
     ))
 
+(defcustom diff-minor-mode-prefix "\C-cd"
+  "Prefix key for `diff-minor-mode' commands."
+  :group 'diff-mode
+  :type '(choice (string "\e") (string "C-cd") string))
+
+(diff-defmap diff-minor-mode-map
+  `((,diff-minor-mode-prefix . diff-mode-shared-map))
+  "Keymap for `diff-minor-mode'.  See also `diff-mode-shared-map'.")
+
 
 ;;;; 
 ;;;; font-lock support
@@ -259,6 +268,17 @@
   (re-search-forward "^[^-+!<>0-9@* \\]" nil 'move)
   (beginning-of-line))
 
+(defun diff-recenter ()
+  "Scroll if necessary to display the current hunk."
+  (interactive)
+  (when (eq (current-buffer) (window-buffer (selected-window)))
+    (let ((endpt (save-excursion (diff-end-of-hunk) (point))))
+      (unless (<= endpt (window-end))
+	(recenter)
+	;;(unless (<= endpt (window-end nil t))
+	;;  (set-window-start (selected-window) (point)))
+	))))
+
 (defun diff-next-hunk (&optional count)
   "Move to next (COUNT'th) hunk."
   (interactive "p")
@@ -268,7 +288,8 @@
     (condition-case ()
 	(re-search-forward diff-hunk-header-re nil nil count)
       (error (error "Can't find next hunk")))
-    (goto-char (match-beginning 0))))
+    (goto-char (match-beginning 0))
+    (diff-recenter)))
 
 (defun diff-prev-hunk (&optional count)
   "Move to previous (COUNT'th) hunk."
@@ -288,7 +309,8 @@
     (condition-case ()
 	(re-search-forward diff-file-header-re nil nil count)
       (error (error "Can't find next file")))
-    (goto-char (match-beginning 0))))
+    (goto-char (match-beginning 0))
+    (diff-recenter)))
 
 (defun diff-prev-file (&optional count)
   "Move to (COUNT'th) previous file header."
@@ -351,9 +373,34 @@
 ;;;; jump to other buffers
 ;;;;
 
+(defvar diff-remembered-files-alist nil)
+
 (defun diff-filename-drop-dir (file)
   (when (string-match "/" file) (substring file (match-end 0))))
 
+(defun diff-merge-strings (ancestor from to)
+  "Merge the diff between ANCESTOR and FROM into TO.
+Returns the merged string if successful or nil otherwise.
+If ANCESTOR = FROM, returns TO.
+If ANCESTOR = TO, returns FROM.
+The heuristic is simplistic and only really works for cases
+like \(diff-merge-strings \"b/foo\" \"b/bar\" \"/a/c/foo\")."
+  ;; Ideally, we want:
+  ;;   AMB ANB CMD -> CND
+  ;; but that's ambiguous if `foo' or `bar' is empty:
+  ;; a/foo a/foo1 b/foo.c -> b/foo1.c but not 1b/foo.c or b/foo.c1
+  (let ((str (concat ancestor " /|/ " from " /|/ " to)))
+    (when (and (string-match (concat
+			      "\\`\\(.*?\\)\\(.*\\)\\(.*\\) /|/ "
+			      "\\1\\(.*\\)\\3 /|/ "
+			      "\\(.*\\(\\2\\).*\\)\\'") str)
+	       (equal to (match-string 5 str)))
+      (concat (substring str (match-beginning 5) (match-beginning 6))
+	      (match-string 4 str)
+	      (substring str (match-end 6) (match-end 5))))))
+
+
+
 (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."
@@ -378,26 +425,33 @@
 		(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))
-	   (file
-	    ;; look for each file in turn.  If none found, try again but
-	    ;; ignoring the first level of directory, ...
-	    (do* ((files fs (delq nil (mapcar 'diff-filename-drop-dir files)))
-		  (file nil nil))
-		((or (null files)
-		     (setq file (do* ((files files (cdr files))
-				      (file (car files) (car files)))
-				    ((or (null file) (file-exists-p file))
-				     file))))
-		 file))))
+	   (fs (delq nil fs)))
       (or
-       file
+       ;; use any previously used preference
+       (cdr (assoc fs diff-remembered-files-alist))
+       ;; try to be clever and use previous choices as an inspiration
+       (dolist (rf diff-remembered-files-alist)
+	 (let ((newfile (diff-merge-strings (caar rf) (car fs) (cdr rf))))
+	   (if (and newfile (file-exists-p newfile)) (return newfile))))
+       ;; look for each file in turn.  If none found, try again but
+       ;; ignoring the first level of directory, ...
+       (do* ((files fs (delq nil (mapcar 'diff-filename-drop-dir files)))
+	     (file nil nil))
+	   ((or (null files)
+		(setq file (do* ((files files (cdr files))
+				 (file (car files) (car files)))
+			       ((or (null file) (file-exists-p file))
+				file))))
+	    file))
+       ;; <foo>.rej patches implicitly apply to <foo>
        (and (string-match "\\.rej\\'" (or buffer-file-name ""))
 	    (let ((file (substring buffer-file-name 0 (match-beginning 0))))
 	      (when (file-exists-p file) file)))
-       ;; FIXME: use a more informative prompt
-       (let ((file (read-file-name "File: " nil (first fs) nil (first fs))))
-	 ;; FIXME: remember for the next invocation
+       ;; if all else fails, ask the user
+       (let ((file (read-file-name (format "Use file %s: " (or (first fs) ""))
+				   nil (first fs) t (first fs))))
+	 (set (make-local-variable 'diff-remembered-files-alist)
+	      (cons (cons fs file) diff-remembered-files-alist))
 	 file)))))
 
 (defun diff-goto-source (&optional other-file)
@@ -760,10 +814,9 @@
 ;;;; The main function
 ;;;; 
 
-;;(autoload 'diff-mode "diff-mode" "Major mode for viewing context diffs." t)
 ;;;###autoload
 (defun diff-mode ()
-  "Major mode for viewing context diffs.
+  "Major mode for viewing/editing context diffs.
 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.
 This mode runs `diff-mode-hook'.
@@ -789,10 +842,10 @@
   (if (not diff-update-on-the-fly-flag)
       (add-hook 'write-contents-hooks 'diff-write-contents-hooks)
     (make-local-variable 'diff-unhandled-changes)
-    (make-local-hook 'after-change-functions)
-    (add-hook 'after-change-functions 'diff-after-change-function nil t)
-    (make-local-hook 'post-command-hook)
-    (add-hook 'post-command-hook 'diff-post-command-hook nil t))
+    (add-hook (make-local-hook 'after-change-functions)
+	      'diff-after-change-function nil t)
+    (add-hook (make-local-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 (make-local-variable 'minor-mode-map-alist)
 	       (cons 'buffer-read-only diff-mode-shared-map))
@@ -800,13 +853,20 @@
   (run-hooks 'diff-mode-hook))
 
 ;;;###autoload
-(add-to-list 'auto-mode-alist '("\\.\\(diffs?\\|patch\\|rej\\)\\'" . diff-mode))
+(define-minor-mode diff-minor-mode
+  "Minor mode for viewing/editing context diffs.
+\\{diff-minor-mode-map}"
+  nil " Diff" nil
+  ;; FIXME: setup font-lock
+  ;; FIXME: setup change hooks
+  )
+
 
 ;; provide the package
 (provide 'diff-mode)
 
 ;;; Change Log:
-;; diff-mode.el,v
+;; $Log: diff-mode.el,v $
 ;; Revision 1.11  1999/10/09 23:38:29  monnier
 ;; (diff-mode-load-hook): dropped.
 ;; (auto-mode-alist): also catch *.diffs.