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