Commit | Line | Data |
---|---|---|
60370d40 | 1 | ;;; diff.el --- run `diff' in compilation-mode |
c0274f38 | 2 | |
90324359 | 3 | ;; Copyright (C) 1992, 1994, 1996, 2001, 2002, 2003, 2004, 2005, 2006, |
5df4f04c | 4 | ;; 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. |
3a801d0c | 5 | |
90324359 GM |
6 | ;; Author: Frank Bresz |
7 | ;; (according to authors.el) | |
6228c05b | 8 | ;; Maintainer: FSF |
9766adfb | 9 | ;; Keywords: unix, vc, tools |
e5167999 | 10 | |
b10a4379 JB |
11 | ;; This file is part of GNU Emacs. |
12 | ||
eb3fa2cf | 13 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
b10a4379 | 14 | ;; it under the terms of the GNU General Public License as published by |
eb3fa2cf GM |
15 | ;; the Free Software Foundation, either version 3 of the License, or |
16 | ;; (at your option) any later version. | |
b10a4379 JB |
17 | |
18 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | ;; GNU General Public License for more details. | |
22 | ||
23 | ;; You should have received a copy of the GNU General Public License | |
eb3fa2cf | 24 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
b10a4379 | 25 | |
e41b2db1 ER |
26 | ;;; Commentary: |
27 | ||
28 | ;; This package helps you explore differences between files, using the | |
29 | ;; UNIX command diff(1). The commands are `diff' and `diff-backup'. | |
30 | ;; You can specify options with `diff-switches'. | |
31 | ||
e5167999 ER |
32 | ;;; Code: |
33 | ||
b2e44819 SM |
34 | (eval-when-compile (require 'cl)) |
35 | ||
85b6275f RS |
36 | (defgroup diff nil |
37 | "Comparing files with `diff'." | |
38 | :group 'tools) | |
39 | ||
40 | ;;;###autoload | |
6bdad9ae | 41 | (defcustom diff-switches (purecopy "-c") |
9201cc28 | 42 | "A string or list of strings specifying switches to be passed to diff." |
85b6275f RS |
43 | :type '(choice string (repeat string)) |
44 | :group 'diff) | |
45 | ||
46 | ;;;###autoload | |
6bdad9ae | 47 | (defcustom diff-command (purecopy "diff") |
9201cc28 | 48 | "The command to use to run diff." |
660d4800 | 49 | :type 'string |
85b6275f | 50 | :group 'diff) |
d009603c | 51 | |
4c11f6a8 SM |
52 | ;; prompt if prefix arg present |
53 | (defun diff-switches () | |
54 | (if current-prefix-arg | |
55 | (read-string "Diff switches: " | |
56 | (if (stringp diff-switches) | |
57 | diff-switches | |
58 | (mapconcat 'identity diff-switches " "))))) | |
59 | ||
c879436a | 60 | (defun diff-sentinel (code &optional old-temp-file new-temp-file) |
540d0666 | 61 | "Code run when the diff process exits. |
4837b516 GM |
62 | CODE is the exit code of the process. It should be 0 only if no diffs |
63 | were found." | |
b2e44819 SM |
64 | (if old-temp-file (delete-file old-temp-file)) |
65 | (if new-temp-file (delete-file new-temp-file)) | |
540d0666 SM |
66 | (save-excursion |
67 | (goto-char (point-max)) | |
53dd481c CY |
68 | (let ((inhibit-read-only t)) |
69 | (insert (format "\nDiff finished%s. %s\n" | |
d02b0e30 CY |
70 | (cond ((equal 0 code) " (no differences)") |
71 | ((equal 2 code) " (diff error)") | |
72 | (t "")) | |
53dd481c | 73 | (current-time-string)))))) |
540d0666 | 74 | |
b10a4379 | 75 | ;;;###autoload |
0f7b0f88 | 76 | (defun diff (old new &optional switches no-async) |
b10a4379 | 77 | "Find and display the differences between OLD and NEW files. |
55fdbbb5 CY |
78 | When called interactively, read OLD and NEW using the minibuffer; |
79 | the default for NEW is the current buffer's file name, and the | |
80 | default for OLD is a backup file for NEW, if one exists. | |
4c11f6a8 | 81 | If NO-ASYNC is non-nil, call diff synchronously. |
55fdbbb5 CY |
82 | |
83 | When called interactively with a prefix argument, prompt | |
84 | interactively for diff switches. Otherwise, the switches | |
85 | specified in `diff-switches' are passed to the diff command." | |
b10a4379 | 86 | (interactive |
5a973d51 | 87 | (let* ((newf (if (and buffer-file-name (file-exists-p buffer-file-name)) |
4c11f6a8 | 88 | (read-file-name |
5b76833f | 89 | (concat "Diff new file (default " |
5a973d51 SM |
90 | (file-name-nondirectory buffer-file-name) "): ") |
91 | nil buffer-file-name t) | |
4c11f6a8 | 92 | (read-file-name "Diff new file: " nil nil t))) |
5a973d51 | 93 | (oldf (file-newest-backup newf))) |
b2e44819 | 94 | (setq oldf (if (and oldf (file-exists-p oldf)) |
4c11f6a8 | 95 | (read-file-name |
5b76833f RF |
96 | (concat "Diff original file (default " |
97 | (file-name-nondirectory oldf) "): ") | |
4c11f6a8 SM |
98 | (file-name-directory oldf) oldf t) |
99 | (read-file-name "Diff original file: " | |
100 | (file-name-directory newf) nil t))) | |
101 | (list oldf newf (diff-switches)))) | |
b2e44819 SM |
102 | (display-buffer |
103 | (diff-no-select old new switches no-async))) | |
104 | ||
105 | (defun diff-file-local-copy (file-or-buf) | |
106 | (if (bufferp file-or-buf) | |
107 | (with-current-buffer file-or-buf | |
108 | (let ((tempfile (make-temp-file "buffer-content-"))) | |
109 | (write-region nil nil tempfile nil 'nomessage) | |
110 | tempfile)) | |
111 | (file-local-copy file-or-buf))) | |
112 | ||
b2e44819 SM |
113 | (defun diff-no-select (old new &optional switches no-async buf) |
114 | ;; Noninteractive helper for creating and reverting diff buffers | |
f48fdaad CY |
115 | (unless (bufferp new) (setq new (expand-file-name new))) |
116 | (unless (bufferp old) (setq old (expand-file-name old))) | |
17c91d79 | 117 | (or switches (setq switches diff-switches)) ; If not specified, use default. |
b2e44819 | 118 | (unless (listp switches) (setq switches (list switches))) |
6a7662bb | 119 | (or buf (setq buf (get-buffer-create "*Diff*"))) |
b2e44819 SM |
120 | (let* ((old-alt (diff-file-local-copy old)) |
121 | (new-alt (diff-file-local-copy new)) | |
540d0666 SM |
122 | (command |
123 | (mapconcat 'identity | |
124 | `(,diff-command | |
125 | ;; Use explicitly specified switches | |
b2e44819 SM |
126 | ,@switches |
127 | ,@(mapcar #'shell-quote-argument | |
128 | (nconc | |
129 | (when (or old-alt new-alt) | |
130 | (list "-L" (if (stringp old) | |
131 | old (prin1-to-string old)) | |
132 | "-L" (if (stringp new) | |
133 | new (prin1-to-string new)))) | |
134 | (list (or old-alt old) | |
135 | (or new-alt new))))) | |
540d0666 | 136 | " ")) |
b2e44819 SM |
137 | (thisdir default-directory)) |
138 | (with-current-buffer buf | |
139 | (setq buffer-read-only t) | |
540d0666 | 140 | (buffer-disable-undo (current-buffer)) |
53dd481c CY |
141 | (let ((inhibit-read-only t)) |
142 | (erase-buffer)) | |
540d0666 SM |
143 | (buffer-enable-undo (current-buffer)) |
144 | (diff-mode) | |
145 | (set (make-local-variable 'revert-buffer-function) | |
b2e44819 SM |
146 | (lexical-let ((old old) (new new) |
147 | (switches switches) | |
148 | (no-async no-async)) | |
149 | (lambda (ignore-auto noconfirm) | |
150 | (diff-no-select old new switches no-async (current-buffer))))) | |
de9dc5e5 | 151 | (setq default-directory thisdir) |
53dd481c CY |
152 | (let ((inhibit-read-only t)) |
153 | (insert command "\n")) | |
540d0666 | 154 | (if (and (not no-async) (fboundp 'start-process)) |
b2e44819 SM |
155 | (let ((proc (start-process "Diff" buf shell-file-name |
156 | shell-command-switch command))) | |
53dd481c | 157 | (set-process-filter proc 'diff-process-filter) |
b2e44819 SM |
158 | (lexical-let ((old-alt old-alt) (new-alt new-alt)) |
159 | (set-process-sentinel | |
160 | proc (lambda (proc msg) | |
161 | (with-current-buffer (process-buffer proc) | |
162 | (diff-sentinel (process-exit-status proc) | |
163 | old-alt new-alt)))))) | |
540d0666 | 164 | ;; Async processes aren't available. |
53dd481c CY |
165 | (let ((inhibit-read-only t)) |
166 | (diff-sentinel | |
167 | (call-process shell-file-name nil buf nil | |
b2e44819 SM |
168 | shell-command-switch command) |
169 | old-alt new-alt)))) | |
540d0666 | 170 | buf)) |
aa228418 | 171 | |
53dd481c CY |
172 | (defun diff-process-filter (proc string) |
173 | (with-current-buffer (process-buffer proc) | |
174 | (let ((moving (= (point) (process-mark proc)))) | |
175 | (save-excursion | |
176 | ;; Insert the text, advancing the process marker. | |
177 | (goto-char (process-mark proc)) | |
178 | (let ((inhibit-read-only t)) | |
179 | (insert string)) | |
180 | (set-marker (process-mark proc) (point))) | |
181 | (if moving (goto-char (process-mark proc)))))) | |
182 | ||
646bd331 RM |
183 | ;;;###autoload |
184 | (defun diff-backup (file &optional switches) | |
37c51f00 RS |
185 | "Diff this file with its backup file or vice versa. |
186 | Uses the latest backup, if there are several numerical backups. | |
187 | If this file is a backup, diff it with its original. | |
4c11f6a8 SM |
188 | The backup file is the first file given to `diff'. |
189 | With prefix arg, prompt for diff switches." | |
646bd331 | 190 | (interactive (list (read-file-name "Diff (file with backup): ") |
4c11f6a8 | 191 | (diff-switches))) |
37c51f00 RS |
192 | (let (bak ori) |
193 | (if (backup-file-name-p file) | |
194 | (setq bak file | |
195 | ori (file-name-sans-versions file)) | |
196 | (setq bak (or (diff-latest-backup-file file) | |
197 | (error "No backup found for %s" file)) | |
198 | ori file)) | |
646bd331 | 199 | (diff bak ori switches))) |
37c51f00 RS |
200 | |
201 | (defun diff-latest-backup-file (fn) ; actually belongs into files.el | |
202 | "Return the latest existing backup of FILE, or nil." | |
a617e913 | 203 | (let ((handler (find-file-name-handler fn 'diff-latest-backup-file))) |
34c46d87 | 204 | (if handler |
6d0d0e8d | 205 | (funcall handler 'diff-latest-backup-file fn) |
a8090e38 | 206 | (file-newest-backup fn)))) |
aa228418 | 207 | |
b2e44819 SM |
208 | ;;;###autoload |
209 | (defun diff-buffer-with-file (&optional buffer) | |
210 | "View the differences between BUFFER and its associated file. | |
211 | This requires the external program `diff' to be in your `exec-path'." | |
212 | (interactive "bBuffer: ") | |
213 | (with-current-buffer (get-buffer (or buffer (current-buffer))) | |
214 | (diff buffer-file-name (current-buffer) nil 'noasync))) | |
215 | ||
a4ef6584 ER |
216 | (provide 'diff) |
217 | ||
c0274f38 | 218 | ;;; diff.el ends here |