Mercurial > hg > xemacs-beta
diff lisp/packages/diff.el @ 70:131b0175ea99 r20-0b30
Import from CVS: tag r20-0b30
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:02:59 +0200 |
parents | e04119814345 |
children | 4be1180a9e89 |
line wrap: on
line diff
--- a/lisp/packages/diff.el Mon Aug 13 09:00:04 2007 +0200 +++ b/lisp/packages/diff.el Mon Aug 13 09:02:59 2007 +0200 @@ -1,312 +1,305 @@ -;; -*-Emacs-Lisp-*- -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; File: diff.el -;; Version: $Revision: 1.4 $ -;; Author: This file is based on diff.el by -;; sunpitt!wpmstr!fbresz@Sun.COM 1/27/89. -;; It has been completely rewritten in July 1994 by -;; Sandy Rutherford <sandy@ibm550.sissa.it> -;; RCS: -;; Description: diff-mode for handling output from unix diff utility. -;; Modified: Wed Jul 17 10:26:57 1996 (Andy Norman) ange@hplb.hpl.hp.com -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; diff.el --- Run `diff' in compilation-mode. + +;; Copyright (C) 1992, 1994 Free Software Foundation, Inc. + +;; Keywords: unix, tools + +;; This file is part of XEmacs. -;;; Copyright (C) 1990 Free Software Foundation, Inc. -;;; Copyright (C) 1994 Sandy Rutherford +;; XEmacs 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 2, or (at your option) +;; any later version. -;;; This file is based on diff.el by sunpitt!wpmstr!fbresz@Sun.COM 1/27/89. -;;; It has been completely rewritten in July 1994 by -;;; Sandy Rutherford <sandy@ibm550.sissa.it> +;; XEmacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. -;;; This program 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 1, or (at your option) -;;; any later version. -;;; -;;; This program is distributed in the hope that it will be useful, -;;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;;; GNU General Public License for more details. -;;; -;;; A copy of the GNU General Public License can be obtained from this -;;; program's author (send electronic mail to sandy@ibm550.sissa.it) or -;;; from the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, -;;; MA 02139, USA. +;; You should have received a copy of the GNU General Public License +;; along with XEmacs; see the file COPYING. If not, write to the Free +;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +;; 02111-1307, USA. + +;;; Synched up with: FSF 19.34. + +;;; Commentary: -(provide 'diff) +;; 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'. -;;; User Variables +;;; Code: -;; should be in to loaddefs.el now. +(require 'compile) + +;;; This is duplicated in vc.el. ;;;###autoload -(defvar diff-switches "-c" - "*A list of switches (strings) to pass to the diff program.") +(defvar diff-switches (purecopy "-c") + "*A string or list of strings specifying switches to be passed to diff.") -(defvar diff-do-narrow nil - "*If non-nil diff buffers are initialized narrowed to each difference.") +(defvar diff-command "diff" + "*The command to use to run diff.") -(defvar diff-load-hooks nil - "Hooks to run after loading diff.el") - -;;; Internal variables +(defvar diff-regexp-alist + '( + ;; -u format: @@ -OLDSTART,OLDEND +NEWSTART,NEWEND @@ + ("^@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@$" 1 2) -(defconst diff-emacs-19-p - (let ((ver (string-to-int (substring emacs-version 0 2)))) - (>= ver 19))) + ;; -c format: *** OLDSTART,OLDEND **** + ("^\\*\\*\\* \\([0-9]+\\),[0-9]+ \\*\\*\\*\\*$" 1 nil) + ;; --- NEWSTART,NEWEND ---- + ("^--- \\([0-9]+\\),[0-9]+ ----$" nil 1) -(or diff-emacs-19-p (require 'emacs-19)) + ;; plain diff format: OLDSTART[,OLDEND]{a,d,c}NEWSTART[,NEWEND] + ("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]\\([0-9]+\\)\\(,[0-9]+\\)?$" 1 3) + + ;; -e (ed) format: OLDSTART[,OLDEND]{a,d,c} + ("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]$" 1) -(defvar diff-old-file nil) -;; A list whose car is the name of the old file, and whose cdr indicates -;; whether we should delete the buffer on quit. -(defvar diff-new-file nil) -;; Same as diff-old-file, except for the new file. -(defvar diff-total-differences "0") -;; Total number of difference hunks as a string. -(defvar diff-current-difference "0") -;; Current difference hunk as a string. -(defvar diff-current-hunk 0) -;; Current difference hunk as an integer. -(defvar diff-total-hunks 0) -;; Total number of difference hunks as an integer. -(defvar diff-hunk-vector (vector 0)) -;; Vector storing the starting positions of the difference hunks. -(defvar diff-old-file-pattern nil) -(defvar diff-new-file-pattern nil) -(defvar diff-hunk-pattern nil) -;; Regexps to use when parsing file lines in difference hunks. + ;; -f format: {a,d,c}OLDSTART[ OLDEND] + ;; -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.") - -(defvar diff-search-pattern-alist - (list - (list ?e "^[0-9]\\(,[0-9]+\\)?[acd]$" "^\\([0-9]+\\)" nil) - (list ?c "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\n" - "^\\*+ \\([0-9]+\\)" "^-+ \\([0-9]+\\)") - (list ?u "^@@ " "^@@ -\\([0-9]+\\)" "^@@ -[0-9,]+ \\+\\([0-9]+\\)") - (list nil "^[0-9]+" "^\\([0-9]+\\)" "^[0-9,]+[acd]\\([0-9]+\\)"))) +(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.") -(defvar diff-keymap-grokked nil) - -(defvar diff-temp-template "/tmp/diff") +;; See compilation-parse-errors-function (compile.el). +(defun diff-parse-differences (limit-search find-at-least) + (setq compilation-error-list nil) + (message "Parsing differences...") -;; Initialize the keymap if it isn't already - -(defvar diff-mode-map nil - "Keymap used in diff mode.") + ;; Don't reparse diffs already seen at last parse. + (if compilation-parsing-end (goto-char compilation-parsing-end)) -(if diff-mode-map - nil - (setq diff-mode-map (make-keymap)) - (suppress-keymap diff-mode-map) - (define-key diff-mode-map "?" 'describe-mode) - (define-key diff-mode-map "." 'diff-display-file) - (define-key diff-mode-map "N" 'diff-narrow) - (define-key diff-mode-map "W" 'widen) - (define-key diff-mode-map "f" 'diff-find-file) - (define-key diff-mode-map "h" 'diff-show-header) - (define-key diff-mode-map "j" 'diff-show-difference) - (define-key diff-mode-map "n" 'diff-next-difference) - (define-key diff-mode-map "o" 'diff-find-file-other-window) - (define-key diff-mode-map "p" 'diff-previous-difference) - (define-key diff-mode-map "q" 'diff-quit) - (define-key diff-mode-map "w" 'diff-find-file-other-frame) - (define-key diff-mode-map "\C-c\C-c" 'diff-find-file-other-window) - (define-key diff-mode-map " " 'diff-advertised-scroll-up) - (define-key diff-mode-map "\177" 'diff-advertised-scroll-down) - (define-key diff-mode-map "\C-n" 'diff-next-line) - (define-key diff-mode-map "\C-p" 'diff-previous-line) - (define-key diff-mode-map "\M->" 'diff-end-of-buffer) - (define-key diff-mode-map "\M-<" 'diff-beginning-of-buffer)) - -;;; Internal functions + ;; Construct in REGEXP a regexp composed of all those in dired-regexp-alist. + (let ((regexp (mapconcat #'(lambda (elt) + (concat "\\(" (car elt) "\\)")) + diff-regexp-alist + "\\|")) + ;; (GROUP-IDX OLD-IDX NEW-IDX) + (groups (let ((subexpr 1)) + (mapcar #'(lambda (elt) + (prog1 + (cons subexpr + (mapcar #'(lambda (n) + (and n + (+ subexpr n))) + (cdr elt))) + (setq subexpr (+ subexpr 1 + ;;#### undefined?? + (count-regexp-groupings + (car elt)))))) + diff-regexp-alist))) -(defun diff-grok-keys (to-command from-command) - ;; Assigns to TO-COMMAND the keys for the global binding of FROM-COMMAND. - ;; Does not clobber anything in the local keymap. - (let ((keys (where-is-internal from-command))) - (while keys - (condition-case nil - (if (eq (global-key-binding (car keys)) (key-binding (car keys))) - (local-set-key (car keys) to-command)) - (error nil)) - (setq keys (cdr keys))))) - -(defun diff-grok-keymap () - (if diff-keymap-grokked - () - (if (and term-setup-hook (boundp 'command-line-args-left)) - (progn - (if diff-emacs-19-p - (run-hooks term-setup-hook) - (funcall term-setup-hook)) - (setq term-setup-hook nil))) - (setq diff-keymap-grokked t) - (diff-grok-keys 'diff-next-line 'next-line) - (diff-grok-keys 'diff-previous-line 'previous-line) - (diff-grok-keys 'diff-forward-char 'forward-char) - (diff-grok-keys 'diff-backward-char 'backward-char) - (diff-grok-keys 'diff-scroll-up 'scroll-up) - (diff-grok-keys 'diff-scroll-down 'scroll-down) - (diff-grok-keys 'diff-beginning-of-buffer 'beginning-of-buffer) - (diff-grok-keys 'diff-end-of-buffer 'end-of-buffer))) + (new-error + (function (lambda (file subexpr) + (setq compilation-error-list + (cons + (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 + (save-match-data + (set-buffer (find-file-noselect file))) + (save-excursion + (goto-line line) + (point-marker))))) + compilation-error-list))))) -(defun diff-buffer-narrowed-p () - ;; Returns t if the current buffer is narrowed - (save-restriction - (let ((min (point-min)) - (max (point-max))) - (widen) - (not (and (= min (point-min)) (= max (point-max))))))) + (found-desired nil) + (num-loci-found 0) + g) + + (while (and (not found-desired) + ;; We don't just pass LIMIT-SEARCH to re-search-forward + ;; because we want to find matches containing LIMIT-SEARCH + ;; but which extend past it. + (re-search-forward regexp nil t)) -(defun diff-current-hunk () - ;; Returns the current diff hunk. - (let ((pt (point)) - (start 0) - (end (1+ diff-total-hunks)) - m) - (while (> (- end start) 1) - (setq m (/ (+ start end) 2)) - (if (>= pt (aref diff-hunk-vector m)) - (setq start m) - (setq end m))) - (if (>= pt (aref diff-hunk-vector end)) - (setq m end) - (setq m start)) - ;; Don't treat end of buffer as start of next hunk - (if (eobp) (1- m) m))) + ;; Find which individual regexp matched. + (setq g groups) + (while (and g (null (match-beginning (car (car g))))) + (setq g (cdr g))) + (setq g (car g)) -(defun diff-hunk-min (n) - ;; Returns the start of the current diff hunk. - (aref diff-hunk-vector n)) - -(defun diff-hunk-max (n) - ;; Returns the end of the current diff hunk. - (aref diff-hunk-vector (1+ n))) + (if (nth 1 g) ;OLD-IDX + (funcall new-error diff-old-file (nth 1 g))) + (if (nth 2 g) ;NEW-IDX + (funcall new-error diff-new-file (nth 2 g))) -(defun diff-parse-hunks () - ;; Parses a buffer of diff output. - (save-excursion - (save-restriction - (message "Parsing differences...") - (widen) - (goto-char (point-min)) - (let ((hunks (list 1))) - (while (re-search-forward diff-hunk-pattern nil t) - (setq hunks (cons (match-beginning 0) hunks))) - (setq diff-total-hunks (1- (length hunks)) - diff-hunk-vector (apply 'vector - (nreverse (cons (point-max) hunks))))))) - (message "Parsing differences...done")) + (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 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")) + (setq compilation-error-list (nreverse compilation-error-list))) -(defun diff-update-modeline () - ;; Updates the mode line to show current diff hunk. - (if (or (< (point) (diff-hunk-min diff-current-hunk)) - (>= (point) (diff-hunk-max diff-current-hunk))) - (progn - (setq diff-current-hunk (diff-current-hunk) - diff-current-difference (int-to-string diff-current-hunk)) - (set-buffer-modified-p (buffer-modified-p))))) - -(defun diff-read-args (oldprompt newprompt switchprompt - &optional file-for-backup) - ;; Grab the args for diff. OLDPROMPT and NEWPROMPT are the prompts - ;; for the old & new filenames, SWITCHPROMPT for the list of - ;; switches. If FILE_FOR_BACKUP is provided (it must be a string if - ;; so), then it will be used to try & work out a file & backup to - ;; diff, & in this case the prompting order is backwards. %s in a - ;; prompt has a guess substituted into it. This is nasty. - (let (oldf newf) - (if file-for-backup - (setq newf file-for-backup +;;;###autoload +(defun diff (old new &optional switches) + "Find and display the differences between OLD and NEW files. +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." + (interactive + (nconc + (let (oldf newf) + (nreverse + (list + (setq newf (buffer-file-name) newf (if (and newf (file-exists-p newf)) (read-file-name - (format newprompt (file-name-nondirectory newf)) + (concat "Diff new file: (" + (file-name-nondirectory newf) ") ") nil newf t) - (read-file-name (format newprompt "") nil nil t)) - oldf (file-newest-backup newf) + (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 - (format oldprompt (file-name-nondirectory oldf)) - nil oldf t) - (read-file-name (format oldprompt "") - (file-name-directory newf) nil t))) - ;; Else we aren't trying to be bright... - (setq oldf (read-file-name (format oldprompt "") nil nil t) - newf (read-file-name - (format newprompt (file-name-nondirectory oldf)) - nil (file-name-directory oldf) t))) - (list oldf newf (diff-read-switches switchprompt)))) - -(defun diff-read-switches (switchprompt) - ;; Read and return a list of switches - (if current-prefix-arg - (let ((default (if (listp diff-switches) - (mapconcat 'identity diff-switches " ") - diff-switches))) - (diff-fix-switches - (read-string (format switchprompt default) default))))) - -(defun diff-fix-switches (switch-spec) - ;; Parse a string into a list of switches or leave it be if it's - ;; not a string - (if (stringp switch-spec) - (let (result (start 0)) - (while (string-match "\\(\\S-+\\)" switch-spec start) - (setq result (cons (substring switch-spec (match-beginning 1) - (match-end 1)) - result) - start (match-end 0))) - (nreverse result)) - switch-spec)) + (concat "Diff original file: (" + (file-name-nondirectory oldf) ") ") + (file-name-directory oldf) oldf t) + (read-file-name "Diff original file: " + (file-name-directory newf) nil t)))))) + (if current-prefix-arg + (list (read-string "Diff switches: " + (if (stringp diff-switches) + diff-switches + (mapconcat 'identity diff-switches " ")))) + nil))) + (setq new (expand-file-name new) + old (expand-file-name old)) + ;; XEmacs addition -- allow (diff "../old/" "new-file.el") to work + (cond ((file-directory-p old) + (or (file-directory-p new) + (setq old (expand-file-name (file-name-nondirectory new) + (file-name-as-directory old))))) + ((file-directory-p new) + (setq new (expand-file-name (file-name-nondirectory old) + (file-name-as-directory new))))) + (let ((old-alt (file-local-copy old)) + (new-alt (file-local-copy new)) + buf) + (unwind-protect + (let ((command + (mapconcat 'identity + (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 + (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)) + (pop-to-buffer buf) + ;; 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))))) + (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))))) + buf)))) -(defun diff-get-file-buffer (file) - ;; Returns \(BUFFER . DEL-P\), where DEL-P is t if diff is expected - ;; to delete the buffer, and nil otherwise. - (let* ((buff (get-file-buffer file)) - (del-p (null buff))) - (if (and buff (buffer-modified-p buff)) - (progn - (message - "Buffer %s is modified. Diffing against buffer contents." - (buffer-name buff)) - (sit-for 1))) - ;; Call find-file-noselect even if we already have the buffer, - ;; as it will run verify-buffer-file-modtime. - (cons (find-file-noselect file) del-p))) +;;;###autoload +(defun diff-backup (file &optional switches) + "Diff this file with its backup file or vice versa. +Uses the latest backup, if there are several numerical backups. +If this file is a backup, diff it with its original. +The backup file is the first file given to `diff'." + (interactive (list (read-file-name "Diff (file with backup): ") + (if current-prefix-arg + (read-string "Diff switches: " + (if (stringp diff-switches) + diff-switches + (mapconcat 'identity + diff-switches " "))) + nil))) + (let (bak ori) + (if (backup-file-name-p file) + (setq bak file + ori (file-name-sans-versions file)) + (setq bak (or (diff-latest-backup-file file) + (error "No backup found for %s" file)) + ori file)) + (diff bak ori switches))) -(defun diff-cleanup-buffers () - ;; Cleans up diff buffers by deleting buffers that we don't expect - ;; the user to care about. - (let ((files (list diff-old-file diff-new-file))) - (while files - (let ((ent (car files)) - buff) - (and (cdr ent) - (setq buff (get-file-buffer (car ent))) - (not (buffer-modified-p buff)) - (kill-buffer buff))) - (setq files (cdr files))) - (if (get-buffer "*Diff Header*") - (kill-buffer "*Diff Header*")))) - -(defun diff-latest-backup-file (file) +(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. - (let* ((file (expand-file-name file)) - (handler (find-file-name-handler file 'diff-latest-backup-file))) + (let ((handler (find-file-name-handler fn 'diff-latest-backup-file))) (if handler - (funcall handler 'diff-latest-backup-file file) + (funcall handler 'diff-latest-backup-file fn) + ;; 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 (file-chase-links (expand-file-name fn))) (or - (let ((bak (make-backup-file-name file))) + (let ((bak (make-backup-file-name fn))) (if (file-exists-p bak) bak)) - (let* ((dir (file-name-directory file)) - (base-versions (concat (file-name-nondirectory file) ".~")) + ;; We use BACKUPNAME to cope with backups stored in a different dir. + (let* ((backupname (car (find-backup-file-name fn))) + (dir (file-name-directory backupname)) + (base-versions (concat (file-name-sans-versions + (file-name-nondirectory backupname)) + ".~")) (bv-length (length base-versions))) (concat dir (car (sort @@ -317,377 +310,6 @@ (> (backup-extract-version fn1) (backup-extract-version fn2)))))))))))) -(defun diff-file-line (&optional old-file-p) - "Return line number of current hunk in `diff-new-file'. -With optional argument OLD-FILE-P, use `diff-old-file' instead." - (save-excursion - (let ((min (diff-hunk-min diff-current-hunk)) - (max (diff-hunk-max diff-current-hunk)) - (regexp (if old-file-p diff-old-file-pattern diff-new-file-pattern))) - (goto-char min) - (or (and regexp (re-search-forward regexp max t)) - (error "Unable to locate a file line for %s file." - (if old-file-p "old" "new"))) - (string-to-int (buffer-substring (match-beginning 1) (match-end 1)))))) - -(defun diff-run-diff (switches old old-temp new new-temp) - ;; Actually run the diff process with SWITCHES on OLD and NEW. - ;; OLD-TEMP and NEW-TEMP are names of temp files that can be used - ;; to dump the data out to. - (insert "diff " (mapconcat 'identity switches " ") " " old - " " new "\n") - (apply 'call-process "diff" nil t nil - (append switches (list old-temp new-temp)))) - - -(defun diff-fix-file-names (old old-temp new new-temp pattern) - ;; Replaces any temp file names with the real names of files. - (save-excursion - (save-restriction - (let ((files (list old new)) - (temps (list old-temp new-temp)) - buffer-read-only case-fold-search) - (goto-char (point-min)) - (if (re-search-forward pattern nil t) - (narrow-to-region (point-min) (match-beginning 0))) - (while files - (let ((regexp (concat "[ \t\n]\\(" - (regexp-quote (car temps)) - "\\)[ \t\n]"))) - (goto-char (point-min)) - (forward-line 1) - (while (re-search-forward regexp nil t) - (goto-char (match-beginning 1)) - (delete-region (point) (match-end 1)) - (insert (car files)))) - (setq files (cdr files) - temps (cdr temps))))))) - -;;;; User commands - -(defun diff-mode () - "Diff Mode is used by \\[diff] for perusing the output from the diff program. -All normal editing commands are turned off. Instead, these are available: -\\<diff-mode-map> -\\[diff-advertised-scroll-up] Scroll to next screen of this difference. -\\[diff-advertised-scroll-down] Scroll to previous screen of this difference. -\\[diff-next-difference] Move to Next Difference. -\\[diff-previous-difference] Move to Previous Difference. -\\[diff-show-difference] Jump to difference specified by numeric position. -\\[diff-find-file] Find current diff in file -\\[diff-find-file-other-window] Find current diff in file in other window -\\[diff-display-file] Display file in other window -\\[diff-narrow] Narrow diff buffer to current difference -\\[widen] Widen diff buffer -\\[diff-show-header] Show diff header describing file name etc. -\\[diff-quit] Quit diff -" - (interactive) - (use-local-map diff-mode-map) - (diff-grok-keymap) - (setq buffer-read-only t - major-mode 'diff-mode - mode-name "Diff" - mode-line-modified "--- " - mode-line-process - '(" " diff-current-difference "/" diff-total-differences)) - (diff-parse-hunks) - (setq diff-total-differences (int-to-string diff-total-hunks))) - -;;; Motion commands - -(defun diff-next-difference (n) - "In diff-mode go the the beginning of the next difference hunk." - (interactive "p") - (if (zerop n) - (goto-char (diff-hunk-min diff-current-hunk)) - (let ((narrow (diff-buffer-narrowed-p)) - (max (point-max)) - (min (point-min))) - (unwind-protect - (progn - (widen) - (setq diff-current-hunk (+ n diff-current-hunk)) - (cond ((> diff-current-hunk diff-total-hunks) - (setq diff-current-hunk diff-total-hunks) - (message "No following difference hunks.")) - ((< diff-current-hunk 0) - (setq diff-current-hunk 0) - (message "No preceding difference hunks."))) - (setq diff-current-difference (int-to-string diff-current-hunk) - min (goto-char (diff-hunk-min diff-current-hunk)) - max (diff-hunk-max diff-current-hunk))) - (if narrow (narrow-to-region min max)))) - (set-buffer-modified-p (buffer-modified-p)))) - -(defun diff-previous-difference (n) - "In diff-mode go the the beginning of the previous difference hunk." - (interactive "p") - (diff-next-difference (- n))) - -(defun diff-next-line (n) - "In diff-mode go to the next line." - (interactive "p") - (condition-case nil - (next-line n) - (error (if (> n 0) (message "End of difference hunk")))) - (diff-update-modeline)) - -(defun diff-previous-line (n) - "In diff-mode go to the previous line." - (interactive "p") - (diff-next-line (- n))) - -(defun diff-forward-char (n) - "In diff-mode move the point forward." - (interactive "p") - (forward-char n) - (diff-update-modeline)) - -(defun diff-backward-char (n) - "In diff-mode move the point backward." - (interactive "p") - (backward-char n) - (diff-update-modeline)) - -(defun diff-scroll-up (n) - "In diff-mode scroll the buffer up." - (interactive "P") - (scroll-up n) - (diff-update-modeline)) - -(fset 'diff-advertised-scroll-up 'diff-scroll-up) - -(defun diff-scroll-down (n) - "In diff-mode scroll the buffer down." - (interactive "P") - (scroll-down n) - (diff-update-modeline)) - -(fset 'diff-advertised-scroll-down 'diff-scroll-down) - -(defun diff-beginning-of-buffer (n) - "In diff-mode go to the beginning of the buffer." - (interactive "P") - (beginning-of-buffer n) - (diff-update-modeline)) - -(defun diff-end-of-buffer (n) - "In diff-mode go to the end of the buffer." - (interactive "P") - (end-of-buffer n) - (diff-update-modeline)) - -;;; The main command. +(provide 'diff) -;;;###autoload -(defun diff (old new &optional switches) - "Find and display the differences between OLD and NEW files. -Interactively you are prompted with the current buffer's file name for NEW -and what appears to be its backup for OLD." - ;; Support for diffing directories is rather limited. It needs work. - (interactive (diff-read-args "Diff original file (%s) " - "Diff new file (%s) " - "Switches for diff (%s) " - (buffer-file-name))) - (setq switches (diff-fix-switches (or switches diff-switches)) - old (expand-file-name old) - new (expand-file-name new)) - (let ((curr-buff (current-buffer)) - doing-dirs old-temp new-temp old-buffer new-buffer flag) - (let ((fdp-old (file-directory-p old)) - (fdp-new (file-directory-p new))) - (cond - ((null (or fdp-new fdp-old))) - ((null fdp-new) - (setq old (expand-file-name (file-name-nondirectory new) old))) - ((null fdp-old) - (setq new (expand-file-name (file-name-nondirectory old) new))) - (t (setq doing-dirs t)))) -;; (message "diff %s %s %s..." -;; (mapconcat (function identity) switches " ") new old) - (message "diff %s %s %s..." - (mapconcat (function identity) switches " ") old new) - (if doing-dirs - (setq diff-old-file nil - diff-new-file nil) - (setq old-temp (make-temp-name (concat diff-temp-template "1")) - new-temp (make-temp-name (concat diff-temp-template "2")) - old-buffer (diff-get-file-buffer old) - new-buffer (diff-get-file-buffer new) - diff-old-file (cons old (cdr old-buffer)) - diff-new-file (cons new (cdr new-buffer)))) - (let (case-fold-search) - (mapcar (function - (lambda (x) - (if (string-match "[ecu]" x) - (setq flag (aref x (match-beginning 0)))))) - switches)) - (unwind-protect - (let ((patterns (assq flag diff-search-pattern-alist))) - (set-buffer (get-buffer-create "*Diff Output*")) - (setq default-directory (file-name-directory new) - diff-old-file-pattern (nth 2 patterns) - diff-new-file-pattern (nth 3 patterns) - diff-hunk-pattern (nth 1 patterns)) - (let (buffer-read-only) - (if (fboundp 'buffer-disable-undo) - (buffer-disable-undo (current-buffer)) - ;; old style (Emacs 18.55 and earlier) - (buffer-disable-undo (current-buffer))) - (widen) - (erase-buffer) - (if doing-dirs - (progn - (diff-run-diff switches old old new new) - (setq diff-hunk-pattern (concat diff-hunk-pattern - "\\|^Only in "))) - (save-excursion - (set-buffer (car old-buffer)) - (write-region (point-min) (point-max) old-temp nil 'quiet) - (set-buffer (car new-buffer)) - (write-region (point-min) (point-max) new-temp nil 'quiet)) - (diff-run-diff switches old old-temp new new-temp)) - ;; Need to replace file names - (if (and (not doing-dirs) (memq flag '(?c ?u))) - (diff-fix-file-names old old-temp new new-temp - diff-hunk-pattern)) - (diff-mode) - (goto-char (point-min)) - (setq diff-current-difference "0" - diff-current-hunk 0) - (if (zerop diff-total-hunks) - (progn - (diff-cleanup-buffers) - (message "No differences")) - (if diff-do-narrow (narrow-to-region (point) (diff-hunk-max 0))) - (display-buffer (current-buffer)) - (message "%s difference hunk%s" diff-total-differences - (if (= diff-total-hunks 1) "" "s"))))) - (condition-case nil - (delete-file old-temp) - (error nil)) - (condition-case nil - (delete-file new-temp) - (error nil)) - (set-buffer curr-buff)))) - -;;;###autoload -(defun diff-backup (file &optional switches) - "Diff this file with its backup file or vice versa. -Uses the latest backup, if there are several numerical backups. -If this file is a backup, diff it with its original. -The backup file is the first file given to `diff'." - (interactive (list (read-file-name "Diff (file with backup): ") - (and current-prefix-arg - (diff-read-switches "Diff switches: ")))) - (let (bak ori) - (if (backup-file-name-p file) - (setq bak file - ori (file-name-sans-versions file)) - (setq bak (or (diff-latest-backup-file file) - (error "No backup found for %s" file)) - ori file)) - (diff bak ori switches))) - -(defun diff-show-difference (n) - "Show difference number N (prefix argument)." - (interactive "p") - (let ((narrowedp (diff-buffer-narrowed-p)) - (min (diff-hunk-min diff-current-hunk)) - (max (diff-hunk-max diff-current-hunk))) - (unwind-protect - (progn - (widen) - (cond - ((< n 0) - (message "No negative hunks.") - (setq n 0)) - ((> n diff-total-hunks) - (message "No hunk %d." n) - (setq n diff-total-hunks))) - (setq diff-current-hunk n - diff-current-difference (int-to-string diff-current-hunk) - min (diff-hunk-min n) - max (diff-hunk-max n)) - (goto-char min)) - (if narrowedp (narrow-to-region min max)) - (set-buffer-modified-p (buffer-modified-p))))) - -(defun diff-show-header () - "Show `diff-header'." - (interactive) - (with-output-to-temp-buffer "*Diff Header*" - (princ (save-restriction - (widen) - (buffer-substring (diff-hunk-min 0) (diff-hunk-max 0)))))) - - -(defun diff-find-file (old-file-p) - "Visit diffed file, at the point corresponding to the current hunk. -Default is to visit the new file; prefix means visit old file instead." - (interactive "P") - (let ((line (diff-file-line old-file-p))) - (find-file - (if old-file-p - (car diff-old-file) - (car diff-new-file))) - (goto-line line) - (recenter 0))) - -(defun diff-find-file-other-window (old-file-p) - "Visit the diffed file in other window, with the point at the current hunk. -Default is to visit the new file; prefix means visit the old file instead." - (interactive "P") - (let ((line (diff-file-line old-file-p))) - (find-file-other-window - (if old-file-p - (car diff-old-file) - (car diff-new-file))) - (goto-line line) - (recenter 0))) - -(defun diff-find-file-other-frame (old-file-p) - "Visit the diffed file in other frame, with point at the current hunk. -Default is to visit the new file; prefix means visit the old file instead." - (interactive "P") - (let ((line (diff-file-line old-file-p))) - (find-file-other-frame - (if old-file-p - (car diff-old-file) - (car diff-new-file))) - (goto-line line) - (recenter 0))) - -(defun diff-display-file (old-file-p) - "Display the diffed file in other window, with point at the current hunk. -Default is to visit the new file; prefix means visit the old file instead." - (interactive "P") - (let ((line (diff-file-line old-file-p)) - (wind (display-buffer (find-file-noselect (if old-file-p - (car diff-old-file) - (car diff-new-file))))) - (curr-wind (selected-window))) - (unwind-protect - (progn - (select-window wind) - (goto-line line) - (recenter 0)) - (select-window curr-wind)))) - -(defun diff-quit () - "Quit diff by killing the diff buffer." - (interactive) - (kill-buffer "*Diff Output*") - (diff-cleanup-buffers)) - -(defun diff-narrow () - "Narrow diff buffer to current difference hunk." - (interactive) - (narrow-to-region (diff-hunk-min diff-current-hunk) - (diff-hunk-max diff-current-hunk))) - -;;; Run any load hooks -(run-hooks 'diff-load-hook) - -;;; end of diff.el +;;; diff.el ends here