Use define-minor-mode in more cases.
[bpt/emacs.git] / lisp / ediff.el
index 7475834..97dc537 100644 (file)
@@ -1,22 +1,27 @@
 ;;; ediff.el --- a comprehensive visual interface to diff & patch
 
 ;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-;;   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
 ;; Created: February 2, 1994
 ;; Keywords: comparing, merging, patching, tools, unix
 
-(defconst ediff-version "2.81.2" "The current version of Ediff")
-(defconst ediff-date "August 18, 2007" "Date of last update")
+;; Yoni Rabkin <yoni@rabkins.net> contacted the maintainer of this
+;; file on 20/3/2008, and the maintainer agreed that when a bug is
+;; filed in the Emacs bug reporting system against this file, a copy
+;; of the bug report be sent to the maintainer's email address.
+
+(defconst ediff-version "2.81.4" "The current version of Ediff")
+(defconst ediff-date "December 7, 2009" "Date of last update")
 
 
 ;; 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
@@ -24,9 +29,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:
 
 
 ;;; Code:
 
+(provide 'ediff)
 
 ;; Compiler pacifier
-(defvar cvs-cookie-handle)
-(defvar ediff-last-dir-patch)
-(defvar ediff-patch-default-directory)
-
-(and noninteractive
-     (eval-when-compile
-        (load-library "dired")
-        (load-library "info")
-        (load "pcl-cvs" 'noerror)))
+(eval-and-compile
+  (unless (fboundp 'declare-function) (defmacro declare-function (&rest  r))))
+
+
 (eval-when-compile
-  (let ((load-path (cons (expand-file-name ".") load-path)))
-    (provide 'ediff) ; to break recursive load cycle
-    (or (featurep 'ediff-init)
-       (load "ediff-init.el" nil nil 'nosuffix))
-    (or (featurep 'ediff-mult)
-       (load "ediff-mult.el" nil nil 'nosuffix))
-    (or (featurep 'ediff-ptch)
-       (load "ediff-ptch.el" nil nil 'nosuffix))
-    (or (featurep 'ediff-vers)
-       (load "ediff-vers.el" nil nil 'nosuffix))
-    ))
+  (require 'dired)
+  (require 'ediff-util)
+  (require 'ediff-ptch))
 ;; end pacifier
 
 (require 'ediff-init)
 
 
 (defcustom ediff-use-last-dir nil
-  "*If t, Ediff will use previous directory as default when reading file name."
+  "If t, Ediff will use previous directory as default when reading file name."
   :type 'boolean
   :group 'ediff)
 
 (defalias 'ediff3 'ediff-files3)
 
 
-;; Visit FILE and arrange its buffer to Ediff's liking.
-;; FILE is actually a variable symbol that must contain a true file name.
-;; BUFFER-NAME is a variable symbol, which will get the buffer object into
-;; which FILE is read.
-;; LAST-DIR is the directory variable symbol where FILE's
-;; directory name should be returned.  HOOKS-VAR is a variable symbol that will
-;; be assigned the hook to be executed after `ediff-startup' is finished.
-;; `ediff-find-file' arranges that the temp files it might create will be
-;; deleted.
 (defun ediff-find-file (file-var buffer-name &optional last-dir hooks-var)
+  "Visit FILE and arrange its buffer to Ediff's liking.
+FILE-VAR is actually a variable symbol whose value must contain a true
+file name.
+BUFFER-NAME is a variable symbol, which will get the buffer object into
+which FILE is read.
+LAST-DIR is the directory variable symbol where FILE's
+directory name should be returned.  HOOKS-VAR is a variable symbol that will
+be assigned the hook to be executed after `ediff-startup' is finished.
+`ediff-find-file' arranges that the temp files it might create will be
+deleted."
   (let* ((file (symbol-value file-var))
         (file-magic (ediff-filename-magic-p file))
         (temp-file-name-prefix (file-name-nondirectory file)))
                 (list (cons 'ediff-job-name job-name))
                 merge-buffer-file)))
 
+(declare-function diff-latest-backup-file "diff" (fn))
 
 ;;;###autoload
 (defalias 'ediff 'ediff-files)
 
+;;;###autoload
+(defun ediff-current-file ()
+  "Start ediff between current buffer and its file on disk.
+This command can be used instead of `revert-buffer'.  If there is
+nothing to revert then this command fails."
+  (interactive)
+  (unless (or revert-buffer-function
+              revert-buffer-insert-file-contents-function
+              (and buffer-file-number
+                   (or (buffer-modified-p)
+                       (not (verify-visited-file-modtime
+                             (current-buffer))))))
+    (error "Nothing to revert"))
+  (let* ((auto-save-p (and (recent-auto-save-p)
+                           buffer-auto-save-file-name
+                           (file-readable-p buffer-auto-save-file-name)
+                           (y-or-n-p
+                            "Buffer has been auto-saved recently.  Compare with auto-save file? ")))
+         (file-name (if auto-save-p
+                        buffer-auto-save-file-name
+                      buffer-file-name))
+         (revert-buf-name (concat "FILE=" file-name))
+         (revert-buf (get-buffer revert-buf-name))
+         (current-major major-mode))
+    (unless file-name
+      (error "Buffer does not seem to be associated with any file"))
+    (when revert-buf
+      (kill-buffer revert-buf)
+      (setq revert-buf nil))
+    (setq revert-buf (get-buffer-create revert-buf-name))
+    (with-current-buffer revert-buf
+      (insert-file-contents file-name)
+      ;; Assume same modes:
+      (funcall current-major))
+    (ediff-buffers revert-buf (current-buffer))))
+
+
 ;;;###autoload
 (defun ediff-backup (file)
   "Run Ediff on FILE and its backup file.
@@ -445,37 +475,45 @@ If this file is a backup, `ediff' it with its original."
         (buf-C-file-name (if buf-C-is-alive
                              (buffer-file-name (get-buffer buf-B))))
         file-A file-B file-C)
-    (if (not (ediff-buffer-live-p buf-A))
-       (error "Buffer %S doesn't exist" buf-A))
-    (if (not (ediff-buffer-live-p buf-B))
-       (error "Buffer %S doesn't exist" buf-B))
-    (let ((ediff-job-name job-name))
-      (if (and ediff-3way-comparison-job
-              (not buf-C-is-alive))
-         (error "Buffer %S doesn't exist" buf-C)))
-    (if (stringp buf-A-file-name)
-       (setq buf-A-file-name (file-name-nondirectory buf-A-file-name)))
-    (if (stringp buf-B-file-name)
-       (setq buf-B-file-name (file-name-nondirectory buf-B-file-name)))
-    (if (stringp buf-C-file-name)
-       (setq buf-C-file-name (file-name-nondirectory buf-C-file-name)))
-
-    (setq file-A (ediff-make-temp-file buf-A buf-A-file-name)
-         file-B (ediff-make-temp-file buf-B buf-B-file-name))
-    (if buf-C-is-alive
-       (setq file-C (ediff-make-temp-file buf-C buf-C-file-name)))
-
-    (ediff-setup (get-buffer buf-A) file-A
-                (get-buffer buf-B) file-B
-                (if buf-C-is-alive (get-buffer buf-C))
-                file-C
-                (cons `(lambda ()
-                         (delete-file ,file-A)
-                         (delete-file ,file-B)
-                         (if (stringp ,file-C) (delete-file ,file-C)))
-                      startup-hooks)
-                (list (cons 'ediff-job-name job-name))
-                merge-buffer-file)))
+    (unwind-protect
+       (progn
+         (if (not (ediff-buffer-live-p buf-A))
+             (error "Buffer %S doesn't exist" buf-A))
+         (if (not (ediff-buffer-live-p buf-B))
+             (error "Buffer %S doesn't exist" buf-B))
+         (let ((ediff-job-name job-name))
+           (if (and ediff-3way-comparison-job
+                    (not buf-C-is-alive))
+               (error "Buffer %S doesn't exist" buf-C)))
+         (if (stringp buf-A-file-name)
+             (setq buf-A-file-name (file-name-nondirectory buf-A-file-name)))
+         (if (stringp buf-B-file-name)
+             (setq buf-B-file-name (file-name-nondirectory buf-B-file-name)))
+         (if (stringp buf-C-file-name)
+             (setq buf-C-file-name (file-name-nondirectory buf-C-file-name)))
+         
+         (setq file-A (ediff-make-temp-file buf-A buf-A-file-name)
+               file-B (ediff-make-temp-file buf-B buf-B-file-name))
+         (if buf-C-is-alive
+             (setq file-C (ediff-make-temp-file buf-C buf-C-file-name)))
+         
+         (ediff-setup (get-buffer buf-A) file-A
+                      (get-buffer buf-B) file-B
+                      (if buf-C-is-alive (get-buffer buf-C))
+                      file-C
+                      (cons `(lambda ()
+                               (delete-file ,file-A)
+                               (delete-file ,file-B)
+                               (if (stringp ,file-C) (delete-file ,file-C)))
+                            startup-hooks)
+                      (list (cons 'ediff-job-name job-name))
+                      merge-buffer-file))
+      (if (and (stringp file-A) (file-exists-p file-A))
+         (delete-file file-A))
+      (if (and (stringp file-B) (file-exists-p file-B))
+         (delete-file file-B))
+      (if (and (stringp file-C) (file-exists-p file-C))
+         (delete-file file-C)))))
 
 
 ;;; Directory and file group operations
@@ -947,8 +985,7 @@ lines.  For large regions, use `ediff-regions-linewise'."
        (buffer-B
          (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-"))
         reg-A-beg reg-A-end reg-B-beg reg-B-end)
-    (save-excursion
-      (set-buffer buffer-A)
+    (with-current-buffer buffer-A
       (setq reg-A-beg (region-beginning)
            reg-A-end (region-end))
       (set-buffer buffer-B)
@@ -988,8 +1025,7 @@ lines.  For small regions, use `ediff-regions-wordwise'."
        (buffer-B
          (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-"))
         reg-A-beg reg-A-end reg-B-beg reg-B-end)
-    (save-excursion
-      (set-buffer buffer-A)
+    (with-current-buffer buffer-A
       (setq reg-A-beg (region-beginning)
            reg-A-end (region-end))
       ;; enlarge the region to hold full lines
@@ -1027,41 +1063,46 @@ lines.  For small regions, use `ediff-regions-wordwise'."
   (let ((tmp-buffer (get-buffer-create ediff-tmp-buffer))
        overl-A overl-B
        file-A file-B)
-
-    ;; in case beg/end-A/B aren't markers--make them into markers
-    (ediff-with-current-buffer buffer-A
-      (setq beg-A (move-marker (make-marker) beg-A)
-           end-A (move-marker (make-marker) end-A)))
-    (ediff-with-current-buffer buffer-B
-      (setq beg-B (move-marker (make-marker) beg-B)
-           end-B (move-marker (make-marker) end-B)))
-
-    ;; make file-A
-    (if word-mode
-       (ediff-wordify beg-A end-A buffer-A tmp-buffer)
-      (ediff-copy-to-buffer beg-A end-A buffer-A tmp-buffer))
-    (setq file-A (ediff-make-temp-file tmp-buffer "regA"))
-
-    ;; make file-B
-    (if word-mode
-       (ediff-wordify beg-B end-B buffer-B tmp-buffer)
-      (ediff-copy-to-buffer beg-B end-B buffer-B tmp-buffer))
-    (setq file-B (ediff-make-temp-file tmp-buffer "regB"))
-
-    (setq overl-A (ediff-make-bullet-proof-overlay beg-A end-A buffer-A))
-    (setq overl-B (ediff-make-bullet-proof-overlay beg-B end-B buffer-B))
-    (ediff-setup buffer-A file-A
-                buffer-B file-B
-                nil nil            ; buffer & file C
-                (cons `(lambda ()
-                           (delete-file ,file-A)
-                           (delete-file ,file-B))
-                      startup-hooks)
-                (append
-                 (list (cons 'ediff-word-mode  word-mode)
-                       (cons 'ediff-narrow-bounds (list overl-A overl-B))
-                       (cons 'ediff-job-name job-name))
-                 setup-parameters))
+    (unwind-protect
+       (progn
+         ;; in case beg/end-A/B aren't markers--make them into markers
+         (ediff-with-current-buffer buffer-A
+           (setq beg-A (move-marker (make-marker) beg-A)
+                 end-A (move-marker (make-marker) end-A)))
+         (ediff-with-current-buffer buffer-B
+           (setq beg-B (move-marker (make-marker) beg-B)
+                 end-B (move-marker (make-marker) end-B)))
+         
+         ;; make file-A
+         (if word-mode
+             (ediff-wordify beg-A end-A buffer-A tmp-buffer)
+           (ediff-copy-to-buffer beg-A end-A buffer-A tmp-buffer))
+         (setq file-A (ediff-make-temp-file tmp-buffer "regA"))
+
+         ;; make file-B
+         (if word-mode
+             (ediff-wordify beg-B end-B buffer-B tmp-buffer)
+           (ediff-copy-to-buffer beg-B end-B buffer-B tmp-buffer))
+         (setq file-B (ediff-make-temp-file tmp-buffer "regB"))
+         
+         (setq overl-A (ediff-make-bullet-proof-overlay beg-A end-A buffer-A))
+         (setq overl-B (ediff-make-bullet-proof-overlay beg-B end-B buffer-B))
+         (ediff-setup buffer-A file-A
+                      buffer-B file-B
+                      nil nil      ; buffer & file C
+                      (cons `(lambda ()
+                               (delete-file ,file-A)
+                               (delete-file ,file-B))
+                            startup-hooks)
+                      (append
+                       (list (cons 'ediff-word-mode  word-mode)
+                             (cons 'ediff-narrow-bounds (list overl-A overl-B))
+                             (cons 'ediff-job-name job-name))
+                       setup-parameters)))
+      (if (and (stringp file-A) (file-exists-p file-A))
+         (delete-file file-A))
+      (if (and (stringp file-B) (file-exists-p file-B))
+         (delete-file file-B)))
     ))
 
 
@@ -1303,20 +1344,6 @@ buffer."
      (intern (format "ediff-%S-merge-internal" ediff-version-control-package))
      rev1 rev2 ancestor-rev startup-hooks merge-buffer-file)))
 
-;; MK: Check. This function doesn't seem to be used any more by pcvs or pcl-cvs
-;;;###autoload
-(defun run-ediff-from-cvs-buffer (pos)
-  "Run Ediff-merge on appropriate revisions of the selected file.
-First run after `M-x cvs-update'.  Then place the cursor on a line describing a
-file and then run `run-ediff-from-cvs-buffer'."
-  (interactive "d")
-  (ediff-load-version-control)
-  (let ((tin (tin-locate cvs-cookie-handle pos)))
-    (if tin
-       (cvs-run-ediff-on-file-descriptor tin)
-      (error "There is no file to merge"))))
-
-
 ;;; Apply patch
 
 ;;;###autoload
@@ -1437,10 +1464,14 @@ Uses `vc.el' or `rcs.el' depending on `ediff-version-control-package'."
   "Return string describing the version of Ediff.
 When called interactively, displays the version."
   (interactive)
+  ;; called-interactively-p - not in XEmacs
+  ;; (if (called-interactively-p 'interactive)
   (if (interactive-p)
-      (message (ediff-version))
+      (message "%s" (ediff-version))
     (format "Ediff %s of %s" ediff-version ediff-date)))
 
+;; info is run first, and will autoload info.el.
+(declare-function Info-goto-node "info" (nodename &optional fork))
 
 ;;;###autoload
 (defun ediff-documentation (&optional node)
@@ -1454,7 +1485,7 @@ With optional NODE, goes to that node."
     (condition-case nil
        (progn
          (pop-to-buffer (get-buffer-create "*info*"))
-         (info (if ediff-xemacs-p "ediff.info" "ediff"))
+         (info (if (featurep 'xemacs) "ediff.info" "ediff"))
          (if node
              (Info-goto-node node)
            (message "Type `i' to search for a specific topic"))
@@ -1523,14 +1554,12 @@ With optional NODE, goes to that node."
 
 (run-hooks 'ediff-load-hook)
 
-(provide 'ediff)
-
 
-;;; Local Variables:
-;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
-;;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1)
-;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body))
-;;; End:
+;; Local Variables:
+;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
+;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1)
+;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body))
+;; End:
 
-;;; arch-tag: 97c71396-db02-4f41-8b48-6a51c3348fcc
+;; arch-tag: 97c71396-db02-4f41-8b48-6a51c3348fcc
 ;;; ediff.el ends here