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 | ||
b2e44819 SM |
35 | (eval-when-compile (require 'cl)) |
36 | ||
85b6275f RS |
37 | (defgroup diff nil |
38 | "Comparing files with `diff'." | |
39 | :group 'tools) | |
40 | ||
41 | ;;;###autoload | |
6bdad9ae | 42 | (defcustom diff-switches (purecopy "-c") |
9201cc28 | 43 | "A string or list of strings specifying switches to be passed to diff." |
85b6275f RS |
44 | :type '(choice string (repeat string)) |
45 | :group 'diff) | |
46 | ||
47 | ;;;###autoload | |
6bdad9ae | 48 | (defcustom diff-command (purecopy "diff") |
9201cc28 | 49 | "The command to use to run diff." |
660d4800 | 50 | :type 'string |
85b6275f | 51 | :group 'diff) |
d009603c | 52 | |
4c11f6a8 SM |
53 | ;; prompt if prefix arg present |
54 | (defun diff-switches () | |
55 | (if current-prefix-arg | |
56 | (read-string "Diff switches: " | |
57 | (if (stringp diff-switches) | |
58 | diff-switches | |
59 | (mapconcat 'identity diff-switches " "))))) | |
60 | ||
c879436a | 61 | (defun diff-sentinel (code &optional old-temp-file new-temp-file) |
540d0666 | 62 | "Code run when the diff process exits. |
4837b516 | 63 | CODE is the exit code of the process. It should be 0 only if no diffs |
e7c1dca8 CY |
64 | were found. |
65 | If optional args OLD-TEMP-FILE and/or NEW-TEMP-FILE are non-nil, | |
66 | delete the temporary files so named." | |
b2e44819 SM |
67 | (if old-temp-file (delete-file old-temp-file)) |
68 | (if new-temp-file (delete-file new-temp-file)) | |
07875ee7 | 69 | (diff-setup-whitespace) |
540d0666 SM |
70 | (save-excursion |
71 | (goto-char (point-max)) | |
53dd481c CY |
72 | (let ((inhibit-read-only t)) |
73 | (insert (format "\nDiff finished%s. %s\n" | |
d02b0e30 CY |
74 | (cond ((equal 0 code) " (no differences)") |
75 | ((equal 2 code) " (diff error)") | |
76 | (t "")) | |
53dd481c | 77 | (current-time-string)))))) |
540d0666 | 78 | |
b10a4379 | 79 | ;;;###autoload |
0f7b0f88 | 80 | (defun diff (old new &optional switches no-async) |
b10a4379 | 81 | "Find and display the differences between OLD and NEW files. |
fc233c9d LMI |
82 | When called interactively, read NEW, then OLD, using the |
83 | minibuffer. The default for NEW is the current buffer's file | |
84 | name, and the default for OLD is a backup file for NEW, if one | |
85 | exists. If NO-ASYNC is non-nil, call diff synchronously. | |
55fdbbb5 CY |
86 | |
87 | When called interactively with a prefix argument, prompt | |
88 | interactively for diff switches. Otherwise, the switches | |
89 | specified in `diff-switches' are passed to the diff command." | |
b10a4379 | 90 | (interactive |
5a973d51 | 91 | (let* ((newf (if (and buffer-file-name (file-exists-p buffer-file-name)) |
4c11f6a8 | 92 | (read-file-name |
5b76833f | 93 | (concat "Diff new file (default " |
5a973d51 SM |
94 | (file-name-nondirectory buffer-file-name) "): ") |
95 | nil buffer-file-name t) | |
4c11f6a8 | 96 | (read-file-name "Diff new file: " nil nil t))) |
5a973d51 | 97 | (oldf (file-newest-backup newf))) |
b2e44819 | 98 | (setq oldf (if (and oldf (file-exists-p oldf)) |
4c11f6a8 | 99 | (read-file-name |
5b76833f RF |
100 | (concat "Diff original file (default " |
101 | (file-name-nondirectory oldf) "): ") | |
4c11f6a8 SM |
102 | (file-name-directory oldf) oldf t) |
103 | (read-file-name "Diff original file: " | |
104 | (file-name-directory newf) nil t))) | |
105 | (list oldf newf (diff-switches)))) | |
b2e44819 SM |
106 | (display-buffer |
107 | (diff-no-select old new switches no-async))) | |
108 | ||
109 | (defun diff-file-local-copy (file-or-buf) | |
110 | (if (bufferp file-or-buf) | |
111 | (with-current-buffer file-or-buf | |
112 | (let ((tempfile (make-temp-file "buffer-content-"))) | |
113 | (write-region nil nil tempfile nil 'nomessage) | |
114 | tempfile)) | |
115 | (file-local-copy file-or-buf))) | |
116 | ||
b2e44819 SM |
117 | (defun diff-no-select (old new &optional switches no-async buf) |
118 | ;; Noninteractive helper for creating and reverting diff buffers | |
f48fdaad CY |
119 | (unless (bufferp new) (setq new (expand-file-name new))) |
120 | (unless (bufferp old) (setq old (expand-file-name old))) | |
17c91d79 | 121 | (or switches (setq switches diff-switches)) ; If not specified, use default. |
b2e44819 | 122 | (unless (listp switches) (setq switches (list switches))) |
6a7662bb | 123 | (or buf (setq buf (get-buffer-create "*Diff*"))) |
b2e44819 SM |
124 | (let* ((old-alt (diff-file-local-copy old)) |
125 | (new-alt (diff-file-local-copy new)) | |
540d0666 SM |
126 | (command |
127 | (mapconcat 'identity | |
128 | `(,diff-command | |
129 | ;; Use explicitly specified switches | |
b2e44819 SM |
130 | ,@switches |
131 | ,@(mapcar #'shell-quote-argument | |
132 | (nconc | |
133 | (when (or old-alt new-alt) | |
134 | (list "-L" (if (stringp old) | |
135 | old (prin1-to-string old)) | |
136 | "-L" (if (stringp new) | |
137 | new (prin1-to-string new)))) | |
138 | (list (or old-alt old) | |
139 | (or new-alt new))))) | |
540d0666 | 140 | " ")) |
b2e44819 SM |
141 | (thisdir default-directory)) |
142 | (with-current-buffer buf | |
143 | (setq buffer-read-only t) | |
540d0666 | 144 | (buffer-disable-undo (current-buffer)) |
53dd481c CY |
145 | (let ((inhibit-read-only t)) |
146 | (erase-buffer)) | |
540d0666 SM |
147 | (buffer-enable-undo (current-buffer)) |
148 | (diff-mode) | |
149 | (set (make-local-variable 'revert-buffer-function) | |
e95a67dc SM |
150 | (lambda (_ignore-auto _noconfirm) |
151 | (diff-no-select old new switches no-async (current-buffer)))) | |
de9dc5e5 | 152 | (setq default-directory thisdir) |
53dd481c CY |
153 | (let ((inhibit-read-only t)) |
154 | (insert command "\n")) | |
540d0666 | 155 | (if (and (not no-async) (fboundp 'start-process)) |
b2e44819 SM |
156 | (let ((proc (start-process "Diff" buf shell-file-name |
157 | shell-command-switch command))) | |
53dd481c | 158 | (set-process-filter proc 'diff-process-filter) |
e95a67dc SM |
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 |