(diff-find-source-location):
[emacs.git] / lisp / diff-mode.el
blobd1535da386ef628156e1242a26dbe733c0f1175d
1 ;;; diff-mode.el --- A mode for viewing/editing context diffs
3 ;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
5 ;; Author: Stefan Monnier <monnier@cs.yale.edu>
6 ;; Keywords: patch diff
7 ;; Revision: $Id: diff-mode.el,v 1.18 2000/09/20 06:40:30 miles Exp $
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
26 ;;; Commentary:
28 ;; Provides support for font-lock patterns, outline-regexps, navigation
29 ;; commands, editing and various conversions as well as jumping
30 ;; to the corresponding source file.
32 ;; inspired by Pavel Machek's patch-mode.el (<pavel@atrey.karlin.mff.cuni.cz>)
33 ;; some efforts were spent to have it somewhat compatible with XEmacs'
34 ;; diff-mode as well as with compilation-minor-mode
36 ;; to use it, simply add to your .emacs the following lines:
37 ;;
38 ;; (autoload 'diff-mode "diff-mode" "Diff major mode" t)
39 ;; (add-to-list 'auto-mode-alist '("\\.\\(diffs?\\|patch\\|rej\\)\\'" . diff-mode))
41 ;; Bugs:
43 ;; - Reverse doesn't work with normal diffs.
44 ;; - (nitpick) The mark is not always quite right in diff-goto-source.
46 ;; Todo:
48 ;; - Add change-log support.
49 ;; - Spice up the minor-mode with font-lock support.
50 ;; - Improve narrowed-view support.
51 ;; - Improve the `compile' support (?).
52 ;; - Recognize pcl-cvs' special string for `cvs-execute-single'.
53 ;; - Support for # comments in context->unified.
54 ;; - Do a fuzzy search in diff-goto-source.
55 ;; - Allow diff.el to use diff-mode.
56 ;; This mostly means ability to jump from half-hunk to half-hunk
57 ;; in context (and normal) diffs and to jump to the corresponding
58 ;; (i.e. new or old) file.
59 ;; - Handle `diff -b' output in context->unified.
61 ;;; Code:
63 (eval-when-compile (require 'cl))
66 (defgroup diff-mode ()
67 "Major-mode for viewing/editing diffs"
68 :version "21.1"
69 :group 'tools
70 :group 'diff)
72 (defcustom diff-jump-to-old-file-flag nil
73 "*Non-nil means `diff-goto-source' jumps to the old file.
74 Else, it jumps to the new file."
75 :group 'diff-mode
76 :type '(boolean))
78 (defcustom diff-update-on-the-fly-flag t
79 "*Non-nil means hunk headers are kept up-to-date on-the-fly.
80 When editing a diff file, the line numbers in the hunk headers
81 need to be kept consistent with the actual diff. This can
82 either be done on the fly (but this sometimes interacts poorly with the
83 undo mechanism) or whenever the file is written (can be slow
84 when editing big diffs)."
85 :group 'diff-mode
86 :type '(boolean))
88 (defvar diff-mode-hook nil
89 "Run after setting up the `diff-mode' major mode.")
91 (defvar diff-outline-regexp
92 "\\([*+][*+][*+] [^0-9]\\|@@ ...\\|\\*\\*\\* [0-9].\\|--- [0-9]..\\)")
94 ;;;;
95 ;;;; keymap, menu, ...
96 ;;;;
98 (easy-mmode-defmap diff-mode-shared-map
99 '(;; From Pavel Machek's patch-mode.
100 ("n" . diff-hunk-next)
101 ("N" . diff-file-next)
102 ("p" . diff-hunk-prev)
103 ("P" . diff-file-prev)
104 ("k" . diff-hunk-kill)
105 ("K" . diff-file-kill)
106 ;; From compilation-minor-mode.
107 ("}" . diff-file-next)
108 ("{" . diff-file-prev)
109 ("\C-m" . diff-goto-source)
110 ([mouse-2] . diff-mouse-goto-source)
111 ;; From XEmacs' diff-mode.
112 ("W" . widen)
113 ;;("." . diff-goto-source) ;display-buffer
114 ;;("f" . diff-goto-source) ;find-file
115 ("o" . diff-goto-source) ;other-window
116 ;;("w" . diff-goto-source) ;other-frame
117 ;;("N" . diff-narrow)
118 ;;("h" . diff-show-header)
119 ;;("j" . diff-show-difference) ;jump to Nth diff
120 ;;("q" . diff-quit)
121 (" " . scroll-up)
122 ("\177" . scroll-down)
123 ;; Our very own bindings.
124 ("A" . diff-ediff-patch)
125 ("r" . diff-restrict-view)
126 ("R" . diff-reverse-direction)
127 ("U" . diff-context->unified)
128 ("C" . diff-unified->context))
129 "Basic keymap for `diff-mode', bound to various prefix keys.")
131 (easy-mmode-defmap diff-mode-map
132 `(("\e" . ,diff-mode-shared-map)
133 ;; From compilation-minor-mode.
134 ("\C-c\C-c" . diff-goto-source)
135 ;; Misc operations.
136 ("\C-cda" . diff-apply-hunk)
137 ("\C-cdt" . diff-test-hunk))
138 "Keymap for `diff-mode'. See also `diff-mode-shared-map'.")
140 (easy-menu-define diff-mode-menu diff-mode-map
141 "Menu for `diff-mode'."
142 '("Diff"
143 ["Jump to Source" diff-goto-source t]
144 ["Apply with Ediff" diff-ediff-patch t]
145 ["-----" nil nil]
146 ["Reverse direction" diff-reverse-direction t]
147 ["Context -> Unified" diff-context->unified t]
148 ["Unified -> Context" diff-unified->context t]
149 ;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)]
152 (defcustom diff-minor-mode-prefix "\C-cd"
153 "Prefix key for `diff-minor-mode' commands."
154 :group 'diff-mode
155 :type '(choice (string "\e") (string "C-cd") string))
157 (easy-mmode-defmap diff-minor-mode-map
158 `((,diff-minor-mode-prefix . ,diff-mode-shared-map))
159 "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'.")
162 ;;;;
163 ;;;; font-lock support
164 ;;;;
166 (defface diff-header-face
167 '((t (:inherit highlight)))
168 "`diff-mode' face inherited by hunk, file and index header faces."
169 :group 'diff-mode)
170 (defvar diff-header-face 'diff-header-face)
172 (defface diff-file-header-face
173 '((t (:bold t))) ;; :height 1.3
174 "`diff-mode' face used to highlight file header lines."
175 :group 'diff-mode)
176 (defvar diff-file-header-face 'diff-file-header-face)
178 (defface diff-index-face
179 '((t (:inherit diff-file-header-face)))
180 "`diff-mode' face used to highlight index header lines."
181 :group 'diff-mode)
182 (defvar diff-index-face 'diff-index-face)
184 (defface diff-hunk-header-face
185 '((t (:inherit diff-header-face)))
186 "`diff-mode' face used to highlight hunk header lines."
187 :group 'diff-mode)
188 (defvar diff-hunk-header-face 'diff-hunk-header-face)
190 (defface diff-removed-face
191 '((t (:inherit diff-changed-face)))
192 "`diff-mode' face used to highlight removed lines."
193 :group 'diff-mode)
194 (defvar diff-removed-face 'diff-removed-face)
196 (defface diff-added-face
197 '((t (:inherit diff-changed-face)))
198 "`diff-mode' face used to highlight added lines."
199 :group 'diff-mode)
200 (defvar diff-added-face 'diff-added-face)
202 (defface diff-changed-face
203 '((t ()))
204 "`diff-mode' face used to highlight changed lines."
205 :group 'diff-mode)
206 (defvar diff-changed-face 'diff-changed-face)
208 (defface diff-comment-face
209 '((t (:inherit font-lock-comment-face)))
210 "`diff-mode' face used to highlight context and other side-information."
211 :group 'diff-mode)
212 (defvar diff-comment-face 'diff-comment-face)
214 (defvar diff-font-lock-keywords
215 '(("^\\(@@ -[0-9,]+ \\+[0-9,]+ @@\\)\\(.*\\)$" ;unified
216 (1 diff-hunk-header-face)
217 (2 diff-comment-face))
218 ("^--- .+ ----$" ;context
219 . diff-hunk-header-face)
220 ("\\(\\*\\{15\\}\\)\\(.*\\)$" ;context
221 (1 diff-hunk-header-face)
222 (2 diff-comment-face))
223 ("^\\*\\*\\* .+ \\*\\*\\*\\*" ;context
224 . diff-hunk-header-face)
225 ("^\\(---\\|\\+\\+\\+\\|\\*\\*\\*\\) \\(\\S-+\\).*[^*-]\n"
226 (0 diff-header-face) (2 diff-file-header-face prepend))
227 ("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face)
228 ("^!.*\n" . diff-changed-face) ;context
229 ("^[+>].*\n" . diff-added-face)
230 ("^[-<].*\n" . diff-removed-face)
231 ("^Index: \\(.+\\).*\n" (0 diff-header-face) (1 diff-index-face prepend))
232 ("^#.*" . font-lock-string-face)
233 ("^[^-=+*!<>].*\n" . diff-comment-face)))
235 (defconst diff-font-lock-defaults
236 '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil)))
238 (defvar diff-imenu-generic-expression
239 ;; Prefer second name as first is most likely to be a backup or
240 ;; version-control name.
241 '((nil "\\+\\+\\+\\ \\([^\t\n]+\\)\t" 1) ; unidiffs
242 (nil "^--- \\([^\t\n]+\\)\t.*\n\\*" 1))) ; context diffs
244 ;;;;
245 ;;;; Compile support
246 ;;;;
248 (defvar diff-file-regexp-alist
249 '(("Index: \\(.+\\)" 1)))
251 (defvar diff-error-regexp-alist
252 '(("@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@" nil 2)
253 ("--- \\([0-9]+\\),[0-9]+ ----" nil 1)
254 ("\\([0-9]+\\)\\(,[0-9]+\\)?[adc]\\([0-9]+\\)" nil 3)))
256 ;;;;
257 ;;;; Movement
258 ;;;;
260 (defconst diff-hunk-header-re "^\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$")
261 (defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+\\|\\*\\*\\* .+\n---\\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1)))
262 (defvar diff-narrowed-to nil)
264 (defun diff-end-of-hunk (&optional style)
265 (if (looking-at diff-hunk-header-re) (goto-char (match-end 0)))
266 (let ((end (and (re-search-forward (case style
267 (unified "^[^-+# \\]")
268 (context "^[^-+#! \\]")
269 (normal "^[^<>#\\]")
270 (t "^[^-+#!<> \\]"))
271 nil t)
272 (match-beginning 0))))
273 ;; The return value is used by easy-mmode-define-navigation.
274 (goto-char (or end (point-max)))))
276 (defun diff-beginning-of-hunk ()
277 (beginning-of-line)
278 (unless (looking-at diff-hunk-header-re)
279 (forward-line 1)
280 (condition-case ()
281 (re-search-backward diff-hunk-header-re)
282 (error (error "Can't find the beginning of the hunk")))))
284 (defun diff-beginning-of-file ()
285 (beginning-of-line)
286 (unless (looking-at diff-file-header-re)
287 (forward-line 2)
288 (condition-case ()
289 (re-search-backward diff-file-header-re)
290 (error (error "Can't find the beginning of the file")))))
292 (defun diff-end-of-file ()
293 (re-search-forward "^[-+#!<>0-9@* \\]" nil t)
294 (re-search-forward "^[^-+#!<>0-9@* \\]" nil 'move)
295 (beginning-of-line))
297 ;; Define diff-{hunk,file}-{prev,next}
298 (easy-mmode-define-navigation
299 diff-hunk diff-hunk-header-re "hunk" diff-end-of-hunk)
300 (easy-mmode-define-navigation
301 diff-file diff-file-header-re "file" diff-end-of-hunk)
303 (defun diff-restrict-view (&optional arg)
304 "Restrict the view to the current hunk.
305 If the prefix ARG is given, restrict the view to the current file instead."
306 (interactive "P")
307 (save-excursion
308 (if arg (diff-beginning-of-file) (diff-beginning-of-hunk))
309 (narrow-to-region (point)
310 (progn (if arg (diff-end-of-file) (diff-end-of-hunk))
311 (point)))
312 (set (make-local-variable 'diff-narrowed-to) (if arg 'file 'hunk))))
315 (defun diff-hunk-kill ()
316 "Kill current hunk."
317 (interactive)
318 (diff-beginning-of-hunk)
319 (let ((start (point))
320 (firsthunk (save-excursion
321 (ignore-errors
322 (diff-beginning-of-file) (diff-hunk-next) (point))))
323 (nexthunk (save-excursion
324 (ignore-errors
325 (diff-hunk-next) (point))))
326 (nextfile (save-excursion
327 (ignore-errors
328 (diff-file-next) (point)))))
329 (if (and firsthunk (= firsthunk start)
330 (or (null nexthunk)
331 (and nextfile (> nexthunk nextfile))))
332 ;; we're the only hunk for this file, so kill the file
333 (diff-file-kill)
334 (diff-end-of-hunk)
335 (kill-region start (point)))))
337 (defun diff-file-kill ()
338 "Kill current file's hunks."
339 (interactive)
340 (diff-beginning-of-file)
341 (let* ((start (point))
342 (prevhunk (save-excursion
343 (ignore-errors
344 (diff-hunk-prev) (point))))
345 (index (save-excursion
346 (re-search-backward "^Index: " prevhunk t))))
347 (when index (setq start index))
348 (diff-end-of-file)
349 (kill-region start (point))))
351 (defun diff-kill-junk ()
352 "Kill spurious empty diffs."
353 (interactive)
354 (save-excursion
355 (let ((inhibit-read-only t))
356 (goto-char (point-min))
357 (while (re-search-forward (concat "^\\(Index: .*\n\\)"
358 "\\([^-+!* <>].*\n\\)*?"
359 "\\(\\(Index:\\) \\|"
360 diff-file-header-re "\\)")
361 nil t)
362 (delete-region (if (match-end 4) (match-beginning 0) (match-end 1))
363 (match-beginning 3))
364 (beginning-of-line)))))
366 ;;;;
367 ;;;; jump to other buffers
368 ;;;;
370 (defvar diff-remembered-files-alist nil)
372 (defun diff-filename-drop-dir (file)
373 (when (string-match "/" file) (substring file (match-end 0))))
375 (defun diff-merge-strings (ancestor from to)
376 "Merge the diff between ANCESTOR and FROM into TO.
377 Returns the merged string if successful or nil otherwise.
378 The strings are assumed not to contain any \"\\n\" (i.e. end of line).
379 If ANCESTOR = FROM, returns TO.
380 If ANCESTOR = TO, returns FROM.
381 The heuristic is simplistic and only really works for cases
382 like \(diff-merge-strings \"b/foo\" \"b/bar\" \"/a/c/foo\")."
383 ;; Ideally, we want:
384 ;; AMB ANB CMD -> CND
385 ;; but that's ambiguous if `foo' or `bar' is empty:
386 ;; a/foo a/foo1 b/foo.c -> b/foo1.c but not 1b/foo.c or b/foo.c1
387 (let ((str (concat ancestor "\n" from "\n" to)))
388 (when (and (string-match (concat
389 "\\`\\(.*?\\)\\(.*\\)\\(.*\\)\n"
390 "\\1\\(.*\\)\\3\n"
391 "\\(.*\\(\\2\\).*\\)\\'") str)
392 (equal to (match-string 5 str)))
393 (concat (substring str (match-beginning 5) (match-beginning 6))
394 (match-string 4 str)
395 (substring str (match-end 6) (match-end 5))))))
397 (defun diff-find-file-name (&optional old)
398 "Return the file corresponding to the current patch.
399 Non-nil OLD means that we want the old file."
400 (save-excursion
401 (unless (looking-at diff-file-header-re)
402 (or (ignore-errors (diff-beginning-of-file))
403 (re-search-forward diff-file-header-re nil t)))
404 (let* ((limit (save-excursion
405 (condition-case ()
406 (progn (diff-hunk-prev) (point))
407 (error (point-min)))))
408 (header-files
409 (if (looking-at "[-*][-*][-*] \\(\\S-+\\)\\s-.*\n[-+][-+][-+] \\(\\S-+\\)\\s-.*$")
410 (list (if old (match-string 1) (match-string 2))
411 (if old (match-string 2) (match-string 1)))
412 (forward-line 1) nil))
413 (fs (append
414 (when (save-excursion
415 (re-search-backward "^Index: \\(.+\\)" limit t))
416 (list (match-string 1)))
417 header-files
418 (when (re-search-backward "^diff \\(-\\S-+ +\\)*\\(\\S-+\\)\\( +\\(\\S-+\\)\\)?" nil t)
419 (list (if old (match-string 2) (match-string 4))
420 (if old (match-string 4) (match-string 2))))))
421 (fs (delq nil fs)))
423 ;; use any previously used preference
424 (cdr (assoc fs diff-remembered-files-alist))
425 ;; try to be clever and use previous choices as an inspiration
426 (dolist (rf diff-remembered-files-alist)
427 (let ((newfile (diff-merge-strings (caar rf) (car fs) (cdr rf))))
428 (if (and newfile (file-exists-p newfile)) (return newfile))))
429 ;; look for each file in turn. If none found, try again but
430 ;; ignoring the first level of directory, ...
431 (do* ((files fs (delq nil (mapcar 'diff-filename-drop-dir files)))
432 (file nil nil))
433 ((or (null files)
434 (setq file (do* ((files files (cdr files))
435 (file (car files) (car files)))
436 ((or (null file) (file-exists-p file))
437 file))))
438 file))
439 ;; <foo>.rej patches implicitly apply to <foo>
440 (and (string-match "\\.rej\\'" (or buffer-file-name ""))
441 (let ((file (substring buffer-file-name 0 (match-beginning 0))))
442 (when (file-exists-p file) file)))
443 ;; if all else fails, ask the user
444 (let ((file (read-file-name (format "Use file %s: " (or (first fs) ""))
445 nil (first fs) t (first fs))))
446 (set (make-local-variable 'diff-remembered-files-alist)
447 (cons (cons fs file) diff-remembered-files-alist))
448 file)))))
450 (defun diff-mouse-goto-source (event)
451 "Run `diff-goto-source' for the diff at a mouse click."
452 (interactive "e")
453 (save-excursion
454 (mouse-set-point event)
455 (diff-goto-source)))
457 (defun diff-ediff-patch ()
458 "Call `ediff-patch-file' on the current buffer."
459 (interactive)
460 (condition-case err
461 (ediff-patch-file nil (current-buffer))
462 (wrong-number-of-arguments (ediff-patch-file))))
464 ;;;;
465 ;;;; Conversion functions
466 ;;;;
468 ;;(defvar diff-inhibit-after-change nil
469 ;; "Non-nil means inhibit `diff-mode's after-change functions.")
471 (defun diff-unified->context (start end)
472 "Convert unified diffs to context diffs.
473 START and END are either taken from the region (if a prefix arg is given) or
474 else cover the whole bufer."
475 (interactive (if current-prefix-arg
476 (list (mark) (point))
477 (list (point-min) (point-max))))
478 (unless (markerp end) (setq end (copy-marker end)))
479 (let (;;(diff-inhibit-after-change t)
480 (inhibit-read-only t))
481 (save-excursion
482 (goto-char start)
483 (while (and (re-search-forward "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|@@ -\\([0-9]+\\),\\([0-9]+\\) \\+\\([0-9]+\\),\\([0-9]+\\) @@.*\\)$" nil t)
484 (< (point) end))
485 (combine-after-change-calls
486 (if (match-beginning 2)
487 ;; we matched a file header
488 (progn
489 ;; use reverse order to make sure the indices are kept valid
490 (replace-match "---" t t nil 3)
491 (replace-match "***" t t nil 2))
492 ;; we matched a hunk header
493 (let ((line1 (match-string 4))
494 (lines1 (match-string 5))
495 (line2 (match-string 6))
496 (lines2 (match-string 7)))
497 (replace-match
498 (concat "***************\n*** " line1 ","
499 (number-to-string (+ (string-to-number line1)
500 (string-to-number lines1)
501 -1)) " ****"))
502 (forward-line 1)
503 (save-restriction
504 (narrow-to-region (point)
505 (progn (diff-end-of-hunk 'unified) (point)))
506 (let ((hunk (buffer-string)))
507 (goto-char (point-min))
508 (if (not (save-excursion (re-search-forward "^-" nil t)))
509 (delete-region (point) (point-max))
510 (goto-char (point-max))
511 (let ((modif nil) last-pt)
512 (while (progn (setq last-pt (point))
513 (= (forward-line -1) 0))
514 (case (char-after)
515 (? (insert " ") (setq modif nil) (backward-char 1))
516 (?+ (delete-region (point) last-pt) (setq modif t))
517 (?- (if (not modif)
518 (progn (forward-char 1)
519 (insert " "))
520 (delete-char 1)
521 (insert "! "))
522 (backward-char 2))
523 (?\\ (when (save-excursion (forward-line -1)
524 (= (char-after) ?+))
525 (delete-region (point) last-pt) (setq modif t)))
526 (t (setq modif nil))))))
527 (goto-char (point-max))
528 (save-excursion
529 (insert "--- " line2 ","
530 (number-to-string (+ (string-to-number line2)
531 (string-to-number lines2)
532 -1)) " ----\n" hunk))
533 ;;(goto-char (point-min))
534 (forward-line 1)
535 (if (not (save-excursion (re-search-forward "^+" nil t)))
536 (delete-region (point) (point-max))
537 (let ((modif nil) (delete nil))
538 (while (not (eobp))
539 (case (char-after)
540 (? (insert " ") (setq modif nil) (backward-char 1))
541 (?- (setq delete t) (setq modif t))
542 (?+ (if (not modif)
543 (progn (forward-char 1)
544 (insert " "))
545 (delete-char 1)
546 (insert "! "))
547 (backward-char 2))
548 (?\\ (when (save-excursion (forward-line 1)
549 (not (eobp)))
550 (setq delete t) (setq modif t)))
551 (t (setq modif nil)))
552 (let ((last-pt (point)))
553 (forward-line 1)
554 (when delete
555 (delete-region last-pt (point))
556 (setq delete nil)))))))))))))))
558 (defun diff-context->unified (start end)
559 "Convert context diffs to unified diffs.
560 START and END are either taken from the region (if a prefix arg is given) or
561 else cover the whole bufer."
562 (interactive (if current-prefix-arg
563 (list (mark) (point))
564 (list (point-min) (point-max))))
565 (unless (markerp end) (setq end (copy-marker end)))
566 (let (;;(diff-inhibit-after-change t)
567 (inhibit-read-only t))
568 (save-excursion
569 (goto-char start)
570 (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)$" nil t)
571 (< (point) end))
572 (combine-after-change-calls
573 (if (match-beginning 2)
574 ;; we matched a file header
575 (progn
576 ;; use reverse order to make sure the indices are kept valid
577 (replace-match "+++" t t nil 3)
578 (replace-match "---" t t nil 2))
579 ;; we matched a hunk header
580 (let ((line1s (match-string 4))
581 (line1e (match-string 5))
582 (pt1 (match-beginning 0)))
583 (replace-match "")
584 (unless (re-search-forward
585 "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t)
586 (error "Can't find matching `--- n1,n2 ----' line"))
587 (let ((line2s (match-string 1))
588 (line2e (match-string 2))
589 (pt2 (progn
590 (delete-region (progn (beginning-of-line) (point))
591 (progn (forward-line 1) (point)))
592 (point-marker))))
593 (goto-char pt1)
594 (forward-line 1)
595 (while (< (point) pt2)
596 (case (char-after)
597 ((?! ?-) (delete-char 2) (insert "-") (forward-line 1))
598 (?\ ;merge with the other half of the chunk
599 (let* ((endline2
600 (save-excursion
601 (goto-char pt2) (forward-line 1) (point)))
602 (c (char-after pt2)))
603 (case c
604 ((?! ?+)
605 (insert "+"
606 (prog1 (buffer-substring (+ pt2 2) endline2)
607 (delete-region pt2 endline2))))
608 (?\ ;FIXME: check consistency
609 (delete-region pt2 endline2)
610 (delete-char 1)
611 (forward-line 1))
612 (?\\ (forward-line 1))
613 (t (delete-char 1) (forward-line 1)))))
614 (t (forward-line 1))))
615 (while (looking-at "[+! ] ")
616 (if (/= (char-after) ?!) (forward-char 1)
617 (delete-char 1) (insert "+"))
618 (delete-char 1) (forward-line 1))
619 (save-excursion
620 (goto-char pt1)
621 (insert "@@ -" line1s ","
622 (number-to-string (- (string-to-number line1e)
623 (string-to-number line1s)
624 -1))
625 " +" line2s ","
626 (number-to-string (- (string-to-number line2e)
627 (string-to-number line2s)
628 -1)) " @@"))))))))))
630 (defun diff-reverse-direction (start end)
631 "Reverse the direction of the diffs.
632 START and END are either taken from the region (if a prefix arg is given) or
633 else cover the whole bufer."
634 (interactive (if current-prefix-arg
635 (list (mark) (point))
636 (list (point-min) (point-max))))
637 (unless (markerp end) (setq end (copy-marker end)))
638 (let (;;(diff-inhibit-after-change t)
639 (inhibit-read-only t))
640 (save-excursion
641 (goto-char start)
642 (while (and (re-search-forward "^\\(\\([-*][-*][-*] \\)\\(.+\\)\n\\([-+][-+][-+] \\)\\(.+\\)\\|\\*\\{15\\}.*\n\\*\\*\\* \\(.+\\) \\*\\*\\*\\*\\|@@ -\\([0-9,]+\\) \\+\\([0-9,]+\\) @@.*\\)$" nil t)
643 (< (point) end))
644 (combine-after-change-calls
645 (cond
646 ;; a file header
647 ((match-beginning 2) (replace-match "\\2\\5\n\\4\\3" nil))
648 ;; a context-diff hunk header
649 ((match-beginning 6)
650 (let ((pt-lines1 (match-beginning 6))
651 (lines1 (match-string 6)))
652 (replace-match "" nil nil nil 6)
653 (forward-line 1)
654 (let ((half1s (point)))
655 (while (looking-at "[-! \\][ \t]\\|#")
656 (when (= (char-after) ?-) (delete-char 1) (insert "+"))
657 (forward-line 1))
658 (let ((half1 (delete-and-extract-region half1s (point))))
659 (unless (looking-at "^--- \\([0-9]+,-?[0-9]+\\) ----$")
660 (insert half1)
661 (error "Can't find matching `--- n1,n2 ----' line"))
662 (let ((str1 (match-string 1)))
663 (replace-match lines1 nil nil nil 1)
664 (forward-line 1)
665 (let ((half2s (point)))
666 (while (looking-at "[!+ \\][ \t]\\|#")
667 (when (= (char-after) ?+) (delete-char 1) (insert "-"))
668 (forward-line 1))
669 (let ((half2 (delete-and-extract-region half2s (point))))
670 (insert (or half1 ""))
671 (goto-char half1s)
672 (insert (or half2 ""))))
673 (goto-char pt-lines1)
674 (insert str1))))))
675 ;; a unified-diff hunk header
676 ((match-beginning 7)
677 (replace-match "@@ -\\8 +\\7 @@" nil)
678 (forward-line 1)
679 (let ((c (char-after)) first last)
680 (while (case (setq c (char-after))
681 (?- (setq first (or first (point)))
682 (delete-char 1) (insert "+") t)
683 (?+ (setq last (or last (point)))
684 (delete-char 1) (insert "-") t)
685 ((?\\ ?#) t)
686 (t (when (and first last (< first last))
687 (let ((str
688 (save-excursion
689 (delete-and-extract-region first last))))
690 (insert str)))
691 (setq first nil last nil)
692 (equal ?\ c)))
693 (forward-line 1))))))))))
695 (defun diff-fixup-modifs (start end)
696 "Fixup the hunk headers (in case the buffer was modified).
697 START and END are either taken from the region (if a prefix arg is given) or
698 else cover the whole bufer."
699 (interactive (if current-prefix-arg
700 (list (mark) (point))
701 (list (point-min) (point-max))))
702 (let ((inhibit-read-only t))
703 (save-excursion
704 (goto-char end) (diff-end-of-hunk)
705 (let ((plus 0) (minus 0) (space 0) (bang 0))
706 (while (and (= (forward-line -1) 0) (<= start (point)))
707 (if (not (looking-at "\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|[-*][-*][-*] .+ [-*][-*][-*][-*]\\)$"))
708 (case (char-after)
709 (?\ (incf space))
710 (?+ (incf plus))
711 (?- (incf minus))
712 (?! (incf bang))
713 ((?\\ ?#) nil)
714 (t (setq space 0 plus 0 minus 0 bang 0)))
715 (cond
716 ((looking-at "@@ -[0-9]+,\\([0-9]*\\) \\+[0-9]+,\\([0-9]*\\) @@.*$")
717 (let* ((old1 (match-string 1))
718 (old2 (match-string 2))
719 (new1 (number-to-string (+ space minus)))
720 (new2 (number-to-string (+ space plus))))
721 (unless (string= new2 old2) (replace-match new2 t t nil 2))
722 (unless (string= new1 old1) (replace-match new1 t t nil 1))))
723 ((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$")
724 (when (> (+ space bang plus) 0)
725 (let* ((old1 (match-string 1))
726 (old2 (match-string 2))
727 (new (number-to-string
728 (+ space bang plus -1 (string-to-number old1)))))
729 (unless (string= new old2) (replace-match new t t nil 2)))))
730 ((looking-at "\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]*\\) \\*\\*\\*\\*$")
731 (when (> (+ space bang minus) 0)
732 (let* ((old (match-string 1))
733 (new (format
734 (concat "%0" (number-to-string (length old)) "d")
735 (+ space bang minus -1 (string-to-number old)))))
736 (unless (string= new old) (replace-match new t t nil 2))))))
737 (setq space 0 plus 0 minus 0 bang 0)))))))
739 ;;;;
740 ;;;; Hooks
741 ;;;;
743 (defun diff-write-contents-hooks ()
744 "Fixup hunk headers if necessary."
745 (if (buffer-modified-p) (diff-fixup-modifs (point-min) (point-max)))
746 nil)
748 ;; It turns out that making changes in the buffer from within an
749 ;; *-change-function is asking for trouble, whereas making them
750 ;; from a post-command-hook doesn't pose much problems
751 (defvar diff-unhandled-changes nil)
752 (defun diff-after-change-function (beg end len)
753 "Remember to fixup the hunk header.
754 See `after-change-functions' for the meaning of BEG, END and LEN."
755 ;; Ignoring changes when inhibit-read-only is set is strictly speaking
756 ;; incorrect, but it turns out that inhibit-read-only is normally not set
757 ;; inside editing commands, while it tends to be set when the buffer gets
758 ;; updated by an async process or by a conversion function, both of which
759 ;; would rather not be uselessly slowed down by this hook.
760 (when (and (not undo-in-progress) (not inhibit-read-only))
761 (if diff-unhandled-changes
762 (setq diff-unhandled-changes
763 (cons (min beg (car diff-unhandled-changes))
764 (max beg (cdr diff-unhandled-changes))))
765 (setq diff-unhandled-changes (cons beg end)))))
767 (defun diff-post-command-hook ()
768 "Fixup hunk headers if necessary."
769 (when (consp diff-unhandled-changes)
770 (ignore-errors
771 (save-excursion
772 (goto-char (car diff-unhandled-changes))
773 (unless (ignore-errors
774 (diff-beginning-of-hunk)
775 (save-excursion
776 (diff-end-of-hunk)
777 (> (point) (car diff-unhandled-changes))))
778 (goto-char (car diff-unhandled-changes))
779 (re-search-forward diff-hunk-header-re (cdr diff-unhandled-changes))
780 (diff-beginning-of-hunk))
781 (diff-fixup-modifs (point) (cdr diff-unhandled-changes))))
782 (setq diff-unhandled-changes nil)))
784 ;;;;
785 ;;;; The main function
786 ;;;;
788 ;;;###autoload
789 (define-derived-mode diff-mode fundamental-mode "Diff"
790 "Major mode for viewing/editing context diffs.
791 Supports unified and context diffs as well as (to a lesser extent) normal diffs.
792 When the buffer is read-only, the ESC prefix is not necessary.
793 This mode runs `diff-mode-hook'.
794 \\{diff-mode-map}"
795 (set (make-local-variable 'font-lock-defaults) diff-font-lock-defaults)
796 (set (make-local-variable 'outline-regexp) diff-outline-regexp)
797 (set (make-local-variable 'imenu-generic-expression)
798 diff-imenu-generic-expression)
799 ;; These are not perfect. They would be better done separately for
800 ;; context diffs and unidiffs.
801 ;; (set (make-local-variable 'paragraph-start)
802 ;; (concat "@@ " ; unidiff hunk
803 ;; "\\|\\*\\*\\* " ; context diff hunk or file start
804 ;; "\\|--- [^\t]+\t")) ; context or unidiff file
805 ;; ; start (first or second line)
806 ;; (set (make-local-variable 'paragraph-separate) paragraph-start)
807 ;; (set (make-local-variable 'page-delimiter) "--- [^\t]+\t")
808 ;; compile support
809 (set (make-local-variable 'compilation-file-regexp-alist)
810 diff-file-regexp-alist)
811 (set (make-local-variable 'compilation-error-regexp-alist)
812 diff-error-regexp-alist)
813 (when (string-match "\\.rej\\'" (or buffer-file-name ""))
814 (set (make-local-variable 'compilation-current-file)
815 (substring buffer-file-name 0 (match-beginning 0))))
816 (compilation-shell-minor-mode 1)
817 ;; setup change hooks
818 (toggle-read-only t)
819 (if (not diff-update-on-the-fly-flag)
820 (add-hook 'write-contents-hooks 'diff-write-contents-hooks)
821 (make-local-variable 'diff-unhandled-changes)
822 (add-hook (make-local-hook 'after-change-functions)
823 'diff-after-change-function nil t)
824 (add-hook (make-local-hook 'post-command-hook)
825 'diff-post-command-hook nil t))
826 ;; Neat trick from Dave Love to add more bindings in read-only mode:
827 (add-to-list (make-local-variable 'minor-mode-overriding-map-alist)
828 (cons 'buffer-read-only diff-mode-shared-map)))
830 ;;;###autoload
831 (define-minor-mode diff-minor-mode
832 "Minor mode for viewing/editing context diffs.
833 \\{diff-minor-mode-map}"
834 nil " Diff" nil
835 ;; FIXME: setup font-lock
836 ;; setup change hooks
837 (if (not diff-update-on-the-fly-flag)
838 (add-hook 'write-contents-hooks 'diff-write-contents-hooks)
839 (make-local-variable 'diff-unhandled-changes)
840 (add-hook (make-local-hook 'after-change-functions)
841 'diff-after-change-function nil t)
842 (add-hook (make-local-hook 'post-command-hook)
843 'diff-post-command-hook nil t)))
847 ;;; Misc operations that have proved useful at some point.
850 (defun diff-next-complex-hunk ()
851 "Jump to the next \"complex\" hunk.
852 \"Complex\" is approximated by \"the hunk changes the number of lines\".
853 Only works for unified diffs."
854 (interactive)
855 (while
856 (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@"
857 nil t)
858 (equal (match-string 1) (match-string 2)))))
860 (defun diff-hunk-text (hunk destp &optional line-offset)
861 "Returns the literal source text from HUNK.
862 if DESTP is nil return the source, otherwise the destination text.
863 If LINE-OFFSET is non-nil, it should be a line-offset in
864 HUNK, and instead of a string, a cons cell is returned whose car is the
865 appropriate text, and whose cdr is the corresponding line-offset in that text."
866 (with-temp-buffer
867 (insert hunk)
868 (goto-char (point-min))
869 (let ((src-pos nil)
870 (dst-pos nil)
871 (divider-pos nil)
872 (num-pfx-chars 2))
873 ;; Set the following variables:
874 ;; SRC-POS buffer pos of the source part of the hunk or nil if none
875 ;; DST-POS buffer pos of the destination part of the hunk or nil
876 ;; DIVIDER-POS buffer pos of any divider line separating the src & dst
877 ;; NUM-PFX-CHARS number of line-prefix characters used by this format"
878 (cond ((looking-at "^@@")
879 ;; unified diff
880 (setq num-pfx-chars 1)
881 (forward-line 1)
882 (setq src-pos (point) dst-pos (point)))
883 ((looking-at "^\\*\\*")
884 ;; context diff
885 (forward-line 2)
886 (setq src-pos (point))
887 (re-search-forward "^--- " nil t)
888 (forward-line 0)
889 (setq divider-pos (point))
890 (forward-line 1)
891 (setq dst-pos (point)))
892 ((looking-at "^[0-9]+a[0-9,]+$")
893 ;; normal diff, insert
894 (forward-line 1)
895 (setq dst-pos (point)))
896 ((looking-at "^[0-9,]+d[0-9]+$")
897 ;; normal diff, delete
898 (forward-line 1)
899 (setq src-pos (point)))
900 ((looking-at "^[0-9,]+c[0-9,]+$")
901 ;; normal diff, change
902 (forward-line 1)
903 (setq src-pos (point))
904 (re-search-forward "^---$" nil t)
905 (forward-line 0)
906 (setq divider-pos (point))
907 (forward-line 1)
908 (setq dst-pos (point)))
910 (error "Unknown diff hunk type")))
911 (if (if destp (null dst-pos) (null src-pos))
912 ;; Implied empty text
913 (if line-offset '("" . 0) "")
915 (when line-offset
916 (goto-char (point-min))
917 (forward-line line-offset))
919 ;; Get rid of anything except the desired text.
920 (save-excursion
921 ;; Delete unused text region
922 (let ((keep (if destp dst-pos src-pos))
923 (kill (or divider-pos (if destp src-pos dst-pos))))
924 (when (and kill (> kill keep))
925 (delete-region kill (point-max)))
926 (delete-region (point-min) keep))
927 ;; Remove line-prefix characters, and unneeded lines (unified diffs).
928 (let ((kill-char (if destp ?- ?+)))
929 (goto-char (point-min))
930 (while (not (eobp))
931 (if (eq (char-after) kill-char)
932 (delete-region (point) (progn (forward-line 1) (point)))
933 (delete-char num-pfx-chars)
934 (forward-line 1)))))
936 (let ((text (buffer-substring-no-properties (point-min) (point-max))))
937 (if line-offset
938 (cons text (count-lines (point-min) (point)))
939 text))))))
941 (defun diff-find-text (text)
942 "Return the buffer position of the nearest occurance of TEXT.
943 If TEXT isn't found, nil is returned."
944 (let* ((orig (point))
945 (forw (and (search-forward text nil t)
946 (match-beginning 0)))
947 (back (and (goto-char (+ orig (length text)))
948 (search-backward text nil t)
949 (match-beginning 0))))
950 ;; Choose the closest match.
951 (if (and forw back)
952 (if (> (- forw orig) (- orig back)) back forw)
953 (or back forw))))
955 (defun diff-find-source-location (&optional other-file reverse)
956 "Find out (BUF LINE POS SRC DST SWITCHED)."
957 (save-excursion
958 (let* ((old (if (not other-file) diff-jump-to-old-file-flag
959 (not diff-jump-to-old-file-flag)))
960 (orig-point (point))
961 (hunk-line-offset
962 (progn (diff-beginning-of-hunk) (count-lines (point) orig-point)))
963 ;; Find the location specification.
964 (line (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?"))
965 (error "Can't find the hunk header")
966 (if old (match-string 1)
967 (if (match-end 3) (match-string 3)
968 (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t)
969 (error "Can't find the hunk separator"))
970 (match-string 1)))))
971 (file (or (diff-find-file-name old) (error "Can't find the file")))
972 (buf (find-file-noselect file))
973 (hunk
974 (buffer-substring (point) (progn (diff-end-of-hunk) (point))))
975 (old (diff-hunk-text hunk reverse hunk-line-offset))
976 (new (diff-hunk-text hunk (not reverse) hunk-line-offset)))
977 ;; Update the user preference if he so wished.
978 (when (> (prefix-numeric-value other-file) 8)
979 (setq diff-jump-to-old-file-flag old))
980 (with-current-buffer buf
981 (goto-line (string-to-number line))
982 (let* ((orig-pos (point))
983 (pos (diff-find-text (car old)))
984 (switched nil))
985 (when (null pos)
986 (setq pos (diff-find-text (car new)) switched t))
987 (list* buf (string-to-number line) pos
988 (if switched (list new old t) (list old new))))))))
990 (defun diff-apply-hunk (&optional reverse other-file dry-run popup noerror)
991 "Apply the current hunk to the source file.
992 By default, the new source file is patched, but if the variable
993 `diff-jump-to-old-file-flag' is non-nil, then the old source file is
994 patched instead (some commands, such as `diff-goto-source' can change
995 the value of this variable when given an appropriate prefix argument).
997 With a prefix argument, REVERSE the hunk.
998 If OTHER-FILE is non-nil, patch the old file by default, and reverse the
999 sense of `diff-jump-to-old-file-flag'.
1000 If DRY-RUN is non-nil, don't actually modify anything, just see whether
1001 it's possible to do so.
1002 If POPUP is non-nil, pop up the patched file in another window; if POPUP
1003 is `select' then select the new window too.
1004 If NOERROR is non-nil, then no error is signaled in the case where the hunk
1005 cannot be found in the source file (other errors may still be signaled).
1007 Return values are t if the hunk was sucessfully applied (or could be
1008 applied, in the case where DRY-RUN was non-nil), `reversed' if the hunk
1009 was applied backwards, or nil if the hunk couldn't be found and NOERROR
1010 was non-nil."
1011 (interactive (list current-prefix-arg nil nil t))
1013 (when other-file
1014 ;; OTHER-FILE inverts the sense of the hunk
1015 (setq reverse (not reverse)))
1016 (when diff-jump-to-old-file-flag
1017 ;; The global variable `diff-jump-to-old-file-flag' inverts the
1018 ;; sense of OTHER-FILE (in `diff-find-source-location')
1019 (setq reverse (not reverse)))
1021 (destructuring-bind (buf patch-line pos old new &optional switched)
1022 (diff-find-source-location other-file reverse)
1024 (when (and pos switched popup)
1025 ;; A reversed patch was detected, perhaps apply it in reverse
1026 ;; (this is only done in `interactive' mode, when POPUP is non-nil).
1027 (if (or dry-run
1028 (save-window-excursion
1029 (pop-to-buffer buf)
1030 (goto-char pos)
1031 (forward-line (cdr old))
1032 (if reverse
1033 (y-or-n-p
1034 "Hunk hasn't been applied yet, so can't reverse it; apply it now? ")
1035 (y-or-n-p "Hunk has already been applied; undo it? "))))
1038 ;; The user has chosen not to apply the reversed hunk, but we
1039 ;; don't want to given an error message, so set things up so
1040 ;; nothing else gets done down below
1041 (setq pos nil)
1042 (message "(Nothing done)")
1043 (setq noerror t)))
1045 (if (null pos)
1046 ;; POS is nil, so we couldn't find the source text.
1047 (unless noerror
1048 (error "Can't find the text to patch"))
1050 (let ((reversed (if switched (not reverse) reverse)))
1051 (unless dry-run
1052 ;; Apply the hunk
1053 (with-current-buffer buf
1054 (goto-char pos)
1055 (delete-char (length (car old)))
1056 (insert (car new))))
1058 (when popup
1059 (with-current-buffer buf
1060 ;; Show a message describing what was done
1061 (let ((real-line (1+ (count-lines (point-min) pos)))
1062 (msg
1063 (if dry-run
1064 (if reversed "already applied" "not yet applied")
1065 (if reversed "undone" "applied"))))
1066 (cond ((= real-line patch-line)
1067 (message "Hunk %s" msg))
1068 ((= real-line (1+ patch-line))
1069 (message "Hunk %s at offset 1 line" msg))
1071 (message "Hunk %s at offset %d lines"
1073 (- real-line patch-line)))))
1075 ;; fixup POS to reflect the hunk line offset
1076 (goto-char pos)
1077 (forward-line (cdr (if dry-run old new)))
1078 (setq pos (point)))
1080 ;; Display BUF in a window, and maybe select it
1081 (let ((win (display-buffer buf)))
1082 (set-window-point win pos)
1083 (when (eq popup 'select)
1084 (select-window win))))
1086 ;; Return an appropriate indicator of success
1087 (if reversed 'reversed t)))))
1090 (defun diff-test-hunk (&optional reverse)
1091 "See whether it's possible to apply the current hunk.
1092 With a prefix argument, REVERSE the hunk."
1093 (interactive "P")
1094 (diff-apply-hunk reverse nil t t))
1096 (defun diff-goto-source (&optional other-file)
1097 "Jump to the corresponding source line.
1098 `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg
1099 is give) determines whether to jump to the old or the new file.
1100 If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument])
1101 then `diff-jump-to-old-file-flag' is also set, for the next invocations."
1102 (interactive "P")
1103 (destructuring-bind (buf patch-line pos src &rest ignore)
1104 (diff-find-source-location other-file)
1105 (pop-to-buffer buf)
1106 (if (null pos)
1107 (progn
1108 (goto-line patch-line)
1109 (message "Hunk text not found"))
1110 (goto-char pos)
1111 (forward-line (cdr src)))))
1115 ;; provide the package
1116 (provide 'diff-mode)
1118 ;;; Old Change Log from when diff-mode wasn't part of Emacs:
1119 ;; Revision 1.11 1999/10/09 23:38:29 monnier
1120 ;; (diff-mode-load-hook): dropped.
1121 ;; (auto-mode-alist): also catch *.diffs.
1122 ;; (diff-find-file-name, diff-mode): add smarts to find the right file
1123 ;; for *.rej files (that lack any file name indication).
1125 ;; Revision 1.10 1999/09/30 15:32:11 monnier
1126 ;; added support for "\ No newline at end of file".
1128 ;; Revision 1.9 1999/09/15 00:01:13 monnier
1129 ;; - added basic `compile' support.
1130 ;; - have diff-kill-hunk call diff-kill-file if it's the only hunk.
1131 ;; - diff-kill-file now tries to kill the leading garbage as well.
1133 ;; Revision 1.8 1999/09/13 21:10:09 monnier
1134 ;; - don't use CL in the autoloaded code
1135 ;; - accept diffs using -T
1137 ;; Revision 1.7 1999/09/05 20:53:03 monnier
1138 ;; interface to ediff-patch
1140 ;; Revision 1.6 1999/09/01 20:55:13 monnier
1141 ;; (ediff=patch-file): add bindings to call ediff-patch.
1142 ;; (diff-find-file-name): taken out of diff-goto-source.
1143 ;; (diff-unified->context, diff-context->unified, diff-reverse-direction,
1144 ;; diff-fixup-modifs): only use the region if a prefix arg is given.
1146 ;; Revision 1.5 1999/08/31 19:18:52 monnier
1147 ;; (diff-beginning-of-file, diff-prev-file): fixed wrong parenthesis.
1149 ;; Revision 1.4 1999/08/31 13:01:44 monnier
1150 ;; use `combine-after-change-calls' to minimize the slowdown of font-lock.
1153 ;;; diff-mode.el ends here