More tweaks of skeleton documentation wrt \n behavior at bol/eol.
[bpt/emacs.git] / lisp / vc / diff.el
CommitLineData
e95a67dc 1;;; diff.el --- run `diff' -*- lexical-binding: t -*-
c0274f38 2
ba318903 3;; Copyright (C) 1992, 1994, 1996, 2001-2014 Free Software Foundation,
ab422c4d 4;; Inc.
3a801d0c 5
90324359
GM
6;; Author: Frank Bresz
7;; (according to authors.el)
34dc21db 8;; Maintainer: emacs-devel@gnu.org
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
07875ee7
CY
34(declare-function diff-setup-whitespace "diff-mode" ())
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 62CODE is the exit code of the process. It should be 0 only if no diffs
e7c1dca8
CY
63were found.
64If optional args OLD-TEMP-FILE and/or NEW-TEMP-FILE are non-nil,
65delete the temporary files so named."
b2e44819
SM
66 (if old-temp-file (delete-file old-temp-file))
67 (if new-temp-file (delete-file new-temp-file))
07875ee7 68 (diff-setup-whitespace)
d1d2e2e8 69 (goto-char (point-min))
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
9fc9a531 89specified in the variable `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
5109429f
GM
117(defvar diff-use-labels 'check
118 "Whether `diff-command' understands the \"--label\" option.
119Possible values are:
120 t -- yes, it does
121 nil -- no, it does not
122 check -- try to probe whether it does")
123
b2e44819
SM
124(defun diff-no-select (old new &optional switches no-async buf)
125 ;; Noninteractive helper for creating and reverting diff buffers
f48fdaad
CY
126 (unless (bufferp new) (setq new (expand-file-name new)))
127 (unless (bufferp old) (setq old (expand-file-name old)))
17c91d79 128 (or switches (setq switches diff-switches)) ; If not specified, use default.
b2e44819 129 (unless (listp switches) (setq switches (list switches)))
6a7662bb 130 (or buf (setq buf (get-buffer-create "*Diff*")))
5109429f
GM
131 (when (eq 'check diff-use-labels)
132 (setq diff-use-labels
133 (with-temp-buffer
134 (when (ignore-errors (call-process diff-command nil t nil "--help"))
135 (if (search-backward "--label" nil t) t)))))
b2e44819
SM
136 (let* ((old-alt (diff-file-local-copy old))
137 (new-alt (diff-file-local-copy new))
540d0666
SM
138 (command
139 (mapconcat 'identity
140 `(,diff-command
141 ;; Use explicitly specified switches
b2e44819
SM
142 ,@switches
143 ,@(mapcar #'shell-quote-argument
144 (nconc
5109429f
GM
145 (and (or old-alt new-alt)
146 (eq diff-use-labels t)
147 (list "--label"
148 (if (stringp old) old
149 (prin1-to-string old))
150 "--label"
151 (if (stringp new) new
152 (prin1-to-string new))))
b2e44819
SM
153 (list (or old-alt old)
154 (or new-alt new)))))
540d0666 155 " "))
b2e44819
SM
156 (thisdir default-directory))
157 (with-current-buffer buf
158 (setq buffer-read-only t)
540d0666 159 (buffer-disable-undo (current-buffer))
53dd481c
CY
160 (let ((inhibit-read-only t))
161 (erase-buffer))
540d0666
SM
162 (buffer-enable-undo (current-buffer))
163 (diff-mode)
164 (set (make-local-variable 'revert-buffer-function)
e95a67dc
SM
165 (lambda (_ignore-auto _noconfirm)
166 (diff-no-select old new switches no-async (current-buffer))))
de9dc5e5 167 (setq default-directory thisdir)
53dd481c
CY
168 (let ((inhibit-read-only t))
169 (insert command "\n"))
540d0666 170 (if (and (not no-async) (fboundp 'start-process))
b2e44819
SM
171 (let ((proc (start-process "Diff" buf shell-file-name
172 shell-command-switch command)))
53dd481c 173 (set-process-filter proc 'diff-process-filter)
e95a67dc
SM
174 (set-process-sentinel
175 proc (lambda (proc _msg)
176 (with-current-buffer (process-buffer proc)
177 (diff-sentinel (process-exit-status proc)
178 old-alt new-alt)))))
540d0666 179 ;; Async processes aren't available.
53dd481c
CY
180 (let ((inhibit-read-only t))
181 (diff-sentinel
182 (call-process shell-file-name nil buf nil
b2e44819
SM
183 shell-command-switch command)
184 old-alt new-alt))))
540d0666 185 buf))
aa228418 186
53dd481c
CY
187(defun diff-process-filter (proc string)
188 (with-current-buffer (process-buffer proc)
189 (let ((moving (= (point) (process-mark proc))))
190 (save-excursion
191 ;; Insert the text, advancing the process marker.
192 (goto-char (process-mark proc))
193 (let ((inhibit-read-only t))
194 (insert string))
195 (set-marker (process-mark proc) (point)))
196 (if moving (goto-char (process-mark proc))))))
197
646bd331
RM
198;;;###autoload
199(defun diff-backup (file &optional switches)
37c51f00
RS
200 "Diff this file with its backup file or vice versa.
201Uses the latest backup, if there are several numerical backups.
202If this file is a backup, diff it with its original.
4c11f6a8
SM
203The backup file is the first file given to `diff'.
204With prefix arg, prompt for diff switches."
646bd331 205 (interactive (list (read-file-name "Diff (file with backup): ")
4c11f6a8 206 (diff-switches)))
37c51f00
RS
207 (let (bak ori)
208 (if (backup-file-name-p file)
209 (setq bak file
210 ori (file-name-sans-versions file))
211 (setq bak (or (diff-latest-backup-file file)
212 (error "No backup found for %s" file))
213 ori file))
646bd331 214 (diff bak ori switches)))
37c51f00 215
9f7c28f0
CY
216;;;###autoload
217(defun diff-latest-backup-file (fn)
37c51f00 218 "Return the latest existing backup of FILE, or nil."
a617e913 219 (let ((handler (find-file-name-handler fn 'diff-latest-backup-file)))
34c46d87 220 (if handler
6d0d0e8d 221 (funcall handler 'diff-latest-backup-file fn)
a8090e38 222 (file-newest-backup fn))))
aa228418 223
b2e44819
SM
224;;;###autoload
225(defun diff-buffer-with-file (&optional buffer)
226 "View the differences between BUFFER and its associated file.
227This requires the external program `diff' to be in your `exec-path'."
228 (interactive "bBuffer: ")
229 (with-current-buffer (get-buffer (or buffer (current-buffer)))
230 (diff buffer-file-name (current-buffer) nil 'noasync)))
231
a4ef6584
ER
232(provide 'diff)
233
c0274f38 234;;; diff.el ends here