Say there is no maintainer.
[bpt/emacs.git] / lisp / diff.el
index c317baf..a5fa702 100644 (file)
@@ -1,8 +1,9 @@
-;;; diff.el --- Run `diff' in compilation-mode.
+;;; diff.el --- run `diff' in compilation-mode
 
-;; Copyright (C) 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1994, 1996, 2001 Free Software Foundation, Inc.
 
-;; Keyword: unix, tools
+;; Maintainer: FSF
+;; Keywords: unix, tools
 
 ;; This file is part of GNU Emacs.
 
 ;; 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; 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.
+
+;;; Commentary:
+
+;; This package helps you explore differences between files, using the
+;; UNIX command diff(1).  The commands are `diff' and `diff-backup'.
+;; You can specify options with `diff-switches'.
 
 ;;; Code:
 
 (require 'compile)
 
-(defvar diff-switches nil
-  "*A string or list of strings specifying switches to be be passed to diff.")
+(defgroup diff nil
+  "Comparing files with `diff'."
+  :group 'tools)
+
+;;;###autoload
+(defcustom diff-switches "-c"
+  "*A string or list of strings specifying switches to be be passed to diff."
+  :type '(choice string (repeat string))
+  :group 'diff)
+
+;;;###autoload
+(defcustom diff-command "diff"
+  "*The command to use to run diff."
+  :type 'string
+  :group 'diff)
 
 (defvar diff-regexp-alist
   '(
     ;; -u format: @@ -OLDSTART,OLDEND +NEWSTART,NEWEND @@
     ("^@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@$" 1 2)
-  
+
     ;; -c format: *** OLDSTART,OLDEND ****
     ("^\\*\\*\\* \\([0-9]+\\),[0-9]+ \\*\\*\\*\\*$" 1 nil)
     ;;            --- NEWSTART,NEWEND ----
     ;; -n format: {a,d,c}OLDSTART LINES-CHANGED
     ("^[adc]\\([0-9]+\\)\\( [0-9]+\\)?$" 1)
     )
-  "Alist (REGEXP OLD-IDX NEW-IDX) of regular expressions to match difference 
-sections in \\[diff] output.  If REGEXP matches, the OLD-IDX'th
-subexpression gives the line number in the old file, and NEW-IDX'th
-subexpression gives the line number in the new file.  If OLD-IDX or NEW-IDX
-is nil, REGEXP matches only half a section.")
+  "Alist of regular expressions to match difference sections in \\[diff] output.
+Each element has the form (REGEXP OLD-IDX NEW-IDX).
+Any text that REGEXP matches identifies one difference hunk
+or the header of a hunk.
+
+The OLD-IDX'th subexpression of REGEXP gives the line number
+in the old file, and NEW-IDX'th subexpression gives the line number
+in the new file.  If OLD-IDX or NEW-IDX
+is nil, REGEXP matches only half a hunk.")
+
+(defvar diff-old-file nil
+  "This is the old file name in the comparison in this buffer.")
+(defvar diff-new-file nil
+  "This is the new file name in the comparison in this buffer.")
+(defvar diff-old-temp-file nil
+  "This is the name of a temp file to be deleted after diff finishes.")
+(defvar diff-new-temp-file nil
+  "This is the name of a temp file to be deleted after diff finishes.")
 
 ;; See compilation-parse-errors-function (compile.el).
 (defun diff-parse-differences (limit-search find-at-least)
@@ -59,7 +93,7 @@ is nil, REGEXP matches only half a section.")
   (message "Parsing differences...")
 
   ;; Don't reparse diffs already seen at last parse.
-  (goto-char compilation-parsing-end)
+  (if compilation-parsing-end (goto-char compilation-parsing-end))
 
   ;; Construct in REGEXP a regexp composed of all those in dired-regexp-alist.
   (let ((regexp (mapconcat (lambda (elt)
@@ -84,21 +118,28 @@ is nil, REGEXP matches only half a section.")
         (function (lambda (file subexpr)
                     (setq compilation-error-list
                           (cons
-                           (cons (set-marker (make-marker)
-                                             (match-beginning subexpr)
-                                             (current-buffer))
+                           (cons (save-excursion
+                                   ;; Report location of message
+                                   ;; at beginning of line.
+                                   (goto-char
+                                    (match-beginning subexpr))
+                                   (beginning-of-line)
+                                   (point-marker))
+                                 ;; Report location of corresponding text.
                                  (let ((line (string-to-int
                                               (buffer-substring
                                                (match-beginning subexpr)
                                                (match-end subexpr)))))
                                    (save-excursion
-                                     (set-buffer (find-file-noselect file))
+                                     (save-match-data
+                                       (set-buffer (find-file-noselect file)))
                                      (save-excursion
                                        (goto-line line)
                                        (point-marker)))))
                            compilation-error-list)))))
 
        (found-desired nil)
+       (num-loci-found 0)
        g)
 
     (while (and (not found-desired)
@@ -118,25 +159,43 @@ is nil, REGEXP matches only half a section.")
       (if (nth 2 g)                    ;NEW-IDX
          (funcall new-error diff-new-file (nth 2 g)))
 
-      (if (or (and find-at-least (>= nfound find-at-least))
+      (setq num-loci-found (1+ num-loci-found))
+      (if (or (and find-at-least
+                  (>= num-loci-found find-at-least))
              (and limit-search (>= (point) limit-search)))
-             ;; We have found as many new errors as the user wants,
+             ;; We have found as many new loci as the user wants,
              ;; or the user wanted a specific diff, and we're past it.
          (setq found-desired t)))
-    (if found-desired
-       (setq compilation-parsing-end (point))
-      ;; Set to point-max, not point, so we don't perpetually
-      ;; parse the last bit of text when it isn't a diff header.
-      (setq compilation-parsing-end (point-max))
-      (message "Parsing differences...done")))
+    (set-marker compilation-parsing-end
+               (if found-desired (point)
+                 ;; Set to point-max, not point, so we don't perpetually
+                 ;; parse the last bit of text when it isn't a diff header.
+                 (point-max)))
+    (message "Parsing differences...done"))
   (setq compilation-error-list (nreverse compilation-error-list)))
 
+(defun diff-process-setup ()
+  "Set up \`compilation-exit-message-function' for \`diff'."
+  ;; Avoid frightening people with "abnormally terminated"
+  ;; if diff finds differences.
+  (set (make-local-variable 'compilation-exit-message-function)
+       (lambda (status code msg)
+        (cond ((not (eq status 'exit))
+               (cons msg code))
+              ((zerop code)
+               '("finished (no differences)\n" . "no differences"))
+              ((= code 1)
+               '("finished\n" . "differences found"))
+              (t
+               (cons msg code))))))
+
 ;;;###autoload
-(defun diff (old new &optional switches)
+(defun diff (old new &optional switches no-async)
   "Find and display the differences between OLD and NEW files.
-Interactively the current buffer's file name is the default for for NEW
+Interactively the current buffer's file name is the default for NEW
 and a backup file for NEW is the default for OLD.
-With prefix arg, prompt for diff switches."
+With prefix arg, prompt for diff switches.
+If NO-ASYNC is non-nil, call diff synchronously."
   (interactive
    (nconc
     (let (oldf newf)
@@ -145,14 +204,14 @@ With prefix arg, prompt for diff switches."
        (setq newf (buffer-file-name)
              newf (if (and newf (file-exists-p newf))
                       (read-file-name
-                       (concat "Diff new file: ("
+                       (concat "Diff new file: (default "
                                (file-name-nondirectory newf) ") ")
                        nil newf t)
                     (read-file-name "Diff new file: " nil nil t)))
        (setq oldf (file-newest-backup newf)
              oldf (if (and oldf (file-exists-p oldf))
                       (read-file-name
-                       (concat "Diff original file: ("
+                       (concat "Diff original file: (default "
                                (file-name-nondirectory oldf) ") ")
                        (file-name-directory oldf) oldf t)
                     (read-file-name "Diff original file: "
@@ -163,51 +222,52 @@ With prefix arg, prompt for diff switches."
                               diff-switches
                             (mapconcat 'identity diff-switches " "))))
       nil)))
-  (message "Comparing files %s %s..." new old)
   (setq new (expand-file-name new)
        old (expand-file-name old))
-  (let ((old-alt (diff-prepare old new))
-       (new-alt (diff-prepare new old))
+  (let ((old-alt (file-local-copy old))
+       (new-alt (file-local-copy new))
        buf)
-    (unwind-protect
-       (let ((command
+    (save-excursion
+       (let ((compilation-process-setup-function 'diff-process-setup)
+             (command
               (mapconcat 'identity
-                         (append '("diff")
-                                 (if (consp diff-switches)
-                                     diff-switches
-                                   (list diff-switches))
+                         (append (list diff-command)
+                                 ;; Use explicitly specified switches
+                                 (if switches
+                                     (if (consp switches)
+                                         switches (list switches))
+                                   ;; If not specified, use default.
+                                   (if (consp diff-switches)
+                                       diff-switches
+                                     (list diff-switches)))
                                  (if (or old-alt new-alt)
                                      (list "-L" old "-L" new))
-                                 (list (or old-alt old))
-                                 (list (or new-alt new)))
+                                 (list
+                                  (shell-quote-argument (or old-alt old)))
+                                 (list
+                                  (shell-quote-argument (or new-alt new))))
                          " ")))
          (setq buf
                (compile-internal command
                                  "No more differences" "Diff"
-                                 'diff-parse-differences)))
-         (save-excursion
-           (set-buffer buf)
-           (set (make-local-variable 'diff-old-file) old)
-           (set (make-local-variable 'diff-new-file) new))
-         buf)
-      (if old-alt (delete-file old-alt))
-      (if new-alt (delete-file new-alt)))))
-
-;; Copy the file FILE into a temporary file if that is necessary
-;; for comparison.  (This is only necessary if the file name has a handler.)
-;; OTHER is the other file to be compared.
-(defun diff-prepare (file other)
-  (let (handler handlers)
-    (setq handlers file-name-handler-alist)
-    (while (and (consp handlers) (null handler))
-      (if (and (consp (car handlers))
-              (stringp (car (car handlers)))
-              (string-match (car (car handlers)) file))
-         (setq handler (cdr (car handlers))))
-      (setq handlers (cdr handlers)))
-    (if handler
-       (funcall handler 'diff-prepare file other)
-      nil)))
+                                 'diff-parse-differences
+                                 nil nil nil nil nil nil no-async))
+         (set-buffer buf)
+         (set (make-local-variable 'diff-old-file) old)
+         (set (make-local-variable 'diff-new-file) new)
+         (set (make-local-variable 'diff-old-temp-file) old-alt)
+         (set (make-local-variable 'diff-new-temp-file) new-alt)
+         (set (make-local-variable 'compilation-finish-function)
+              (function (lambda (buff msg)
+                          (if diff-old-temp-file
+                              (delete-file diff-old-temp-file))
+                          (if diff-new-temp-file
+                              (delete-file diff-new-temp-file)))))
+         ;; When async processes aren't available, the compilation finish
+         ;; function doesn't get chance to run.  Invoke it by hand.
+         (or (fboundp 'start-process)
+             (funcall compilation-finish-function nil nil))
+         buf))))
 
 ;;;###autoload
 (defun diff-backup (file &optional switches)
@@ -234,25 +294,11 @@ The backup file is the first file given to `diff'."
 
 (defun diff-latest-backup-file (fn)    ; actually belongs into files.el
   "Return the latest existing backup of FILE, or nil."
-  ;; First try simple backup, then the highest numbered of the
-  ;; numbered backups.
-  ;; Ignore the value of version-control because we look for existing
-  ;; backups, which maybe were made earlier or by another user with
-  ;; a different value of version-control.
-  (setq fn (expand-file-name fn))
-  (or
-   (let ((bak (make-backup-file-name fn)))
-     (if (file-exists-p bak) bak))
-   (let* ((dir (file-name-directory fn))
-         (base-versions (concat (file-name-nondirectory fn) ".~"))
-         (bv-length (length base-versions)))
-     (concat dir
-            (car (sort
-                  (file-name-all-completions base-versions dir)
-                  ;; bv-length is a fluid var for backup-extract-version:
-                  (function
-                   (lambda (fn1 fn2)
-                     (> (backup-extract-version fn1)
-                        (backup-extract-version fn2))))))))))
+  (let ((handler (find-file-name-handler fn 'diff-latest-backup-file)))
+    (if handler
+       (funcall handler 'diff-latest-backup-file fn)
+      (file-newest-backup fn))))
+
+(provide 'diff)
 
 ;;; diff.el ends here