X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/bfe81e789a0838e336f71f182cf500e5068765fd..749eb4018ac32b337a14d18a8ca41e1aa6a15621:/lisp/diff.el diff --git a/lisp/diff.el b/lisp/diff.el index c317baf328..a5fa702650 100644 --- a/lisp/diff.el +++ b/lisp/diff.el @@ -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. @@ -17,21 +18,41 @@ ;; 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 ---- @@ -47,11 +68,24 @@ ;; -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