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.