Mercurial > emacs
comparison lisp/vc/diff-mode.el @ 109404:e93288477c43
Merge from mainline.
author | Katsumi Yamaoka <yamaoka@jpl.org> |
---|---|
date | Sun, 13 Jun 2010 22:57:55 +0000 |
parents | lisp/diff-mode.el@dc9565b08f10 lisp/diff-mode.el@d928a6a7c3f2 |
children | 25b707510762 3226ac2da7f7 |
comparison
equal
deleted
inserted
replaced
109403:681cd08dc0f7 | 109404:e93288477c43 |
---|---|
1 ;;; diff-mode.el --- a mode for viewing/editing context diffs | |
2 | |
3 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,2005, 2006, | |
4 ;; 2007, 2008, 2009, 2010 Free Software Foundation, Inc. | |
5 | |
6 ;; Author: Stefan Monnier <monnier@iro.umontreal.ca> | |
7 ;; Keywords: convenience patch diff vc | |
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 3 of the License, or | |
14 ;; (at your option) 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. If not, see <http://www.gnu.org/licenses/>. | |
23 | |
24 ;;; Commentary: | |
25 | |
26 ;; Provides support for font-lock, outline, navigation | |
27 ;; commands, editing and various conversions as well as jumping | |
28 ;; to the corresponding source file. | |
29 | |
30 ;; Inspired by Pavel Machek's patch-mode.el (<pavel@@atrey.karlin.mff.cuni.cz>) | |
31 ;; Some efforts were spent to have it somewhat compatible with XEmacs' | |
32 ;; diff-mode as well as with compilation-minor-mode | |
33 | |
34 ;; Bugs: | |
35 | |
36 ;; - Reverse doesn't work with normal diffs. | |
37 | |
38 ;; Todo: | |
39 | |
40 ;; - Improve `diff-add-change-log-entries-other-window', | |
41 ;; it is very simplistic now. | |
42 ;; | |
43 ;; - Add a `delete-after-apply' so C-c C-a automatically deletes hunks. | |
44 ;; Also allow C-c C-a to delete already-applied hunks. | |
45 ;; | |
46 ;; - Try `diff <file> <hunk>' to try and fuzzily discover the source location | |
47 ;; of a hunk. Show then the changes between <file> and <hunk> and make it | |
48 ;; possible to apply them to <file>, <hunk-src>, or <hunk-dst>. | |
49 ;; Or maybe just make it into a ".rej to diff3-markers converter". | |
50 ;; Maybe just use `wiggle' (by Neil Brown) to do it for us. | |
51 ;; | |
52 ;; - in diff-apply-hunk, strip context in replace-match to better | |
53 ;; preserve markers and spacing. | |
54 ;; - Handle `diff -b' output in context->unified. | |
55 | |
56 ;;; Code: | |
57 (eval-when-compile (require 'cl)) | |
58 | |
59 (defvar add-log-buffer-file-name-function) | |
60 | |
61 | |
62 (defgroup diff-mode () | |
63 "Major mode for viewing/editing diffs." | |
64 :version "21.1" | |
65 :group 'tools | |
66 :group 'diff) | |
67 | |
68 (defcustom diff-default-read-only nil | |
69 "If non-nil, `diff-mode' buffers default to being read-only." | |
70 :type 'boolean | |
71 :group 'diff-mode) | |
72 | |
73 (defcustom diff-jump-to-old-file nil | |
74 "Non-nil means `diff-goto-source' jumps to the old file. | |
75 Else, it jumps to the new file." | |
76 :type 'boolean | |
77 :group 'diff-mode) | |
78 | |
79 (defcustom diff-update-on-the-fly t | |
80 "Non-nil means hunk headers are kept up-to-date on-the-fly. | |
81 When editing a diff file, the line numbers in the hunk headers | |
82 need to be kept consistent with the actual diff. This can | |
83 either be done on the fly (but this sometimes interacts poorly with the | |
84 undo mechanism) or whenever the file is written (can be slow | |
85 when editing big diffs)." | |
86 :type 'boolean | |
87 :group 'diff-mode) | |
88 | |
89 (defcustom diff-advance-after-apply-hunk t | |
90 "Non-nil means `diff-apply-hunk' will move to the next hunk after applying." | |
91 :type 'boolean | |
92 :group 'diff-mode) | |
93 | |
94 (defcustom diff-mode-hook nil | |
95 "Run after setting up the `diff-mode' major mode." | |
96 :type 'hook | |
97 :options '(diff-delete-empty-files diff-make-unified) | |
98 :group 'diff-mode) | |
99 | |
100 (defvar diff-outline-regexp | |
101 "\\([*+][*+][*+] [^0-9]\\|@@ ...\\|\\*\\*\\* [0-9].\\|--- [0-9]..\\)") | |
102 | |
103 ;;;; | |
104 ;;;; keymap, menu, ... | |
105 ;;;; | |
106 | |
107 (easy-mmode-defmap diff-mode-shared-map | |
108 '(;; From Pavel Machek's patch-mode. | |
109 ("n" . diff-hunk-next) | |
110 ("N" . diff-file-next) | |
111 ("p" . diff-hunk-prev) | |
112 ("P" . diff-file-prev) | |
113 ("\t" . diff-hunk-next) | |
114 ([backtab] . diff-hunk-prev) | |
115 ("k" . diff-hunk-kill) | |
116 ("K" . diff-file-kill) | |
117 ;; From compilation-minor-mode. | |
118 ("}" . diff-file-next) | |
119 ("{" . diff-file-prev) | |
120 ("\C-m" . diff-goto-source) | |
121 ([mouse-2] . diff-goto-source) | |
122 ;; From XEmacs' diff-mode. | |
123 ;; Standard M-w is useful, so don't change M-W. | |
124 ;;("W" . widen) | |
125 ;;("." . diff-goto-source) ;display-buffer | |
126 ;;("f" . diff-goto-source) ;find-file | |
127 ("o" . diff-goto-source) ;other-window | |
128 ;;("w" . diff-goto-source) ;other-frame | |
129 ;;("N" . diff-narrow) | |
130 ;;("h" . diff-show-header) | |
131 ;;("j" . diff-show-difference) ;jump to Nth diff | |
132 ;;("q" . diff-quit) | |
133 ;; Not useful if you have to metafy them. | |
134 ;;(" " . scroll-up) | |
135 ;;("\177" . scroll-down) | |
136 ;; Standard M-a is useful, so don't change M-A. | |
137 ;;("A" . diff-ediff-patch) | |
138 ;; Standard M-r is useful, so don't change M-r or M-R. | |
139 ;;("r" . diff-restrict-view) | |
140 ;;("R" . diff-reverse-direction) | |
141 ("q" . quit-window)) | |
142 "Basic keymap for `diff-mode', bound to various prefix keys.") | |
143 | |
144 (easy-mmode-defmap diff-mode-map | |
145 `(("\e" . ,diff-mode-shared-map) | |
146 ;; From compilation-minor-mode. | |
147 ("\C-c\C-c" . diff-goto-source) | |
148 ;; By analogy with the global C-x 4 a binding. | |
149 ("\C-x4A" . diff-add-change-log-entries-other-window) | |
150 ;; Misc operations. | |
151 ("\C-c\C-a" . diff-apply-hunk) | |
152 ("\C-c\C-e" . diff-ediff-patch) | |
153 ("\C-c\C-n" . diff-restrict-view) | |
154 ("\C-c\C-s" . diff-split-hunk) | |
155 ("\C-c\C-t" . diff-test-hunk) | |
156 ("\C-c\C-r" . diff-reverse-direction) | |
157 ("\C-c\C-u" . diff-context->unified) | |
158 ;; `d' because it duplicates the context :-( --Stef | |
159 ("\C-c\C-d" . diff-unified->context) | |
160 ("\C-c\C-w" . diff-ignore-whitespace-hunk) | |
161 ("\C-c\C-b" . diff-refine-hunk) ;No reason for `b' :-( | |
162 ("\C-c\C-f" . next-error-follow-minor-mode)) | |
163 "Keymap for `diff-mode'. See also `diff-mode-shared-map'.") | |
164 | |
165 (easy-menu-define diff-mode-menu diff-mode-map | |
166 "Menu for `diff-mode'." | |
167 '("Diff" | |
168 ["Jump to Source" diff-goto-source | |
169 :help "Jump to the corresponding source line"] | |
170 ["Apply hunk" diff-apply-hunk | |
171 :help "Apply the current hunk to the source file and go to the next"] | |
172 ["Test applying hunk" diff-test-hunk | |
173 :help "See whether it's possible to apply the current hunk"] | |
174 ["Apply diff with Ediff" diff-ediff-patch | |
175 :help "Call `ediff-patch-file' on the current buffer"] | |
176 ["Create Change Log entries" diff-add-change-log-entries-other-window | |
177 :help "Create ChangeLog entries for the changes in the diff buffer"] | |
178 "-----" | |
179 ["Reverse direction" diff-reverse-direction | |
180 :help "Reverse the direction of the diffs"] | |
181 ["Context -> Unified" diff-context->unified | |
182 :help "Convert context diffs to unified diffs"] | |
183 ["Unified -> Context" diff-unified->context | |
184 :help "Convert unified diffs to context diffs"] | |
185 ;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)] | |
186 ["Show trailing whitespace" whitespace-mode | |
187 :style toggle :selected (bound-and-true-p whitespace-mode) | |
188 :help "Show trailing whitespace in modified lines"] | |
189 "-----" | |
190 ["Split hunk" diff-split-hunk | |
191 :active (diff-splittable-p) | |
192 :help "Split the current (unified diff) hunk at point into two hunks"] | |
193 ["Ignore whitespace changes" diff-ignore-whitespace-hunk | |
194 :help "Re-diff the current hunk, ignoring whitespace differences"] | |
195 ["Highlight fine changes" diff-refine-hunk | |
196 :help "Highlight changes of hunk at point at a finer granularity"] | |
197 ["Kill current hunk" diff-hunk-kill | |
198 :help "Kill current hunk"] | |
199 ["Kill current file's hunks" diff-file-kill | |
200 :help "Kill all current file's hunks"] | |
201 "-----" | |
202 ["Previous Hunk" diff-hunk-prev | |
203 :help "Go to the previous count'th hunk"] | |
204 ["Next Hunk" diff-hunk-next | |
205 :help "Go to the next count'th hunk"] | |
206 ["Previous File" diff-file-prev | |
207 :help "Go to the previous count'th file"] | |
208 ["Next File" diff-file-next | |
209 :help "Go to the next count'th file"] | |
210 )) | |
211 | |
212 (defcustom diff-minor-mode-prefix "\C-c=" | |
213 "Prefix key for `diff-minor-mode' commands." | |
214 :type '(choice (string "\e") (string "C-c=") string) | |
215 :group 'diff-mode) | |
216 | |
217 (easy-mmode-defmap diff-minor-mode-map | |
218 `((,diff-minor-mode-prefix . ,diff-mode-shared-map)) | |
219 "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'.") | |
220 | |
221 (define-minor-mode diff-auto-refine-mode | |
222 "Automatically highlight changes in detail as the user visits hunks. | |
223 When transitioning from disabled to enabled, | |
224 try to refine the current hunk, as well." | |
225 :group 'diff-mode :init-value t :lighter nil ;; " Auto-Refine" | |
226 (when diff-auto-refine-mode | |
227 (condition-case-no-debug nil (diff-refine-hunk) (error nil)))) | |
228 | |
229 ;;;; | |
230 ;;;; font-lock support | |
231 ;;;; | |
232 | |
233 (defface diff-header | |
234 '((((class color) (min-colors 88) (background light)) | |
235 :background "grey80") | |
236 (((class color) (min-colors 88) (background dark)) | |
237 :background "grey45") | |
238 (((class color) (background light)) | |
239 :foreground "blue1" :weight bold) | |
240 (((class color) (background dark)) | |
241 :foreground "green" :weight bold) | |
242 (t :weight bold)) | |
243 "`diff-mode' face inherited by hunk and index header faces." | |
244 :group 'diff-mode) | |
245 (define-obsolete-face-alias 'diff-header-face 'diff-header "22.1") | |
246 (defvar diff-header-face 'diff-header) | |
247 | |
248 (defface diff-file-header | |
249 '((((class color) (min-colors 88) (background light)) | |
250 :background "grey70" :weight bold) | |
251 (((class color) (min-colors 88) (background dark)) | |
252 :background "grey60" :weight bold) | |
253 (((class color) (background light)) | |
254 :foreground "green" :weight bold) | |
255 (((class color) (background dark)) | |
256 :foreground "cyan" :weight bold) | |
257 (t :weight bold)) ; :height 1.3 | |
258 "`diff-mode' face used to highlight file header lines." | |
259 :group 'diff-mode) | |
260 (define-obsolete-face-alias 'diff-file-header-face 'diff-file-header "22.1") | |
261 (defvar diff-file-header-face 'diff-file-header) | |
262 | |
263 (defface diff-index | |
264 '((t :inherit diff-file-header)) | |
265 "`diff-mode' face used to highlight index header lines." | |
266 :group 'diff-mode) | |
267 (define-obsolete-face-alias 'diff-index-face 'diff-index "22.1") | |
268 (defvar diff-index-face 'diff-index) | |
269 | |
270 (defface diff-hunk-header | |
271 '((t :inherit diff-header)) | |
272 "`diff-mode' face used to highlight hunk header lines." | |
273 :group 'diff-mode) | |
274 (define-obsolete-face-alias 'diff-hunk-header-face 'diff-hunk-header "22.1") | |
275 (defvar diff-hunk-header-face 'diff-hunk-header) | |
276 | |
277 (defface diff-removed | |
278 '((t :inherit diff-changed)) | |
279 "`diff-mode' face used to highlight removed lines." | |
280 :group 'diff-mode) | |
281 (define-obsolete-face-alias 'diff-removed-face 'diff-removed "22.1") | |
282 (defvar diff-removed-face 'diff-removed) | |
283 | |
284 (defface diff-added | |
285 '((t :inherit diff-changed)) | |
286 "`diff-mode' face used to highlight added lines." | |
287 :group 'diff-mode) | |
288 (define-obsolete-face-alias 'diff-added-face 'diff-added "22.1") | |
289 (defvar diff-added-face 'diff-added) | |
290 | |
291 (defface diff-changed | |
292 '((((type tty pc) (class color) (background light)) | |
293 :foreground "magenta" :weight bold :slant italic) | |
294 (((type tty pc) (class color) (background dark)) | |
295 :foreground "yellow" :weight bold :slant italic)) | |
296 "`diff-mode' face used to highlight changed lines." | |
297 :group 'diff-mode) | |
298 (define-obsolete-face-alias 'diff-changed-face 'diff-changed "22.1") | |
299 (defvar diff-changed-face 'diff-changed) | |
300 | |
301 (defface diff-indicator-removed | |
302 '((t :inherit diff-removed)) | |
303 "`diff-mode' face used to highlight indicator of removed lines (-, <)." | |
304 :group 'diff-mode | |
305 :version "22.1") | |
306 (defvar diff-indicator-removed-face 'diff-indicator-removed) | |
307 | |
308 (defface diff-indicator-added | |
309 '((t :inherit diff-added)) | |
310 "`diff-mode' face used to highlight indicator of added lines (+, >)." | |
311 :group 'diff-mode | |
312 :version "22.1") | |
313 (defvar diff-indicator-added-face 'diff-indicator-added) | |
314 | |
315 (defface diff-indicator-changed | |
316 '((t :inherit diff-changed)) | |
317 "`diff-mode' face used to highlight indicator of changed lines." | |
318 :group 'diff-mode | |
319 :version "22.1") | |
320 (defvar diff-indicator-changed-face 'diff-indicator-changed) | |
321 | |
322 (defface diff-function | |
323 '((t :inherit diff-header)) | |
324 "`diff-mode' face used to highlight function names produced by \"diff -p\"." | |
325 :group 'diff-mode) | |
326 (define-obsolete-face-alias 'diff-function-face 'diff-function "22.1") | |
327 (defvar diff-function-face 'diff-function) | |
328 | |
329 (defface diff-context | |
330 '((((class color grayscale) (min-colors 88)) :inherit shadow)) | |
331 "`diff-mode' face used to highlight context and other side-information." | |
332 :group 'diff-mode) | |
333 (define-obsolete-face-alias 'diff-context-face 'diff-context "22.1") | |
334 (defvar diff-context-face 'diff-context) | |
335 | |
336 (defface diff-nonexistent | |
337 '((t :inherit diff-file-header)) | |
338 "`diff-mode' face used to highlight nonexistent files in recursive diffs." | |
339 :group 'diff-mode) | |
340 (define-obsolete-face-alias 'diff-nonexistent-face 'diff-nonexistent "22.1") | |
341 (defvar diff-nonexistent-face 'diff-nonexistent) | |
342 | |
343 (defconst diff-yank-handler '(diff-yank-function)) | |
344 (defun diff-yank-function (text) | |
345 ;; FIXME: the yank-handler is now called separately on each piece of text | |
346 ;; with a yank-handler property, so the next-single-property-change call | |
347 ;; below will always return nil :-( --stef | |
348 (let ((mixed (next-single-property-change 0 'yank-handler text)) | |
349 (start (point))) | |
350 ;; First insert the text. | |
351 (insert text) | |
352 ;; If the text does not include any diff markers and if we're not | |
353 ;; yanking back into a diff-mode buffer, get rid of the prefixes. | |
354 (unless (or mixed (derived-mode-p 'diff-mode)) | |
355 (undo-boundary) ; Just in case the user wanted the prefixes. | |
356 (let ((re (save-excursion | |
357 (if (re-search-backward "^[><!][ \t]" start t) | |
358 (if (eq (char-after) ?!) | |
359 "^[!+- ][ \t]" "^[<>][ \t]") | |
360 "^[ <>!+-]")))) | |
361 (save-excursion | |
362 (while (re-search-backward re start t) | |
363 (replace-match "" t t))))))) | |
364 | |
365 (defconst diff-hunk-header-re-unified | |
366 "^@@ -\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? \\+\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? @@") | |
367 (defconst diff-context-mid-hunk-header-re | |
368 "--- \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? ----$") | |
369 | |
370 (defvar diff-font-lock-keywords | |
371 `((,(concat "\\(" diff-hunk-header-re-unified "\\)\\(.*\\)$") | |
372 (1 diff-hunk-header-face) (6 diff-function-face)) | |
373 ("^\\(\\*\\{15\\}\\)\\(.*\\)$" ;context | |
374 (1 diff-hunk-header-face) (2 diff-function-face)) | |
375 ("^\\*\\*\\* .+ \\*\\*\\*\\*". diff-hunk-header-face) ;context | |
376 (,diff-context-mid-hunk-header-re . diff-hunk-header-face) ;context | |
377 ("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face) ;normal | |
378 ("^---$" . diff-hunk-header-face) ;normal | |
379 ;; For file headers, accept files with spaces, but be careful to rule | |
380 ;; out false-positives when matching hunk headers. | |
381 ("^\\(---\\|\\+\\+\\+\\|\\*\\*\\*\\) \\([^\t\n]+?\\)\\(?:\t.*\\| \\(\\*\\*\\*\\*\\|----\\)\\)?\n" | |
382 (0 diff-header-face) | |
383 (2 (if (not (match-end 3)) diff-file-header-face) prepend)) | |
384 ("^\\([-<]\\)\\(.*\n\\)" | |
385 (1 diff-indicator-removed-face) (2 diff-removed-face)) | |
386 ("^\\([+>]\\)\\(.*\n\\)" | |
387 (1 diff-indicator-added-face) (2 diff-added-face)) | |
388 ("^\\(!\\)\\(.*\n\\)" | |
389 (1 diff-indicator-changed-face) (2 diff-changed-face)) | |
390 ("^Index: \\(.+\\).*\n" | |
391 (0 diff-header-face) (1 diff-index-face prepend)) | |
392 ("^Only in .*\n" . diff-nonexistent-face) | |
393 ("^\\(#\\)\\(.*\\)" | |
394 (1 font-lock-comment-delimiter-face) | |
395 (2 font-lock-comment-face)) | |
396 ("^[^-=+*!<>#].*\n" (0 diff-context-face)))) | |
397 | |
398 (defconst diff-font-lock-defaults | |
399 '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil))) | |
400 | |
401 (defvar diff-imenu-generic-expression | |
402 ;; Prefer second name as first is most likely to be a backup or | |
403 ;; version-control name. The [\t\n] at the end of the unidiff pattern | |
404 ;; catches Debian source diff files (which lack the trailing date). | |
405 '((nil "\\+\\+\\+\\ \\([^\t\n]+\\)[\t\n]" 1) ; unidiffs | |
406 (nil "^--- \\([^\t\n]+\\)\t.*\n\\*" 1))) ; context diffs | |
407 | |
408 ;;;; | |
409 ;;;; Movement | |
410 ;;;; | |
411 | |
412 (defvar diff-valid-unified-empty-line t | |
413 "If non-nil, empty lines are valid in unified diffs. | |
414 Some versions of diff replace all-blank context lines in unified format with | |
415 empty lines. This makes the format less robust, but is tolerated. | |
416 See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html") | |
417 | |
418 (defconst diff-hunk-header-re | |
419 (concat "^\\(?:" diff-hunk-header-re-unified ".*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$")) | |
420 (defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* \n]\\).+\n" (substring diff-hunk-header-re 1))) | |
421 (defvar diff-narrowed-to nil) | |
422 | |
423 (defun diff-hunk-style (&optional style) | |
424 (when (looking-at diff-hunk-header-re) | |
425 (setq style (cdr (assq (char-after) '((?@ . unified) (?* . context))))) | |
426 (goto-char (match-end 0))) | |
427 style) | |
428 | |
429 (defun diff-end-of-hunk (&optional style donttrustheader) | |
430 (let (end) | |
431 (when (looking-at diff-hunk-header-re) | |
432 ;; Especially important for unified (because headers are ambiguous). | |
433 (setq style (diff-hunk-style style)) | |
434 (goto-char (match-end 0)) | |
435 (when (and (not donttrustheader) (match-end 2)) | |
436 (let* ((nold (string-to-number (or (match-string 2) "1"))) | |
437 (nnew (string-to-number (or (match-string 4) "1"))) | |
438 (endold | |
439 (save-excursion | |
440 (re-search-forward (if diff-valid-unified-empty-line | |
441 "^[- \n]" "^[- ]") | |
442 nil t nold) | |
443 (line-beginning-position 2))) | |
444 (endnew | |
445 ;; The hunk may end with a bunch of "+" lines, so the `end' is | |
446 ;; then further than computed above. | |
447 (save-excursion | |
448 (re-search-forward (if diff-valid-unified-empty-line | |
449 "^[+ \n]" "^[+ ]") | |
450 nil t nnew) | |
451 (line-beginning-position 2)))) | |
452 (setq end (max endold endnew))))) | |
453 ;; We may have a first evaluation of `end' thanks to the hunk header. | |
454 (unless end | |
455 (setq end (and (re-search-forward | |
456 (case style | |
457 (unified (concat (if diff-valid-unified-empty-line | |
458 "^[^-+# \\\n]\\|" "^[^-+# \\]\\|") | |
459 ;; A `unified' header is ambiguous. | |
460 diff-file-header-re)) | |
461 (context "^[^-+#! \\]") | |
462 (normal "^[^<>#\\]") | |
463 (t "^[^-+#!<> \\]")) | |
464 nil t) | |
465 (match-beginning 0))) | |
466 (when diff-valid-unified-empty-line | |
467 ;; While empty lines may be valid inside hunks, they are also likely | |
468 ;; to be unrelated to the hunk. | |
469 (goto-char (or end (point-max))) | |
470 (while (eq ?\n (char-before (1- (point)))) | |
471 (forward-char -1) | |
472 (setq end (point))))) | |
473 ;; The return value is used by easy-mmode-define-navigation. | |
474 (goto-char (or end (point-max))))) | |
475 | |
476 (defun diff-beginning-of-hunk (&optional try-harder) | |
477 "Move back to beginning of hunk. | |
478 If TRY-HARDER is non-nil, try to cater to the case where we're not in a hunk | |
479 but in the file header instead, in which case move forward to the first hunk." | |
480 (beginning-of-line) | |
481 (unless (looking-at diff-hunk-header-re) | |
482 (forward-line 1) | |
483 (condition-case () | |
484 (re-search-backward diff-hunk-header-re) | |
485 (error | |
486 (if (not try-harder) | |
487 (error "Can't find the beginning of the hunk") | |
488 (diff-beginning-of-file-and-junk) | |
489 (diff-hunk-next)))))) | |
490 | |
491 (defun diff-unified-hunk-p () | |
492 (save-excursion | |
493 (ignore-errors | |
494 (diff-beginning-of-hunk) | |
495 (looking-at "^@@")))) | |
496 | |
497 (defun diff-beginning-of-file () | |
498 (beginning-of-line) | |
499 (unless (looking-at diff-file-header-re) | |
500 (let ((start (point)) | |
501 res) | |
502 ;; diff-file-header-re may need to match up to 4 lines, so in case | |
503 ;; we're inside the header, we need to move up to 3 lines forward. | |
504 (forward-line 3) | |
505 (if (and (setq res (re-search-backward diff-file-header-re nil t)) | |
506 ;; Maybe the 3 lines forward were too much and we matched | |
507 ;; a file header after our starting point :-( | |
508 (or (<= (point) start) | |
509 (setq res (re-search-backward diff-file-header-re nil t)))) | |
510 res | |
511 (goto-char start) | |
512 (error "Can't find the beginning of the file"))))) | |
513 | |
514 | |
515 (defun diff-end-of-file () | |
516 (re-search-forward "^[-+#!<>0-9@* \\]" nil t) | |
517 (re-search-forward (concat "^[^-+#!<>0-9@* \\]\\|" diff-file-header-re) | |
518 nil 'move) | |
519 (if (match-beginning 1) | |
520 (goto-char (match-beginning 1)) | |
521 (beginning-of-line))) | |
522 | |
523 ;; Define diff-{hunk,file}-{prev,next} | |
524 (easy-mmode-define-navigation | |
525 diff-hunk diff-hunk-header-re "hunk" diff-end-of-hunk diff-restrict-view | |
526 (if diff-auto-refine-mode | |
527 (condition-case-no-debug nil (diff-refine-hunk) (error nil)))) | |
528 | |
529 (easy-mmode-define-navigation | |
530 diff-file diff-file-header-re "file" diff-end-of-hunk) | |
531 | |
532 (defun diff-restrict-view (&optional arg) | |
533 "Restrict the view to the current hunk. | |
534 If the prefix ARG is given, restrict the view to the current file instead." | |
535 (interactive "P") | |
536 (save-excursion | |
537 (if arg (diff-beginning-of-file) (diff-beginning-of-hunk 'try-harder)) | |
538 (narrow-to-region (point) | |
539 (progn (if arg (diff-end-of-file) (diff-end-of-hunk)) | |
540 (point))) | |
541 (set (make-local-variable 'diff-narrowed-to) (if arg 'file 'hunk)))) | |
542 | |
543 | |
544 (defun diff-hunk-kill () | |
545 "Kill current hunk." | |
546 (interactive) | |
547 (diff-beginning-of-hunk) | |
548 (let* ((start (point)) | |
549 ;; Search the second match, since we're looking at the first. | |
550 (nexthunk (when (re-search-forward diff-hunk-header-re nil t 2) | |
551 (match-beginning 0))) | |
552 (firsthunk (ignore-errors | |
553 (goto-char start) | |
554 (diff-beginning-of-file) (diff-hunk-next) (point))) | |
555 (nextfile (ignore-errors (diff-file-next) (point))) | |
556 (inhibit-read-only t)) | |
557 (goto-char start) | |
558 (if (and firsthunk (= firsthunk start) | |
559 (or (null nexthunk) | |
560 (and nextfile (> nexthunk nextfile)))) | |
561 ;; It's the only hunk for this file, so kill the file. | |
562 (diff-file-kill) | |
563 (diff-end-of-hunk) | |
564 (kill-region start (point))))) | |
565 | |
566 ;; "index ", "old mode", "new mode", "new file mode" and | |
567 ;; "deleted file mode" are output by git-diff. | |
568 (defconst diff-file-junk-re | |
569 "diff \\|index \\|\\(?:deleted file\\|new\\(?: file\\)?\\|old\\) mode") | |
570 | |
571 (defun diff-beginning-of-file-and-junk () | |
572 "Go to the beginning of file-related diff-info. | |
573 This is like `diff-beginning-of-file' except it tries to skip back over leading | |
574 data such as \"Index: ...\" and such." | |
575 (let* ((orig (point)) | |
576 ;; Skip forward over what might be "leading junk" so as to get | |
577 ;; closer to the actual diff. | |
578 (_ (progn (beginning-of-line) | |
579 (while (looking-at diff-file-junk-re) | |
580 (forward-line 1)))) | |
581 (start (point)) | |
582 (prevfile (condition-case err | |
583 (save-excursion (diff-beginning-of-file) (point)) | |
584 (error err))) | |
585 (err (if (consp prevfile) prevfile)) | |
586 (nextfile (ignore-errors | |
587 (save-excursion | |
588 (goto-char start) (diff-file-next) (point)))) | |
589 ;; prevhunk is one of the limits. | |
590 (prevhunk (save-excursion | |
591 (ignore-errors | |
592 (if (numberp prevfile) (goto-char prevfile)) | |
593 (diff-hunk-prev) (point)))) | |
594 (previndex (save-excursion | |
595 (forward-line 1) ;In case we're looking at "Index:". | |
596 (re-search-backward "^Index: " prevhunk t)))) | |
597 ;; If we're in the junk, we should use nextfile instead of prevfile. | |
598 (if (and (numberp nextfile) | |
599 (or (not (numberp prevfile)) | |
600 (and previndex (> previndex prevfile)))) | |
601 (setq prevfile nextfile)) | |
602 (if (and previndex (numberp prevfile) (< previndex prevfile)) | |
603 (setq prevfile previndex)) | |
604 (if (and (numberp prevfile) (<= prevfile start)) | |
605 (progn | |
606 (goto-char prevfile) | |
607 ;; Now skip backward over the leading junk we may have before the | |
608 ;; diff itself. | |
609 (while (save-excursion | |
610 (and (zerop (forward-line -1)) | |
611 (looking-at diff-file-junk-re))) | |
612 (forward-line -1))) | |
613 ;; File starts *after* the starting point: we really weren't in | |
614 ;; a file diff but elsewhere. | |
615 (goto-char orig) | |
616 (signal (car err) (cdr err))))) | |
617 | |
618 (defun diff-file-kill () | |
619 "Kill current file's hunks." | |
620 (interactive) | |
621 (let ((orig (point)) | |
622 (start (progn (diff-beginning-of-file-and-junk) (point))) | |
623 (inhibit-read-only t)) | |
624 (diff-end-of-file) | |
625 (if (looking-at "^\n") (forward-char 1)) ;`tla' generates such diffs. | |
626 (if (> orig (point)) (error "Not inside a file diff")) | |
627 (kill-region start (point)))) | |
628 | |
629 (defun diff-kill-junk () | |
630 "Kill spurious empty diffs." | |
631 (interactive) | |
632 (save-excursion | |
633 (let ((inhibit-read-only t)) | |
634 (goto-char (point-min)) | |
635 (while (re-search-forward (concat "^\\(Index: .*\n\\)" | |
636 "\\([^-+!* <>].*\n\\)*?" | |
637 "\\(\\(Index:\\) \\|" | |
638 diff-file-header-re "\\)") | |
639 nil t) | |
640 (delete-region (if (match-end 4) (match-beginning 0) (match-end 1)) | |
641 (match-beginning 3)) | |
642 (beginning-of-line))))) | |
643 | |
644 (defun diff-count-matches (re start end) | |
645 (save-excursion | |
646 (let ((n 0)) | |
647 (goto-char start) | |
648 (while (re-search-forward re end t) (incf n)) | |
649 n))) | |
650 | |
651 (defun diff-splittable-p () | |
652 (save-excursion | |
653 (beginning-of-line) | |
654 (and (looking-at "^[-+ ]") | |
655 (progn (forward-line -1) (looking-at "^[-+ ]")) | |
656 (diff-unified-hunk-p)))) | |
657 | |
658 (defun diff-split-hunk () | |
659 "Split the current (unified diff) hunk at point into two hunks." | |
660 (interactive) | |
661 (beginning-of-line) | |
662 (let ((pos (point)) | |
663 (start (progn (diff-beginning-of-hunk) (point)))) | |
664 (unless (looking-at diff-hunk-header-re-unified) | |
665 (error "diff-split-hunk only works on unified context diffs")) | |
666 (forward-line 1) | |
667 (let* ((start1 (string-to-number (match-string 1))) | |
668 (start2 (string-to-number (match-string 3))) | |
669 (newstart1 (+ start1 (diff-count-matches "^[- \t]" (point) pos))) | |
670 (newstart2 (+ start2 (diff-count-matches "^[+ \t]" (point) pos))) | |
671 (inhibit-read-only t)) | |
672 (goto-char pos) | |
673 ;; Hopefully the after-change-function will not screw us over. | |
674 (insert "@@ -" (number-to-string newstart1) ",1 +" | |
675 (number-to-string newstart2) ",1 @@\n") | |
676 ;; Fix the original hunk-header. | |
677 (diff-fixup-modifs start pos)))) | |
678 | |
679 | |
680 ;;;; | |
681 ;;;; jump to other buffers | |
682 ;;;; | |
683 | |
684 (defvar diff-remembered-files-alist nil) | |
685 (defvar diff-remembered-defdir nil) | |
686 | |
687 (defun diff-filename-drop-dir (file) | |
688 (when (string-match "/" file) (substring file (match-end 0)))) | |
689 | |
690 (defun diff-merge-strings (ancestor from to) | |
691 "Merge the diff between ANCESTOR and FROM into TO. | |
692 Returns the merged string if successful or nil otherwise. | |
693 The strings are assumed not to contain any \"\\n\" (i.e. end of line). | |
694 If ANCESTOR = FROM, returns TO. | |
695 If ANCESTOR = TO, returns FROM. | |
696 The heuristic is simplistic and only really works for cases | |
697 like \(diff-merge-strings \"b/foo\" \"b/bar\" \"/a/c/foo\")." | |
698 ;; Ideally, we want: | |
699 ;; AMB ANB CMD -> CND | |
700 ;; but that's ambiguous if `foo' or `bar' is empty: | |
701 ;; a/foo a/foo1 b/foo.c -> b/foo1.c but not 1b/foo.c or b/foo.c1 | |
702 (let ((str (concat ancestor "\n" from "\n" to))) | |
703 (when (and (string-match (concat | |
704 "\\`\\(.*?\\)\\(.*\\)\\(.*\\)\n" | |
705 "\\1\\(.*\\)\\3\n" | |
706 "\\(.*\\(\\2\\).*\\)\\'") str) | |
707 (equal to (match-string 5 str))) | |
708 (concat (substring str (match-beginning 5) (match-beginning 6)) | |
709 (match-string 4 str) | |
710 (substring str (match-end 6) (match-end 5)))))) | |
711 | |
712 (defun diff-tell-file-name (old name) | |
713 "Tell Emacs where the find the source file of the current hunk. | |
714 If the OLD prefix arg is passed, tell the file NAME of the old file." | |
715 (interactive | |
716 (let* ((old current-prefix-arg) | |
717 (fs (diff-hunk-file-names current-prefix-arg))) | |
718 (unless fs (error "No file name to look for")) | |
719 (list old (read-file-name (format "File for %s: " (car fs)) | |
720 nil (diff-find-file-name old 'noprompt) t)))) | |
721 (let ((fs (diff-hunk-file-names old))) | |
722 (unless fs (error "No file name to look for")) | |
723 (push (cons fs name) diff-remembered-files-alist))) | |
724 | |
725 (defun diff-hunk-file-names (&optional old) | |
726 "Give the list of file names textually mentioned for the current hunk." | |
727 (save-excursion | |
728 (unless (looking-at diff-file-header-re) | |
729 (or (ignore-errors (diff-beginning-of-file)) | |
730 (re-search-forward diff-file-header-re nil t))) | |
731 (let ((limit (save-excursion | |
732 (condition-case () | |
733 (progn (diff-hunk-prev) (point)) | |
734 (error (point-min))))) | |
735 (header-files | |
736 (if (looking-at "[-*][-*][-*] \\(\\S-+\\)\\(\\s-.*\\)?\n[-+][-+][-+] \\(\\S-+\\)") | |
737 (list (if old (match-string 1) (match-string 3)) | |
738 (if old (match-string 3) (match-string 1))) | |
739 (forward-line 1) nil))) | |
740 (delq nil | |
741 (append | |
742 (when (and (not old) | |
743 (save-excursion | |
744 (re-search-backward "^Index: \\(.+\\)" limit t))) | |
745 (list (match-string 1))) | |
746 header-files | |
747 (when (re-search-backward | |
748 "^diff \\(-\\S-+ +\\)*\\(\\S-+\\)\\( +\\(\\S-+\\)\\)?" | |
749 nil t) | |
750 (list (if old (match-string 2) (match-string 4)) | |
751 (if old (match-string 4) (match-string 2))))))))) | |
752 | |
753 (defun diff-find-file-name (&optional old noprompt prefix) | |
754 "Return the file corresponding to the current patch. | |
755 Non-nil OLD means that we want the old file. | |
756 Non-nil NOPROMPT means to prefer returning nil than to prompt the user. | |
757 PREFIX is only used internally: don't use it." | |
758 (unless (equal diff-remembered-defdir default-directory) | |
759 ;; Flush diff-remembered-files-alist if the default-directory is changed. | |
760 (set (make-local-variable 'diff-remembered-defdir) default-directory) | |
761 (set (make-local-variable 'diff-remembered-files-alist) nil)) | |
762 (save-excursion | |
763 (unless (looking-at diff-file-header-re) | |
764 (or (ignore-errors (diff-beginning-of-file)) | |
765 (re-search-forward diff-file-header-re nil t))) | |
766 (let ((fs (diff-hunk-file-names old))) | |
767 (if prefix (setq fs (mapcar (lambda (f) (concat prefix f)) fs))) | |
768 (or | |
769 ;; use any previously used preference | |
770 (cdr (assoc fs diff-remembered-files-alist)) | |
771 ;; try to be clever and use previous choices as an inspiration | |
772 (dolist (rf diff-remembered-files-alist) | |
773 (let ((newfile (diff-merge-strings (caar rf) (car fs) (cdr rf)))) | |
774 (if (and newfile (file-exists-p newfile)) (return newfile)))) | |
775 ;; look for each file in turn. If none found, try again but | |
776 ;; ignoring the first level of directory, ... | |
777 (do* ((files fs (delq nil (mapcar 'diff-filename-drop-dir files))) | |
778 (file nil nil)) | |
779 ((or (null files) | |
780 (setq file (do* ((files files (cdr files)) | |
781 (file (car files) (car files))) | |
782 ;; Use file-regular-p to avoid | |
783 ;; /dev/null, directories, etc. | |
784 ((or (null file) (file-regular-p file)) | |
785 file)))) | |
786 file)) | |
787 ;; <foo>.rej patches implicitly apply to <foo> | |
788 (and (string-match "\\.rej\\'" (or buffer-file-name "")) | |
789 (let ((file (substring buffer-file-name 0 (match-beginning 0)))) | |
790 (when (file-exists-p file) file))) | |
791 ;; If we haven't found the file, maybe it's because we haven't paid | |
792 ;; attention to the PCL-CVS hint. | |
793 (and (not prefix) | |
794 (boundp 'cvs-pcl-cvs-dirchange-re) | |
795 (save-excursion | |
796 (re-search-backward cvs-pcl-cvs-dirchange-re nil t)) | |
797 (diff-find-file-name old noprompt (match-string 1))) | |
798 ;; if all else fails, ask the user | |
799 (unless noprompt | |
800 (let ((file (read-file-name (format "Use file %s: " | |
801 (or (first fs) "")) | |
802 nil (first fs) t (first fs)))) | |
803 (set (make-local-variable 'diff-remembered-files-alist) | |
804 (cons (cons fs file) diff-remembered-files-alist)) | |
805 file)))))) | |
806 | |
807 | |
808 (defun diff-ediff-patch () | |
809 "Call `ediff-patch-file' on the current buffer." | |
810 (interactive) | |
811 (condition-case err | |
812 (ediff-patch-file nil (current-buffer)) | |
813 (wrong-number-of-arguments (ediff-patch-file)))) | |
814 | |
815 ;;;; | |
816 ;;;; Conversion functions | |
817 ;;;; | |
818 | |
819 ;;(defvar diff-inhibit-after-change nil | |
820 ;; "Non-nil means inhibit `diff-mode's after-change functions.") | |
821 | |
822 (defun diff-unified->context (start end) | |
823 "Convert unified diffs to context diffs. | |
824 START and END are either taken from the region (if a prefix arg is given) or | |
825 else cover the whole buffer." | |
826 (interactive (if (or current-prefix-arg (and transient-mark-mode mark-active)) | |
827 (list (region-beginning) (region-end)) | |
828 (list (point-min) (point-max)))) | |
829 (unless (markerp end) (setq end (copy-marker end t))) | |
830 (let (;;(diff-inhibit-after-change t) | |
831 (inhibit-read-only t)) | |
832 (save-excursion | |
833 (goto-char start) | |
834 (while (and (re-search-forward | |
835 (concat "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|" | |
836 diff-hunk-header-re-unified ".*\\)$") | |
837 nil t) | |
838 (< (point) end)) | |
839 (combine-after-change-calls | |
840 (if (match-beginning 2) | |
841 ;; we matched a file header | |
842 (progn | |
843 ;; use reverse order to make sure the indices are kept valid | |
844 (replace-match "---" t t nil 3) | |
845 (replace-match "***" t t nil 2)) | |
846 ;; we matched a hunk header | |
847 (let ((line1 (match-string 4)) | |
848 (lines1 (or (match-string 5) "1")) | |
849 (line2 (match-string 6)) | |
850 (lines2 (or (match-string 7) "1")) | |
851 ;; Variables to use the special undo function. | |
852 (old-undo buffer-undo-list) | |
853 (old-end (marker-position end)) | |
854 (start (match-beginning 0)) | |
855 (reversible t)) | |
856 (replace-match | |
857 (concat "***************\n*** " line1 "," | |
858 (number-to-string (+ (string-to-number line1) | |
859 (string-to-number lines1) | |
860 -1)) | |
861 " ****")) | |
862 (save-restriction | |
863 (narrow-to-region (line-beginning-position 2) | |
864 ;; Call diff-end-of-hunk from just before | |
865 ;; the hunk header so it can use the hunk | |
866 ;; header info. | |
867 (progn (diff-end-of-hunk 'unified) (point))) | |
868 (let ((hunk (buffer-string))) | |
869 (goto-char (point-min)) | |
870 (if (not (save-excursion (re-search-forward "^-" nil t))) | |
871 (delete-region (point) (point-max)) | |
872 (goto-char (point-max)) | |
873 (let ((modif nil) last-pt) | |
874 (while (progn (setq last-pt (point)) | |
875 (= (forward-line -1) 0)) | |
876 (case (char-after) | |
877 (?\s (insert " ") (setq modif nil) (backward-char 1)) | |
878 (?+ (delete-region (point) last-pt) (setq modif t)) | |
879 (?- (if (not modif) | |
880 (progn (forward-char 1) | |
881 (insert " ")) | |
882 (delete-char 1) | |
883 (insert "! ")) | |
884 (backward-char 2)) | |
885 (?\\ (when (save-excursion (forward-line -1) | |
886 (= (char-after) ?+)) | |
887 (delete-region (point) last-pt) (setq modif t))) | |
888 ;; diff-valid-unified-empty-line. | |
889 (?\n (insert " ") (setq modif nil) (backward-char 2)) | |
890 (t (setq modif nil)))))) | |
891 (goto-char (point-max)) | |
892 (save-excursion | |
893 (insert "--- " line2 "," | |
894 (number-to-string (+ (string-to-number line2) | |
895 (string-to-number lines2) | |
896 -1)) | |
897 " ----\n" hunk)) | |
898 ;;(goto-char (point-min)) | |
899 (forward-line 1) | |
900 (if (not (save-excursion (re-search-forward "^+" nil t))) | |
901 (delete-region (point) (point-max)) | |
902 (let ((modif nil) (delete nil)) | |
903 (if (save-excursion (re-search-forward "^\\+.*\n-" nil t)) | |
904 ;; Normally, lines in a substitution come with | |
905 ;; first the removals and then the additions, and | |
906 ;; the context->unified function follows this | |
907 ;; convention, of course. Yet, other alternatives | |
908 ;; are valid as well, but they preclude the use of | |
909 ;; context->unified as an undo command. | |
910 (setq reversible nil)) | |
911 (while (not (eobp)) | |
912 (case (char-after) | |
913 (?\s (insert " ") (setq modif nil) (backward-char 1)) | |
914 (?- (setq delete t) (setq modif t)) | |
915 (?+ (if (not modif) | |
916 (progn (forward-char 1) | |
917 (insert " ")) | |
918 (delete-char 1) | |
919 (insert "! ")) | |
920 (backward-char 2)) | |
921 (?\\ (when (save-excursion (forward-line 1) | |
922 (not (eobp))) | |
923 (setq delete t) (setq modif t))) | |
924 ;; diff-valid-unified-empty-line. | |
925 (?\n (insert " ") (setq modif nil) (backward-char 2) | |
926 (setq reversible nil)) | |
927 (t (setq modif nil))) | |
928 (let ((last-pt (point))) | |
929 (forward-line 1) | |
930 (when delete | |
931 (delete-region last-pt (point)) | |
932 (setq delete nil))))))) | |
933 (unless (or (not reversible) (eq buffer-undo-list t)) | |
934 ;; Drop the many undo entries and replace them with | |
935 ;; a single entry that uses diff-context->unified to do | |
936 ;; the work. | |
937 (setq buffer-undo-list | |
938 (cons (list 'apply (- old-end end) start (point-max) | |
939 'diff-context->unified start (point-max)) | |
940 old-undo))))))))))) | |
941 | |
942 (defun diff-context->unified (start end &optional to-context) | |
943 "Convert context diffs to unified diffs. | |
944 START and END are either taken from the region | |
945 \(when it is highlighted) or else cover the whole buffer. | |
946 With a prefix argument, convert unified format to context format." | |
947 (interactive (if (and transient-mark-mode mark-active) | |
948 (list (region-beginning) (region-end) current-prefix-arg) | |
949 (list (point-min) (point-max) current-prefix-arg))) | |
950 (if to-context | |
951 (diff-unified->context start end) | |
952 (unless (markerp end) (setq end (copy-marker end t))) | |
953 (let ( ;;(diff-inhibit-after-change t) | |
954 (inhibit-read-only t)) | |
955 (save-excursion | |
956 (goto-char start) | |
957 (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)$" nil t) | |
958 (< (point) end)) | |
959 (combine-after-change-calls | |
960 (if (match-beginning 2) | |
961 ;; we matched a file header | |
962 (progn | |
963 ;; use reverse order to make sure the indices are kept valid | |
964 (replace-match "+++" t t nil 3) | |
965 (replace-match "---" t t nil 2)) | |
966 ;; we matched a hunk header | |
967 (let ((line1s (match-string 4)) | |
968 (line1e (match-string 5)) | |
969 (pt1 (match-beginning 0)) | |
970 ;; Variables to use the special undo function. | |
971 (old-undo buffer-undo-list) | |
972 (old-end (marker-position end)) | |
973 (reversible t)) | |
974 (replace-match "") | |
975 (unless (re-search-forward | |
976 diff-context-mid-hunk-header-re nil t) | |
977 (error "Can't find matching `--- n1,n2 ----' line")) | |
978 (let ((line2s (match-string 1)) | |
979 (line2e (match-string 2)) | |
980 (pt2 (progn | |
981 (delete-region (progn (beginning-of-line) (point)) | |
982 (progn (forward-line 1) (point))) | |
983 (point-marker)))) | |
984 (goto-char pt1) | |
985 (forward-line 1) | |
986 (while (< (point) pt2) | |
987 (case (char-after) | |
988 (?! (delete-char 2) (insert "-") (forward-line 1)) | |
989 (?- (forward-char 1) (delete-char 1) (forward-line 1)) | |
990 (?\s ;merge with the other half of the chunk | |
991 (let* ((endline2 | |
992 (save-excursion | |
993 (goto-char pt2) (forward-line 1) (point)))) | |
994 (case (char-after pt2) | |
995 ((?! ?+) | |
996 (insert "+" | |
997 (prog1 (buffer-substring (+ pt2 2) endline2) | |
998 (delete-region pt2 endline2)))) | |
999 (?\s | |
1000 (unless (= (- endline2 pt2) | |
1001 (- (line-beginning-position 2) (point))) | |
1002 ;; If the two lines we're merging don't have the | |
1003 ;; same length (can happen with "diff -b"), then | |
1004 ;; diff-unified->context will not properly undo | |
1005 ;; this operation. | |
1006 (setq reversible nil)) | |
1007 (delete-region pt2 endline2) | |
1008 (delete-char 1) | |
1009 (forward-line 1)) | |
1010 (?\\ (forward-line 1)) | |
1011 (t (setq reversible nil) | |
1012 (delete-char 1) (forward-line 1))))) | |
1013 (t (setq reversible nil) (forward-line 1)))) | |
1014 (while (looking-at "[+! ] ") | |
1015 (if (/= (char-after) ?!) (forward-char 1) | |
1016 (delete-char 1) (insert "+")) | |
1017 (delete-char 1) (forward-line 1)) | |
1018 (save-excursion | |
1019 (goto-char pt1) | |
1020 (insert "@@ -" line1s "," | |
1021 (number-to-string (- (string-to-number line1e) | |
1022 (string-to-number line1s) | |
1023 -1)) | |
1024 " +" line2s "," | |
1025 (number-to-string (- (string-to-number line2e) | |
1026 (string-to-number line2s) | |
1027 -1)) " @@")) | |
1028 (set-marker pt2 nil) | |
1029 ;; The whole procedure succeeded, let's replace the myriad | |
1030 ;; of undo elements with just a single special one. | |
1031 (unless (or (not reversible) (eq buffer-undo-list t)) | |
1032 (setq buffer-undo-list | |
1033 (cons (list 'apply (- old-end end) pt1 (point) | |
1034 'diff-unified->context pt1 (point)) | |
1035 old-undo))) | |
1036 ))))))))) | |
1037 | |
1038 (defun diff-reverse-direction (start end) | |
1039 "Reverse the direction of the diffs. | |
1040 START and END are either taken from the region (if a prefix arg is given) or | |
1041 else cover the whole buffer." | |
1042 (interactive (if (or current-prefix-arg (and transient-mark-mode mark-active)) | |
1043 (list (region-beginning) (region-end)) | |
1044 (list (point-min) (point-max)))) | |
1045 (unless (markerp end) (setq end (copy-marker end t))) | |
1046 (let (;;(diff-inhibit-after-change t) | |
1047 (inhibit-read-only t)) | |
1048 (save-excursion | |
1049 (goto-char start) | |
1050 (while (and (re-search-forward "^\\(\\([-*][-*][-*] \\)\\(.+\\)\n\\([-+][-+][-+] \\)\\(.+\\)\\|\\*\\{15\\}.*\n\\*\\*\\* \\(.+\\) \\*\\*\\*\\*\\|@@ -\\([0-9,]+\\) \\+\\([0-9,]+\\) @@.*\\)$" nil t) | |
1051 (< (point) end)) | |
1052 (combine-after-change-calls | |
1053 (cond | |
1054 ;; a file header | |
1055 ((match-beginning 2) (replace-match "\\2\\5\n\\4\\3" nil)) | |
1056 ;; a context-diff hunk header | |
1057 ((match-beginning 6) | |
1058 (let ((pt-lines1 (match-beginning 6)) | |
1059 (lines1 (match-string 6))) | |
1060 (replace-match "" nil nil nil 6) | |
1061 (forward-line 1) | |
1062 (let ((half1s (point))) | |
1063 (while (looking-at "[-! \\][ \t]\\|#") | |
1064 (when (= (char-after) ?-) (delete-char 1) (insert "+")) | |
1065 (forward-line 1)) | |
1066 (let ((half1 (delete-and-extract-region half1s (point)))) | |
1067 (unless (looking-at diff-context-mid-hunk-header-re) | |
1068 (insert half1) | |
1069 (error "Can't find matching `--- n1,n2 ----' line")) | |
1070 (let* ((str1end (or (match-end 2) (match-end 1))) | |
1071 (str1 (buffer-substring (match-beginning 1) str1end))) | |
1072 (goto-char str1end) | |
1073 (insert lines1) | |
1074 (delete-region (match-beginning 1) str1end) | |
1075 (forward-line 1) | |
1076 (let ((half2s (point))) | |
1077 (while (looking-at "[!+ \\][ \t]\\|#") | |
1078 (when (= (char-after) ?+) (delete-char 1) (insert "-")) | |
1079 (forward-line 1)) | |
1080 (let ((half2 (delete-and-extract-region half2s (point)))) | |
1081 (insert (or half1 "")) | |
1082 (goto-char half1s) | |
1083 (insert (or half2 "")))) | |
1084 (goto-char pt-lines1) | |
1085 (insert str1)))))) | |
1086 ;; a unified-diff hunk header | |
1087 ((match-beginning 7) | |
1088 (replace-match "@@ -\\8 +\\7 @@" nil) | |
1089 (forward-line 1) | |
1090 (let ((c (char-after)) first last) | |
1091 (while (case (setq c (char-after)) | |
1092 (?- (setq first (or first (point))) | |
1093 (delete-char 1) (insert "+") t) | |
1094 (?+ (setq last (or last (point))) | |
1095 (delete-char 1) (insert "-") t) | |
1096 ((?\\ ?#) t) | |
1097 (t (when (and first last (< first last)) | |
1098 (insert (delete-and-extract-region first last))) | |
1099 (setq first nil last nil) | |
1100 (memq c (if diff-valid-unified-empty-line | |
1101 '(?\s ?\n) '(?\s))))) | |
1102 (forward-line 1)))))))))) | |
1103 | |
1104 (defun diff-fixup-modifs (start end) | |
1105 "Fixup the hunk headers (in case the buffer was modified). | |
1106 START and END are either taken from the region (if a prefix arg is given) or | |
1107 else cover the whole buffer." | |
1108 (interactive (if (or current-prefix-arg (and transient-mark-mode mark-active)) | |
1109 (list (region-beginning) (region-end)) | |
1110 (list (point-min) (point-max)))) | |
1111 (let ((inhibit-read-only t)) | |
1112 (save-excursion | |
1113 (goto-char end) (diff-end-of-hunk nil 'donttrustheader) | |
1114 (let ((plus 0) (minus 0) (space 0) (bang 0)) | |
1115 (while (and (= (forward-line -1) 0) (<= start (point))) | |
1116 (if (not (looking-at | |
1117 (concat diff-hunk-header-re-unified | |
1118 "\\|[-*][-*][-*] [0-9,]+ [-*][-*][-*][-*]$" | |
1119 "\\|--- .+\n\\+\\+\\+ "))) | |
1120 (case (char-after) | |
1121 (?\s (incf space)) | |
1122 (?+ (incf plus)) | |
1123 (?- (incf minus)) | |
1124 (?! (incf bang)) | |
1125 ((?\\ ?#) nil) | |
1126 (t (setq space 0 plus 0 minus 0 bang 0))) | |
1127 (cond | |
1128 ((looking-at diff-hunk-header-re-unified) | |
1129 (let* ((old1 (match-string 2)) | |
1130 (old2 (match-string 4)) | |
1131 (new1 (number-to-string (+ space minus))) | |
1132 (new2 (number-to-string (+ space plus)))) | |
1133 (if old2 | |
1134 (unless (string= new2 old2) (replace-match new2 t t nil 4)) | |
1135 (goto-char (match-end 4)) (insert "," new2)) | |
1136 (if old1 | |
1137 (unless (string= new1 old1) (replace-match new1 t t nil 2)) | |
1138 (goto-char (match-end 2)) (insert "," new1)))) | |
1139 ((looking-at diff-context-mid-hunk-header-re) | |
1140 (when (> (+ space bang plus) 0) | |
1141 (let* ((old1 (match-string 1)) | |
1142 (old2 (match-string 2)) | |
1143 (new (number-to-string | |
1144 (+ space bang plus -1 (string-to-number old1))))) | |
1145 (unless (string= new old2) (replace-match new t t nil 2))))) | |
1146 ((looking-at "\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]*\\) \\*\\*\\*\\*$") | |
1147 (when (> (+ space bang minus) 0) | |
1148 (let* ((old (match-string 1)) | |
1149 (new (format | |
1150 (concat "%0" (number-to-string (length old)) "d") | |
1151 (+ space bang minus -1 (string-to-number old))))) | |
1152 (unless (string= new old) (replace-match new t t nil 2)))))) | |
1153 (setq space 0 plus 0 minus 0 bang 0))))))) | |
1154 | |
1155 ;;;; | |
1156 ;;;; Hooks | |
1157 ;;;; | |
1158 | |
1159 (defun diff-write-contents-hooks () | |
1160 "Fixup hunk headers if necessary." | |
1161 (if (buffer-modified-p) (diff-fixup-modifs (point-min) (point-max))) | |
1162 nil) | |
1163 | |
1164 ;; It turns out that making changes in the buffer from within an | |
1165 ;; *-change-function is asking for trouble, whereas making them | |
1166 ;; from a post-command-hook doesn't pose much problems | |
1167 (defvar diff-unhandled-changes nil) | |
1168 (defun diff-after-change-function (beg end len) | |
1169 "Remember to fixup the hunk header. | |
1170 See `after-change-functions' for the meaning of BEG, END and LEN." | |
1171 ;; Ignoring changes when inhibit-read-only is set is strictly speaking | |
1172 ;; incorrect, but it turns out that inhibit-read-only is normally not set | |
1173 ;; inside editing commands, while it tends to be set when the buffer gets | |
1174 ;; updated by an async process or by a conversion function, both of which | |
1175 ;; would rather not be uselessly slowed down by this hook. | |
1176 (when (and (not undo-in-progress) (not inhibit-read-only)) | |
1177 (if diff-unhandled-changes | |
1178 (setq diff-unhandled-changes | |
1179 (cons (min beg (car diff-unhandled-changes)) | |
1180 (max end (cdr diff-unhandled-changes)))) | |
1181 (setq diff-unhandled-changes (cons beg end))))) | |
1182 | |
1183 (defun diff-post-command-hook () | |
1184 "Fixup hunk headers if necessary." | |
1185 (when (consp diff-unhandled-changes) | |
1186 (ignore-errors | |
1187 (save-excursion | |
1188 (goto-char (car diff-unhandled-changes)) | |
1189 ;; Maybe we've cut the end of the hunk before point. | |
1190 (if (and (bolp) (not (bobp))) (backward-char 1)) | |
1191 ;; We used to fixup modifs on all the changes, but it turns out that | |
1192 ;; it's safer not to do it on big changes, e.g. when yanking a big | |
1193 ;; diff, or when the user edits the header, since we might then | |
1194 ;; screw up perfectly correct values. --Stef | |
1195 (diff-beginning-of-hunk) | |
1196 (let* ((style (if (looking-at "\\*\\*\\*") 'context)) | |
1197 (start (line-beginning-position (if (eq style 'context) 3 2))) | |
1198 (mid (if (eq style 'context) | |
1199 (save-excursion | |
1200 (re-search-forward diff-context-mid-hunk-header-re | |
1201 nil t))))) | |
1202 (when (and ;; Don't try to fixup changes in the hunk header. | |
1203 (> (car diff-unhandled-changes) start) | |
1204 ;; Don't try to fixup changes in the mid-hunk header either. | |
1205 (or (not mid) | |
1206 (< (cdr diff-unhandled-changes) (match-beginning 0)) | |
1207 (> (car diff-unhandled-changes) (match-end 0))) | |
1208 (save-excursion | |
1209 (diff-end-of-hunk nil 'donttrustheader) | |
1210 ;; Don't try to fixup changes past the end of the hunk. | |
1211 (>= (point) (cdr diff-unhandled-changes)))) | |
1212 (diff-fixup-modifs (point) (cdr diff-unhandled-changes))))) | |
1213 (setq diff-unhandled-changes nil)))) | |
1214 | |
1215 (defun diff-next-error (arg reset) | |
1216 ;; Select a window that displays the current buffer so that point | |
1217 ;; movements are reflected in that window. Otherwise, the user might | |
1218 ;; never see the hunk corresponding to the source she's jumping to. | |
1219 (pop-to-buffer (current-buffer)) | |
1220 (if reset (goto-char (point-min))) | |
1221 (diff-hunk-next arg) | |
1222 (diff-goto-source)) | |
1223 | |
1224 (defvar whitespace-style) | |
1225 (defvar whitespace-trailing-regexp) | |
1226 | |
1227 ;;;###autoload | |
1228 (define-derived-mode diff-mode fundamental-mode "Diff" | |
1229 "Major mode for viewing/editing context diffs. | |
1230 Supports unified and context diffs as well as (to a lesser extent) | |
1231 normal diffs. | |
1232 | |
1233 When the buffer is read-only, the ESC prefix is not necessary. | |
1234 If you edit the buffer manually, diff-mode will try to update the hunk | |
1235 headers for you on-the-fly. | |
1236 | |
1237 You can also switch between context diff and unified diff with \\[diff-context->unified], | |
1238 or vice versa with \\[diff-unified->context] and you can also reverse the direction of | |
1239 a diff with \\[diff-reverse-direction]. | |
1240 | |
1241 \\{diff-mode-map}" | |
1242 | |
1243 (set (make-local-variable 'font-lock-defaults) diff-font-lock-defaults) | |
1244 (set (make-local-variable 'outline-regexp) diff-outline-regexp) | |
1245 (set (make-local-variable 'imenu-generic-expression) | |
1246 diff-imenu-generic-expression) | |
1247 ;; These are not perfect. They would be better done separately for | |
1248 ;; context diffs and unidiffs. | |
1249 ;; (set (make-local-variable 'paragraph-start) | |
1250 ;; (concat "@@ " ; unidiff hunk | |
1251 ;; "\\|\\*\\*\\* " ; context diff hunk or file start | |
1252 ;; "\\|--- [^\t]+\t")) ; context or unidiff file | |
1253 ;; ; start (first or second line) | |
1254 ;; (set (make-local-variable 'paragraph-separate) paragraph-start) | |
1255 ;; (set (make-local-variable 'page-delimiter) "--- [^\t]+\t") | |
1256 ;; compile support | |
1257 (set (make-local-variable 'next-error-function) 'diff-next-error) | |
1258 | |
1259 (set (make-local-variable 'beginning-of-defun-function) | |
1260 'diff-beginning-of-file-and-junk) | |
1261 (set (make-local-variable 'end-of-defun-function) | |
1262 'diff-end-of-file) | |
1263 | |
1264 ;; Set up `whitespace-mode' so that turning it on will show trailing | |
1265 ;; whitespace problems on the modified lines of the diff. | |
1266 (set (make-local-variable 'whitespace-style) '(trailing)) | |
1267 (set (make-local-variable 'whitespace-trailing-regexp) | |
1268 "^[-\+!<>].*?\\([\t ]+\\)$") | |
1269 | |
1270 (setq buffer-read-only diff-default-read-only) | |
1271 ;; setup change hooks | |
1272 (if (not diff-update-on-the-fly) | |
1273 (add-hook 'write-contents-functions 'diff-write-contents-hooks nil t) | |
1274 (make-local-variable 'diff-unhandled-changes) | |
1275 (add-hook 'after-change-functions 'diff-after-change-function nil t) | |
1276 (add-hook 'post-command-hook 'diff-post-command-hook nil t)) | |
1277 ;; Neat trick from Dave Love to add more bindings in read-only mode: | |
1278 (lexical-let ((ro-bind (cons 'buffer-read-only diff-mode-shared-map))) | |
1279 (add-to-list 'minor-mode-overriding-map-alist ro-bind) | |
1280 ;; Turn off this little trick in case the buffer is put in view-mode. | |
1281 (add-hook 'view-mode-hook | |
1282 (lambda () | |
1283 (setq minor-mode-overriding-map-alist | |
1284 (delq ro-bind minor-mode-overriding-map-alist))) | |
1285 nil t)) | |
1286 ;; add-log support | |
1287 (set (make-local-variable 'add-log-current-defun-function) | |
1288 'diff-current-defun) | |
1289 (set (make-local-variable 'add-log-buffer-file-name-function) | |
1290 (lambda () (diff-find-file-name nil 'noprompt))) | |
1291 (unless (buffer-file-name) | |
1292 (hack-dir-local-variables-non-file-buffer))) | |
1293 | |
1294 ;;;###autoload | |
1295 (define-minor-mode diff-minor-mode | |
1296 "Minor mode for viewing/editing context diffs. | |
1297 \\{diff-minor-mode-map}" | |
1298 :group 'diff-mode :lighter " Diff" | |
1299 ;; FIXME: setup font-lock | |
1300 ;; setup change hooks | |
1301 (if (not diff-update-on-the-fly) | |
1302 (add-hook 'write-contents-functions 'diff-write-contents-hooks nil t) | |
1303 (make-local-variable 'diff-unhandled-changes) | |
1304 (add-hook 'after-change-functions 'diff-after-change-function nil t) | |
1305 (add-hook 'post-command-hook 'diff-post-command-hook nil t))) | |
1306 | |
1307 ;;; Handy hook functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
1308 | |
1309 (defun diff-delete-if-empty () | |
1310 ;; An empty diff file means there's no more diffs to integrate, so we | |
1311 ;; can just remove the file altogether. Very handy for .rej files if we | |
1312 ;; remove hunks as we apply them. | |
1313 (when (and buffer-file-name | |
1314 (eq 0 (nth 7 (file-attributes buffer-file-name)))) | |
1315 (delete-file buffer-file-name))) | |
1316 | |
1317 (defun diff-delete-empty-files () | |
1318 "Arrange for empty diff files to be removed." | |
1319 (add-hook 'after-save-hook 'diff-delete-if-empty nil t)) | |
1320 | |
1321 (defun diff-make-unified () | |
1322 "Turn context diffs into unified diffs if applicable." | |
1323 (if (save-excursion | |
1324 (goto-char (point-min)) | |
1325 (and (looking-at diff-hunk-header-re) (eq (char-after) ?*))) | |
1326 (let ((mod (buffer-modified-p))) | |
1327 (unwind-protect | |
1328 (diff-context->unified (point-min) (point-max)) | |
1329 (restore-buffer-modified-p mod))))) | |
1330 | |
1331 ;;; | |
1332 ;;; Misc operations that have proved useful at some point. | |
1333 ;;; | |
1334 | |
1335 (defun diff-next-complex-hunk () | |
1336 "Jump to the next \"complex\" hunk. | |
1337 \"Complex\" is approximated by \"the hunk changes the number of lines\". | |
1338 Only works for unified diffs." | |
1339 (interactive) | |
1340 (while | |
1341 (and (re-search-forward diff-hunk-header-re-unified nil t) | |
1342 (equal (match-string 2) (match-string 4))))) | |
1343 | |
1344 (defun diff-sanity-check-context-hunk-half (lines) | |
1345 (let ((count lines)) | |
1346 (while | |
1347 (cond | |
1348 ((and (memq (char-after) '(?\s ?! ?+ ?-)) | |
1349 (memq (char-after (1+ (point))) '(?\s ?\t))) | |
1350 (decf count) t) | |
1351 ((or (zerop count) (= count lines)) nil) | |
1352 ((memq (char-after) '(?! ?+ ?-)) | |
1353 (if (not (and (eq (char-after (1+ (point))) ?\n) | |
1354 (y-or-n-p "Try to auto-fix whitespace loss damage? "))) | |
1355 (error "End of hunk ambiguously marked") | |
1356 (forward-char 1) (insert " ") (forward-line -1) t)) | |
1357 ((< lines 0) | |
1358 (error "End of hunk ambiguously marked")) | |
1359 ((not (y-or-n-p "Try to auto-fix whitespace loss and word-wrap damage? ")) | |
1360 (error "Abort!")) | |
1361 ((eolp) (insert " ") (forward-line -1) t) | |
1362 (t (insert " ") (delete-region (- (point) 2) (- (point) 1)) t)) | |
1363 (forward-line)))) | |
1364 | |
1365 (defun diff-sanity-check-hunk () | |
1366 (let (;; Every modification is protected by a y-or-n-p, so it's probably | |
1367 ;; OK to override a read-only setting. | |
1368 (inhibit-read-only t)) | |
1369 (save-excursion | |
1370 (cond | |
1371 ((not (looking-at diff-hunk-header-re)) | |
1372 (error "Not recognizable hunk header")) | |
1373 | |
1374 ;; A context diff. | |
1375 ((eq (char-after) ?*) | |
1376 (if (not (looking-at "\\*\\{15\\}\\(?: .*\\)?\n\\*\\*\\* \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? \\*\\*\\*\\*")) | |
1377 (error "Unrecognized context diff first hunk header format") | |
1378 (forward-line 2) | |
1379 (diff-sanity-check-context-hunk-half | |
1380 (if (match-end 2) | |
1381 (1+ (- (string-to-number (match-string 2)) | |
1382 (string-to-number (match-string 1)))) | |
1383 1)) | |
1384 (if (not (looking-at diff-context-mid-hunk-header-re)) | |
1385 (error "Unrecognized context diff second hunk header format") | |
1386 (forward-line) | |
1387 (diff-sanity-check-context-hunk-half | |
1388 (if (match-end 2) | |
1389 (1+ (- (string-to-number (match-string 2)) | |
1390 (string-to-number (match-string 1)))) | |
1391 1))))) | |
1392 | |
1393 ;; A unified diff. | |
1394 ((eq (char-after) ?@) | |
1395 (if (not (looking-at diff-hunk-header-re-unified)) | |
1396 (error "Unrecognized unified diff hunk header format") | |
1397 (let ((before (string-to-number (or (match-string 2) "1"))) | |
1398 (after (string-to-number (or (match-string 4) "1")))) | |
1399 (forward-line) | |
1400 (while | |
1401 (case (char-after) | |
1402 (?\s (decf before) (decf after) t) | |
1403 (?- | |
1404 (if (and (looking-at diff-file-header-re) | |
1405 (zerop before) (zerop after)) | |
1406 ;; No need to query: this is a case where two patches | |
1407 ;; are concatenated and only counting the lines will | |
1408 ;; give the right result. Let's just add an empty | |
1409 ;; line so that our code which doesn't count lines | |
1410 ;; will not get confused. | |
1411 (progn (save-excursion (insert "\n")) nil) | |
1412 (decf before) t)) | |
1413 (?+ (decf after) t) | |
1414 (t | |
1415 (cond | |
1416 ((and diff-valid-unified-empty-line | |
1417 ;; Not just (eolp) so we don't infloop at eob. | |
1418 (eq (char-after) ?\n) | |
1419 (> before 0) (> after 0)) | |
1420 (decf before) (decf after) t) | |
1421 ((and (zerop before) (zerop after)) nil) | |
1422 ((or (< before 0) (< after 0)) | |
1423 (error (if (or (zerop before) (zerop after)) | |
1424 "End of hunk ambiguously marked" | |
1425 "Hunk seriously messed up"))) | |
1426 ((not (y-or-n-p (concat "Try to auto-fix " (if (eolp) "whitespace loss" "word-wrap damage") "? "))) | |
1427 (error "Abort!")) | |
1428 ((eolp) (insert " ") (forward-line -1) t) | |
1429 (t (insert " ") | |
1430 (delete-region (- (point) 2) (- (point) 1)) t)))) | |
1431 (forward-line))))) | |
1432 | |
1433 ;; A plain diff. | |
1434 (t | |
1435 ;; TODO. | |
1436 ))))) | |
1437 | |
1438 (defun diff-hunk-text (hunk destp char-offset) | |
1439 "Return the literal source text from HUNK as (TEXT . OFFSET). | |
1440 If DESTP is nil, TEXT is the source, otherwise the destination text. | |
1441 CHAR-OFFSET is a char-offset in HUNK, and OFFSET is the corresponding | |
1442 char-offset in TEXT." | |
1443 (with-temp-buffer | |
1444 (insert hunk) | |
1445 (goto-char (point-min)) | |
1446 (let ((src-pos nil) | |
1447 (dst-pos nil) | |
1448 (divider-pos nil) | |
1449 (num-pfx-chars 2)) | |
1450 ;; Set the following variables: | |
1451 ;; SRC-POS buffer pos of the source part of the hunk or nil if none | |
1452 ;; DST-POS buffer pos of the destination part of the hunk or nil | |
1453 ;; DIVIDER-POS buffer pos of any divider line separating the src & dst | |
1454 ;; NUM-PFX-CHARS number of line-prefix characters used by this format" | |
1455 (cond ((looking-at "^@@") | |
1456 ;; unified diff | |
1457 (setq num-pfx-chars 1) | |
1458 (forward-line 1) | |
1459 (setq src-pos (point) dst-pos (point))) | |
1460 ((looking-at "^\\*\\*") | |
1461 ;; context diff | |
1462 (forward-line 2) | |
1463 (setq src-pos (point)) | |
1464 (re-search-forward diff-context-mid-hunk-header-re nil t) | |
1465 (forward-line 0) | |
1466 (setq divider-pos (point)) | |
1467 (forward-line 1) | |
1468 (setq dst-pos (point))) | |
1469 ((looking-at "^[0-9]+a[0-9,]+$") | |
1470 ;; normal diff, insert | |
1471 (forward-line 1) | |
1472 (setq dst-pos (point))) | |
1473 ((looking-at "^[0-9,]+d[0-9]+$") | |
1474 ;; normal diff, delete | |
1475 (forward-line 1) | |
1476 (setq src-pos (point))) | |
1477 ((looking-at "^[0-9,]+c[0-9,]+$") | |
1478 ;; normal diff, change | |
1479 (forward-line 1) | |
1480 (setq src-pos (point)) | |
1481 (re-search-forward "^---$" nil t) | |
1482 (forward-line 0) | |
1483 (setq divider-pos (point)) | |
1484 (forward-line 1) | |
1485 (setq dst-pos (point))) | |
1486 (t | |
1487 (error "Unknown diff hunk type"))) | |
1488 | |
1489 (if (if destp (null dst-pos) (null src-pos)) | |
1490 ;; Implied empty text | |
1491 (if char-offset '("" . 0) "") | |
1492 | |
1493 ;; For context diffs, either side can be empty, (if there's only | |
1494 ;; added or only removed text). We should then use the other side. | |
1495 (cond ((equal src-pos divider-pos) (setq src-pos dst-pos)) | |
1496 ((equal dst-pos (point-max)) (setq dst-pos src-pos))) | |
1497 | |
1498 (when char-offset (goto-char (+ (point-min) char-offset))) | |
1499 | |
1500 ;; Get rid of anything except the desired text. | |
1501 (save-excursion | |
1502 ;; Delete unused text region | |
1503 (let ((keep (if destp dst-pos src-pos))) | |
1504 (when (and divider-pos (> divider-pos keep)) | |
1505 (delete-region divider-pos (point-max))) | |
1506 (delete-region (point-min) keep)) | |
1507 ;; Remove line-prefix characters, and unneeded lines (unified diffs). | |
1508 (let ((kill-char (if destp ?- ?+))) | |
1509 (goto-char (point-min)) | |
1510 (while (not (eobp)) | |
1511 (if (eq (char-after) kill-char) | |
1512 (delete-region (point) (progn (forward-line 1) (point))) | |
1513 (delete-char num-pfx-chars) | |
1514 (forward-line 1))))) | |
1515 | |
1516 (let ((text (buffer-substring-no-properties (point-min) (point-max)))) | |
1517 (if char-offset (cons text (- (point) (point-min))) text)))))) | |
1518 | |
1519 | |
1520 (defun diff-find-text (text) | |
1521 "Return the buffer position (BEG . END) of the nearest occurrence of TEXT. | |
1522 If TEXT isn't found, nil is returned." | |
1523 (let* ((orig (point)) | |
1524 (forw (and (search-forward text nil t) | |
1525 (cons (match-beginning 0) (match-end 0)))) | |
1526 (back (and (goto-char (+ orig (length text))) | |
1527 (search-backward text nil t) | |
1528 (cons (match-beginning 0) (match-end 0))))) | |
1529 ;; Choose the closest match. | |
1530 (if (and forw back) | |
1531 (if (> (- (car forw) orig) (- orig (car back))) back forw) | |
1532 (or back forw)))) | |
1533 | |
1534 (defun diff-find-approx-text (text) | |
1535 "Return the buffer position (BEG . END) of the nearest occurrence of TEXT. | |
1536 Whitespace differences are ignored." | |
1537 (let* ((orig (point)) | |
1538 (re (concat "^[ \t\n]*" | |
1539 (mapconcat 'regexp-quote (split-string text) "[ \t\n]+") | |
1540 "[ \t\n]*\n")) | |
1541 (forw (and (re-search-forward re nil t) | |
1542 (cons (match-beginning 0) (match-end 0)))) | |
1543 (back (and (goto-char (+ orig (length text))) | |
1544 (re-search-backward re nil t) | |
1545 (cons (match-beginning 0) (match-end 0))))) | |
1546 ;; Choose the closest match. | |
1547 (if (and forw back) | |
1548 (if (> (- (car forw) orig) (- orig (car back))) back forw) | |
1549 (or back forw)))) | |
1550 | |
1551 (defsubst diff-xor (a b) (if a (if (not b) a) b)) | |
1552 | |
1553 (defun diff-find-source-location (&optional other-file reverse noprompt) | |
1554 "Find out (BUF LINE-OFFSET POS SRC DST SWITCHED). | |
1555 BUF is the buffer corresponding to the source file. | |
1556 LINE-OFFSET is the offset between the expected and actual positions | |
1557 of the text of the hunk or nil if the text was not found. | |
1558 POS is a pair (BEG . END) indicating the position of the text in the buffer. | |
1559 SRC and DST are the two variants of text as returned by `diff-hunk-text'. | |
1560 SRC is the variant that was found in the buffer. | |
1561 SWITCHED is non-nil if the patch is already applied. | |
1562 NOPROMPT, if non-nil, means not to prompt the user." | |
1563 (save-excursion | |
1564 (let* ((other (diff-xor other-file diff-jump-to-old-file)) | |
1565 (char-offset (- (point) (progn (diff-beginning-of-hunk 'try-harder) | |
1566 (point)))) | |
1567 ;; Check that the hunk is well-formed. Otherwise diff-mode and | |
1568 ;; the user may disagree on what constitutes the hunk | |
1569 ;; (e.g. because an empty line truncates the hunk mid-course), | |
1570 ;; leading to potentially nasty surprises for the user. | |
1571 ;; | |
1572 ;; Suppress check when NOPROMPT is non-nil (Bug#3033). | |
1573 (_ (unless noprompt (diff-sanity-check-hunk))) | |
1574 (hunk (buffer-substring | |
1575 (point) (save-excursion (diff-end-of-hunk) (point)))) | |
1576 (old (diff-hunk-text hunk reverse char-offset)) | |
1577 (new (diff-hunk-text hunk (not reverse) char-offset)) | |
1578 ;; Find the location specification. | |
1579 (line (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?")) | |
1580 (error "Can't find the hunk header") | |
1581 (if other (match-string 1) | |
1582 (if (match-end 3) (match-string 3) | |
1583 (unless (re-search-forward | |
1584 diff-context-mid-hunk-header-re nil t) | |
1585 (error "Can't find the hunk separator")) | |
1586 (match-string 1))))) | |
1587 (file (or (diff-find-file-name other noprompt) | |
1588 (error "Can't find the file"))) | |
1589 (buf (find-file-noselect file))) | |
1590 ;; Update the user preference if he so wished. | |
1591 (when (> (prefix-numeric-value other-file) 8) | |
1592 (setq diff-jump-to-old-file other)) | |
1593 (with-current-buffer buf | |
1594 (goto-char (point-min)) (forward-line (1- (string-to-number line))) | |
1595 (let* ((orig-pos (point)) | |
1596 (switched nil) | |
1597 ;; FIXME: Check for case where both OLD and NEW are found. | |
1598 (pos (or (diff-find-text (car old)) | |
1599 (progn (setq switched t) (diff-find-text (car new))) | |
1600 (progn (setq switched nil) | |
1601 (condition-case nil | |
1602 (diff-find-approx-text (car old)) | |
1603 (invalid-regexp nil))) ;Regex too big. | |
1604 (progn (setq switched t) | |
1605 (condition-case nil | |
1606 (diff-find-approx-text (car new)) | |
1607 (invalid-regexp nil))) ;Regex too big. | |
1608 (progn (setq switched nil) nil)))) | |
1609 (nconc | |
1610 (list buf) | |
1611 (if pos | |
1612 (list (count-lines orig-pos (car pos)) pos) | |
1613 (list nil (cons orig-pos (+ orig-pos (length (car old)))))) | |
1614 (if switched (list new old t) (list old new)))))))) | |
1615 | |
1616 | |
1617 (defun diff-hunk-status-msg (line-offset reversed dry-run) | |
1618 (let ((msg (if dry-run | |
1619 (if reversed "already applied" "not yet applied") | |
1620 (if reversed "undone" "applied")))) | |
1621 (message (cond ((null line-offset) "Hunk text not found") | |
1622 ((= line-offset 0) "Hunk %s") | |
1623 ((= line-offset 1) "Hunk %s at offset %d line") | |
1624 (t "Hunk %s at offset %d lines")) | |
1625 msg line-offset))) | |
1626 | |
1627 (defvar diff-apply-hunk-to-backup-file nil) | |
1628 | |
1629 (defun diff-apply-hunk (&optional reverse) | |
1630 "Apply the current hunk to the source file and go to the next. | |
1631 By default, the new source file is patched, but if the variable | |
1632 `diff-jump-to-old-file' is non-nil, then the old source file is | |
1633 patched instead (some commands, such as `diff-goto-source' can change | |
1634 the value of this variable when given an appropriate prefix argument). | |
1635 | |
1636 With a prefix argument, REVERSE the hunk." | |
1637 (interactive "P") | |
1638 (destructuring-bind (buf line-offset pos old new &optional switched) | |
1639 ;; Sometimes we'd like to have the following behavior: if REVERSE go | |
1640 ;; to the new file, otherwise go to the old. But that means that by | |
1641 ;; default we use the old file, which is the opposite of the default | |
1642 ;; for diff-goto-source, and is thus confusing. Also when you don't | |
1643 ;; know about it it's pretty surprising. | |
1644 ;; TODO: make it possible to ask explicitly for this behavior. | |
1645 ;; | |
1646 ;; This is duplicated in diff-test-hunk. | |
1647 (diff-find-source-location nil reverse) | |
1648 (cond | |
1649 ((null line-offset) | |
1650 (error "Can't find the text to patch")) | |
1651 ((with-current-buffer buf | |
1652 (and buffer-file-name | |
1653 (backup-file-name-p buffer-file-name) | |
1654 (not diff-apply-hunk-to-backup-file) | |
1655 (not (set (make-local-variable 'diff-apply-hunk-to-backup-file) | |
1656 (yes-or-no-p (format "Really apply this hunk to %s? " | |
1657 (file-name-nondirectory | |
1658 buffer-file-name))))))) | |
1659 (error "%s" | |
1660 (substitute-command-keys | |
1661 (format "Use %s\\[diff-apply-hunk] to apply it to the other file" | |
1662 (if (not reverse) "\\[universal-argument] "))))) | |
1663 ((and switched | |
1664 ;; A reversed patch was detected, perhaps apply it in reverse. | |
1665 (not (save-window-excursion | |
1666 (pop-to-buffer buf) | |
1667 (goto-char (+ (car pos) (cdr old))) | |
1668 (y-or-n-p | |
1669 (if reverse | |
1670 "Hunk hasn't been applied yet; apply it now? " | |
1671 "Hunk has already been applied; undo it? "))))) | |
1672 (message "(Nothing done)")) | |
1673 (t | |
1674 ;; Apply the hunk | |
1675 (with-current-buffer buf | |
1676 (goto-char (car pos)) | |
1677 (delete-region (car pos) (cdr pos)) | |
1678 (insert (car new))) | |
1679 ;; Display BUF in a window | |
1680 (set-window-point (display-buffer buf) (+ (car pos) (cdr new))) | |
1681 (diff-hunk-status-msg line-offset (diff-xor switched reverse) nil) | |
1682 (when diff-advance-after-apply-hunk | |
1683 (diff-hunk-next)))))) | |
1684 | |
1685 | |
1686 (defun diff-test-hunk (&optional reverse) | |
1687 "See whether it's possible to apply the current hunk. | |
1688 With a prefix argument, try to REVERSE the hunk." | |
1689 (interactive "P") | |
1690 (destructuring-bind (buf line-offset pos src dst &optional switched) | |
1691 (diff-find-source-location nil reverse) | |
1692 (set-window-point (display-buffer buf) (+ (car pos) (cdr src))) | |
1693 (diff-hunk-status-msg line-offset (diff-xor reverse switched) t))) | |
1694 | |
1695 | |
1696 (defalias 'diff-mouse-goto-source 'diff-goto-source) | |
1697 | |
1698 (defun diff-goto-source (&optional other-file event) | |
1699 "Jump to the corresponding source line. | |
1700 `diff-jump-to-old-file' (or its opposite if the OTHER-FILE prefix arg | |
1701 is given) determines whether to jump to the old or the new file. | |
1702 If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument]) | |
1703 then `diff-jump-to-old-file' is also set, for the next invocations." | |
1704 (interactive (list current-prefix-arg last-input-event)) | |
1705 ;; When pointing at a removal line, we probably want to jump to | |
1706 ;; the old location, and else to the new (i.e. as if reverting). | |
1707 ;; This is a convenient detail when using smerge-diff. | |
1708 (if event (posn-set-point (event-end event))) | |
1709 (let ((rev (not (save-excursion (beginning-of-line) (looking-at "[-<]"))))) | |
1710 (destructuring-bind (buf line-offset pos src dst &optional switched) | |
1711 (diff-find-source-location other-file rev) | |
1712 (pop-to-buffer buf) | |
1713 (goto-char (+ (car pos) (cdr src))) | |
1714 (diff-hunk-status-msg line-offset (diff-xor rev switched) t)))) | |
1715 | |
1716 | |
1717 (defun diff-current-defun () | |
1718 "Find the name of function at point. | |
1719 For use in `add-log-current-defun-function'." | |
1720 ;; Kill change-log-default-name so it gets recomputed each time, since | |
1721 ;; each hunk may belong to another file which may belong to another | |
1722 ;; directory and hence have a different ChangeLog file. | |
1723 (kill-local-variable 'change-log-default-name) | |
1724 (save-excursion | |
1725 (when (looking-at diff-hunk-header-re) | |
1726 (forward-line 1) | |
1727 (re-search-forward "^[^ ]" nil t)) | |
1728 (destructuring-bind (&optional buf line-offset pos src dst switched) | |
1729 ;; Use `noprompt' since this is used in which-func-mode and such. | |
1730 (ignore-errors ;Signals errors in place of prompting. | |
1731 (diff-find-source-location nil nil 'noprompt)) | |
1732 (when buf | |
1733 (beginning-of-line) | |
1734 (or (when (memq (char-after) '(?< ?-)) | |
1735 ;; Cursor is pointing at removed text. This could be a removed | |
1736 ;; function, in which case, going to the source buffer will | |
1737 ;; not help since the function is now removed. Instead, | |
1738 ;; try to figure out the function name just from the | |
1739 ;; code-fragment. | |
1740 (let ((old (if switched dst src))) | |
1741 (with-temp-buffer | |
1742 (insert (car old)) | |
1743 (funcall (buffer-local-value 'major-mode buf)) | |
1744 (goto-char (+ (point-min) (cdr old))) | |
1745 (add-log-current-defun)))) | |
1746 (with-current-buffer buf | |
1747 (goto-char (+ (car pos) (cdr src))) | |
1748 (add-log-current-defun))))))) | |
1749 | |
1750 (defun diff-ignore-whitespace-hunk () | |
1751 "Re-diff the current hunk, ignoring whitespace differences." | |
1752 (interactive) | |
1753 (let* ((char-offset (- (point) (progn (diff-beginning-of-hunk 'try-harder) | |
1754 (point)))) | |
1755 (opts (case (char-after) (?@ "-bu") (?* "-bc") (t "-b"))) | |
1756 (line-nb (and (or (looking-at "[^0-9]+\\([0-9]+\\)") | |
1757 (error "Can't find line number")) | |
1758 (string-to-number (match-string 1)))) | |
1759 (inhibit-read-only t) | |
1760 (hunk (delete-and-extract-region | |
1761 (point) (save-excursion (diff-end-of-hunk) (point)))) | |
1762 (lead (make-string (1- line-nb) ?\n)) ;Line nums start at 1. | |
1763 (file1 (make-temp-file "diff1")) | |
1764 (file2 (make-temp-file "diff2")) | |
1765 (coding-system-for-read buffer-file-coding-system) | |
1766 old new) | |
1767 (unwind-protect | |
1768 (save-excursion | |
1769 (setq old (diff-hunk-text hunk nil char-offset)) | |
1770 (setq new (diff-hunk-text hunk t char-offset)) | |
1771 (write-region (concat lead (car old)) nil file1 nil 'nomessage) | |
1772 (write-region (concat lead (car new)) nil file2 nil 'nomessage) | |
1773 (with-temp-buffer | |
1774 (let ((status | |
1775 (call-process diff-command nil t nil | |
1776 opts file1 file2))) | |
1777 (case status | |
1778 (0 nil) ;Nothing to reformat. | |
1779 (1 (goto-char (point-min)) | |
1780 ;; Remove the file-header. | |
1781 (when (re-search-forward diff-hunk-header-re nil t) | |
1782 (delete-region (point-min) (match-beginning 0)))) | |
1783 (t (goto-char (point-max)) | |
1784 (unless (bolp) (insert "\n")) | |
1785 (insert hunk))) | |
1786 (setq hunk (buffer-string)) | |
1787 (unless (memq status '(0 1)) | |
1788 (error "Diff returned: %s" status))))) | |
1789 ;; Whatever happens, put back some equivalent text: either the new | |
1790 ;; one or the original one in case some error happened. | |
1791 (insert hunk) | |
1792 (delete-file file1) | |
1793 (delete-file file2)))) | |
1794 | |
1795 ;;; Fine change highlighting. | |
1796 | |
1797 (defface diff-refine-change | |
1798 '((((class color) (min-colors 88) (background light)) | |
1799 :background "grey85") | |
1800 (((class color) (min-colors 88) (background dark)) | |
1801 :background "grey60") | |
1802 (((class color) (background light)) | |
1803 :background "yellow") | |
1804 (((class color) (background dark)) | |
1805 :background "green") | |
1806 (t :weight bold)) | |
1807 "Face used for char-based changes shown by `diff-refine-hunk'." | |
1808 :group 'diff-mode) | |
1809 | |
1810 (defun diff-refine-preproc () | |
1811 (while (re-search-forward "^[+>]" nil t) | |
1812 ;; Remove spurious changes due to the fact that one side of the hunk is | |
1813 ;; marked with leading + or > and the other with leading - or <. | |
1814 ;; We used to replace all the prefix chars with " " but this only worked | |
1815 ;; when we did char-based refinement (or when using | |
1816 ;; smerge-refine-weight-hack) since otherwise, the `forward' motion done | |
1817 ;; in chopup do not necessarily do the same as the ones in highlight | |
1818 ;; since the "_" is not treated the same as " ". | |
1819 (replace-match (cdr (assq (char-before) '((?+ . "-") (?> . "<")))))) | |
1820 ) | |
1821 | |
1822 (defun diff-refine-hunk () | |
1823 "Highlight changes of hunk at point at a finer granularity." | |
1824 (interactive) | |
1825 (eval-and-compile (require 'smerge-mode)) | |
1826 (save-excursion | |
1827 (diff-beginning-of-hunk 'try-harder) | |
1828 (let* ((style (diff-hunk-style)) ;Skips the hunk header as well. | |
1829 (beg (point)) | |
1830 (props '((diff-mode . fine) (face diff-refine-change))) | |
1831 (end (progn (diff-end-of-hunk) (point)))) | |
1832 | |
1833 (remove-overlays beg end 'diff-mode 'fine) | |
1834 | |
1835 (goto-char beg) | |
1836 (case style | |
1837 (unified | |
1838 (while (re-search-forward "^\\(?:-.*\n\\)+\\(\\)\\(?:\\+.*\n\\)+" | |
1839 end t) | |
1840 (smerge-refine-subst (match-beginning 0) (match-end 1) | |
1841 (match-end 1) (match-end 0) | |
1842 props 'diff-refine-preproc))) | |
1843 (context | |
1844 (let* ((middle (save-excursion (re-search-forward "^---"))) | |
1845 (other middle)) | |
1846 (while (re-search-forward "^\\(?:!.*\n\\)+" middle t) | |
1847 (smerge-refine-subst (match-beginning 0) (match-end 0) | |
1848 (save-excursion | |
1849 (goto-char other) | |
1850 (re-search-forward "^\\(?:!.*\n\\)+" end) | |
1851 (setq other (match-end 0)) | |
1852 (match-beginning 0)) | |
1853 other | |
1854 props 'diff-refine-preproc)))) | |
1855 (t ;; Normal diffs. | |
1856 (let ((beg1 (1+ (point)))) | |
1857 (when (re-search-forward "^---.*\n" end t) | |
1858 ;; It's a combined add&remove, so there's something to do. | |
1859 (smerge-refine-subst beg1 (match-beginning 0) | |
1860 (match-end 0) end | |
1861 props 'diff-refine-preproc)))))))) | |
1862 | |
1863 | |
1864 (defun diff-add-change-log-entries-other-window () | |
1865 "Iterate through the current diff and create ChangeLog entries. | |
1866 I.e. like `add-change-log-entry-other-window' but applied to all hunks." | |
1867 (interactive) | |
1868 ;; XXX: Currently add-change-log-entry-other-window is only called | |
1869 ;; once per hunk. Some hunks have multiple changes, it would be | |
1870 ;; good to call it for each change. | |
1871 (save-excursion | |
1872 (goto-char (point-min)) | |
1873 (let ((orig-buffer (current-buffer))) | |
1874 (condition-case nil | |
1875 ;; Call add-change-log-entry-other-window for each hunk in | |
1876 ;; the diff buffer. | |
1877 (while (progn | |
1878 (diff-hunk-next) | |
1879 ;; Move to where the changes are, | |
1880 ;; `add-change-log-entry-other-window' works better in | |
1881 ;; that case. | |
1882 (re-search-forward | |
1883 (concat "\n[!+-<>]" | |
1884 ;; If the hunk is a context hunk with an empty first | |
1885 ;; half, recognize the "--- NNN,MMM ----" line | |
1886 "\\(-- [0-9]+\\(,[0-9]+\\)? ----\n" | |
1887 ;; and skip to the next non-context line. | |
1888 "\\( .*\n\\)*[+]\\)?") | |
1889 nil t)) | |
1890 (save-excursion | |
1891 ;; FIXME: this pops up windows of all the buffers. | |
1892 (add-change-log-entry nil nil t nil t))) | |
1893 ;; When there's no more hunks, diff-hunk-next signals an error. | |
1894 (error nil))))) | |
1895 | |
1896 ;; provide the package | |
1897 (provide 'diff-mode) | |
1898 | |
1899 ;;; Old Change Log from when diff-mode wasn't part of Emacs: | |
1900 ;; Revision 1.11 1999/10/09 23:38:29 monnier | |
1901 ;; (diff-mode-load-hook): dropped. | |
1902 ;; (auto-mode-alist): also catch *.diffs. | |
1903 ;; (diff-find-file-name, diff-mode): add smarts to find the right file | |
1904 ;; for *.rej files (that lack any file name indication). | |
1905 ;; | |
1906 ;; Revision 1.10 1999/09/30 15:32:11 monnier | |
1907 ;; added support for "\ No newline at end of file". | |
1908 ;; | |
1909 ;; Revision 1.9 1999/09/15 00:01:13 monnier | |
1910 ;; - added basic `compile' support. | |
1911 ;; - have diff-kill-hunk call diff-kill-file if it's the only hunk. | |
1912 ;; - diff-kill-file now tries to kill the leading garbage as well. | |
1913 ;; | |
1914 ;; Revision 1.8 1999/09/13 21:10:09 monnier | |
1915 ;; - don't use CL in the autoloaded code | |
1916 ;; - accept diffs using -T | |
1917 ;; | |
1918 ;; Revision 1.7 1999/09/05 20:53:03 monnier | |
1919 ;; interface to ediff-patch | |
1920 ;; | |
1921 ;; Revision 1.6 1999/09/01 20:55:13 monnier | |
1922 ;; (ediff=patch-file): add bindings to call ediff-patch. | |
1923 ;; (diff-find-file-name): taken out of diff-goto-source. | |
1924 ;; (diff-unified->context, diff-context->unified, diff-reverse-direction, | |
1925 ;; diff-fixup-modifs): only use the region if a prefix arg is given. | |
1926 ;; | |
1927 ;; Revision 1.5 1999/08/31 19:18:52 monnier | |
1928 ;; (diff-beginning-of-file, diff-prev-file): fixed wrong parenthesis. | |
1929 ;; | |
1930 ;; Revision 1.4 1999/08/31 13:01:44 monnier | |
1931 ;; use `combine-after-change-calls' to minimize the slowdown of font-lock. | |
1932 ;; | |
1933 | |
1934 ;; arch-tag: 2571d7ff-bc28-4cf9-8585-42e21890be66 | |
1935 ;;; diff-mode.el ends here |