Commit | Line | Data |
---|---|---|
610a6418 SM |
1 | ;;; diff-mode.el --- A mode for viewing/editing context diffs |
2 | ||
3 | ;; Copyright (C) 1998-1999 Free Software Foundation, Inc. | |
4 | ||
5 | ;; Author: Stefan Monnier <monnier@cs.yale.edu> | |
6 | ;; Keywords: patch diff | |
c0078a04 | 7 | ;; Revision: $Id: diff-mode.el,v 1.7 2000/05/10 22:12:46 monnier Exp $ |
610a6418 SM |
8 | |
9 | ;; This file is part of GNU Emacs. | |
10 | ||
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. | |
15 | ||
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. | |
20 | ||
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. | |
25 | ||
26 | ;;; Commentary: | |
27 | ||
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. | |
31 | ||
610a6418 SM |
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 | |
35 | ||
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)) | |
40 | ||
41 | ;; Bugs: | |
42 | ||
43 | ;; - reverse doesn't work with normal diffs. | |
44 | ;; - (nitpick) the mark is not always quite right in diff-goto-source. | |
45 | ||
46 | ;; Todo: | |
47 | ||
0e104800 SM |
48 | ;; - spice up the minor-mode with font-lock support |
49 | ;; - improve narrowed-view support | |
50 | ;; - improve the `compile' support (?) | |
51 | ;; - recognize pcl-cvs' special string for `cvs-execute-single' | |
52 | ;; - support for # comments in context->unified | |
53 | ;; - diff-apply-hunk | |
54 | ;; - do a fuzzy search in diff-goto-source | |
55 | ;; - allow diff.el to use diff-mode | |
56 | ;; - imenu support | |
57 | ;; - handle `diff -b' output in context->unified | |
610a6418 SM |
58 | |
59 | ;;; Code: | |
60 | ||
61 | (eval-when-compile (require 'cl)) | |
62 | ||
63 | ||
64 | (defgroup diff-mode () | |
65 | "Major-mode for viewing/editing diffs" | |
66 | :group 'tools | |
67 | :group 'diff) | |
68 | ||
69 | (defcustom diff-jump-to-old-file-flag nil | |
70 | "*Non-nil means `diff-goto-source' jumps to the old file. | |
71 | Else, it jumps to the new file." | |
72 | :group 'diff-mode | |
73 | :type '(boolean)) | |
74 | ||
75 | (defcustom diff-update-on-the-fly-flag t | |
76 | "*Non-nil means hunk headers are kept up-to-date on-the-fly. | |
77 | When editing a diff file, the line numbers in the hunk headers | |
78 | need to be kept consistent with the actual diff. This can | |
79 | either be done on the fly (but this sometimes interacts poorly with the | |
80 | undo mechanism) or whenever the file is written (can be slow | |
81 | when editing big diffs)." | |
82 | :group 'diff-mode | |
83 | :type '(boolean)) | |
84 | ||
85 | (defvar diff-mode-hook nil | |
86 | "Run after setting up the `diff-mode' major mode.") | |
87 | ||
88 | (defvar diff-outline-regexp | |
89 | "\\([*+][*+][*+] [^0-9]\\|@@ ...\\|\\*\\*\\* [0-9].\\|--- [0-9]..\\)") | |
90 | ||
91 | ;;;; | |
92 | ;;;; keymap, menu, ... | |
93 | ;;;; | |
94 | ||
d87e5627 | 95 | (easy-mmode-defmap diff-mode-shared-map |
610a6418 | 96 | '(;; from Pavel Machek's patch-mode |
d87e5627 SM |
97 | ("n" . diff-hunk-next) |
98 | ("N" . diff-file-next) | |
99 | ("p" . diff-hunk-prev) | |
100 | ("P" . diff-file-prev) | |
101 | ("k" . diff-hunk-kill) | |
102 | ("K" . diff-file-kill) | |
610a6418 | 103 | ;; from compilation-minor-mode |
d87e5627 SM |
104 | ("}" . diff-file-next) |
105 | ("{" . diff-file-prev) | |
610a6418 SM |
106 | ("\C-m" . diff-goto-source) |
107 | ;; from XEmacs' diff-mode | |
108 | ("W" . widen) | |
109 | ;;("." . diff-goto-source) ;display-buffer | |
110 | ;;("f" . diff-goto-source) ;find-file | |
111 | ("o" . diff-goto-source) ;other-window | |
112 | ;;("w" . diff-goto-source) ;other-frame | |
113 | ;;("N" . diff-narrow) | |
114 | ;;("h" . diff-show-header) | |
115 | ;;("j" . diff-show-difference) ;jump to Nth diff | |
116 | ;;("q" . diff-quit) | |
117 | (" " . scroll-up) | |
118 | ("\177" . scroll-down) | |
119 | ;; our very own bindings | |
120 | ("A" . diff-ediff-patch) | |
121 | ("r" . diff-restrict-view) | |
122 | ("R" . diff-reverse-direction) | |
123 | ("U" . diff-context->unified) | |
124 | ("C" . diff-unified->context)) | |
0b82e382 | 125 | "Basic keymap for `diff-mode', bound to various prefix keys.") |
610a6418 | 126 | |
d87e5627 | 127 | (easy-mmode-defmap diff-mode-map |
610a6418 SM |
128 | `(("\e" . ,diff-mode-shared-map) |
129 | ;; from compilation-minor-mode | |
130 | ("\C-c\C-c" . diff-goto-source)) | |
131 | "Keymap for `diff-mode'. See also `diff-mode-shared-map'.") | |
132 | ||
133 | (easy-menu-define diff-mode-menu diff-mode-map | |
134 | "Menu for `diff-mode'." | |
135 | '("Diff" | |
136 | ["Jump to Source" diff-goto-source t] | |
137 | ["Apply with Ediff" diff-ediff-patch t] | |
138 | ["-----" nil nil] | |
139 | ["Reverse direction" diff-reverse-direction t] | |
140 | ["Context -> Unified" diff-context->unified t] | |
141 | ["Unified -> Context" diff-unified->context t] | |
142 | ;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)] | |
143 | )) | |
144 | ||
0b82e382 SM |
145 | (defcustom diff-minor-mode-prefix "\C-cd" |
146 | "Prefix key for `diff-minor-mode' commands." | |
147 | :group 'diff-mode | |
148 | :type '(choice (string "\e") (string "C-cd") string)) | |
149 | ||
d87e5627 SM |
150 | (easy-mmode-defmap diff-minor-mode-map |
151 | `((,diff-minor-mode-prefix . ,diff-mode-shared-map)) | |
0b82e382 SM |
152 | "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'.") |
153 | ||
610a6418 SM |
154 | |
155 | ;;;; | |
156 | ;;;; font-lock support | |
157 | ;;;; | |
158 | ||
159 | (defface diff-file-header-face | |
160 | '((((class color) (background light)) | |
161 | (:background "grey70" :bold t)) | |
162 | (t (:bold t))) | |
163 | "diff-mode face used to highlight file header lines." | |
164 | :group 'diff-mode) | |
165 | (defvar diff-file-header-face 'diff-file-header-face) | |
166 | ||
167 | (defface diff-index-face | |
168 | '((((class color) (background light)) | |
169 | (:background "grey70" :bold t)) | |
170 | (t (:bold t))) | |
171 | "diff-mode face used to highlight index header lines." | |
172 | :group 'diff-mode) | |
173 | (defvar diff-index-face 'diff-index-face) | |
174 | ||
175 | (defface diff-hunk-header-face | |
176 | '((((class color) (background light)) | |
177 | (:background "grey85")) | |
178 | (t (:bold t))) | |
179 | "diff-mode face used to highlight hunk header lines." | |
180 | :group 'diff-mode) | |
181 | (defvar diff-hunk-header-face 'diff-hunk-header-face) | |
182 | ||
183 | (defface diff-removed-face | |
184 | '((t ())) | |
185 | "diff-mode face used to highlight removed lines." | |
186 | :group 'diff-mode) | |
187 | (defvar diff-removed-face 'diff-removed-face) | |
188 | ||
189 | (defface diff-added-face | |
190 | '((t ())) | |
191 | "diff-mode face used to highlight added lines." | |
192 | :group 'diff-mode) | |
193 | (defvar diff-added-face 'diff-added-face) | |
194 | ||
195 | (defface diff-changed-face | |
196 | '((t ())) | |
197 | "diff-mode face used to highlight changed lines." | |
198 | :group 'diff-mode) | |
199 | (defvar diff-changed-face 'diff-changed-face) | |
200 | ||
201 | (defvar diff-font-lock-keywords | |
c0078a04 | 202 | '(("^@@ -[0-9,]+ \\+[0-9,]+ @@.*$" . diff-hunk-header-face) ;unified |
610a6418 | 203 | ("^--- .+ ----$" . diff-hunk-header-face) ;context |
c0078a04 | 204 | ("^\\*\\*\\*\\(.+\\*\\*\\*\\|\\*\\{12\\}.*\\)\n" . diff-hunk-header-face) ;context |
610a6418 SM |
205 | ("^\\(---\\|\\+\\+\\+\\|\\*\\*\\*\\) .*\n" . diff-file-header-face) |
206 | ("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face) | |
207 | ("^!.*\n" . diff-changed-face) ;context | |
208 | ("^[+>].*\n" . diff-added-face) | |
209 | ("^[-<].*\n" . diff-removed-face) | |
210 | ("^Index: .*\n" . diff-index-face) | |
0e104800 | 211 | ("^#.*" . font-lock-string-face) |
610a6418 SM |
212 | ("^[^-=+*!<>].*\n" . font-lock-comment-face))) |
213 | ||
214 | (defconst diff-font-lock-defaults | |
0e104800 | 215 | '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil))) |
610a6418 SM |
216 | |
217 | ;;;; | |
218 | ;;;; Compile support | |
219 | ;;;; | |
220 | ||
221 | (defvar diff-file-regexp-alist | |
222 | '(("Index: \\(.+\\)" 1))) | |
223 | ||
224 | (defvar diff-error-regexp-alist | |
225 | '(("@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@" nil 2) | |
226 | ("--- \\([0-9]+\\),[0-9]+ ----" nil 1) | |
227 | ("\\([0-9]+\\)\\(,[0-9]+\\)?[adc]\\([0-9]+\\)" nil 3))) | |
228 | ||
229 | ;;;; | |
230 | ;;;; Movement | |
231 | ;;;; | |
232 | ||
c0078a04 | 233 | (defconst diff-hunk-header-re "^\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$") |
610a6418 SM |
234 | (defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+\\|\\*\\*\\* .+\n---\\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1))) |
235 | (defvar diff-narrowed-to nil) | |
236 | ||
237 | (defun diff-end-of-hunk (&optional style) | |
238 | (if (looking-at diff-hunk-header-re) (goto-char (match-end 0))) | |
0e104800 SM |
239 | (let ((end (and (re-search-forward (case style |
240 | (unified "^[^-+# \\]") | |
241 | (context "^[^-+#! \\]") | |
242 | (normal "^[^<>#\\]") | |
243 | (t "^[^-+#!<> \\]")) | |
244 | nil t) | |
245 | (match-beginning 0)))) | |
246 | ;; The return value is used by easy-mmode-define-navigation. | |
247 | (goto-char (or end (point-max))))) | |
610a6418 SM |
248 | |
249 | (defun diff-beginning-of-hunk () | |
250 | (beginning-of-line) | |
251 | (unless (looking-at diff-hunk-header-re) | |
252 | (forward-line 1) | |
253 | (condition-case () | |
254 | (re-search-backward diff-hunk-header-re) | |
255 | (error (error "Can't find the beginning of the hunk"))))) | |
256 | ||
257 | (defun diff-beginning-of-file () | |
258 | (beginning-of-line) | |
259 | (unless (looking-at diff-file-header-re) | |
260 | (forward-line 2) | |
261 | (condition-case () | |
262 | (re-search-backward diff-file-header-re) | |
263 | (error (error "Can't find the beginning of the file"))))) | |
264 | ||
265 | (defun diff-end-of-file () | |
0e104800 SM |
266 | (re-search-forward "^[-+#!<>0-9@* \\]" nil t) |
267 | (re-search-forward "^[^-+#!<>0-9@* \\]" nil 'move) | |
610a6418 SM |
268 | (beginning-of-line)) |
269 | ||
d87e5627 SM |
270 | ;; Define diff-{hunk,file}-{prev,next} |
271 | (easy-mmode-define-navigation | |
272 | diff-hunk diff-hunk-header-re "hunk" diff-end-of-hunk) | |
273 | (easy-mmode-define-navigation | |
274 | diff-file diff-file-header-re "file" diff-end-of-hunk) | |
610a6418 SM |
275 | |
276 | (defun diff-restrict-view (&optional arg) | |
277 | "Restrict the view to the current hunk. | |
278 | If the prefix ARG is given, restrict the view to the current file instead." | |
279 | (interactive "P") | |
280 | (save-excursion | |
281 | (if arg (diff-beginning-of-file) (diff-beginning-of-hunk)) | |
282 | (narrow-to-region (point) | |
283 | (progn (if arg (diff-end-of-file) (diff-end-of-hunk)) | |
284 | (point))) | |
285 | (set (make-local-variable 'diff-narrowed-to) (if arg 'file 'hunk)))) | |
286 | ||
287 | ||
d87e5627 | 288 | (defun diff-hunk-kill () |
610a6418 SM |
289 | "Kill current hunk." |
290 | (interactive) | |
291 | (diff-beginning-of-hunk) | |
292 | (let ((start (point)) | |
293 | (firsthunk (save-excursion | |
294 | (ignore-errors | |
d87e5627 | 295 | (diff-beginning-of-file) (diff-hunk-next) (point)))) |
610a6418 SM |
296 | (nexthunk (save-excursion |
297 | (ignore-errors | |
d87e5627 | 298 | (diff-hunk-next) (point)))) |
610a6418 SM |
299 | (nextfile (save-excursion |
300 | (ignore-errors | |
d87e5627 | 301 | (diff-file-next) (point))))) |
610a6418 SM |
302 | (if (and firsthunk (= firsthunk start) |
303 | (or (null nexthunk) | |
304 | (and nextfile (> nexthunk nextfile)))) | |
305 | ;; we're the only hunk for this file, so kill the file | |
d87e5627 | 306 | (diff-file-kill) |
610a6418 SM |
307 | (diff-end-of-hunk) |
308 | (kill-region start (point))))) | |
309 | ||
d87e5627 | 310 | (defun diff-file-kill () |
610a6418 SM |
311 | "Kill current file's hunks." |
312 | (interactive) | |
313 | (diff-beginning-of-file) | |
314 | (let* ((start (point)) | |
315 | (prevhunk (save-excursion | |
316 | (ignore-errors | |
d87e5627 | 317 | (diff-hunk-prev) (point)))) |
610a6418 SM |
318 | (index (save-excursion |
319 | (re-search-backward "^Index: " prevhunk t)))) | |
320 | (when index (setq start index)) | |
321 | (diff-end-of-file) | |
322 | (kill-region start (point)))) | |
323 | ||
fef8c55b SM |
324 | (defun diff-kill-junk () |
325 | "Kill spurious empty diffs." | |
326 | (interactive) | |
327 | (save-excursion | |
328 | (let ((inhibit-read-only t)) | |
329 | (goto-char (point-min)) | |
330 | (while (re-search-forward (concat "^\\(Index: .*\n\\)" | |
331 | "\\([^-+!* <>].*\n\\)*?" | |
332 | "\\(\\(Index:\\) \\|" | |
333 | diff-file-header-re "\\)") | |
334 | nil t) | |
335 | (delete-region (if (match-end 4) (match-beginning 0) (match-end 1)) | |
336 | (match-beginning 3)) | |
337 | (beginning-of-line))))) | |
338 | ||
610a6418 SM |
339 | ;;;; |
340 | ;;;; jump to other buffers | |
341 | ;;;; | |
342 | ||
0b82e382 SM |
343 | (defvar diff-remembered-files-alist nil) |
344 | ||
610a6418 SM |
345 | (defun diff-filename-drop-dir (file) |
346 | (when (string-match "/" file) (substring file (match-end 0)))) | |
347 | ||
0b82e382 SM |
348 | (defun diff-merge-strings (ancestor from to) |
349 | "Merge the diff between ANCESTOR and FROM into TO. | |
350 | Returns the merged string if successful or nil otherwise. | |
d87e5627 | 351 | The strings are assumed not to contain any \"\\n\" (i.e. end of line). |
0b82e382 SM |
352 | If ANCESTOR = FROM, returns TO. |
353 | If ANCESTOR = TO, returns FROM. | |
354 | The heuristic is simplistic and only really works for cases | |
355 | like \(diff-merge-strings \"b/foo\" \"b/bar\" \"/a/c/foo\")." | |
356 | ;; Ideally, we want: | |
357 | ;; AMB ANB CMD -> CND | |
358 | ;; but that's ambiguous if `foo' or `bar' is empty: | |
359 | ;; a/foo a/foo1 b/foo.c -> b/foo1.c but not 1b/foo.c or b/foo.c1 | |
d87e5627 | 360 | (let ((str (concat ancestor "\n" from "\n" to))) |
0b82e382 | 361 | (when (and (string-match (concat |
d87e5627 SM |
362 | "\\`\\(.*?\\)\\(.*\\)\\(.*\\)\n" |
363 | "\\1\\(.*\\)\\3\n" | |
0b82e382 SM |
364 | "\\(.*\\(\\2\\).*\\)\\'") str) |
365 | (equal to (match-string 5 str))) | |
366 | (concat (substring str (match-beginning 5) (match-beginning 6)) | |
367 | (match-string 4 str) | |
368 | (substring str (match-end 6) (match-end 5)))))) | |
369 | ||
610a6418 SM |
370 | (defun diff-find-file-name (&optional old) |
371 | "Return the file corresponding to the current patch. | |
372 | Non-nil OLD means that we want the old file." | |
373 | (save-excursion | |
374 | (unless (looking-at diff-file-header-re) | |
375 | (or (ignore-errors (diff-beginning-of-file)) | |
376 | (re-search-forward diff-file-header-re nil t))) | |
377 | (let* ((limit (save-excursion | |
378 | (condition-case () | |
d87e5627 | 379 | (progn (diff-hunk-prev) (point)) |
610a6418 | 380 | (error (point-min))))) |
83b7b03b SM |
381 | (header-files |
382 | (if (looking-at "[-*][-*][-*] \\(\\S-+\\)\\s-.*\n[-+][-+][-+] \\(\\S-+\\)\\s-.*$") | |
383 | (list (if old (match-string 1) (match-string 2)) | |
384 | (if old (match-string 2) (match-string 1))) | |
385 | (forward-line 1) nil)) | |
610a6418 | 386 | (fs (append |
610a6418 SM |
387 | (when (save-excursion |
388 | (re-search-backward "^Index: \\(.+\\)" limit t)) | |
389 | (list (match-string 1))) | |
83b7b03b | 390 | header-files |
610a6418 SM |
391 | (when (re-search-backward "^diff \\(-\\S-+ +\\)*\\(\\S-+\\)\\( +\\(\\S-+\\)\\)?" nil t) |
392 | (list (if old (match-string 2) (match-string 4)) | |
393 | (if old (match-string 4) (match-string 2)))))) | |
0b82e382 | 394 | (fs (delq nil fs))) |
610a6418 | 395 | (or |
0b82e382 SM |
396 | ;; use any previously used preference |
397 | (cdr (assoc fs diff-remembered-files-alist)) | |
398 | ;; try to be clever and use previous choices as an inspiration | |
399 | (dolist (rf diff-remembered-files-alist) | |
400 | (let ((newfile (diff-merge-strings (caar rf) (car fs) (cdr rf)))) | |
401 | (if (and newfile (file-exists-p newfile)) (return newfile)))) | |
402 | ;; look for each file in turn. If none found, try again but | |
403 | ;; ignoring the first level of directory, ... | |
404 | (do* ((files fs (delq nil (mapcar 'diff-filename-drop-dir files))) | |
405 | (file nil nil)) | |
406 | ((or (null files) | |
407 | (setq file (do* ((files files (cdr files)) | |
408 | (file (car files) (car files))) | |
409 | ((or (null file) (file-exists-p file)) | |
410 | file)))) | |
411 | file)) | |
412 | ;; <foo>.rej patches implicitly apply to <foo> | |
610a6418 SM |
413 | (and (string-match "\\.rej\\'" (or buffer-file-name "")) |
414 | (let ((file (substring buffer-file-name 0 (match-beginning 0)))) | |
415 | (when (file-exists-p file) file))) | |
0b82e382 SM |
416 | ;; if all else fails, ask the user |
417 | (let ((file (read-file-name (format "Use file %s: " (or (first fs) "")) | |
418 | nil (first fs) t (first fs)))) | |
419 | (set (make-local-variable 'diff-remembered-files-alist) | |
420 | (cons (cons fs file) diff-remembered-files-alist)) | |
610a6418 SM |
421 | file))))) |
422 | ||
423 | (defun diff-goto-source (&optional other-file) | |
424 | "Jump to the corresponding source line. | |
425 | `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg | |
426 | is give) determines whether to jump to the old or the new file. | |
427 | If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument]) | |
428 | then `diff-jump-to-old-file-flag' is also set, for the next invokations." | |
429 | (interactive "P") | |
430 | (save-excursion | |
431 | (let ((old (if (not other-file) diff-jump-to-old-file-flag | |
432 | (not diff-jump-to-old-file-flag)))) | |
433 | (when (> (prefix-numeric-value other-file) 8) | |
434 | (setq diff-jump-to-old-file-flag old)) | |
435 | (diff-beginning-of-hunk) | |
c0078a04 | 436 | (let* ((loc (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?")) |
610a6418 SM |
437 | (error "Can't find the hunk header") |
438 | (if old (match-string 1) | |
439 | (if (match-end 3) (match-string 3) | |
440 | (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t) | |
441 | (error "Can't find the hunk separator")) | |
442 | (match-string 1))))) | |
443 | (lines (if (string-match "^\\([0-9]*\\),\\([0-9]*\\)" loc) | |
444 | (cons (string-to-number (match-string 1 loc)) | |
445 | (string-to-number (match-string 2 loc))) | |
446 | (cons (string-to-number loc) nil))) | |
447 | (file (diff-find-file-name old))) | |
448 | (unless file (error "Can't find the file")) | |
449 | (pop-to-buffer (find-file-noselect file)) | |
450 | (let* ((line (car lines)) | |
451 | (span (if (or (null (cdr lines)) (< (cdr lines) 0)) 0 | |
452 | (if (< (cdr lines) line) (cdr lines) | |
453 | (- (cdr lines) line))))) | |
454 | (ignore-errors | |
455 | (goto-line line) | |
456 | (forward-line span) | |
457 | (push-mark (point) t t) | |
458 | (goto-line line))))))) | |
459 | ||
460 | ||
461 | (defun diff-ediff-patch () | |
462 | "Call `ediff-patch-file' on the current buffer." | |
463 | (interactive) | |
464 | (condition-case err | |
0e104800 | 465 | (ediff-patch-file nil (current-buffer)) |
610a6418 SM |
466 | (wrong-number-of-arguments (ediff-patch-file)))) |
467 | ||
468 | ;;;; | |
469 | ;;;; Conversion functions | |
470 | ;;;; | |
471 | ||
472 | ;;(defvar diff-inhibit-after-change nil | |
473 | ;; "Non-nil means inhibit `diff-mode's after-change functions.") | |
474 | ||
475 | (defun diff-unified->context (start end) | |
476 | "Convert unified diffs to context diffs. | |
477 | START and END are either taken from the region (if a prefix arg is given) or | |
478 | else cover the whole bufer." | |
479 | (interactive (if current-prefix-arg | |
480 | (list (mark) (point)) | |
481 | (list (point-min) (point-max)))) | |
482 | (unless (markerp end) (setq end (copy-marker end))) | |
483 | (let (;;(diff-inhibit-after-change t) | |
484 | (inhibit-read-only t)) | |
485 | (save-excursion | |
486 | (goto-char start) | |
c0078a04 | 487 | (while (and (re-search-forward "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|@@ -\\([0-9]+\\),\\([0-9]+\\) \\+\\([0-9]+\\),\\([0-9]+\\) @@.*\\)$" nil t) |
610a6418 SM |
488 | (< (point) end)) |
489 | (combine-after-change-calls | |
490 | (if (match-beginning 2) | |
0e104800 | 491 | ;; we matched a file header |
610a6418 SM |
492 | (progn |
493 | ;; use reverse order to make sure the indices are kept valid | |
494 | (replace-match "---" t t nil 3) | |
495 | (replace-match "***" t t nil 2)) | |
496 | ;; we matched a hunk header | |
497 | (let ((line1 (match-string 4)) | |
498 | (lines1 (match-string 5)) | |
499 | (line2 (match-string 6)) | |
500 | (lines2 (match-string 7))) | |
501 | (replace-match | |
502 | (concat "***************\n*** " line1 "," | |
503 | (number-to-string (+ (string-to-number line1) | |
504 | (string-to-number lines1) | |
505 | -1)) " ****")) | |
506 | (forward-line 1) | |
507 | (save-restriction | |
508 | (narrow-to-region (point) | |
509 | (progn (diff-end-of-hunk 'unified) (point))) | |
510 | (let ((hunk (buffer-string))) | |
511 | (goto-char (point-min)) | |
512 | (if (not (save-excursion (re-search-forward "^-" nil t))) | |
513 | (delete-region (point) (point-max)) | |
514 | (goto-char (point-max)) | |
515 | (let ((modif nil) last-pt) | |
516 | (while (progn (setq last-pt (point)) | |
517 | (= (forward-line -1) 0)) | |
518 | (case (char-after) | |
519 | (? (insert " ") (setq modif nil) (backward-char 1)) | |
520 | (?+ (delete-region (point) last-pt) (setq modif t)) | |
521 | (?- (if (not modif) | |
522 | (progn (forward-char 1) | |
523 | (insert " ")) | |
524 | (delete-char 1) | |
525 | (insert "! ")) | |
526 | (backward-char 2)) | |
527 | (?\\ (when (save-excursion (forward-line -1) | |
528 | (= (char-after) ?+)) | |
529 | (delete-region (point) last-pt) (setq modif t))) | |
530 | (t (setq modif nil)))))) | |
531 | (goto-char (point-max)) | |
532 | (save-excursion | |
533 | (insert "--- " line2 "," | |
534 | (number-to-string (+ (string-to-number line2) | |
535 | (string-to-number lines2) | |
536 | -1)) " ----\n" hunk)) | |
537 | ;;(goto-char (point-min)) | |
538 | (forward-line 1) | |
539 | (if (not (save-excursion (re-search-forward "^+" nil t))) | |
540 | (delete-region (point) (point-max)) | |
541 | (let ((modif nil) (delete nil)) | |
542 | (while (not (eobp)) | |
543 | (case (char-after) | |
544 | (? (insert " ") (setq modif nil) (backward-char 1)) | |
545 | (?- (setq delete t) (setq modif t)) | |
546 | (?+ (if (not modif) | |
547 | (progn (forward-char 1) | |
548 | (insert " ")) | |
549 | (delete-char 1) | |
550 | (insert "! ")) | |
551 | (backward-char 2)) | |
552 | (?\\ (when (save-excursion (forward-line 1) | |
553 | (not (eobp))) | |
554 | (setq delete t) (setq modif t))) | |
555 | (t (setq modif nil))) | |
556 | (let ((last-pt (point))) | |
557 | (forward-line 1) | |
558 | (when delete | |
559 | (delete-region last-pt (point)) | |
560 | (setq delete nil))))))))))))))) | |
561 | ||
562 | (defun diff-context->unified (start end) | |
563 | "Convert context diffs to unified diffs. | |
564 | START and END are either taken from the region (if a prefix arg is given) or | |
565 | else cover the whole bufer." | |
566 | (interactive (if current-prefix-arg | |
567 | (list (mark) (point)) | |
568 | (list (point-min) (point-max)))) | |
569 | (unless (markerp end) (setq end (copy-marker end))) | |
570 | (let (;;(diff-inhibit-after-change t) | |
571 | (inhibit-read-only t)) | |
572 | (save-excursion | |
573 | (goto-char start) | |
c0078a04 | 574 | (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)$" nil t) |
610a6418 SM |
575 | (< (point) end)) |
576 | (combine-after-change-calls | |
577 | (if (match-beginning 2) | |
578 | ;; we matched a file header | |
579 | (progn | |
580 | ;; use reverse order to make sure the indices are kept valid | |
581 | (replace-match "+++" t t nil 3) | |
582 | (replace-match "---" t t nil 2)) | |
583 | ;; we matched a hunk header | |
584 | (let ((line1s (match-string 4)) | |
585 | (line1e (match-string 5)) | |
586 | (pt1 (match-beginning 0))) | |
587 | (replace-match "") | |
588 | (unless (re-search-forward | |
589 | "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t) | |
590 | (error "Can't find matching `--- n1,n2 ----' line")) | |
591 | (let ((line2s (match-string 1)) | |
592 | (line2e (match-string 2)) | |
593 | (pt2 (progn | |
594 | (delete-region (progn (beginning-of-line) (point)) | |
595 | (progn (forward-line 1) (point))) | |
596 | (point-marker)))) | |
597 | (goto-char pt1) | |
598 | (forward-line 1) | |
599 | (while (< (point) pt2) | |
600 | (case (char-after) | |
601 | ((?! ?-) (delete-char 2) (insert "-") (forward-line 1)) | |
602 | (?\ ;merge with the other half of the chunk | |
603 | (let* ((endline2 | |
604 | (save-excursion | |
605 | (goto-char pt2) (forward-line 1) (point))) | |
606 | (c (char-after pt2))) | |
607 | (case c | |
608 | ((?! ?+) | |
609 | (insert "+" | |
610 | (prog1 (buffer-substring (+ pt2 2) endline2) | |
611 | (delete-region pt2 endline2)))) | |
612 | (?\ ;FIXME: check consistency | |
613 | (delete-region pt2 endline2) | |
614 | (delete-char 1) | |
615 | (forward-line 1)) | |
616 | (?\\ (forward-line 1)) | |
617 | (t (delete-char 1) (forward-line 1))))) | |
618 | (t (forward-line 1)))) | |
619 | (while (looking-at "[+! ] ") | |
620 | (if (/= (char-after) ?!) (forward-char 1) | |
621 | (delete-char 1) (insert "+")) | |
622 | (delete-char 1) (forward-line 1)) | |
623 | (save-excursion | |
624 | (goto-char pt1) | |
625 | (insert "@@ -" line1s "," | |
626 | (number-to-string (- (string-to-number line1e) | |
627 | (string-to-number line1s) | |
628 | -1)) | |
629 | " +" line2s "," | |
630 | (number-to-string (- (string-to-number line2e) | |
631 | (string-to-number line2s) | |
632 | -1)) " @@")))))))))) | |
633 | ||
634 | (defun diff-reverse-direction (start end) | |
635 | "Reverse the direction of the diffs. | |
636 | START and END are either taken from the region (if a prefix arg is given) or | |
637 | else cover the whole bufer." | |
638 | (interactive (if current-prefix-arg | |
639 | (list (mark) (point)) | |
640 | (list (point-min) (point-max)))) | |
641 | (unless (markerp end) (setq end (copy-marker end))) | |
642 | (let (;;(diff-inhibit-after-change t) | |
643 | (inhibit-read-only t)) | |
644 | (save-excursion | |
645 | (goto-char start) | |
c0078a04 | 646 | (while (and (re-search-forward "^\\(\\([-*][-*][-*] \\)\\(.+\\)\n\\([-+][-+][-+] \\)\\(.+\\)\\|\\*\\{15\\}.*\n\\*\\*\\* \\(.+\\) \\*\\*\\*\\*\\|@@ -\\([0-9,]+\\) \\+\\([0-9,]+\\) @@.*\\)$" nil t) |
610a6418 SM |
647 | (< (point) end)) |
648 | (combine-after-change-calls | |
649 | (cond | |
650 | ;; a file header | |
651 | ((match-beginning 2) (replace-match "\\2\\5\n\\4\\3" nil)) | |
652 | ;; a context-diff hunk header | |
653 | ((match-beginning 6) | |
654 | (let ((pt-lines1 (match-beginning 6)) | |
655 | (lines1 (match-string 6))) | |
656 | (replace-match "" nil nil nil 6) | |
657 | (forward-line 1) | |
658 | (let ((half1s (point))) | |
0e104800 | 659 | (while (looking-at "[-! \\][ \t]\\|#") |
610a6418 SM |
660 | (when (= (char-after) ?-) (delete-char 1) (insert "+")) |
661 | (forward-line 1)) | |
fef8c55b | 662 | (let ((half1 (delete-and-extract-region half1s (point)))) |
610a6418 SM |
663 | (unless (looking-at "^--- \\([0-9]+,-?[0-9]+\\) ----$") |
664 | (insert half1) | |
665 | (error "Can't find matching `--- n1,n2 ----' line")) | |
666 | (let ((str1 (match-string 1))) | |
667 | (replace-match lines1 nil nil nil 1) | |
668 | (forward-line 1) | |
669 | (let ((half2s (point))) | |
0e104800 | 670 | (while (looking-at "[!+ \\][ \t]\\|#") |
610a6418 SM |
671 | (when (= (char-after) ?+) (delete-char 1) (insert "-")) |
672 | (forward-line 1)) | |
fef8c55b | 673 | (let ((half2 (delete-and-extract-region half2s (point)))) |
0e104800 | 674 | (insert (or half1 "")) |
610a6418 | 675 | (goto-char half1s) |
0e104800 | 676 | (insert (or half2 "")))) |
610a6418 SM |
677 | (goto-char pt-lines1) |
678 | (insert str1)))))) | |
679 | ;; a unified-diff hunk header | |
680 | ((match-beginning 7) | |
681 | (replace-match "@@ -\\8 +\\7 @@" nil) | |
682 | (forward-line 1) | |
683 | (let ((c (char-after)) first last) | |
684 | (while (case (setq c (char-after)) | |
685 | (?- (setq first (or first (point))) | |
686 | (delete-char 1) (insert "+") t) | |
687 | (?+ (setq last (or last (point))) | |
688 | (delete-char 1) (insert "-") t) | |
0e104800 | 689 | ((?\\ ?#) t) |
610a6418 | 690 | (t (when (and first last (< first last)) |
fef8c55b SM |
691 | (let ((str |
692 | (save-excursion | |
693 | (delete-and-extract-region first last)))) | |
610a6418 SM |
694 | (insert str))) |
695 | (setq first nil last nil) | |
696 | (equal ?\ c))) | |
697 | (forward-line 1)))))))))) | |
698 | ||
699 | (defun diff-fixup-modifs (start end) | |
700 | "Fixup the hunk headers (in case the buffer was modified). | |
701 | START and END are either taken from the region (if a prefix arg is given) or | |
702 | else cover the whole bufer." | |
703 | (interactive (if current-prefix-arg | |
704 | (list (mark) (point)) | |
705 | (list (point-min) (point-max)))) | |
706 | (let ((inhibit-read-only t)) | |
707 | (save-excursion | |
708 | (goto-char end) (diff-end-of-hunk) | |
709 | (let ((plus 0) (minus 0) (space 0) (bang 0)) | |
710 | (while (and (= (forward-line -1) 0) (<= start (point))) | |
c0078a04 | 711 | (if (not (looking-at "\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|[-*][-*][-*] .+ [-*][-*][-*][-*]\\)$")) |
610a6418 SM |
712 | (case (char-after) |
713 | (?\ (incf space)) | |
714 | (?+ (incf plus)) | |
715 | (?- (incf minus)) | |
716 | (?! (incf bang)) | |
0e104800 | 717 | ((?\\ ?#) nil) |
610a6418 SM |
718 | (t (setq space 0 plus 0 minus 0 bang 0))) |
719 | (cond | |
c0078a04 | 720 | ((looking-at "@@ -[0-9]+,\\([0-9]*\\) \\+[0-9]+,\\([0-9]*\\) @@.*$") |
610a6418 SM |
721 | (let* ((old1 (match-string 1)) |
722 | (old2 (match-string 2)) | |
723 | (new1 (number-to-string (+ space minus))) | |
724 | (new2 (number-to-string (+ space plus)))) | |
725 | (unless (string= new2 old2) (replace-match new2 t t nil 2)) | |
726 | (unless (string= new1 old1) (replace-match new1 t t nil 1)))) | |
727 | ((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$") | |
728 | (when (> (+ space bang plus) 0) | |
729 | (let* ((old1 (match-string 1)) | |
730 | (old2 (match-string 2)) | |
731 | (new (number-to-string | |
732 | (+ space bang plus -1 (string-to-number old1))))) | |
733 | (unless (string= new old2) (replace-match new t t nil 2))))) | |
734 | ((looking-at "\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]*\\) \\*\\*\\*\\*$") | |
735 | (when (> (+ space bang minus) 0) | |
736 | (let* ((old (match-string 1)) | |
737 | (new (format | |
738 | (concat "%0" (number-to-string (length old)) "d") | |
739 | (+ space bang minus -1 (string-to-number old))))) | |
740 | (unless (string= new old) (replace-match new t t nil 2)))))) | |
741 | (setq space 0 plus 0 minus 0 bang 0))))))) | |
742 | ||
743 | ;;;; | |
744 | ;;;; Hooks | |
745 | ;;;; | |
746 | ||
747 | (defun diff-write-contents-hooks () | |
748 | "Fixup hunk headers if necessary." | |
749 | (if (buffer-modified-p) (diff-fixup-modifs (point-min) (point-max))) | |
750 | nil) | |
751 | ||
610a6418 SM |
752 | ;; It turns out that making changes in the buffer from within an |
753 | ;; *-change-function is asking for trouble, whereas making them | |
754 | ;; from a post-command-hook doesn't pose much problems | |
755 | (defvar diff-unhandled-changes nil) | |
756 | (defun diff-after-change-function (beg end len) | |
757 | "Remember to fixup the hunk header. | |
758 | See `after-change-functions' for the meaning of BEG, END and LEN." | |
c0078a04 SM |
759 | ;; Ignoring changes when inhibit-read-only is set is strictly speaking |
760 | ;; incorrect, but it turns out that inhibit-read-only is normally not set | |
761 | ;; inside editing commands, while it tends to be set when the buffer gets | |
762 | ;; updated by an async process or by a conversion function, both of which | |
763 | ;; would rather not be uselessly slowed down by this hook. | |
610a6418 SM |
764 | (when (and (not undo-in-progress) (not inhibit-read-only)) |
765 | (if diff-unhandled-changes | |
766 | (setq diff-unhandled-changes | |
767 | (cons (min beg (car diff-unhandled-changes)) | |
768 | (max beg (cdr diff-unhandled-changes)))) | |
769 | (setq diff-unhandled-changes (cons beg end))))) | |
770 | ||
771 | (defun diff-post-command-hook () | |
772 | "Fixup hunk headers if necessary." | |
773 | (when (consp diff-unhandled-changes) | |
774 | (ignore-errors | |
775 | (save-excursion | |
fef8c55b SM |
776 | (goto-char (car diff-unhandled-changes)) |
777 | (unless (ignore-errors | |
778 | (diff-beginning-of-hunk) | |
779 | (save-excursion | |
780 | (diff-end-of-hunk) | |
781 | (> (point) (car diff-unhandled-changes)))) | |
782 | (goto-char (car diff-unhandled-changes)) | |
783 | (re-search-forward diff-hunk-header-re (cdr diff-unhandled-changes)) | |
784 | (diff-beginning-of-hunk)) | |
610a6418 SM |
785 | (diff-fixup-modifs (point) (cdr diff-unhandled-changes)))) |
786 | (setq diff-unhandled-changes nil))) | |
787 | ||
610a6418 SM |
788 | ;;;; |
789 | ;;;; The main function | |
790 | ;;;; | |
791 | ||
610a6418 | 792 | ;;;###autoload |
d87e5627 | 793 | (define-derived-mode diff-mode fundamental-mode "Diff" |
0b82e382 | 794 | "Major mode for viewing/editing context diffs. |
610a6418 SM |
795 | Supports unified and context diffs as well as (to a lesser extent) normal diffs. |
796 | When the buffer is read-only, the ESC prefix is not necessary. | |
797 | This mode runs `diff-mode-hook'. | |
798 | \\{diff-mode-map}" | |
610a6418 SM |
799 | (set (make-local-variable 'font-lock-defaults) diff-font-lock-defaults) |
800 | (set (make-local-variable 'outline-regexp) diff-outline-regexp) | |
801 | ;; compile support | |
802 | (set (make-local-variable 'compilation-file-regexp-alist) | |
803 | diff-file-regexp-alist) | |
804 | (set (make-local-variable 'compilation-error-regexp-alist) | |
805 | diff-error-regexp-alist) | |
806 | (when (string-match "\\.rej\\'" (or buffer-file-name "")) | |
807 | (set (make-local-variable 'compilation-current-file) | |
808 | (substring buffer-file-name 0 (match-beginning 0)))) | |
809 | (compilation-shell-minor-mode 1) | |
fef8c55b SM |
810 | ;; setup change hooks |
811 | (toggle-read-only t) | |
610a6418 SM |
812 | (if (not diff-update-on-the-fly-flag) |
813 | (add-hook 'write-contents-hooks 'diff-write-contents-hooks) | |
814 | (make-local-variable 'diff-unhandled-changes) | |
0b82e382 SM |
815 | (add-hook (make-local-hook 'after-change-functions) |
816 | 'diff-after-change-function nil t) | |
817 | (add-hook (make-local-hook 'post-command-hook) | |
818 | 'diff-post-command-hook nil t)) | |
610a6418 | 819 | ;; Neat trick from Dave Love to add more bindings in read-only mode: |
fef8c55b | 820 | (add-to-list (make-local-variable 'minor-mode-overriding-map-alist) |
d87e5627 | 821 | (cons 'buffer-read-only diff-mode-shared-map))) |
610a6418 SM |
822 | |
823 | ;;;###autoload | |
0b82e382 SM |
824 | (define-minor-mode diff-minor-mode |
825 | "Minor mode for viewing/editing context diffs. | |
826 | \\{diff-minor-mode-map}" | |
827 | nil " Diff" nil | |
828 | ;; FIXME: setup font-lock | |
fef8c55b SM |
829 | ;; setup change hooks |
830 | (if (not diff-update-on-the-fly-flag) | |
831 | (add-hook 'write-contents-hooks 'diff-write-contents-hooks) | |
832 | (make-local-variable 'diff-unhandled-changes) | |
833 | (add-hook (make-local-hook 'after-change-functions) | |
834 | 'diff-after-change-function nil t) | |
835 | (add-hook (make-local-hook 'post-command-hook) | |
836 | 'diff-post-command-hook nil t))) | |
0b82e382 | 837 | |
610a6418 SM |
838 | |
839 | ;; provide the package | |
840 | (provide 'diff-mode) | |
841 | ||
842 | ;;; Change Log: | |
0b82e382 | 843 | ;; $Log: diff-mode.el,v $ |
c0078a04 SM |
844 | ;; Revision 1.7 2000/05/10 22:12:46 monnier |
845 | ;; (diff-font-lock-keywords): Recognize comments. | |
846 | ;; (diff-font-lock-defaults): Explicitly turn off multiline. | |
847 | ;; (diff-end-of-hunk): Handle comments and fix end-of-buffer bug. | |
848 | ;; (diff-ediff-patch): Fix call to ediff-patch-file. | |
849 | ;; (diff-end-of-file, diff-reverse-direction, diff-fixup-modifs): | |
850 | ;; Handle comments. | |
851 | ;; | |
0e104800 SM |
852 | ;; Revision 1.6 2000/03/21 16:59:17 monnier |
853 | ;; (diff-mode-*-map): use `easy-mmode-defmap'. | |
854 | ;; (diff-end-of-hunk): Return the end position for use in | |
855 | ;; `easy-mmode-define-navigation'. | |
856 | ;; (diff-recenter): Remove. | |
857 | ;; (diff-(next|prev)-*): Rename `diff-*-(prev|next)' and defined in terms | |
858 | ;; of `easy-mmode-define-navigation'. | |
859 | ;; (diff-kill-*): Rename `diff-*-kill' (for consistency with the | |
860 | ;; previous renaming) and fix to use new names. | |
861 | ;; (diff-merge-strings): Use \n as separator: simpler, faster. | |
862 | ;; (diff-mode): Use `define-derived-mode'. | |
863 | ;; | |
d87e5627 SM |
864 | ;; Revision 1.5 2000/02/07 02:01:07 monnier |
865 | ;; (diff-kill-junk): New interactive function. | |
866 | ;; (diff-reverse-direction): Use delete-and-extract-region. | |
867 | ;; (diff-post-command-hook): Restrict the area so that the hook also works | |
868 | ;; outside of any diff hunk. This is necessary for the minor-mode. | |
869 | ;; (diff-mode): Use toggle-read-only and minor-mode-overriding-map-alist. | |
870 | ;; (diff-minor-mode): Setup the hooks for header-hunk rewriting. | |
871 | ;; | |
fef8c55b SM |
872 | ;; Revision 1.4 1999/12/07 07:04:03 monnier |
873 | ;; * diff-mode.el (diff-mode-shared-map): fset'd and doc change. | |
874 | ;; (diff-minor-mode, diff-minor-mode-prefix, diff-minor-mode-map): | |
875 | ;; New code to support the minor mode version. | |
876 | ;; (diff-recenter): New function. | |
877 | ;; (diff-next-hunk, diff-next-file): Use it. | |
878 | ;; (diff-remembered-files-alist): New var. | |
879 | ;; (diff-merge-strings): New function. | |
880 | ;; (diff-find-file-name): Make it smarter and use the user's input more. | |
881 | ;; (diff-mode): Cosmetic changes. | |
882 | ;; | |
610a6418 SM |
883 | ;; Revision 1.11 1999/10/09 23:38:29 monnier |
884 | ;; (diff-mode-load-hook): dropped. | |
885 | ;; (auto-mode-alist): also catch *.diffs. | |
886 | ;; (diff-find-file-name, diff-mode): add smarts to find the right file | |
887 | ;; for *.rej files (that lack any file name indication). | |
888 | ;; | |
889 | ;; Revision 1.10 1999/09/30 15:32:11 monnier | |
890 | ;; added support for "\ No newline at end of file". | |
891 | ;; | |
892 | ;; Revision 1.9 1999/09/15 00:01:13 monnier | |
893 | ;; - added basic `compile' support. | |
894 | ;; - have diff-kill-hunk call diff-kill-file if it's the only hunk. | |
895 | ;; - diff-kill-file now tries to kill the leading garbage as well. | |
896 | ;; | |
897 | ;; Revision 1.8 1999/09/13 21:10:09 monnier | |
898 | ;; - don't use CL in the autoloaded code | |
899 | ;; - accept diffs using -T | |
900 | ;; | |
901 | ;; Revision 1.7 1999/09/05 20:53:03 monnier | |
902 | ;; interface to ediff-patch | |
903 | ;; | |
904 | ;; Revision 1.6 1999/09/01 20:55:13 monnier | |
905 | ;; (ediff=patch-file): add bindings to call ediff-patch. | |
906 | ;; (diff-find-file-name): taken out of diff-goto-source. | |
907 | ;; (diff-unified->context, diff-context->unified, diff-reverse-direction, | |
908 | ;; diff-fixup-modifs): only use the region if a prefix arg is given. | |
909 | ;; | |
910 | ;; Revision 1.5 1999/08/31 19:18:52 monnier | |
911 | ;; (diff-beginning-of-file, diff-prev-file): fixed wrong parenthesis. | |
912 | ;; | |
913 | ;; Revision 1.4 1999/08/31 13:01:44 monnier | |
914 | ;; use `combine-after-change-calls' to minimize the slowdown of font-lock. | |
915 | ;; | |
916 | ||
917 | ;;; diff-mode.el ends here |