Mercurial > hg > xemacs-beta
comparison lisp/packages/diff.el @ 100:4be1180a9e89 r20-1b2
Import from CVS: tag r20-1b2
| author | cvs |
|---|---|
| date | Mon, 13 Aug 2007 09:15:11 +0200 |
| parents | 131b0175ea99 |
| children | cf808b4c4290 |
comparison
equal
deleted
inserted
replaced
| 99:2d83cbd90d8d | 100:4be1180a9e89 |
|---|---|
| 1 ;;; diff.el --- Run `diff' in compilation-mode. | 1 ;; -*-Emacs-Lisp-*- |
| 2 | 2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 3 ;; Copyright (C) 1992, 1994 Free Software Foundation, Inc. | 3 ;; |
| 4 | 4 ;; File: diff.el |
| 5 ;; Keywords: unix, tools | 5 ;; Version: $Revision: 1.2 $ |
| 6 | 6 ;; Author: This file is based on diff.el by |
| 7 ;; This file is part of XEmacs. | 7 ;; sunpitt!wpmstr!fbresz@Sun.COM 1/27/89. |
| 8 | 8 ;; It has been completely rewritten in July 1994 by |
| 9 ;; XEmacs is free software; you can redistribute it and/or modify it | 9 ;; Sandy Rutherford <sandy@ibm550.sissa.it> |
| 10 ;; under the terms of the GNU General Public License as published by | 10 ;; RCS: |
| 11 ;; the Free Software Foundation; either version 2, or (at your option) | 11 ;; Description: diff-mode for handling output from unix diff utility. |
| 12 ;; any later version. | 12 ;; Modified: Wed Jul 17 10:26:57 1996 (Andy Norman) ange@hplb.hpl.hp.com |
| 13 | 13 ;; |
| 14 ;; XEmacs is distributed in the hope that it will be useful, but | 14 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 15 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | 15 |
| 16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 ;;; Copyright (C) 1990 Free Software Foundation, Inc. |
| 17 ;; General Public License for more details. | 17 ;;; Copyright (C) 1994 Sandy Rutherford |
| 18 | 18 |
| 19 ;; You should have received a copy of the GNU General Public License | 19 ;;; This file is based on diff.el by sunpitt!wpmstr!fbresz@Sun.COM 1/27/89. |
| 20 ;; along with XEmacs; see the file COPYING. If not, write to the Free | 20 ;;; It has been completely rewritten in July 1994 by |
| 21 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | 21 ;;; Sandy Rutherford <sandy@ibm550.sissa.it> |
| 22 ;; 02111-1307, USA. | 22 |
| 23 | 23 ;;; This program is free software; you can redistribute it and/or modify |
| 24 ;;; Synched up with: FSF 19.34. | 24 ;;; it under the terms of the GNU General Public License as published by |
| 25 | 25 ;;; the Free Software Foundation; either version 1, or (at your option) |
| 26 ;;; Commentary: | 26 ;;; any later version. |
| 27 | 27 ;;; |
| 28 ;; This package helps you explore differences between files, using the | 28 ;;; This program is distributed in the hope that it will be useful, |
| 29 ;; UNIX command diff(1). The commands are `diff' and `diff-backup'. | 29 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 30 ;; You can specify options with `diff-switches'. | 30 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 31 | 31 ;;; GNU General Public License for more details. |
| 32 ;;; Code: | 32 ;;; |
| 33 | 33 ;;; A copy of the GNU General Public License can be obtained from this |
| 34 (require 'compile) | 34 ;;; program's author (send electronic mail to sandy@ibm550.sissa.it) or |
| 35 | 35 ;;; from the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, |
| 36 ;;; This is duplicated in vc.el. | 36 ;;; MA 02139, USA. |
| 37 ;;;###autoload | 37 |
| 38 (defvar diff-switches (purecopy "-c") | 38 (provide 'diff) |
| 39 "*A string or list of strings specifying switches to be passed to diff.") | 39 |
| 40 | 40 ;;; User Variables |
| 41 (defvar diff-command "diff" | 41 |
| 42 "*The command to use to run diff.") | 42 ;; should be in to loaddefs.el now. |
| 43 | 43 (defvar diff-switches nil |
| 44 (defvar diff-regexp-alist | 44 "*A list of switches (strings) to pass to the diff program.") |
| 45 '( | 45 |
| 46 ;; -u format: @@ -OLDSTART,OLDEND +NEWSTART,NEWEND @@ | 46 (defvar diff-do-narrow nil |
| 47 ("^@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@$" 1 2) | 47 "*If non-nil diff buffers are initialized narrowed to each difference.") |
| 48 | 48 |
| 49 ;; -c format: *** OLDSTART,OLDEND **** | 49 (defvar diff-load-hooks nil |
| 50 ("^\\*\\*\\* \\([0-9]+\\),[0-9]+ \\*\\*\\*\\*$" 1 nil) | 50 "Hooks to run after loading diff.el") |
| 51 ;; --- NEWSTART,NEWEND ---- | 51 |
| 52 ("^--- \\([0-9]+\\),[0-9]+ ----$" nil 1) | 52 ;;; Internal variables |
| 53 | 53 |
| 54 ;; plain diff format: OLDSTART[,OLDEND]{a,d,c}NEWSTART[,NEWEND] | 54 (defconst diff-emacs-19-p |
| 55 ("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]\\([0-9]+\\)\\(,[0-9]+\\)?$" 1 3) | 55 (let ((ver (string-to-int (substring emacs-version 0 2)))) |
| 56 | 56 (>= ver 19))) |
| 57 ;; -e (ed) format: OLDSTART[,OLDEND]{a,d,c} | 57 |
| 58 ("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]$" 1) | 58 (or diff-emacs-19-p (require 'emacs-19)) |
| 59 | 59 |
| 60 ;; -f format: {a,d,c}OLDSTART[ OLDEND] | 60 (defvar diff-old-file nil) |
| 61 ;; -n format: {a,d,c}OLDSTART LINES-CHANGED | 61 ;; A list whose car is the name of the old file, and whose cdr indicates |
| 62 ("^[adc]\\([0-9]+\\)\\( [0-9]+\\)?$" 1) | 62 ;; whether we should delete the buffer on quit. |
| 63 ) | 63 (defvar diff-new-file nil) |
| 64 "Alist (REGEXP OLD-IDX NEW-IDX) of regular expressions to match difference | 64 ;; Same as diff-old-file, except for the new file. |
| 65 sections in \\[diff] output. If REGEXP matches, the OLD-IDX'th | 65 (defvar diff-total-differences "0") |
| 66 subexpression gives the line number in the old file, and NEW-IDX'th | 66 ;; Total number of difference hunks as a string. |
| 67 subexpression gives the line number in the new file. If OLD-IDX or NEW-IDX | 67 (defvar diff-current-difference "0") |
| 68 is nil, REGEXP matches only half a section.") | 68 ;; Current difference hunk as a string. |
| 69 | 69 (defvar diff-current-hunk 0) |
| 70 (defvar diff-old-file nil | 70 ;; Current difference hunk as an integer. |
| 71 "This is the old file name in the comparison in this buffer.") | 71 (defvar diff-total-hunks 0) |
| 72 (defvar diff-new-file nil | 72 ;; Total number of difference hunks as an integer. |
| 73 "This is the new file name in the comparison in this buffer.") | 73 (defvar diff-hunk-vector (vector 0)) |
| 74 (defvar diff-old-temp-file nil | 74 ;; Vector storing the starting positions of the difference hunks. |
| 75 "This is the name of a temp file to be deleted after diff finishes.") | 75 (defvar diff-old-file-pattern nil) |
| 76 (defvar diff-new-temp-file nil | 76 (defvar diff-new-file-pattern nil) |
| 77 "This is the name of a temp file to be deleted after diff finishes.") | 77 (defvar diff-hunk-pattern nil) |
| 78 | 78 ;; Regexps to use when parsing file lines in difference hunks. |
| 79 ;; See compilation-parse-errors-function (compile.el). | 79 |
| 80 (defun diff-parse-differences (limit-search find-at-least) | 80 |
| 81 (setq compilation-error-list nil) | 81 (defvar diff-search-pattern-alist |
| 82 (message "Parsing differences...") | 82 (list |
| 83 | 83 (list ?e "^[0-9]\\(,[0-9]+\\)?[acd]$" "^\\([0-9]+\\)" nil) |
| 84 ;; Don't reparse diffs already seen at last parse. | 84 (list ?c "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\n" |
| 85 (if compilation-parsing-end (goto-char compilation-parsing-end)) | 85 "^\\*+ \\([0-9]+\\)" "^-+ \\([0-9]+\\)") |
| 86 | 86 (list ?u "^@@ " "^@@ -\\([0-9]+\\)" "^@@ -[0-9,]+ \\+\\([0-9]+\\)") |
| 87 ;; Construct in REGEXP a regexp composed of all those in dired-regexp-alist. | 87 (list nil "^[0-9]+" "^\\([0-9]+\\)" "^[0-9,]+[acd]\\([0-9]+\\)"))) |
| 88 (let ((regexp (mapconcat #'(lambda (elt) | 88 |
| 89 (concat "\\(" (car elt) "\\)")) | 89 (defvar diff-keymap-grokked nil) |
| 90 diff-regexp-alist | 90 |
| 91 "\\|")) | 91 (defvar diff-temp-template "/tmp/diff") |
| 92 ;; (GROUP-IDX OLD-IDX NEW-IDX) | 92 |
| 93 (groups (let ((subexpr 1)) | 93 ;; Initialize the keymap if it isn't already |
| 94 (mapcar #'(lambda (elt) | 94 |
| 95 (prog1 | 95 (defvar diff-mode-map nil |
| 96 (cons subexpr | 96 "Keymap used in diff mode.") |
| 97 (mapcar #'(lambda (n) | 97 |
| 98 (and n | 98 (if diff-mode-map |
| 99 (+ subexpr n))) | 99 nil |
| 100 (cdr elt))) | 100 (setq diff-mode-map (make-keymap)) |
| 101 (setq subexpr (+ subexpr 1 | 101 (suppress-keymap diff-mode-map) |
| 102 ;;#### undefined?? | 102 (define-key diff-mode-map "?" 'describe-mode) |
| 103 (count-regexp-groupings | 103 (define-key diff-mode-map "." 'diff-display-file) |
| 104 (car elt)))))) | 104 (define-key diff-mode-map "N" 'diff-narrow) |
| 105 diff-regexp-alist))) | 105 (define-key diff-mode-map "W" 'widen) |
| 106 | 106 (define-key diff-mode-map "f" 'diff-find-file) |
| 107 (new-error | 107 (define-key diff-mode-map "h" 'diff-show-header) |
| 108 (function (lambda (file subexpr) | 108 (define-key diff-mode-map "j" 'diff-show-difference) |
| 109 (setq compilation-error-list | 109 (define-key diff-mode-map "n" 'diff-next-difference) |
| 110 (cons | 110 (define-key diff-mode-map "o" 'diff-find-file-other-window) |
| 111 (cons (save-excursion | 111 (define-key diff-mode-map "p" 'diff-previous-difference) |
| 112 ;; Report location of message | 112 (define-key diff-mode-map "q" 'diff-quit) |
| 113 ;; at beginning of line. | 113 (define-key diff-mode-map "w" 'diff-find-file-other-frame) |
| 114 (goto-char | 114 (define-key diff-mode-map "\C-c\C-c" 'diff-find-file-other-window) |
| 115 (match-beginning subexpr)) | 115 (define-key diff-mode-map " " 'diff-advertised-scroll-up) |
| 116 (beginning-of-line) | 116 (define-key diff-mode-map "\177" 'diff-advertised-scroll-down) |
| 117 (point-marker)) | 117 (define-key diff-mode-map "\C-n" 'diff-next-line) |
| 118 ;; Report location of corresponding text. | 118 (define-key diff-mode-map "\C-p" 'diff-previous-line) |
| 119 (let ((line (string-to-int | 119 (define-key diff-mode-map "\M->" 'diff-end-of-buffer) |
| 120 (buffer-substring | 120 (define-key diff-mode-map "\M-<" 'diff-beginning-of-buffer)) |
| 121 (match-beginning subexpr) | 121 |
| 122 (match-end subexpr))))) | 122 ;;; Internal functions |
| 123 (save-excursion | 123 |
| 124 (save-match-data | 124 (defun diff-grok-keys (to-command from-command) |
| 125 (set-buffer (find-file-noselect file))) | 125 ;; Assigns to TO-COMMAND the keys for the global binding of FROM-COMMAND. |
| 126 (save-excursion | 126 ;; Does not clobber anything in the local keymap. |
| 127 (goto-line line) | 127 (let ((keys (where-is-internal from-command))) |
| 128 (point-marker))))) | 128 (while keys |
| 129 compilation-error-list))))) | 129 (condition-case nil |
| 130 | 130 (if (eq (global-key-binding (car keys)) (key-binding (car keys))) |
| 131 (found-desired nil) | 131 (local-set-key (car keys) to-command)) |
| 132 (num-loci-found 0) | 132 (error nil)) |
| 133 g) | 133 (setq keys (cdr keys))))) |
| 134 | 134 |
| 135 (while (and (not found-desired) | 135 (defun diff-grok-keymap () |
| 136 ;; We don't just pass LIMIT-SEARCH to re-search-forward | 136 (if diff-keymap-grokked |
| 137 ;; because we want to find matches containing LIMIT-SEARCH | 137 () |
| 138 ;; but which extend past it. | 138 (if (and term-setup-hook (boundp 'command-line-args-left)) |
| 139 (re-search-forward regexp nil t)) | 139 (progn |
| 140 | 140 (if diff-emacs-19-p |
| 141 ;; Find which individual regexp matched. | 141 (run-hooks term-setup-hook) |
| 142 (setq g groups) | 142 (funcall term-setup-hook)) |
| 143 (while (and g (null (match-beginning (car (car g))))) | 143 (setq term-setup-hook nil))) |
| 144 (setq g (cdr g))) | 144 (setq diff-keymap-grokked t) |
| 145 (setq g (car g)) | 145 (diff-grok-keys 'diff-next-line 'next-line) |
| 146 | 146 (diff-grok-keys 'diff-previous-line 'previous-line) |
| 147 (if (nth 1 g) ;OLD-IDX | 147 (diff-grok-keys 'diff-forward-char 'forward-char) |
| 148 (funcall new-error diff-old-file (nth 1 g))) | 148 (diff-grok-keys 'diff-backward-char 'backward-char) |
| 149 (if (nth 2 g) ;NEW-IDX | 149 (diff-grok-keys 'diff-scroll-up 'scroll-up) |
| 150 (funcall new-error diff-new-file (nth 2 g))) | 150 (diff-grok-keys 'diff-scroll-down 'scroll-down) |
| 151 | 151 (diff-grok-keys 'diff-beginning-of-buffer 'beginning-of-buffer) |
| 152 (setq num-loci-found (1+ num-loci-found)) | 152 (diff-grok-keys 'diff-end-of-buffer 'end-of-buffer))) |
| 153 (if (or (and find-at-least | 153 |
| 154 (>= num-loci-found find-at-least)) | 154 (defun diff-buffer-narrowed-p () |
| 155 (and limit-search (>= (point) limit-search))) | 155 ;; Returns t if the current buffer is narrowed |
| 156 ;; We have found as many new loci as the user wants, | 156 (save-restriction |
| 157 ;; or the user wanted a specific diff, and we're past it. | 157 (let ((min (point-min)) |
| 158 (setq found-desired t))) | 158 (max (point-max))) |
| 159 (if found-desired | 159 (widen) |
| 160 (setq compilation-parsing-end (point)) | 160 (not (and (= min (point-min)) (= max (point-max))))))) |
| 161 ;; Set to point-max, not point, so we don't perpetually | 161 |
| 162 ;; parse the last bit of text when it isn't a diff header. | 162 (defun diff-current-hunk () |
| 163 (setq compilation-parsing-end (point-max))) | 163 ;; Returns the current diff hunk. |
| 164 (message "Parsing differences...done")) | 164 (let ((pt (point)) |
| 165 (setq compilation-error-list (nreverse compilation-error-list))) | 165 (start 0) |
| 166 | 166 (end (1+ diff-total-hunks)) |
| 167 ;;;###autoload | 167 m) |
| 168 (defun diff (old new &optional switches) | 168 (while (> (- end start) 1) |
| 169 "Find and display the differences between OLD and NEW files. | 169 (setq m (/ (+ start end) 2)) |
| 170 Interactively the current buffer's file name is the default for NEW | 170 (if (>= pt (aref diff-hunk-vector m)) |
| 171 and a backup file for NEW is the default for OLD. | 171 (setq start m) |
| 172 With prefix arg, prompt for diff switches." | 172 (setq end m))) |
| 173 (interactive | 173 (if (>= pt (aref diff-hunk-vector end)) |
| 174 (nconc | 174 (setq m end) |
| 175 (let (oldf newf) | 175 (setq m start)) |
| 176 (nreverse | 176 ;; Don't treat end of buffer as start of next hunk |
| 177 (list | 177 (if (eobp) (1- m) m))) |
| 178 (setq newf (buffer-file-name) | 178 |
| 179 (defun diff-hunk-min (n) | |
| 180 ;; Returns the start of the current diff hunk. | |
| 181 (aref diff-hunk-vector n)) | |
| 182 | |
| 183 (defun diff-hunk-max (n) | |
| 184 ;; Returns the end of the current diff hunk. | |
| 185 (aref diff-hunk-vector (1+ n))) | |
| 186 | |
| 187 (defun diff-parse-hunks () | |
| 188 ;; Parses a buffer of diff output. | |
| 189 (save-excursion | |
| 190 (save-restriction | |
| 191 (message "Parsing differences...") | |
| 192 (widen) | |
| 193 (goto-char (point-min)) | |
| 194 (let ((hunks (list 1))) | |
| 195 (while (re-search-forward diff-hunk-pattern nil t) | |
| 196 (setq hunks (cons (match-beginning 0) hunks))) | |
| 197 (setq diff-total-hunks (1- (length hunks)) | |
| 198 diff-hunk-vector (apply 'vector | |
| 199 (nreverse (cons (point-max) hunks))))))) | |
| 200 (message "Parsing differences...done")) | |
| 201 | |
| 202 (defun diff-update-modeline () | |
| 203 ;; Updates the mode line to show current diff hunk. | |
| 204 (if (or (< (point) (diff-hunk-min diff-current-hunk)) | |
| 205 (>= (point) (diff-hunk-max diff-current-hunk))) | |
| 206 (progn | |
| 207 (setq diff-current-hunk (diff-current-hunk) | |
| 208 diff-current-difference (int-to-string diff-current-hunk)) | |
| 209 (set-buffer-modified-p (buffer-modified-p))))) | |
| 210 | |
| 211 (defun diff-read-args (oldprompt newprompt switchprompt | |
| 212 &optional file-for-backup) | |
| 213 ;; Grab the args for diff. OLDPROMPT and NEWPROMPT are the prompts | |
| 214 ;; for the old & new filenames, SWITCHPROMPT for the list of | |
| 215 ;; switches. If FILE_FOR_BACKUP is provided (it must be a string if | |
| 216 ;; so), then it will be used to try & work out a file & backup to | |
| 217 ;; diff, & in this case the prompting order is backwards. %s in a | |
| 218 ;; prompt has a guess substituted into it. This is nasty. | |
| 219 (let (oldf newf) | |
| 220 (if file-for-backup | |
| 221 (setq newf file-for-backup | |
| 179 newf (if (and newf (file-exists-p newf)) | 222 newf (if (and newf (file-exists-p newf)) |
| 180 (read-file-name | 223 (read-file-name |
| 181 (concat "Diff new file: (" | 224 (format newprompt (file-name-nondirectory newf)) |
| 182 (file-name-nondirectory newf) ") ") | |
| 183 nil newf t) | 225 nil newf t) |
| 184 (read-file-name "Diff new file: " nil nil t))) | 226 (read-file-name (format newprompt "") nil nil t)) |
| 185 (setq oldf (file-newest-backup newf) | 227 oldf (file-newest-backup newf) |
| 186 oldf (if (and oldf (file-exists-p oldf)) | 228 oldf (if (and oldf (file-exists-p oldf)) |
| 187 (read-file-name | 229 (read-file-name |
| 188 (concat "Diff original file: (" | 230 (format oldprompt (file-name-nondirectory oldf)) |
| 189 (file-name-nondirectory oldf) ") ") | 231 nil oldf t) |
| 190 (file-name-directory oldf) oldf t) | 232 (read-file-name (format oldprompt "") |
| 191 (read-file-name "Diff original file: " | 233 (file-name-directory newf) nil t))) |
| 192 (file-name-directory newf) nil t)))))) | 234 ;; Else we aren't trying to be bright... |
| 193 (if current-prefix-arg | 235 (setq oldf (read-file-name (format oldprompt "") nil nil t) |
| 194 (list (read-string "Diff switches: " | 236 newf (read-file-name |
| 195 (if (stringp diff-switches) | 237 (format newprompt (file-name-nondirectory oldf)) |
| 196 diff-switches | 238 nil (file-name-directory oldf) t))) |
| 197 (mapconcat 'identity diff-switches " ")))) | 239 (list oldf newf (diff-read-switches switchprompt)))) |
| 198 nil))) | 240 |
| 199 (setq new (expand-file-name new) | 241 (defun diff-read-switches (switchprompt) |
| 200 old (expand-file-name old)) | 242 ;; Read and return a list of switches |
| 201 ;; XEmacs addition -- allow (diff "../old/" "new-file.el") to work | 243 (if current-prefix-arg |
| 202 (cond ((file-directory-p old) | 244 (let ((default (if (listp diff-switches) |
| 203 (or (file-directory-p new) | 245 (mapconcat 'identity diff-switches " ") |
| 204 (setq old (expand-file-name (file-name-nondirectory new) | 246 diff-switches))) |
| 205 (file-name-as-directory old))))) | 247 (diff-fix-switches |
| 206 ((file-directory-p new) | 248 (read-string (format switchprompt default) default))))) |
| 207 (setq new (expand-file-name (file-name-nondirectory old) | 249 |
| 208 (file-name-as-directory new))))) | 250 (defun diff-fix-switches (switch-spec) |
| 209 (let ((old-alt (file-local-copy old)) | 251 ;; Parse a string into a list of switches or leave it be if it's |
| 210 (new-alt (file-local-copy new)) | 252 ;; not a string |
| 211 buf) | 253 (if (stringp switch-spec) |
| 212 (unwind-protect | 254 (let (result (start 0)) |
| 213 (let ((command | 255 (while (string-match "\\(\\S-+\\)" switch-spec start) |
| 214 (mapconcat 'identity | 256 (setq result (cons (substring switch-spec (match-beginning 1) |
| 215 (append (list diff-command) | 257 (match-end 1)) |
| 216 ;; Use explicitly specified switches | 258 result) |
| 217 (if switches | 259 start (match-end 0))) |
| 218 (if (consp switches) | 260 (nreverse result)) |
| 219 switches (list switches)) | 261 switch-spec)) |
| 220 ;; If not specified, use default. | 262 |
| 221 (if (consp diff-switches) | 263 (defun diff-get-file-buffer (file) |
| 222 diff-switches | 264 ;; Returns \(BUFFER . DEL-P\), where DEL-P is t if diff is expected |
| 223 (list diff-switches))) | 265 ;; to delete the buffer, and nil otherwise. |
| 224 (if (or old-alt new-alt) | 266 (let* ((buff (get-file-buffer file)) |
| 225 (list "-L" old "-L" new)) | 267 (del-p (null buff))) |
| 226 (list | 268 (if (and buff (buffer-modified-p buff)) |
| 227 (shell-quote-argument (or old-alt old))) | 269 (progn |
| 228 (list | 270 (message |
| 229 (shell-quote-argument (or new-alt new)))) | 271 "Buffer %s is modified. Diffing against buffer contents." |
| 230 " "))) | 272 (buffer-name buff)) |
| 231 (setq buf | 273 (sit-for 1))) |
| 232 (compile-internal command | 274 ;; Call find-file-noselect even if we already have the buffer, |
| 233 "No more differences" "Diff" | 275 ;; as it will run verify-buffer-file-modtime. |
| 234 'diff-parse-differences)) | 276 (cons (find-file-noselect file) del-p))) |
| 235 (pop-to-buffer buf) | 277 |
| 236 ;; Avoid frightening people with "abnormally terminated" | 278 (defun diff-cleanup-buffers () |
| 237 ;; if diff finds differences. | 279 ;; Cleans up diff buffers by deleting buffers that we don't expect |
| 238 (set (make-local-variable 'compilation-exit-message-function) | 280 ;; the user to care about. |
| 239 (lambda (status code msg) | 281 (let ((files (list diff-old-file diff-new-file))) |
| 240 (cond ((not (eq status 'exit)) | 282 (while files |
| 241 (cons msg code)) | 283 (let ((ent (car files)) |
| 242 ((zerop code) | 284 buff) |
| 243 '("finished (no differences)\n" . "no differences")) | 285 (and (cdr ent) |
| 244 ((= code 1) | 286 (setq buff (get-file-buffer (car ent))) |
| 245 '("finished\n" . "differences found")) | 287 (not (buffer-modified-p buff)) |
| 246 (t | 288 (kill-buffer buff))) |
| 247 (cons msg code))))) | 289 (setq files (cdr files))) |
| 248 (set (make-local-variable 'diff-old-file) old) | 290 (if (get-buffer "*Diff Header*") |
| 249 (set (make-local-variable 'diff-new-file) new) | 291 (kill-buffer "*Diff Header*")))) |
| 250 (set (make-local-variable 'diff-old-temp-file) old-alt) | 292 |
| 251 (set (make-local-variable 'diff-new-temp-file) new-alt) | 293 (defun diff-latest-backup-file (file) |
| 252 (set (make-local-variable 'compilation-finish-function) | |
| 253 (function (lambda (buff msg) | |
| 254 (if diff-old-temp-file | |
| 255 (delete-file diff-old-temp-file)) | |
| 256 (if diff-new-temp-file | |
| 257 (delete-file diff-new-temp-file))))) | |
| 258 buf)))) | |
| 259 | |
| 260 ;;;###autoload | |
| 261 (defun diff-backup (file &optional switches) | |
| 262 "Diff this file with its backup file or vice versa. | |
| 263 Uses the latest backup, if there are several numerical backups. | |
| 264 If this file is a backup, diff it with its original. | |
| 265 The backup file is the first file given to `diff'." | |
| 266 (interactive (list (read-file-name "Diff (file with backup): ") | |
| 267 (if current-prefix-arg | |
| 268 (read-string "Diff switches: " | |
| 269 (if (stringp diff-switches) | |
| 270 diff-switches | |
| 271 (mapconcat 'identity | |
| 272 diff-switches " "))) | |
| 273 nil))) | |
| 274 (let (bak ori) | |
| 275 (if (backup-file-name-p file) | |
| 276 (setq bak file | |
| 277 ori (file-name-sans-versions file)) | |
| 278 (setq bak (or (diff-latest-backup-file file) | |
| 279 (error "No backup found for %s" file)) | |
| 280 ori file)) | |
| 281 (diff bak ori switches))) | |
| 282 | |
| 283 (defun diff-latest-backup-file (fn) ; actually belongs into files.el | |
| 284 "Return the latest existing backup of FILE, or nil." | 294 "Return the latest existing backup of FILE, or nil." |
| 285 (let ((handler (find-file-name-handler fn 'diff-latest-backup-file))) | 295 ;; First try simple backup, then the highest numbered of the |
| 296 ;; numbered backups. | |
| 297 ;; Ignore the value of version-control because we look for existing | |
| 298 ;; backups, which maybe were made earlier or by another user with | |
| 299 ;; a different value of version-control. | |
| 300 (let* ((file (expand-file-name file)) | |
| 301 (handler (find-file-name-handler file 'diff-latest-backup-file))) | |
| 286 (if handler | 302 (if handler |
| 287 (funcall handler 'diff-latest-backup-file fn) | 303 (funcall handler 'diff-latest-backup-file file) |
| 288 ;; First try simple backup, then the highest numbered of the | |
| 289 ;; numbered backups. | |
| 290 ;; Ignore the value of version-control because we look for existing | |
| 291 ;; backups, which maybe were made earlier or by another user with | |
| 292 ;; a different value of version-control. | |
| 293 (setq fn (file-chase-links (expand-file-name fn))) | |
| 294 (or | 304 (or |
| 295 (let ((bak (make-backup-file-name fn))) | 305 (let ((bak (make-backup-file-name file))) |
| 296 (if (file-exists-p bak) bak)) | 306 (if (file-exists-p bak) bak)) |
| 297 ;; We use BACKUPNAME to cope with backups stored in a different dir. | 307 (let* ((dir (file-name-directory file)) |
| 298 (let* ((backupname (car (find-backup-file-name fn))) | 308 (base-versions (concat (file-name-nondirectory file) ".~")) |
| 299 (dir (file-name-directory backupname)) | |
| 300 (base-versions (concat (file-name-sans-versions | |
| 301 (file-name-nondirectory backupname)) | |
| 302 ".~")) | |
| 303 (bv-length (length base-versions))) | 309 (bv-length (length base-versions))) |
| 304 (concat dir | 310 (concat dir |
| 305 (car (sort | 311 (car (sort |
| 306 (file-name-all-completions base-versions dir) | 312 (file-name-all-completions base-versions dir) |
| 307 ;; bv-length is a fluid var for backup-extract-version: | 313 ;; bv-length is a fluid var for backup-extract-version: |
| 308 (function | 314 (function |
| 309 (lambda (fn1 fn2) | 315 (lambda (fn1 fn2) |
| 310 (> (backup-extract-version fn1) | 316 (> (backup-extract-version fn1) |
| 311 (backup-extract-version fn2)))))))))))) | 317 (backup-extract-version fn2)))))))))))) |
| 312 | 318 |
| 313 (provide 'diff) | 319 (defun diff-file-line (&optional old-file-p) |
| 314 | 320 "Return line number of current hunk in `diff-new-file'. |
| 315 ;;; diff.el ends here | 321 With optional argument OLD-FILE-P, use `diff-old-file' instead." |
| 322 (save-excursion | |
| 323 (let ((min (diff-hunk-min diff-current-hunk)) | |
| 324 (max (diff-hunk-max diff-current-hunk)) | |
| 325 (regexp (if old-file-p diff-old-file-pattern diff-new-file-pattern))) | |
| 326 (goto-char min) | |
| 327 (or (and regexp (re-search-forward regexp max t)) | |
| 328 (error "Unable to locate a file line for %s file." | |
| 329 (if old-file-p "old" "new"))) | |
| 330 (string-to-int (buffer-substring (match-beginning 1) (match-end 1)))))) | |
| 331 | |
| 332 (defun diff-run-diff (switches old old-temp new new-temp) | |
| 333 ;; Actually run the diff process with SWITCHES on OLD and NEW. | |
| 334 ;; OLD-TEMP and NEW-TEMP are names of temp files that can be used | |
| 335 ;; to dump the data out to. | |
| 336 (insert "diff " (mapconcat 'identity switches " ") " " old | |
| 337 " " new "\n") | |
| 338 (apply 'call-process "diff" nil t nil | |
| 339 (append switches (list old-temp new-temp)))) | |
| 340 | |
| 341 | |
| 342 (defun diff-fix-file-names (old old-temp new new-temp pattern) | |
| 343 ;; Replaces any temp file names with the real names of files. | |
| 344 (save-excursion | |
| 345 (save-restriction | |
| 346 (let ((files (list old new)) | |
| 347 (temps (list old-temp new-temp)) | |
| 348 buffer-read-only case-fold-search) | |
| 349 (goto-char (point-min)) | |
| 350 (if (re-search-forward pattern nil t) | |
| 351 (narrow-to-region (point-min) (match-beginning 0))) | |
| 352 (while files | |
| 353 (let ((regexp (concat "[ \t\n]\\(" | |
| 354 (regexp-quote (car temps)) | |
| 355 "\\)[ \t\n]"))) | |
| 356 (goto-char (point-min)) | |
| 357 (forward-line 1) | |
| 358 (while (re-search-forward regexp nil t) | |
| 359 (goto-char (match-beginning 1)) | |
| 360 (delete-region (point) (match-end 1)) | |
| 361 (insert (car files)))) | |
| 362 (setq files (cdr files) | |
| 363 temps (cdr temps))))))) | |
| 364 | |
| 365 ;;;; User commands | |
| 366 | |
| 367 (defun diff-mode () | |
| 368 "Diff Mode is used by \\[diff] for perusing the output from the diff program. | |
| 369 All normal editing commands are turned off. Instead, these are available: | |
| 370 \\<diff-mode-map> | |
| 371 \\[diff-advertised-scroll-up] Scroll to next screen of this difference. | |
| 372 \\[diff-advertised-scroll-down] Scroll to previous screen of this difference. | |
| 373 \\[diff-next-difference] Move to Next Difference. | |
| 374 \\[diff-previous-difference] Move to Previous Difference. | |
| 375 \\[diff-show-difference] Jump to difference specified by numeric position. | |
| 376 \\[diff-find-file] Find current diff in file | |
| 377 \\[diff-find-file-other-window] Find current diff in file in other window | |
| 378 \\[diff-display-file] Display file in other window | |
| 379 \\[diff-narrow] Narrow diff buffer to current difference | |
| 380 \\[widen] Widen diff buffer | |
| 381 \\[diff-show-header] Show diff header describing file name etc. | |
| 382 \\[diff-quit] Quit diff | |
| 383 " | |
| 384 (interactive) | |
| 385 (use-local-map diff-mode-map) | |
| 386 (diff-grok-keymap) | |
| 387 (setq buffer-read-only t | |
| 388 major-mode 'diff-mode | |
| 389 mode-name "Diff" | |
| 390 mode-line-modified "--- " | |
| 391 mode-line-process | |
| 392 '(" " diff-current-difference "/" diff-total-differences)) | |
| 393 (diff-parse-hunks) | |
| 394 (setq diff-total-differences (int-to-string diff-total-hunks))) | |
| 395 | |
| 396 ;;; Motion commands | |
| 397 | |
| 398 (defun diff-next-difference (n) | |
| 399 "In diff-mode go the the beginning of the next difference hunk." | |
| 400 (interactive "p") | |
| 401 (if (zerop n) | |
| 402 (goto-char (diff-hunk-min diff-current-hunk)) | |
| 403 (let ((narrow (diff-buffer-narrowed-p)) | |
| 404 (max (point-max)) | |
| 405 (min (point-min))) | |
| 406 (unwind-protect | |
| 407 (progn | |
| 408 (widen) | |
| 409 (setq diff-current-hunk (+ n diff-current-hunk)) | |
| 410 (cond ((> diff-current-hunk diff-total-hunks) | |
| 411 (setq diff-current-hunk diff-total-hunks) | |
| 412 (message "No following difference hunks.")) | |
| 413 ((< diff-current-hunk 0) | |
| 414 (setq diff-current-hunk 0) | |
| 415 (message "No preceding difference hunks."))) | |
| 416 (setq diff-current-difference (int-to-string diff-current-hunk) | |
| 417 min (goto-char (diff-hunk-min diff-current-hunk)) | |
| 418 max (diff-hunk-max diff-current-hunk))) | |
| 419 (if narrow (narrow-to-region min max)))) | |
| 420 (set-buffer-modified-p (buffer-modified-p)))) | |
| 421 | |
| 422 (defun diff-previous-difference (n) | |
| 423 "In diff-mode go the the beginning of the previous difference hunk." | |
| 424 (interactive "p") | |
| 425 (diff-next-difference (- n))) | |
| 426 | |
| 427 (defun diff-next-line (n) | |
| 428 "In diff-mode go to the next line." | |
| 429 (interactive "p") | |
| 430 (condition-case nil | |
| 431 (next-line n) | |
| 432 (error (if (> n 0) (message "End of difference hunk")))) | |
| 433 (diff-update-modeline)) | |
| 434 | |
| 435 (defun diff-previous-line (n) | |
| 436 "In diff-mode go to the previous line." | |
| 437 (interactive "p") | |
| 438 (diff-next-line (- n))) | |
| 439 | |
| 440 (defun diff-forward-char (n) | |
| 441 "In diff-mode move the point forward." | |
| 442 (interactive "p") | |
| 443 (forward-char n) | |
| 444 (diff-update-modeline)) | |
| 445 | |
| 446 (defun diff-backward-char (n) | |
| 447 "In diff-mode move the point backward." | |
| 448 (interactive "p") | |
| 449 (backward-char n) | |
| 450 (diff-update-modeline)) | |
| 451 | |
| 452 (defun diff-scroll-up (n) | |
| 453 "In diff-mode scroll the buffer up." | |
| 454 (interactive "P") | |
| 455 (scroll-up n) | |
| 456 (diff-update-modeline)) | |
| 457 | |
| 458 (fset 'diff-advertised-scroll-up 'diff-scroll-up) | |
| 459 | |
| 460 (defun diff-scroll-down (n) | |
| 461 "In diff-mode scroll the buffer down." | |
| 462 (interactive "P") | |
| 463 (scroll-down n) | |
| 464 (diff-update-modeline)) | |
| 465 | |
| 466 (fset 'diff-advertised-scroll-down 'diff-scroll-down) | |
| 467 | |
| 468 (defun diff-beginning-of-buffer (n) | |
| 469 "In diff-mode go to the beginning of the buffer." | |
| 470 (interactive "P") | |
| 471 (beginning-of-buffer n) | |
| 472 (diff-update-modeline)) | |
| 473 | |
| 474 (defun diff-end-of-buffer (n) | |
| 475 "In diff-mode go to the end of the buffer." | |
| 476 (interactive "P") | |
| 477 (end-of-buffer n) | |
| 478 (diff-update-modeline)) | |
| 479 | |
| 480 ;;; The main command. | |
| 481 | |
| 482 (defun diff (old new &optional switches) | |
| 483 "Find and display the differences between OLD and NEW files. | |
| 484 Interactively you are prompted with the current buffer's file name for NEW | |
| 485 and what appears to be its backup for OLD." | |
| 486 ;; Support for diffing directories is rather limited. It needs work. | |
| 487 (interactive (diff-read-args "Diff original file (%s) " | |
| 488 "Diff new file (%s) " | |
| 489 "Switches for diff (%s) " | |
| 490 (buffer-file-name))) | |
| 491 (setq switches (diff-fix-switches (or switches diff-switches)) | |
| 492 old (expand-file-name old) | |
| 493 new (expand-file-name new)) | |
| 494 (let ((curr-buff (current-buffer)) | |
| 495 doing-dirs old-temp new-temp old-buffer new-buffer flag) | |
| 496 (let ((fdp-old (file-directory-p old)) | |
| 497 (fdp-new (file-directory-p new))) | |
| 498 (cond | |
| 499 ((null (or fdp-new fdp-old))) | |
| 500 ((null fdp-new) | |
| 501 (setq old (expand-file-name (file-name-nondirectory new) old))) | |
| 502 ((null fdp-old) | |
| 503 (setq new (expand-file-name (file-name-nondirectory old) new))) | |
| 504 (t (setq doing-dirs t)))) | |
| 505 ;; (message "diff %s %s %s..." | |
| 506 ;; (mapconcat (function identity) switches " ") new old) | |
| 507 (message "diff %s %s %s..." | |
| 508 (mapconcat (function identity) switches " ") old new) | |
| 509 (if doing-dirs | |
| 510 (setq diff-old-file nil | |
| 511 diff-new-file nil) | |
| 512 (setq old-temp (make-temp-name (concat diff-temp-template "1")) | |
| 513 new-temp (make-temp-name (concat diff-temp-template "2")) | |
| 514 old-buffer (diff-get-file-buffer old) | |
| 515 new-buffer (diff-get-file-buffer new) | |
| 516 diff-old-file (cons old (cdr old-buffer)) | |
| 517 diff-new-file (cons new (cdr new-buffer)))) | |
| 518 (let (case-fold-search) | |
| 519 (mapcar (function | |
| 520 (lambda (x) | |
| 521 (if (string-match "[ecu]" x) | |
| 522 (setq flag (aref x (match-beginning 0)))))) | |
| 523 switches)) | |
| 524 (unwind-protect | |
| 525 (let ((patterns (assq flag diff-search-pattern-alist))) | |
| 526 (set-buffer (get-buffer-create "*Diff Output*")) | |
| 527 (setq default-directory (file-name-directory new) | |
| 528 diff-old-file-pattern (nth 2 patterns) | |
| 529 diff-new-file-pattern (nth 3 patterns) | |
| 530 diff-hunk-pattern (nth 1 patterns)) | |
| 531 (let (buffer-read-only) | |
| 532 (if (fboundp 'buffer-disable-undo) | |
| 533 (buffer-disable-undo (current-buffer)) | |
| 534 ;; old style (Emacs 18.55 and earlier) | |
| 535 (buffer-disable-undo (current-buffer))) | |
| 536 (widen) | |
| 537 (erase-buffer) | |
| 538 (if doing-dirs | |
| 539 (progn | |
| 540 (diff-run-diff switches old old new new) | |
| 541 (setq diff-hunk-pattern (concat diff-hunk-pattern | |
| 542 "\\|^Only in "))) | |
| 543 (save-excursion | |
| 544 (set-buffer (car old-buffer)) | |
| 545 (write-region (point-min) (point-max) old-temp nil 'quiet) | |
| 546 (set-buffer (car new-buffer)) | |
| 547 (write-region (point-min) (point-max) new-temp nil 'quiet)) | |
| 548 (diff-run-diff switches old old-temp new new-temp)) | |
| 549 ;; Need to replace file names | |
| 550 (if (and (not doing-dirs) (memq flag '(?c ?u))) | |
| 551 (diff-fix-file-names old old-temp new new-temp | |
| 552 diff-hunk-pattern)) | |
| 553 (diff-mode) | |
| 554 (goto-char (point-min)) | |
| 555 (setq diff-current-difference "0" | |
| 556 diff-current-hunk 0) | |
| 557 (if (zerop diff-total-hunks) | |
| 558 (progn | |
| 559 (diff-cleanup-buffers) | |
| 560 (message "No differences")) | |
| 561 (if diff-do-narrow (narrow-to-region (point) (diff-hunk-max 0))) | |
| 562 (display-buffer (current-buffer)) | |
| 563 (message "%s difference hunk%s" diff-total-differences | |
| 564 (if (= diff-total-hunks 1) "" "s"))))) | |
| 565 (condition-case nil | |
| 566 (delete-file old-temp) | |
| 567 (error nil)) | |
| 568 (condition-case nil | |
| 569 (delete-file new-temp) | |
| 570 (error nil)) | |
| 571 (set-buffer curr-buff)))) | |
| 572 | |
| 573 (defun diff-backup (file &optional switches) | |
| 574 "Diff this file with its backup file or vice versa. | |
| 575 Uses the latest backup, if there are several numerical backups. | |
| 576 If this file is a backup, diff it with its original. | |
| 577 The backup file is the first file given to `diff'." | |
| 578 (interactive (list (read-file-name "Diff (file with backup): ") | |
| 579 (and current-prefix-arg | |
| 580 (diff-read-switches "Diff switches: ")))) | |
| 581 (let (bak ori) | |
| 582 (if (backup-file-name-p file) | |
| 583 (setq bak file | |
| 584 ori (file-name-sans-versions file)) | |
| 585 (setq bak (or (diff-latest-backup-file file) | |
| 586 (error "No backup found for %s" file)) | |
| 587 ori file)) | |
| 588 (diff bak ori switches))) | |
| 589 | |
| 590 (defun diff-show-difference (n) | |
| 591 "Show difference number N (prefix argument)." | |
| 592 (interactive "p") | |
| 593 (let ((narrowedp (diff-buffer-narrowed-p)) | |
| 594 (min (diff-hunk-min diff-current-hunk)) | |
| 595 (max (diff-hunk-max diff-current-hunk))) | |
| 596 (unwind-protect | |
| 597 (progn | |
| 598 (widen) | |
| 599 (cond | |
| 600 ((< n 0) | |
| 601 (message "No negative hunks.") | |
| 602 (setq n 0)) | |
| 603 ((> n diff-total-hunks) | |
| 604 (message "No hunk %d." n) | |
| 605 (setq n diff-total-hunks))) | |
| 606 (setq diff-current-hunk n | |
| 607 diff-current-difference (int-to-string diff-current-hunk) | |
| 608 min (diff-hunk-min n) | |
| 609 max (diff-hunk-max n)) | |
| 610 (goto-char min)) | |
| 611 (if narrowedp (narrow-to-region min max)) | |
| 612 (set-buffer-modified-p (buffer-modified-p))))) | |
| 613 | |
| 614 (defun diff-show-header () | |
| 615 "Show `diff-header'." | |
| 616 (interactive) | |
| 617 (with-output-to-temp-buffer "*Diff Header*" | |
| 618 (princ (save-restriction | |
| 619 (widen) | |
| 620 (buffer-substring (diff-hunk-min 0) (diff-hunk-max 0)))))) | |
| 621 | |
| 622 | |
| 623 (defun diff-find-file (old-file-p) | |
| 624 "Visit diffed file, at the point corresponding to the current hunk. | |
| 625 Default is to visit the new file; prefix means visit old file instead." | |
| 626 (interactive "P") | |
| 627 (let ((line (diff-file-line old-file-p))) | |
| 628 (find-file | |
| 629 (if old-file-p | |
| 630 (car diff-old-file) | |
| 631 (car diff-new-file))) | |
| 632 (goto-line line) | |
| 633 (recenter 0))) | |
| 634 | |
| 635 (defun diff-find-file-other-window (old-file-p) | |
| 636 "Visit the diffed file in other window, with the point at the current hunk. | |
| 637 Default is to visit the new file; prefix means visit the old file instead." | |
| 638 (interactive "P") | |
| 639 (let ((line (diff-file-line old-file-p))) | |
| 640 (find-file-other-window | |
| 641 (if old-file-p | |
| 642 (car diff-old-file) | |
| 643 (car diff-new-file))) | |
| 644 (goto-line line) | |
| 645 (recenter 0))) | |
| 646 | |
| 647 (defun diff-find-file-other-frame (old-file-p) | |
| 648 "Visit the diffed file in other frame, with point at the current hunk. | |
| 649 Default is to visit the new file; prefix means visit the old file instead." | |
| 650 (interactive "P") | |
| 651 (let ((line (diff-file-line old-file-p))) | |
| 652 (find-file-other-frame | |
| 653 (if old-file-p | |
| 654 (car diff-old-file) | |
| 655 (car diff-new-file))) | |
| 656 (goto-line line) | |
| 657 (recenter 0))) | |
| 658 | |
| 659 (defun diff-display-file (old-file-p) | |
| 660 "Display the diffed file in other window, with point at the current hunk. | |
| 661 Default is to visit the new file; prefix means visit the old file instead." | |
| 662 (interactive "P") | |
| 663 (let ((line (diff-file-line old-file-p)) | |
| 664 (wind (display-buffer (find-file-noselect (if old-file-p | |
| 665 (car diff-old-file) | |
| 666 (car diff-new-file))))) | |
| 667 (curr-wind (selected-window))) | |
| 668 (unwind-protect | |
| 669 (progn | |
| 670 (select-window wind) | |
| 671 (goto-line line) | |
| 672 (recenter 0)) | |
| 673 (select-window curr-wind)))) | |
| 674 | |
| 675 (defun diff-quit () | |
| 676 "Quit diff by killing the diff buffer." | |
| 677 (interactive) | |
| 678 (kill-buffer "*Diff Output*") | |
| 679 (diff-cleanup-buffers)) | |
| 680 | |
| 681 (defun diff-narrow () | |
| 682 "Narrow diff buffer to current difference hunk." | |
| 683 (interactive) | |
| 684 (narrow-to-region (diff-hunk-min diff-current-hunk) | |
| 685 (diff-hunk-max diff-current-hunk))) | |
| 686 | |
| 687 ;;; Run any load hooks | |
| 688 (run-hooks 'diff-load-hook) | |
| 689 | |
| 690 ;;; end of diff.el |
