Replace lexical-let by lexical-binding (except Gnus, CEDET, ERT).
[bpt/emacs.git] / lisp / vc / diff.el
CommitLineData
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 63CODE is the exit code of the process. It should be 0 only if no diffs
e7c1dca8
CY
64were found.
65If optional args OLD-TEMP-FILE and/or NEW-TEMP-FILE are non-nil,
66delete 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
82When called interactively, read NEW, then OLD, using the
83minibuffer. The default for NEW is the current buffer's file
84name, and the default for OLD is a backup file for NEW, if one
85exists. If NO-ASYNC is non-nil, call diff synchronously.
55fdbbb5
CY
86
87When called interactively with a prefix argument, prompt
88interactively for diff switches. Otherwise, the switches
89specified 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.
186Uses the latest backup, if there are several numerical backups.
187If this file is a backup, diff it with its original.
4c11f6a8
SM
188The backup file is the first file given to `diff'.
189With 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.
211This 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