Mercurial > hg > xemacs-beta
comparison 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 |
comparison
equal
deleted
inserted
replaced
69:804d1389bcd6 | 70:131b0175ea99 |
---|---|
1 ;; -*-Emacs-Lisp-*- | 1 ;;; diff.el --- Run `diff' in compilation-mode. |
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 2 |
3 ;; | 3 ;; Copyright (C) 1992, 1994 Free Software Foundation, Inc. |
4 ;; File: diff.el | 4 |
5 ;; Version: $Revision: 1.4 $ | 5 ;; Keywords: unix, tools |
6 ;; Author: This file is based on diff.el by | 6 |
7 ;; sunpitt!wpmstr!fbresz@Sun.COM 1/27/89. | 7 ;; This file is part of XEmacs. |
8 ;; It has been completely rewritten in July 1994 by | 8 |
9 ;; Sandy Rutherford <sandy@ibm550.sissa.it> | 9 ;; XEmacs is free software; you can redistribute it and/or modify it |
10 ;; RCS: | 10 ;; under the terms of the GNU General Public License as published by |
11 ;; Description: diff-mode for handling output from unix diff utility. | 11 ;; the Free Software Foundation; either version 2, or (at your option) |
12 ;; Modified: Wed Jul 17 10:26:57 1996 (Andy Norman) ange@hplb.hpl.hp.com | 12 ;; any later version. |
13 ;; | 13 |
14 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 14 ;; XEmacs is distributed in the hope that it will be useful, but |
15 | 15 ;; WITHOUT ANY WARRANTY; without even the implied warranty of |
16 ;;; Copyright (C) 1990 Free Software Foundation, Inc. | 16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 ;;; Copyright (C) 1994 Sandy Rutherford | 17 ;; General Public License for more details. |
18 | 18 |
19 ;;; This file is based on diff.el by sunpitt!wpmstr!fbresz@Sun.COM 1/27/89. | 19 ;; You should have received a copy of the GNU General Public License |
20 ;;; It has been completely rewritten in July 1994 by | 20 ;; along with XEmacs; see the file COPYING. If not, write to the Free |
21 ;;; Sandy Rutherford <sandy@ibm550.sissa.it> | 21 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
22 | 22 ;; 02111-1307, USA. |
23 ;;; This program is free software; you can redistribute it and/or modify | 23 |
24 ;;; it under the terms of the GNU General Public License as published by | 24 ;;; Synched up with: FSF 19.34. |
25 ;;; the Free Software Foundation; either version 1, or (at your option) | 25 |
26 ;;; any later version. | 26 ;;; Commentary: |
27 ;;; | 27 |
28 ;;; This program is distributed in the hope that it will be useful, | 28 ;; This package helps you explore differences between files, using the |
29 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | 29 ;; UNIX command diff(1). The commands are `diff' and `diff-backup'. |
30 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 30 ;; You can specify options with `diff-switches'. |
31 ;;; GNU General Public License for more details. | 31 |
32 ;;; | 32 ;;; Code: |
33 ;;; A copy of the GNU General Public License can be obtained from this | 33 |
34 ;;; program's author (send electronic mail to sandy@ibm550.sissa.it) or | 34 (require 'compile) |
35 ;;; from the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, | 35 |
36 ;;; MA 02139, USA. | 36 ;;; This is duplicated in vc.el. |
37 | |
38 (provide 'diff) | |
39 | |
40 ;;; User Variables | |
41 | |
42 ;; should be in to loaddefs.el now. | |
43 ;;;###autoload | 37 ;;;###autoload |
44 (defvar diff-switches "-c" | 38 (defvar diff-switches (purecopy "-c") |
45 "*A list of switches (strings) to pass to the diff program.") | 39 "*A string or list of strings specifying switches to be passed to diff.") |
46 | 40 |
47 (defvar diff-do-narrow nil | 41 (defvar diff-command "diff" |
48 "*If non-nil diff buffers are initialized narrowed to each difference.") | 42 "*The command to use to run diff.") |
49 | 43 |
50 (defvar diff-load-hooks nil | 44 (defvar diff-regexp-alist |
51 "Hooks to run after loading diff.el") | 45 '( |
52 | 46 ;; -u format: @@ -OLDSTART,OLDEND +NEWSTART,NEWEND @@ |
53 ;;; Internal variables | 47 ("^@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@$" 1 2) |
54 | 48 |
55 (defconst diff-emacs-19-p | 49 ;; -c format: *** OLDSTART,OLDEND **** |
56 (let ((ver (string-to-int (substring emacs-version 0 2)))) | 50 ("^\\*\\*\\* \\([0-9]+\\),[0-9]+ \\*\\*\\*\\*$" 1 nil) |
57 (>= ver 19))) | 51 ;; --- NEWSTART,NEWEND ---- |
58 | 52 ("^--- \\([0-9]+\\),[0-9]+ ----$" nil 1) |
59 (or diff-emacs-19-p (require 'emacs-19)) | 53 |
60 | 54 ;; plain diff format: OLDSTART[,OLDEND]{a,d,c}NEWSTART[,NEWEND] |
61 (defvar diff-old-file nil) | 55 ("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]\\([0-9]+\\)\\(,[0-9]+\\)?$" 1 3) |
62 ;; A list whose car is the name of the old file, and whose cdr indicates | 56 |
63 ;; whether we should delete the buffer on quit. | 57 ;; -e (ed) format: OLDSTART[,OLDEND]{a,d,c} |
64 (defvar diff-new-file nil) | 58 ("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]$" 1) |
65 ;; Same as diff-old-file, except for the new file. | 59 |
66 (defvar diff-total-differences "0") | 60 ;; -f format: {a,d,c}OLDSTART[ OLDEND] |
67 ;; Total number of difference hunks as a string. | 61 ;; -n format: {a,d,c}OLDSTART LINES-CHANGED |
68 (defvar diff-current-difference "0") | 62 ("^[adc]\\([0-9]+\\)\\( [0-9]+\\)?$" 1) |
69 ;; Current difference hunk as a string. | 63 ) |
70 (defvar diff-current-hunk 0) | 64 "Alist (REGEXP OLD-IDX NEW-IDX) of regular expressions to match difference |
71 ;; Current difference hunk as an integer. | 65 sections in \\[diff] output. If REGEXP matches, the OLD-IDX'th |
72 (defvar diff-total-hunks 0) | 66 subexpression gives the line number in the old file, and NEW-IDX'th |
73 ;; Total number of difference hunks as an integer. | 67 subexpression gives the line number in the new file. If OLD-IDX or NEW-IDX |
74 (defvar diff-hunk-vector (vector 0)) | 68 is nil, REGEXP matches only half a section.") |
75 ;; Vector storing the starting positions of the difference hunks. | 69 |
76 (defvar diff-old-file-pattern nil) | 70 (defvar diff-old-file nil |
77 (defvar diff-new-file-pattern nil) | 71 "This is the old file name in the comparison in this buffer.") |
78 (defvar diff-hunk-pattern nil) | 72 (defvar diff-new-file nil |
79 ;; Regexps to use when parsing file lines in difference hunks. | 73 "This is the new file name in the comparison in this buffer.") |
80 | 74 (defvar diff-old-temp-file nil |
81 | 75 "This is the name of a temp file to be deleted after diff finishes.") |
82 (defvar diff-search-pattern-alist | 76 (defvar diff-new-temp-file nil |
83 (list | 77 "This is the name of a temp file to be deleted after diff finishes.") |
84 (list ?e "^[0-9]\\(,[0-9]+\\)?[acd]$" "^\\([0-9]+\\)" nil) | 78 |
85 (list ?c "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\n" | 79 ;; See compilation-parse-errors-function (compile.el). |
86 "^\\*+ \\([0-9]+\\)" "^-+ \\([0-9]+\\)") | 80 (defun diff-parse-differences (limit-search find-at-least) |
87 (list ?u "^@@ " "^@@ -\\([0-9]+\\)" "^@@ -[0-9,]+ \\+\\([0-9]+\\)") | 81 (setq compilation-error-list nil) |
88 (list nil "^[0-9]+" "^\\([0-9]+\\)" "^[0-9,]+[acd]\\([0-9]+\\)"))) | 82 (message "Parsing differences...") |
89 | 83 |
90 (defvar diff-keymap-grokked nil) | 84 ;; Don't reparse diffs already seen at last parse. |
91 | 85 (if compilation-parsing-end (goto-char compilation-parsing-end)) |
92 (defvar diff-temp-template "/tmp/diff") | 86 |
93 | 87 ;; Construct in REGEXP a regexp composed of all those in dired-regexp-alist. |
94 ;; Initialize the keymap if it isn't already | 88 (let ((regexp (mapconcat #'(lambda (elt) |
95 | 89 (concat "\\(" (car elt) "\\)")) |
96 (defvar diff-mode-map nil | 90 diff-regexp-alist |
97 "Keymap used in diff mode.") | 91 "\\|")) |
98 | 92 ;; (GROUP-IDX OLD-IDX NEW-IDX) |
99 (if diff-mode-map | 93 (groups (let ((subexpr 1)) |
100 nil | 94 (mapcar #'(lambda (elt) |
101 (setq diff-mode-map (make-keymap)) | 95 (prog1 |
102 (suppress-keymap diff-mode-map) | 96 (cons subexpr |
103 (define-key diff-mode-map "?" 'describe-mode) | 97 (mapcar #'(lambda (n) |
104 (define-key diff-mode-map "." 'diff-display-file) | 98 (and n |
105 (define-key diff-mode-map "N" 'diff-narrow) | 99 (+ subexpr n))) |
106 (define-key diff-mode-map "W" 'widen) | 100 (cdr elt))) |
107 (define-key diff-mode-map "f" 'diff-find-file) | 101 (setq subexpr (+ subexpr 1 |
108 (define-key diff-mode-map "h" 'diff-show-header) | 102 ;;#### undefined?? |
109 (define-key diff-mode-map "j" 'diff-show-difference) | 103 (count-regexp-groupings |
110 (define-key diff-mode-map "n" 'diff-next-difference) | 104 (car elt)))))) |
111 (define-key diff-mode-map "o" 'diff-find-file-other-window) | 105 diff-regexp-alist))) |
112 (define-key diff-mode-map "p" 'diff-previous-difference) | 106 |
113 (define-key diff-mode-map "q" 'diff-quit) | 107 (new-error |
114 (define-key diff-mode-map "w" 'diff-find-file-other-frame) | 108 (function (lambda (file subexpr) |
115 (define-key diff-mode-map "\C-c\C-c" 'diff-find-file-other-window) | 109 (setq compilation-error-list |
116 (define-key diff-mode-map " " 'diff-advertised-scroll-up) | 110 (cons |
117 (define-key diff-mode-map "\177" 'diff-advertised-scroll-down) | 111 (cons (save-excursion |
118 (define-key diff-mode-map "\C-n" 'diff-next-line) | 112 ;; Report location of message |
119 (define-key diff-mode-map "\C-p" 'diff-previous-line) | 113 ;; at beginning of line. |
120 (define-key diff-mode-map "\M->" 'diff-end-of-buffer) | 114 (goto-char |
121 (define-key diff-mode-map "\M-<" 'diff-beginning-of-buffer)) | 115 (match-beginning subexpr)) |
122 | 116 (beginning-of-line) |
123 ;;; Internal functions | 117 (point-marker)) |
124 | 118 ;; Report location of corresponding text. |
125 (defun diff-grok-keys (to-command from-command) | 119 (let ((line (string-to-int |
126 ;; Assigns to TO-COMMAND the keys for the global binding of FROM-COMMAND. | 120 (buffer-substring |
127 ;; Does not clobber anything in the local keymap. | 121 (match-beginning subexpr) |
128 (let ((keys (where-is-internal from-command))) | 122 (match-end subexpr))))) |
129 (while keys | 123 (save-excursion |
130 (condition-case nil | 124 (save-match-data |
131 (if (eq (global-key-binding (car keys)) (key-binding (car keys))) | 125 (set-buffer (find-file-noselect file))) |
132 (local-set-key (car keys) to-command)) | 126 (save-excursion |
133 (error nil)) | 127 (goto-line line) |
134 (setq keys (cdr keys))))) | 128 (point-marker))))) |
135 | 129 compilation-error-list))))) |
136 (defun diff-grok-keymap () | 130 |
137 (if diff-keymap-grokked | 131 (found-desired nil) |
138 () | 132 (num-loci-found 0) |
139 (if (and term-setup-hook (boundp 'command-line-args-left)) | 133 g) |
140 (progn | 134 |
141 (if diff-emacs-19-p | 135 (while (and (not found-desired) |
142 (run-hooks term-setup-hook) | 136 ;; We don't just pass LIMIT-SEARCH to re-search-forward |
143 (funcall term-setup-hook)) | 137 ;; because we want to find matches containing LIMIT-SEARCH |
144 (setq term-setup-hook nil))) | 138 ;; but which extend past it. |
145 (setq diff-keymap-grokked t) | 139 (re-search-forward regexp nil t)) |
146 (diff-grok-keys 'diff-next-line 'next-line) | 140 |
147 (diff-grok-keys 'diff-previous-line 'previous-line) | 141 ;; Find which individual regexp matched. |
148 (diff-grok-keys 'diff-forward-char 'forward-char) | 142 (setq g groups) |
149 (diff-grok-keys 'diff-backward-char 'backward-char) | 143 (while (and g (null (match-beginning (car (car g))))) |
150 (diff-grok-keys 'diff-scroll-up 'scroll-up) | 144 (setq g (cdr g))) |
151 (diff-grok-keys 'diff-scroll-down 'scroll-down) | 145 (setq g (car g)) |
152 (diff-grok-keys 'diff-beginning-of-buffer 'beginning-of-buffer) | 146 |
153 (diff-grok-keys 'diff-end-of-buffer 'end-of-buffer))) | 147 (if (nth 1 g) ;OLD-IDX |
154 | 148 (funcall new-error diff-old-file (nth 1 g))) |
155 (defun diff-buffer-narrowed-p () | 149 (if (nth 2 g) ;NEW-IDX |
156 ;; Returns t if the current buffer is narrowed | 150 (funcall new-error diff-new-file (nth 2 g))) |
157 (save-restriction | 151 |
158 (let ((min (point-min)) | 152 (setq num-loci-found (1+ num-loci-found)) |
159 (max (point-max))) | 153 (if (or (and find-at-least |
160 (widen) | 154 (>= num-loci-found find-at-least)) |
161 (not (and (= min (point-min)) (= max (point-max))))))) | 155 (and limit-search (>= (point) limit-search))) |
162 | 156 ;; We have found as many new loci as the user wants, |
163 (defun diff-current-hunk () | 157 ;; or the user wanted a specific diff, and we're past it. |
164 ;; Returns the current diff hunk. | 158 (setq found-desired t))) |
165 (let ((pt (point)) | 159 (if found-desired |
166 (start 0) | 160 (setq compilation-parsing-end (point)) |
167 (end (1+ diff-total-hunks)) | 161 ;; Set to point-max, not point, so we don't perpetually |
168 m) | 162 ;; parse the last bit of text when it isn't a diff header. |
169 (while (> (- end start) 1) | 163 (setq compilation-parsing-end (point-max))) |
170 (setq m (/ (+ start end) 2)) | 164 (message "Parsing differences...done")) |
171 (if (>= pt (aref diff-hunk-vector m)) | 165 (setq compilation-error-list (nreverse compilation-error-list))) |
172 (setq start m) | 166 |
173 (setq end m))) | 167 ;;;###autoload |
174 (if (>= pt (aref diff-hunk-vector end)) | 168 (defun diff (old new &optional switches) |
175 (setq m end) | 169 "Find and display the differences between OLD and NEW files. |
176 (setq m start)) | 170 Interactively the current buffer's file name is the default for NEW |
177 ;; Don't treat end of buffer as start of next hunk | 171 and a backup file for NEW is the default for OLD. |
178 (if (eobp) (1- m) m))) | 172 With prefix arg, prompt for diff switches." |
179 | 173 (interactive |
180 (defun diff-hunk-min (n) | 174 (nconc |
181 ;; Returns the start of the current diff hunk. | 175 (let (oldf newf) |
182 (aref diff-hunk-vector n)) | 176 (nreverse |
183 | 177 (list |
184 (defun diff-hunk-max (n) | 178 (setq newf (buffer-file-name) |
185 ;; Returns the end of the current diff hunk. | |
186 (aref diff-hunk-vector (1+ n))) | |
187 | |
188 (defun diff-parse-hunks () | |
189 ;; Parses a buffer of diff output. | |
190 (save-excursion | |
191 (save-restriction | |
192 (message "Parsing differences...") | |
193 (widen) | |
194 (goto-char (point-min)) | |
195 (let ((hunks (list 1))) | |
196 (while (re-search-forward diff-hunk-pattern nil t) | |
197 (setq hunks (cons (match-beginning 0) hunks))) | |
198 (setq diff-total-hunks (1- (length hunks)) | |
199 diff-hunk-vector (apply 'vector | |
200 (nreverse (cons (point-max) hunks))))))) | |
201 (message "Parsing differences...done")) | |
202 | |
203 (defun diff-update-modeline () | |
204 ;; Updates the mode line to show current diff hunk. | |
205 (if (or (< (point) (diff-hunk-min diff-current-hunk)) | |
206 (>= (point) (diff-hunk-max diff-current-hunk))) | |
207 (progn | |
208 (setq diff-current-hunk (diff-current-hunk) | |
209 diff-current-difference (int-to-string diff-current-hunk)) | |
210 (set-buffer-modified-p (buffer-modified-p))))) | |
211 | |
212 (defun diff-read-args (oldprompt newprompt switchprompt | |
213 &optional file-for-backup) | |
214 ;; Grab the args for diff. OLDPROMPT and NEWPROMPT are the prompts | |
215 ;; for the old & new filenames, SWITCHPROMPT for the list of | |
216 ;; switches. If FILE_FOR_BACKUP is provided (it must be a string if | |
217 ;; so), then it will be used to try & work out a file & backup to | |
218 ;; diff, & in this case the prompting order is backwards. %s in a | |
219 ;; prompt has a guess substituted into it. This is nasty. | |
220 (let (oldf newf) | |
221 (if file-for-backup | |
222 (setq newf file-for-backup | |
223 newf (if (and newf (file-exists-p newf)) | 179 newf (if (and newf (file-exists-p newf)) |
224 (read-file-name | 180 (read-file-name |
225 (format newprompt (file-name-nondirectory newf)) | 181 (concat "Diff new file: (" |
182 (file-name-nondirectory newf) ") ") | |
226 nil newf t) | 183 nil newf t) |
227 (read-file-name (format newprompt "") nil nil t)) | 184 (read-file-name "Diff new file: " nil nil t))) |
228 oldf (file-newest-backup newf) | 185 (setq oldf (file-newest-backup newf) |
229 oldf (if (and oldf (file-exists-p oldf)) | 186 oldf (if (and oldf (file-exists-p oldf)) |
230 (read-file-name | 187 (read-file-name |
231 (format oldprompt (file-name-nondirectory oldf)) | 188 (concat "Diff original file: (" |
232 nil oldf t) | 189 (file-name-nondirectory oldf) ") ") |
233 (read-file-name (format oldprompt "") | 190 (file-name-directory oldf) oldf t) |
234 (file-name-directory newf) nil t))) | 191 (read-file-name "Diff original file: " |
235 ;; Else we aren't trying to be bright... | 192 (file-name-directory newf) nil t)))))) |
236 (setq oldf (read-file-name (format oldprompt "") nil nil t) | 193 (if current-prefix-arg |
237 newf (read-file-name | 194 (list (read-string "Diff switches: " |
238 (format newprompt (file-name-nondirectory oldf)) | 195 (if (stringp diff-switches) |
239 nil (file-name-directory oldf) t))) | 196 diff-switches |
240 (list oldf newf (diff-read-switches switchprompt)))) | 197 (mapconcat 'identity diff-switches " ")))) |
241 | 198 nil))) |
242 (defun diff-read-switches (switchprompt) | 199 (setq new (expand-file-name new) |
243 ;; Read and return a list of switches | 200 old (expand-file-name old)) |
244 (if current-prefix-arg | 201 ;; XEmacs addition -- allow (diff "../old/" "new-file.el") to work |
245 (let ((default (if (listp diff-switches) | 202 (cond ((file-directory-p old) |
246 (mapconcat 'identity diff-switches " ") | 203 (or (file-directory-p new) |
247 diff-switches))) | 204 (setq old (expand-file-name (file-name-nondirectory new) |
248 (diff-fix-switches | 205 (file-name-as-directory old))))) |
249 (read-string (format switchprompt default) default))))) | 206 ((file-directory-p new) |
250 | 207 (setq new (expand-file-name (file-name-nondirectory old) |
251 (defun diff-fix-switches (switch-spec) | 208 (file-name-as-directory new))))) |
252 ;; Parse a string into a list of switches or leave it be if it's | 209 (let ((old-alt (file-local-copy old)) |
253 ;; not a string | 210 (new-alt (file-local-copy new)) |
254 (if (stringp switch-spec) | 211 buf) |
255 (let (result (start 0)) | 212 (unwind-protect |
256 (while (string-match "\\(\\S-+\\)" switch-spec start) | 213 (let ((command |
257 (setq result (cons (substring switch-spec (match-beginning 1) | 214 (mapconcat 'identity |
258 (match-end 1)) | 215 (append (list diff-command) |
259 result) | 216 ;; Use explicitly specified switches |
260 start (match-end 0))) | 217 (if switches |
261 (nreverse result)) | 218 (if (consp switches) |
262 switch-spec)) | 219 switches (list switches)) |
263 | 220 ;; If not specified, use default. |
264 (defun diff-get-file-buffer (file) | 221 (if (consp diff-switches) |
265 ;; Returns \(BUFFER . DEL-P\), where DEL-P is t if diff is expected | 222 diff-switches |
266 ;; to delete the buffer, and nil otherwise. | 223 (list diff-switches))) |
267 (let* ((buff (get-file-buffer file)) | 224 (if (or old-alt new-alt) |
268 (del-p (null buff))) | 225 (list "-L" old "-L" new)) |
269 (if (and buff (buffer-modified-p buff)) | 226 (list |
270 (progn | 227 (shell-quote-argument (or old-alt old))) |
271 (message | 228 (list |
272 "Buffer %s is modified. Diffing against buffer contents." | 229 (shell-quote-argument (or new-alt new)))) |
273 (buffer-name buff)) | 230 " "))) |
274 (sit-for 1))) | 231 (setq buf |
275 ;; Call find-file-noselect even if we already have the buffer, | 232 (compile-internal command |
276 ;; as it will run verify-buffer-file-modtime. | 233 "No more differences" "Diff" |
277 (cons (find-file-noselect file) del-p))) | 234 'diff-parse-differences)) |
278 | 235 (pop-to-buffer buf) |
279 (defun diff-cleanup-buffers () | 236 ;; Avoid frightening people with "abnormally terminated" |
280 ;; Cleans up diff buffers by deleting buffers that we don't expect | 237 ;; if diff finds differences. |
281 ;; the user to care about. | 238 (set (make-local-variable 'compilation-exit-message-function) |
282 (let ((files (list diff-old-file diff-new-file))) | 239 (lambda (status code msg) |
283 (while files | 240 (cond ((not (eq status 'exit)) |
284 (let ((ent (car files)) | 241 (cons msg code)) |
285 buff) | 242 ((zerop code) |
286 (and (cdr ent) | 243 '("finished (no differences)\n" . "no differences")) |
287 (setq buff (get-file-buffer (car ent))) | 244 ((= code 1) |
288 (not (buffer-modified-p buff)) | 245 '("finished\n" . "differences found")) |
289 (kill-buffer buff))) | 246 (t |
290 (setq files (cdr files))) | 247 (cons msg code))))) |
291 (if (get-buffer "*Diff Header*") | 248 (set (make-local-variable 'diff-old-file) old) |
292 (kill-buffer "*Diff Header*")))) | 249 (set (make-local-variable 'diff-new-file) new) |
293 | 250 (set (make-local-variable 'diff-old-temp-file) old-alt) |
294 (defun diff-latest-backup-file (file) | 251 (set (make-local-variable 'diff-new-temp-file) new-alt) |
252 (set (make-local-variable 'compilation-finish-function) | |
253 (function (lambda (buff msg) | |
254 (if diff-old-temp-file | |
255 (delete-file diff-old-temp-file)) | |
256 (if diff-new-temp-file | |
257 (delete-file diff-new-temp-file))))) | |
258 buf)))) | |
259 | |
260 ;;;###autoload | |
261 (defun diff-backup (file &optional switches) | |
262 "Diff this file with its backup file or vice versa. | |
263 Uses the latest backup, if there are several numerical backups. | |
264 If this file is a backup, diff it with its original. | |
265 The backup file is the first file given to `diff'." | |
266 (interactive (list (read-file-name "Diff (file with backup): ") | |
267 (if current-prefix-arg | |
268 (read-string "Diff switches: " | |
269 (if (stringp diff-switches) | |
270 diff-switches | |
271 (mapconcat 'identity | |
272 diff-switches " "))) | |
273 nil))) | |
274 (let (bak ori) | |
275 (if (backup-file-name-p file) | |
276 (setq bak file | |
277 ori (file-name-sans-versions file)) | |
278 (setq bak (or (diff-latest-backup-file file) | |
279 (error "No backup found for %s" file)) | |
280 ori file)) | |
281 (diff bak ori switches))) | |
282 | |
283 (defun diff-latest-backup-file (fn) ; actually belongs into files.el | |
295 "Return the latest existing backup of FILE, or nil." | 284 "Return the latest existing backup of FILE, or nil." |
296 ;; First try simple backup, then the highest numbered of the | 285 (let ((handler (find-file-name-handler fn 'diff-latest-backup-file))) |
297 ;; numbered backups. | |
298 ;; Ignore the value of version-control because we look for existing | |
299 ;; backups, which maybe were made earlier or by another user with | |
300 ;; a different value of version-control. | |
301 (let* ((file (expand-file-name file)) | |
302 (handler (find-file-name-handler file 'diff-latest-backup-file))) | |
303 (if handler | 286 (if handler |
304 (funcall handler 'diff-latest-backup-file file) | 287 (funcall handler 'diff-latest-backup-file fn) |
288 ;; First try simple backup, then the highest numbered of the | |
289 ;; numbered backups. | |
290 ;; Ignore the value of version-control because we look for existing | |
291 ;; backups, which maybe were made earlier or by another user with | |
292 ;; a different value of version-control. | |
293 (setq fn (file-chase-links (expand-file-name fn))) | |
305 (or | 294 (or |
306 (let ((bak (make-backup-file-name file))) | 295 (let ((bak (make-backup-file-name fn))) |
307 (if (file-exists-p bak) bak)) | 296 (if (file-exists-p bak) bak)) |
308 (let* ((dir (file-name-directory file)) | 297 ;; We use BACKUPNAME to cope with backups stored in a different dir. |
309 (base-versions (concat (file-name-nondirectory file) ".~")) | 298 (let* ((backupname (car (find-backup-file-name fn))) |
299 (dir (file-name-directory backupname)) | |
300 (base-versions (concat (file-name-sans-versions | |
301 (file-name-nondirectory backupname)) | |
302 ".~")) | |
310 (bv-length (length base-versions))) | 303 (bv-length (length base-versions))) |
311 (concat dir | 304 (concat dir |
312 (car (sort | 305 (car (sort |
313 (file-name-all-completions base-versions dir) | 306 (file-name-all-completions base-versions dir) |
314 ;; bv-length is a fluid var for backup-extract-version: | 307 ;; bv-length is a fluid var for backup-extract-version: |
315 (function | 308 (function |
316 (lambda (fn1 fn2) | 309 (lambda (fn1 fn2) |
317 (> (backup-extract-version fn1) | 310 (> (backup-extract-version fn1) |
318 (backup-extract-version fn2)))))))))))) | 311 (backup-extract-version fn2)))))))))))) |
319 | 312 |
320 (defun diff-file-line (&optional old-file-p) | 313 (provide 'diff) |
321 "Return line number of current hunk in `diff-new-file'. | 314 |
322 With optional argument OLD-FILE-P, use `diff-old-file' instead." | 315 ;;; diff.el ends here |
323 (save-excursion | |
324 (let ((min (diff-hunk-min diff-current-hunk)) | |
325 (max (diff-hunk-max diff-current-hunk)) | |
326 (regexp (if old-file-p diff-old-file-pattern diff-new-file-pattern))) | |
327 (goto-char min) | |
328 (or (and regexp (re-search-forward regexp max t)) | |
329 (error "Unable to locate a file line for %s file." | |
330 (if old-file-p "old" "new"))) | |
331 (string-to-int (buffer-substring (match-beginning 1) (match-end 1)))))) | |
332 | |
333 (defun diff-run-diff (switches old old-temp new new-temp) | |
334 ;; Actually run the diff process with SWITCHES on OLD and NEW. | |
335 ;; OLD-TEMP and NEW-TEMP are names of temp files that can be used | |
336 ;; to dump the data out to. | |
337 (insert "diff " (mapconcat 'identity switches " ") " " old | |
338 " " new "\n") | |
339 (apply 'call-process "diff" nil t nil | |
340 (append switches (list old-temp new-temp)))) | |
341 | |
342 | |
343 (defun diff-fix-file-names (old old-temp new new-temp pattern) | |
344 ;; Replaces any temp file names with the real names of files. | |
345 (save-excursion | |
346 (save-restriction | |
347 (let ((files (list old new)) | |
348 (temps (list old-temp new-temp)) | |
349 buffer-read-only case-fold-search) | |
350 (goto-char (point-min)) | |
351 (if (re-search-forward pattern nil t) | |
352 (narrow-to-region (point-min) (match-beginning 0))) | |
353 (while files | |
354 (let ((regexp (concat "[ \t\n]\\(" | |
355 (regexp-quote (car temps)) | |
356 "\\)[ \t\n]"))) | |
357 (goto-char (point-min)) | |
358 (forward-line 1) | |
359 (while (re-search-forward regexp nil t) | |
360 (goto-char (match-beginning 1)) | |
361 (delete-region (point) (match-end 1)) | |
362 (insert (car files)))) | |
363 (setq files (cdr files) | |
364 temps (cdr temps))))))) | |
365 | |
366 ;;;; User commands | |
367 | |
368 (defun diff-mode () | |
369 "Diff Mode is used by \\[diff] for perusing the output from the diff program. | |
370 All normal editing commands are turned off. Instead, these are available: | |
371 \\<diff-mode-map> | |
372 \\[diff-advertised-scroll-up] Scroll to next screen of this difference. | |
373 \\[diff-advertised-scroll-down] Scroll to previous screen of this difference. | |
374 \\[diff-next-difference] Move to Next Difference. | |
375 \\[diff-previous-difference] Move to Previous Difference. | |
376 \\[diff-show-difference] Jump to difference specified by numeric position. | |
377 \\[diff-find-file] Find current diff in file | |
378 \\[diff-find-file-other-window] Find current diff in file in other window | |
379 \\[diff-display-file] Display file in other window | |
380 \\[diff-narrow] Narrow diff buffer to current difference | |
381 \\[widen] Widen diff buffer | |
382 \\[diff-show-header] Show diff header describing file name etc. | |
383 \\[diff-quit] Quit diff | |
384 " | |
385 (interactive) | |
386 (use-local-map diff-mode-map) | |
387 (diff-grok-keymap) | |
388 (setq buffer-read-only t | |
389 major-mode 'diff-mode | |
390 mode-name "Diff" | |
391 mode-line-modified "--- " | |
392 mode-line-process | |
393 '(" " diff-current-difference "/" diff-total-differences)) | |
394 (diff-parse-hunks) | |
395 (setq diff-total-differences (int-to-string diff-total-hunks))) | |
396 | |
397 ;;; Motion commands | |
398 | |
399 (defun diff-next-difference (n) | |
400 "In diff-mode go the the beginning of the next difference hunk." | |
401 (interactive "p") | |
402 (if (zerop n) | |
403 (goto-char (diff-hunk-min diff-current-hunk)) | |
404 (let ((narrow (diff-buffer-narrowed-p)) | |
405 (max (point-max)) | |
406 (min (point-min))) | |
407 (unwind-protect | |
408 (progn | |
409 (widen) | |
410 (setq diff-current-hunk (+ n diff-current-hunk)) | |
411 (cond ((> diff-current-hunk diff-total-hunks) | |
412 (setq diff-current-hunk diff-total-hunks) | |
413 (message "No following difference hunks.")) | |
414 ((< diff-current-hunk 0) | |
415 (setq diff-current-hunk 0) | |
416 (message "No preceding difference hunks."))) | |
417 (setq diff-current-difference (int-to-string diff-current-hunk) | |
418 min (goto-char (diff-hunk-min diff-current-hunk)) | |
419 max (diff-hunk-max diff-current-hunk))) | |
420 (if narrow (narrow-to-region min max)))) | |
421 (set-buffer-modified-p (buffer-modified-p)))) | |
422 | |
423 (defun diff-previous-difference (n) | |
424 "In diff-mode go the the beginning of the previous difference hunk." | |
425 (interactive "p") | |
426 (diff-next-difference (- n))) | |
427 | |
428 (defun diff-next-line (n) | |
429 "In diff-mode go to the next line." | |
430 (interactive "p") | |
431 (condition-case nil | |
432 (next-line n) | |
433 (error (if (> n 0) (message "End of difference hunk")))) | |
434 (diff-update-modeline)) | |
435 | |
436 (defun diff-previous-line (n) | |
437 "In diff-mode go to the previous line." | |
438 (interactive "p") | |
439 (diff-next-line (- n))) | |
440 | |
441 (defun diff-forward-char (n) | |
442 "In diff-mode move the point forward." | |
443 (interactive "p") | |
444 (forward-char n) | |
445 (diff-update-modeline)) | |
446 | |
447 (defun diff-backward-char (n) | |
448 "In diff-mode move the point backward." | |
449 (interactive "p") | |
450 (backward-char n) | |
451 (diff-update-modeline)) | |
452 | |
453 (defun diff-scroll-up (n) | |
454 "In diff-mode scroll the buffer up." | |
455 (interactive "P") | |
456 (scroll-up n) | |
457 (diff-update-modeline)) | |
458 | |
459 (fset 'diff-advertised-scroll-up 'diff-scroll-up) | |
460 | |
461 (defun diff-scroll-down (n) | |
462 "In diff-mode scroll the buffer down." | |
463 (interactive "P") | |
464 (scroll-down n) | |
465 (diff-update-modeline)) | |
466 | |
467 (fset 'diff-advertised-scroll-down 'diff-scroll-down) | |
468 | |
469 (defun diff-beginning-of-buffer (n) | |
470 "In diff-mode go to the beginning of the buffer." | |
471 (interactive "P") | |
472 (beginning-of-buffer n) | |
473 (diff-update-modeline)) | |
474 | |
475 (defun diff-end-of-buffer (n) | |
476 "In diff-mode go to the end of the buffer." | |
477 (interactive "P") | |
478 (end-of-buffer n) | |
479 (diff-update-modeline)) | |
480 | |
481 ;;; The main command. | |
482 | |
483 ;;;###autoload | |
484 (defun diff (old new &optional switches) | |
485 "Find and display the differences between OLD and NEW files. | |
486 Interactively you are prompted with the current buffer's file name for NEW | |
487 and what appears to be its backup for OLD." | |
488 ;; Support for diffing directories is rather limited. It needs work. | |
489 (interactive (diff-read-args "Diff original file (%s) " | |
490 "Diff new file (%s) " | |
491 "Switches for diff (%s) " | |
492 (buffer-file-name))) | |
493 (setq switches (diff-fix-switches (or switches diff-switches)) | |
494 old (expand-file-name old) | |
495 new (expand-file-name new)) | |
496 (let ((curr-buff (current-buffer)) | |
497 doing-dirs old-temp new-temp old-buffer new-buffer flag) | |
498 (let ((fdp-old (file-directory-p old)) | |
499 (fdp-new (file-directory-p new))) | |
500 (cond | |
501 ((null (or fdp-new fdp-old))) | |
502 ((null fdp-new) | |
503 (setq old (expand-file-name (file-name-nondirectory new) old))) | |
504 ((null fdp-old) | |
505 (setq new (expand-file-name (file-name-nondirectory old) new))) | |
506 (t (setq doing-dirs t)))) | |
507 ;; (message "diff %s %s %s..." | |
508 ;; (mapconcat (function identity) switches " ") new old) | |
509 (message "diff %s %s %s..." | |
510 (mapconcat (function identity) switches " ") old new) | |
511 (if doing-dirs | |
512 (setq diff-old-file nil | |
513 diff-new-file nil) | |
514 (setq old-temp (make-temp-name (concat diff-temp-template "1")) | |
515 new-temp (make-temp-name (concat diff-temp-template "2")) | |
516 old-buffer (diff-get-file-buffer old) | |
517 new-buffer (diff-get-file-buffer new) | |
518 diff-old-file (cons old (cdr old-buffer)) | |
519 diff-new-file (cons new (cdr new-buffer)))) | |
520 (let (case-fold-search) | |
521 (mapcar (function | |
522 (lambda (x) | |
523 (if (string-match "[ecu]" x) | |
524 (setq flag (aref x (match-beginning 0)))))) | |
525 switches)) | |
526 (unwind-protect | |
527 (let ((patterns (assq flag diff-search-pattern-alist))) | |
528 (set-buffer (get-buffer-create "*Diff Output*")) | |
529 (setq default-directory (file-name-directory new) | |
530 diff-old-file-pattern (nth 2 patterns) | |
531 diff-new-file-pattern (nth 3 patterns) | |
532 diff-hunk-pattern (nth 1 patterns)) | |
533 (let (buffer-read-only) | |
534 (if (fboundp 'buffer-disable-undo) | |
535 (buffer-disable-undo (current-buffer)) | |
536 ;; old style (Emacs 18.55 and earlier) | |
537 (buffer-disable-undo (current-buffer))) | |
538 (widen) | |
539 (erase-buffer) | |
540 (if doing-dirs | |
541 (progn | |
542 (diff-run-diff switches old old new new) | |
543 (setq diff-hunk-pattern (concat diff-hunk-pattern | |
544 "\\|^Only in "))) | |
545 (save-excursion | |
546 (set-buffer (car old-buffer)) | |
547 (write-region (point-min) (point-max) old-temp nil 'quiet) | |
548 (set-buffer (car new-buffer)) | |
549 (write-region (point-min) (point-max) new-temp nil 'quiet)) | |
550 (diff-run-diff switches old old-temp new new-temp)) | |
551 ;; Need to replace file names | |
552 (if (and (not doing-dirs) (memq flag '(?c ?u))) | |
553 (diff-fix-file-names old old-temp new new-temp | |
554 diff-hunk-pattern)) | |
555 (diff-mode) | |
556 (goto-char (point-min)) | |
557 (setq diff-current-difference "0" | |
558 diff-current-hunk 0) | |
559 (if (zerop diff-total-hunks) | |
560 (progn | |
561 (diff-cleanup-buffers) | |
562 (message "No differences")) | |
563 (if diff-do-narrow (narrow-to-region (point) (diff-hunk-max 0))) | |
564 (display-buffer (current-buffer)) | |
565 (message "%s difference hunk%s" diff-total-differences | |
566 (if (= diff-total-hunks 1) "" "s"))))) | |
567 (condition-case nil | |
568 (delete-file old-temp) | |
569 (error nil)) | |
570 (condition-case nil | |
571 (delete-file new-temp) | |
572 (error nil)) | |
573 (set-buffer curr-buff)))) | |
574 | |
575 ;;;###autoload | |
576 (defun diff-backup (file &optional switches) | |
577 "Diff this file with its backup file or vice versa. | |
578 Uses the latest backup, if there are several numerical backups. | |
579 If this file is a backup, diff it with its original. | |
580 The backup file is the first file given to `diff'." | |
581 (interactive (list (read-file-name "Diff (file with backup): ") | |
582 (and current-prefix-arg | |
583 (diff-read-switches "Diff switches: ")))) | |
584 (let (bak ori) | |
585 (if (backup-file-name-p file) | |
586 (setq bak file | |
587 ori (file-name-sans-versions file)) | |
588 (setq bak (or (diff-latest-backup-file file) | |
589 (error "No backup found for %s" file)) | |
590 ori file)) | |
591 (diff bak ori switches))) | |
592 | |
593 (defun diff-show-difference (n) | |
594 "Show difference number N (prefix argument)." | |
595 (interactive "p") | |
596 (let ((narrowedp (diff-buffer-narrowed-p)) | |
597 (min (diff-hunk-min diff-current-hunk)) | |
598 (max (diff-hunk-max diff-current-hunk))) | |
599 (unwind-protect | |
600 (progn | |
601 (widen) | |
602 (cond | |
603 ((< n 0) | |
604 (message "No negative hunks.") | |
605 (setq n 0)) | |
606 ((> n diff-total-hunks) | |
607 (message "No hunk %d." n) | |
608 (setq n diff-total-hunks))) | |
609 (setq diff-current-hunk n | |
610 diff-current-difference (int-to-string diff-current-hunk) | |
611 min (diff-hunk-min n) | |
612 max (diff-hunk-max n)) | |
613 (goto-char min)) | |
614 (if narrowedp (narrow-to-region min max)) | |
615 (set-buffer-modified-p (buffer-modified-p))))) | |
616 | |
617 (defun diff-show-header () | |
618 "Show `diff-header'." | |
619 (interactive) | |
620 (with-output-to-temp-buffer "*Diff Header*" | |
621 (princ (save-restriction | |
622 (widen) | |
623 (buffer-substring (diff-hunk-min 0) (diff-hunk-max 0)))))) | |
624 | |
625 | |
626 (defun diff-find-file (old-file-p) | |
627 "Visit diffed file, at the point corresponding to the current hunk. | |
628 Default is to visit the new file; prefix means visit old file instead." | |
629 (interactive "P") | |
630 (let ((line (diff-file-line old-file-p))) | |
631 (find-file | |
632 (if old-file-p | |
633 (car diff-old-file) | |
634 (car diff-new-file))) | |
635 (goto-line line) | |
636 (recenter 0))) | |
637 | |
638 (defun diff-find-file-other-window (old-file-p) | |
639 "Visit the diffed file in other window, with the point at the current hunk. | |
640 Default is to visit the new file; prefix means visit the old file instead." | |
641 (interactive "P") | |
642 (let ((line (diff-file-line old-file-p))) | |
643 (find-file-other-window | |
644 (if old-file-p | |
645 (car diff-old-file) | |
646 (car diff-new-file))) | |
647 (goto-line line) | |
648 (recenter 0))) | |
649 | |
650 (defun diff-find-file-other-frame (old-file-p) | |
651 "Visit the diffed file in other frame, with point at the current hunk. | |
652 Default is to visit the new file; prefix means visit the old file instead." | |
653 (interactive "P") | |
654 (let ((line (diff-file-line old-file-p))) | |
655 (find-file-other-frame | |
656 (if old-file-p | |
657 (car diff-old-file) | |
658 (car diff-new-file))) | |
659 (goto-line line) | |
660 (recenter 0))) | |
661 | |
662 (defun diff-display-file (old-file-p) | |
663 "Display the diffed file in other window, with point at the current hunk. | |
664 Default is to visit the new file; prefix means visit the old file instead." | |
665 (interactive "P") | |
666 (let ((line (diff-file-line old-file-p)) | |
667 (wind (display-buffer (find-file-noselect (if old-file-p | |
668 (car diff-old-file) | |
669 (car diff-new-file))))) | |
670 (curr-wind (selected-window))) | |
671 (unwind-protect | |
672 (progn | |
673 (select-window wind) | |
674 (goto-line line) | |
675 (recenter 0)) | |
676 (select-window curr-wind)))) | |
677 | |
678 (defun diff-quit () | |
679 "Quit diff by killing the diff buffer." | |
680 (interactive) | |
681 (kill-buffer "*Diff Output*") | |
682 (diff-cleanup-buffers)) | |
683 | |
684 (defun diff-narrow () | |
685 "Narrow diff buffer to current difference hunk." | |
686 (interactive) | |
687 (narrow-to-region (diff-hunk-min diff-current-hunk) | |
688 (diff-hunk-max diff-current-hunk))) | |
689 | |
690 ;;; Run any load hooks | |
691 (run-hooks 'diff-load-hook) | |
692 | |
693 ;;; end of diff.el |