Mercurial > emacs
comparison lisp/diff-mode.el @ 28240:a0b15838fd22
(diff-mode-*-map): use `easy-mmode-defmap'.
(diff-end-of-hunk): Return the end position for use in
`easy-mmode-define-navigation'.
(diff-recenter): Remove.
(diff-(next|prev)-*): Rename `diff-*-(prev|next)' and defined in terms
of `easy-mmode-define-navigation'.
(diff-kill-*): Rename `diff-*-kill' (for consistency with the
previous renaming) and fix to use new names.
(diff-merge-strings): Use \n as separator: simpler, faster.
(diff-mode): Use `define-derived-mode'.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Tue, 21 Mar 2000 16:59:17 +0000 |
parents | 79014abee3cc |
children | 85d4cc0b8741 |
comparison
equal
deleted
inserted
replaced
28239:21cd65af5197 | 28240:a0b15838fd22 |
---|---|
2 | 2 |
3 ;; Copyright (C) 1998-1999 Free Software Foundation, Inc. | 3 ;; Copyright (C) 1998-1999 Free Software Foundation, Inc. |
4 | 4 |
5 ;; Author: Stefan Monnier <monnier@cs.yale.edu> | 5 ;; Author: Stefan Monnier <monnier@cs.yale.edu> |
6 ;; Keywords: patch diff | 6 ;; Keywords: patch diff |
7 ;; Revision: $Id: diff-mode.el,v 1.4 1999/12/07 07:04:03 monnier Exp $ | 7 ;; Revision: $Id: diff-mode.el,v 1.5 2000/02/07 02:01:07 monnier Exp $ |
8 | 8 |
9 ;; This file is part of GNU Emacs. | 9 ;; This file is part of GNU Emacs. |
10 | 10 |
11 ;; GNU Emacs is free software; you can redistribute it and/or modify | 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 | 12 ;; it under the terms of the GNU General Public License as published by |
47 | 47 |
48 ;; - spice up the minor-mode with editing and font-lock support. | 48 ;; - spice up the minor-mode with editing and font-lock support. |
49 ;; - improve narrowed-view support. | 49 ;; - improve narrowed-view support. |
50 ;; - improve the `compile' support (?). | 50 ;; - improve the `compile' support (?). |
51 ;; - recognize pcl-cvs' special string for `cvs-execute-single'. | 51 ;; - recognize pcl-cvs' special string for `cvs-execute-single'. |
52 ;; - add support for # comments in diffs | |
52 | 53 |
53 ;;; Code: | 54 ;;; Code: |
54 | 55 |
55 (eval-when-compile (require 'cl)) | 56 (eval-when-compile (require 'cl)) |
56 | 57 |
84 | 85 |
85 ;;;; | 86 ;;;; |
86 ;;;; keymap, menu, ... | 87 ;;;; keymap, menu, ... |
87 ;;;; | 88 ;;;; |
88 | 89 |
89 (defmacro diff-defmap (var bindings doc) | 90 (easy-mmode-defmap diff-mode-shared-map |
90 `(defvar ,var | |
91 (let ((m (make-keymap))) | |
92 (dolist (b ,bindings) | |
93 (define-key m (car b) (cdr b))) | |
94 m) | |
95 ,doc)) | |
96 | |
97 (diff-defmap diff-mode-shared-map | |
98 '(;; from Pavel Machek's patch-mode | 91 '(;; from Pavel Machek's patch-mode |
99 ("n" . diff-next-hunk) | 92 ("n" . diff-hunk-next) |
100 ("N" . diff-next-file) | 93 ("N" . diff-file-next) |
101 ("p" . diff-prev-hunk) | 94 ("p" . diff-hunk-prev) |
102 ("P" . diff-prev-file) | 95 ("P" . diff-file-prev) |
103 ("k" . diff-kill-hunk) | 96 ("k" . diff-hunk-kill) |
104 ("K" . diff-kill-file) | 97 ("K" . diff-file-kill) |
105 ;; from compilation-minor-mode | 98 ;; from compilation-minor-mode |
106 ("}" . diff-next-file) | 99 ("}" . diff-file-next) |
107 ("{" . diff-prev-file) | 100 ("{" . diff-file-prev) |
108 ("\C-m" . diff-goto-source) | 101 ("\C-m" . diff-goto-source) |
109 ;; from XEmacs' diff-mode | 102 ;; from XEmacs' diff-mode |
110 ("W" . widen) | 103 ("W" . widen) |
111 ;;("\C-l" . diff-recenter) | |
112 ;;("." . diff-goto-source) ;display-buffer | 104 ;;("." . diff-goto-source) ;display-buffer |
113 ;;("f" . diff-goto-source) ;find-file | 105 ;;("f" . diff-goto-source) ;find-file |
114 ("o" . diff-goto-source) ;other-window | 106 ("o" . diff-goto-source) ;other-window |
115 ;;("w" . diff-goto-source) ;other-frame | 107 ;;("w" . diff-goto-source) ;other-frame |
116 ;;("N" . diff-narrow) | 108 ;;("N" . diff-narrow) |
124 ("r" . diff-restrict-view) | 116 ("r" . diff-restrict-view) |
125 ("R" . diff-reverse-direction) | 117 ("R" . diff-reverse-direction) |
126 ("U" . diff-context->unified) | 118 ("U" . diff-context->unified) |
127 ("C" . diff-unified->context)) | 119 ("C" . diff-unified->context)) |
128 "Basic keymap for `diff-mode', bound to various prefix keys.") | 120 "Basic keymap for `diff-mode', bound to various prefix keys.") |
129 (fset 'diff-mode-shared-map diff-mode-shared-map) | 121 |
130 | 122 (easy-mmode-defmap diff-mode-map |
131 (diff-defmap diff-mode-map | |
132 `(("\e" . ,diff-mode-shared-map) | 123 `(("\e" . ,diff-mode-shared-map) |
133 ;; from compilation-minor-mode | 124 ;; from compilation-minor-mode |
134 ("\C-c\C-c" . diff-goto-source)) | 125 ("\C-c\C-c" . diff-goto-source)) |
135 "Keymap for `diff-mode'. See also `diff-mode-shared-map'.") | 126 "Keymap for `diff-mode'. See also `diff-mode-shared-map'.") |
136 | 127 |
149 (defcustom diff-minor-mode-prefix "\C-cd" | 140 (defcustom diff-minor-mode-prefix "\C-cd" |
150 "Prefix key for `diff-minor-mode' commands." | 141 "Prefix key for `diff-minor-mode' commands." |
151 :group 'diff-mode | 142 :group 'diff-mode |
152 :type '(choice (string "\e") (string "C-cd") string)) | 143 :type '(choice (string "\e") (string "C-cd") string)) |
153 | 144 |
154 (diff-defmap diff-minor-mode-map | 145 (easy-mmode-defmap diff-minor-mode-map |
155 `((,diff-minor-mode-prefix . diff-mode-shared-map)) | 146 `((,diff-minor-mode-prefix . ,diff-mode-shared-map)) |
156 "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'.") | 147 "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'.") |
157 | 148 |
158 | 149 |
159 ;;;; | 150 ;;;; |
160 ;;;; font-lock support | 151 ;;;; font-lock support |
243 (unified "^[^-+ \\]") | 234 (unified "^[^-+ \\]") |
244 (context "^\\([^-+! \\][ \t]\\|--- .+ ----\\)") | 235 (context "^\\([^-+! \\][ \t]\\|--- .+ ----\\)") |
245 (normal "^\\([<>\\][ \t]\\|---\\)") | 236 (normal "^\\([<>\\][ \t]\\|---\\)") |
246 (t "^[^-+!<> \\]")) | 237 (t "^[^-+!<> \\]")) |
247 nil 'move) | 238 nil 'move) |
248 (beginning-of-line)) | 239 (beginning-of-line) |
240 (point)) | |
249 | 241 |
250 (defun diff-beginning-of-hunk () | 242 (defun diff-beginning-of-hunk () |
251 (beginning-of-line) | 243 (beginning-of-line) |
252 (unless (looking-at diff-hunk-header-re) | 244 (unless (looking-at diff-hunk-header-re) |
253 (forward-line 1) | 245 (forward-line 1) |
266 (defun diff-end-of-file () | 258 (defun diff-end-of-file () |
267 (re-search-forward "^[-+!<>0-9@* \\]" nil t) | 259 (re-search-forward "^[-+!<>0-9@* \\]" nil t) |
268 (re-search-forward "^[^-+!<>0-9@* \\]" nil 'move) | 260 (re-search-forward "^[^-+!<>0-9@* \\]" nil 'move) |
269 (beginning-of-line)) | 261 (beginning-of-line)) |
270 | 262 |
271 (defun diff-recenter () | 263 ;; Define diff-{hunk,file}-{prev,next} |
272 "Scroll if necessary to display the current hunk." | 264 (easy-mmode-define-navigation |
273 (interactive) | 265 diff-hunk diff-hunk-header-re "hunk" diff-end-of-hunk) |
274 (when (eq (current-buffer) (window-buffer (selected-window))) | 266 (easy-mmode-define-navigation |
275 (let ((endpt (save-excursion (diff-end-of-hunk) (point)))) | 267 diff-file diff-file-header-re "file" diff-end-of-hunk) |
276 (unless (<= endpt (window-end)) | |
277 (recenter) | |
278 ;;(unless (<= endpt (window-end nil t)) | |
279 ;; (set-window-start (selected-window) (point))) | |
280 )))) | |
281 | |
282 (defun diff-next-hunk (&optional count) | |
283 "Move to next (COUNT'th) hunk." | |
284 (interactive "p") | |
285 (unless count (setq count 1)) | |
286 (if (< count 0) (diff-prev-hunk (- count)) | |
287 (when (looking-at diff-hunk-header-re) (incf count)) | |
288 (condition-case () | |
289 (re-search-forward diff-hunk-header-re nil nil count) | |
290 (error (error "Can't find next hunk"))) | |
291 (goto-char (match-beginning 0)) | |
292 (diff-recenter))) | |
293 | |
294 (defun diff-prev-hunk (&optional count) | |
295 "Move to previous (COUNT'th) hunk." | |
296 (interactive "p") | |
297 (unless count (setq count 1)) | |
298 (if (< count 0) (diff-next-hunk (- count)) | |
299 (condition-case () | |
300 (re-search-backward diff-hunk-header-re nil nil count) | |
301 (error (error "Can't find previous hunk"))))) | |
302 | |
303 (defun diff-next-file (&optional count) | |
304 "Move to next (COUNT'th) file header." | |
305 (interactive "p") | |
306 (unless count (setq count 1)) | |
307 (if (< count 0) (diff-prev-file (- count)) | |
308 (when (looking-at diff-file-header-re) (incf count)) | |
309 (condition-case () | |
310 (re-search-forward diff-file-header-re nil nil count) | |
311 (error (error "Can't find next file"))) | |
312 (goto-char (match-beginning 0)) | |
313 (diff-recenter))) | |
314 | |
315 (defun diff-prev-file (&optional count) | |
316 "Move to (COUNT'th) previous file header." | |
317 (interactive "p") | |
318 (unless count (setq count 1)) | |
319 (if (< count 0) (diff-next-file (- count)) | |
320 (condition-case () | |
321 (re-search-backward diff-file-header-re nil nil count) | |
322 (error (error "Can't find previous file"))))) | |
323 | 268 |
324 (defun diff-restrict-view (&optional arg) | 269 (defun diff-restrict-view (&optional arg) |
325 "Restrict the view to the current hunk. | 270 "Restrict the view to the current hunk. |
326 If the prefix ARG is given, restrict the view to the current file instead." | 271 If the prefix ARG is given, restrict the view to the current file instead." |
327 (interactive "P") | 272 (interactive "P") |
331 (progn (if arg (diff-end-of-file) (diff-end-of-hunk)) | 276 (progn (if arg (diff-end-of-file) (diff-end-of-hunk)) |
332 (point))) | 277 (point))) |
333 (set (make-local-variable 'diff-narrowed-to) (if arg 'file 'hunk)))) | 278 (set (make-local-variable 'diff-narrowed-to) (if arg 'file 'hunk)))) |
334 | 279 |
335 | 280 |
336 (defun diff-kill-hunk () | 281 (defun diff-hunk-kill () |
337 "Kill current hunk." | 282 "Kill current hunk." |
338 (interactive) | 283 (interactive) |
339 (diff-beginning-of-hunk) | 284 (diff-beginning-of-hunk) |
340 (let ((start (point)) | 285 (let ((start (point)) |
341 (firsthunk (save-excursion | 286 (firsthunk (save-excursion |
342 (ignore-errors | 287 (ignore-errors |
343 (diff-beginning-of-file) (diff-next-hunk) (point)))) | 288 (diff-beginning-of-file) (diff-hunk-next) (point)))) |
344 (nexthunk (save-excursion | 289 (nexthunk (save-excursion |
345 (ignore-errors | 290 (ignore-errors |
346 (diff-next-hunk) (point)))) | 291 (diff-hunk-next) (point)))) |
347 (nextfile (save-excursion | 292 (nextfile (save-excursion |
348 (ignore-errors | 293 (ignore-errors |
349 (diff-next-file) (point))))) | 294 (diff-file-next) (point))))) |
350 (if (and firsthunk (= firsthunk start) | 295 (if (and firsthunk (= firsthunk start) |
351 (or (null nexthunk) | 296 (or (null nexthunk) |
352 (and nextfile (> nexthunk nextfile)))) | 297 (and nextfile (> nexthunk nextfile)))) |
353 ;; we're the only hunk for this file, so kill the file | 298 ;; we're the only hunk for this file, so kill the file |
354 (diff-kill-file) | 299 (diff-file-kill) |
355 (diff-end-of-hunk) | 300 (diff-end-of-hunk) |
356 (kill-region start (point))))) | 301 (kill-region start (point))))) |
357 | 302 |
358 (defun diff-kill-file () | 303 (defun diff-file-kill () |
359 "Kill current file's hunks." | 304 "Kill current file's hunks." |
360 (interactive) | 305 (interactive) |
361 (diff-beginning-of-file) | 306 (diff-beginning-of-file) |
362 (let* ((start (point)) | 307 (let* ((start (point)) |
363 (prevhunk (save-excursion | 308 (prevhunk (save-excursion |
364 (ignore-errors | 309 (ignore-errors |
365 (diff-prev-hunk) (point)))) | 310 (diff-hunk-prev) (point)))) |
366 (index (save-excursion | 311 (index (save-excursion |
367 (re-search-backward "^Index: " prevhunk t)))) | 312 (re-search-backward "^Index: " prevhunk t)))) |
368 (when index (setq start index)) | 313 (when index (setq start index)) |
369 (diff-end-of-file) | 314 (diff-end-of-file) |
370 (kill-region start (point)))) | 315 (kill-region start (point)))) |
394 (when (string-match "/" file) (substring file (match-end 0)))) | 339 (when (string-match "/" file) (substring file (match-end 0)))) |
395 | 340 |
396 (defun diff-merge-strings (ancestor from to) | 341 (defun diff-merge-strings (ancestor from to) |
397 "Merge the diff between ANCESTOR and FROM into TO. | 342 "Merge the diff between ANCESTOR and FROM into TO. |
398 Returns the merged string if successful or nil otherwise. | 343 Returns the merged string if successful or nil otherwise. |
344 The strings are assumed not to contain any \"\\n\" (i.e. end of line). | |
399 If ANCESTOR = FROM, returns TO. | 345 If ANCESTOR = FROM, returns TO. |
400 If ANCESTOR = TO, returns FROM. | 346 If ANCESTOR = TO, returns FROM. |
401 The heuristic is simplistic and only really works for cases | 347 The heuristic is simplistic and only really works for cases |
402 like \(diff-merge-strings \"b/foo\" \"b/bar\" \"/a/c/foo\")." | 348 like \(diff-merge-strings \"b/foo\" \"b/bar\" \"/a/c/foo\")." |
403 ;; Ideally, we want: | 349 ;; Ideally, we want: |
404 ;; AMB ANB CMD -> CND | 350 ;; AMB ANB CMD -> CND |
405 ;; but that's ambiguous if `foo' or `bar' is empty: | 351 ;; but that's ambiguous if `foo' or `bar' is empty: |
406 ;; a/foo a/foo1 b/foo.c -> b/foo1.c but not 1b/foo.c or b/foo.c1 | 352 ;; a/foo a/foo1 b/foo.c -> b/foo1.c but not 1b/foo.c or b/foo.c1 |
407 (let ((str (concat ancestor " /|/ " from " /|/ " to))) | 353 (let ((str (concat ancestor "\n" from "\n" to))) |
408 (when (and (string-match (concat | 354 (when (and (string-match (concat |
409 "\\`\\(.*?\\)\\(.*\\)\\(.*\\) /|/ " | 355 "\\`\\(.*?\\)\\(.*\\)\\(.*\\)\n" |
410 "\\1\\(.*\\)\\3 /|/ " | 356 "\\1\\(.*\\)\\3\n" |
411 "\\(.*\\(\\2\\).*\\)\\'") str) | 357 "\\(.*\\(\\2\\).*\\)\\'") str) |
412 (equal to (match-string 5 str))) | 358 (equal to (match-string 5 str))) |
413 (concat (substring str (match-beginning 5) (match-beginning 6)) | 359 (concat (substring str (match-beginning 5) (match-beginning 6)) |
414 (match-string 4 str) | 360 (match-string 4 str) |
415 (substring str (match-end 6) (match-end 5)))))) | 361 (substring str (match-end 6) (match-end 5)))))) |
423 (unless (looking-at diff-file-header-re) | 369 (unless (looking-at diff-file-header-re) |
424 (or (ignore-errors (diff-beginning-of-file)) | 370 (or (ignore-errors (diff-beginning-of-file)) |
425 (re-search-forward diff-file-header-re nil t))) | 371 (re-search-forward diff-file-header-re nil t))) |
426 (let* ((limit (save-excursion | 372 (let* ((limit (save-excursion |
427 (condition-case () | 373 (condition-case () |
428 (progn (diff-prev-hunk) (point)) | 374 (progn (diff-hunk-prev) (point)) |
429 (error (point-min))))) | 375 (error (point-min))))) |
430 (header-files | 376 (header-files |
431 (if (looking-at "[-*][-*][-*] \\(\\S-+\\)\\s-.*\n[-+][-+][-+] \\(\\S-+\\)\\s-.*$") | 377 (if (looking-at "[-*][-*][-*] \\(\\S-+\\)\\s-.*\n[-+][-+][-+] \\(\\S-+\\)\\s-.*$") |
432 (list (if old (match-string 1) (match-string 2)) | 378 (list (if old (match-string 1) (match-string 2)) |
433 (if old (match-string 2) (match-string 1))) | 379 (if old (match-string 2) (match-string 1))) |
832 ;;;; | 778 ;;;; |
833 ;;;; The main function | 779 ;;;; The main function |
834 ;;;; | 780 ;;;; |
835 | 781 |
836 ;;;###autoload | 782 ;;;###autoload |
837 (defun diff-mode () | 783 (define-derived-mode diff-mode fundamental-mode "Diff" |
838 "Major mode for viewing/editing context diffs. | 784 "Major mode for viewing/editing context diffs. |
839 Supports unified and context diffs as well as (to a lesser extent) normal diffs. | 785 Supports unified and context diffs as well as (to a lesser extent) normal diffs. |
840 When the buffer is read-only, the ESC prefix is not necessary. | 786 When the buffer is read-only, the ESC prefix is not necessary. |
841 This mode runs `diff-mode-hook'. | 787 This mode runs `diff-mode-hook'. |
842 \\{diff-mode-map}" | 788 \\{diff-mode-map}" |
843 (interactive) | |
844 (kill-all-local-variables) | |
845 (setq major-mode 'diff-mode) | |
846 (setq mode-name "Diff") | |
847 (use-local-map diff-mode-map) | |
848 (set (make-local-variable 'font-lock-defaults) diff-font-lock-defaults) | 789 (set (make-local-variable 'font-lock-defaults) diff-font-lock-defaults) |
849 (set (make-local-variable 'outline-regexp) diff-outline-regexp) | 790 (set (make-local-variable 'outline-regexp) diff-outline-regexp) |
850 ;; compile support | 791 ;; compile support |
851 (set (make-local-variable 'compilation-file-regexp-alist) | 792 (set (make-local-variable 'compilation-file-regexp-alist) |
852 diff-file-regexp-alist) | 793 diff-file-regexp-alist) |
865 'diff-after-change-function nil t) | 806 'diff-after-change-function nil t) |
866 (add-hook (make-local-hook 'post-command-hook) | 807 (add-hook (make-local-hook 'post-command-hook) |
867 'diff-post-command-hook nil t)) | 808 'diff-post-command-hook nil t)) |
868 ;; Neat trick from Dave Love to add more bindings in read-only mode: | 809 ;; Neat trick from Dave Love to add more bindings in read-only mode: |
869 (add-to-list (make-local-variable 'minor-mode-overriding-map-alist) | 810 (add-to-list (make-local-variable 'minor-mode-overriding-map-alist) |
870 (cons 'buffer-read-only diff-mode-shared-map)) | 811 (cons 'buffer-read-only diff-mode-shared-map))) |
871 (run-hooks 'diff-mode-hook)) | |
872 | 812 |
873 ;;;###autoload | 813 ;;;###autoload |
874 (define-minor-mode diff-minor-mode | 814 (define-minor-mode diff-minor-mode |
875 "Minor mode for viewing/editing context diffs. | 815 "Minor mode for viewing/editing context diffs. |
876 \\{diff-minor-mode-map}" | 816 \\{diff-minor-mode-map}" |
889 ;; provide the package | 829 ;; provide the package |
890 (provide 'diff-mode) | 830 (provide 'diff-mode) |
891 | 831 |
892 ;;; Change Log: | 832 ;;; Change Log: |
893 ;; $Log: diff-mode.el,v $ | 833 ;; $Log: diff-mode.el,v $ |
834 ;; Revision 1.5 2000/02/07 02:01:07 monnier | |
835 ;; (diff-kill-junk): New interactive function. | |
836 ;; (diff-reverse-direction): Use delete-and-extract-region. | |
837 ;; (diff-post-command-hook): Restrict the area so that the hook also works | |
838 ;; outside of any diff hunk. This is necessary for the minor-mode. | |
839 ;; (diff-mode): Use toggle-read-only and minor-mode-overriding-map-alist. | |
840 ;; (diff-minor-mode): Setup the hooks for header-hunk rewriting. | |
841 ;; | |
894 ;; Revision 1.4 1999/12/07 07:04:03 monnier | 842 ;; Revision 1.4 1999/12/07 07:04:03 monnier |
895 ;; * diff-mode.el (diff-mode-shared-map): fset'd and doc change. | 843 ;; * diff-mode.el (diff-mode-shared-map): fset'd and doc change. |
896 ;; (diff-minor-mode, diff-minor-mode-prefix, diff-minor-mode-map): | 844 ;; (diff-minor-mode, diff-minor-mode-prefix, diff-minor-mode-map): |
897 ;; New code to support the minor mode version. | 845 ;; New code to support the minor mode version. |
898 ;; (diff-recenter): New function. | 846 ;; (diff-recenter): New function. |