diff options
| author | Stefan Monnier | 1999-12-07 07:04:03 +0000 |
|---|---|---|
| committer | Stefan Monnier | 1999-12-07 07:04:03 +0000 |
| commit | 0b82e382e320f9e0d3be725b3a5e9b6cd2bf03d2 (patch) | |
| tree | 63c2d9da72da848a22dc1b7faeb11d6135d80c5e /lisp/diff-mode.el | |
| parent | 01e924b8c772e17333b2d6a3ab241d19a8748cff (diff) | |
| download | emacs-0b82e382e320f9e0d3be725b3a5e9b6cd2bf03d2.tar.gz emacs-0b82e382e320f9e0d3be725b3a5e9b6cd2bf03d2.zip | |
* diff-mode.el (diff-mode-shared-map): fset'd and doc change.
(diff-minor-mode, diff-minor-mode-prefix, diff-minor-mode-map):
New code to support the minor mode version.
(diff-recenter): New function.
(diff-next-hunk, diff-next-file): Use it.
(diff-remembered-files-alist): New var.
(diff-merge-strings): New function.
(diff-find-file-name): Make it smarter and use the user's input more.
(diff-mode): Cosmetic changes.
Diffstat (limited to 'lisp/diff-mode.el')
| -rw-r--r-- | lisp/diff-mode.el | 126 |
1 files changed, 93 insertions, 33 deletions
diff --git a/lisp/diff-mode.el b/lisp/diff-mode.el index 852737742dd..2c0553efcc2 100644 --- a/lisp/diff-mode.el +++ b/lisp/diff-mode.el | |||
| @@ -4,8 +4,7 @@ | |||
| 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 | ;; Version: v1_8 | 7 | ;; Revision: $Id$ |
| 8 | ;; Revision: diff-mode.el,v 1.11 1999/10/09 23:38:29 monnier Exp | ||
| 9 | 8 | ||
| 10 | ;; This file is part of GNU Emacs. | 9 | ;; This file is part of GNU Emacs. |
| 11 | 10 | ||
| @@ -30,8 +29,6 @@ | |||
| 30 | ;; commands, editing and various conversions as well as jumping | 29 | ;; commands, editing and various conversions as well as jumping |
| 31 | ;; to the corresponding source file. | 30 | ;; to the corresponding source file. |
| 32 | 31 | ||
| 33 | ;; History: | ||
| 34 | |||
| 35 | ;; inspired by Pavel Machek's patch-mode.el (<pavel@atrey.karlin.mff.cuni.cz>) | 32 | ;; inspired by Pavel Machek's patch-mode.el (<pavel@atrey.karlin.mff.cuni.cz>) |
| 36 | ;; some efforts were spent to have it somewhat compatible with XEmacs' | 33 | ;; some efforts were spent to have it somewhat compatible with XEmacs' |
| 37 | ;; diff-mode as well as with compilation-minor-mode | 34 | ;; diff-mode as well as with compilation-minor-mode |
| @@ -48,9 +45,10 @@ | |||
| 48 | 45 | ||
| 49 | ;; Todo: | 46 | ;; Todo: |
| 50 | 47 | ||
| 48 | ;; - spice up the minor-mode with editing and font-lock support. | ||
| 51 | ;; - improve narrowed-view support. | 49 | ;; - improve narrowed-view support. |
| 52 | ;; - improve diff-find-file-name. | 50 | ;; - improve the `compile' support (?). |
| 53 | ;; - improve the `compile' support. | 51 | ;; - recognize pcl-cvs' special string for `cvs-execute-single'. |
| 54 | 52 | ||
| 55 | ;;; Code: | 53 | ;;; Code: |
| 56 | 54 | ||
| @@ -110,6 +108,7 @@ when editing big diffs)." | |||
| 110 | ("\C-m" . diff-goto-source) | 108 | ("\C-m" . diff-goto-source) |
| 111 | ;; from XEmacs' diff-mode | 109 | ;; from XEmacs' diff-mode |
| 112 | ("W" . widen) | 110 | ("W" . widen) |
| 111 | ;;("\C-l" . diff-recenter) | ||
| 113 | ;;("." . diff-goto-source) ;display-buffer | 112 | ;;("." . diff-goto-source) ;display-buffer |
| 114 | ;;("f" . diff-goto-source) ;find-file | 113 | ;;("f" . diff-goto-source) ;find-file |
| 115 | ("o" . diff-goto-source) ;other-window | 114 | ("o" . diff-goto-source) ;other-window |
| @@ -126,7 +125,8 @@ when editing big diffs)." | |||
| 126 | ("R" . diff-reverse-direction) | 125 | ("R" . diff-reverse-direction) |
| 127 | ("U" . diff-context->unified) | 126 | ("U" . diff-context->unified) |
| 128 | ("C" . diff-unified->context)) | 127 | ("C" . diff-unified->context)) |
| 129 | "Keymap for read-only `diff-mode'. Only active in read-only mode.") | 128 | "Basic keymap for `diff-mode', bound to various prefix keys.") |
| 129 | (fset 'diff-mode-shared-map diff-mode-shared-map) | ||
| 130 | 130 | ||
| 131 | (diff-defmap diff-mode-map | 131 | (diff-defmap diff-mode-map |
| 132 | `(("\e" . ,diff-mode-shared-map) | 132 | `(("\e" . ,diff-mode-shared-map) |
| @@ -146,6 +146,15 @@ when editing big diffs)." | |||
| 146 | ;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)] | 146 | ;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)] |
| 147 | )) | 147 | )) |
| 148 | 148 | ||
| 149 | (defcustom diff-minor-mode-prefix "\C-cd" | ||
| 150 | "Prefix key for `diff-minor-mode' commands." | ||
| 151 | :group 'diff-mode | ||
| 152 | :type '(choice (string "\e") (string "C-cd") string)) | ||
| 153 | |||
| 154 | (diff-defmap diff-minor-mode-map | ||
| 155 | `((,diff-minor-mode-prefix . diff-mode-shared-map)) | ||
| 156 | "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'.") | ||
| 157 | |||
| 149 | 158 | ||
| 150 | ;;;; | 159 | ;;;; |
| 151 | ;;;; font-lock support | 160 | ;;;; font-lock support |
| @@ -259,6 +268,17 @@ when editing big diffs)." | |||
| 259 | (re-search-forward "^[^-+!<>0-9@* \\]" nil 'move) | 268 | (re-search-forward "^[^-+!<>0-9@* \\]" nil 'move) |
| 260 | (beginning-of-line)) | 269 | (beginning-of-line)) |
| 261 | 270 | ||
| 271 | (defun diff-recenter () | ||
| 272 | "Scroll if necessary to display the current hunk." | ||
| 273 | (interactive) | ||
| 274 | (when (eq (current-buffer) (window-buffer (selected-window))) | ||
| 275 | (let ((endpt (save-excursion (diff-end-of-hunk) (point)))) | ||
| 276 | (unless (<= endpt (window-end)) | ||
| 277 | (recenter) | ||
| 278 | ;;(unless (<= endpt (window-end nil t)) | ||
| 279 | ;; (set-window-start (selected-window) (point))) | ||
| 280 | )))) | ||
| 281 | |||
| 262 | (defun diff-next-hunk (&optional count) | 282 | (defun diff-next-hunk (&optional count) |
| 263 | "Move to next (COUNT'th) hunk." | 283 | "Move to next (COUNT'th) hunk." |
| 264 | (interactive "p") | 284 | (interactive "p") |
| @@ -268,7 +288,8 @@ when editing big diffs)." | |||
| 268 | (condition-case () | 288 | (condition-case () |
| 269 | (re-search-forward diff-hunk-header-re nil nil count) | 289 | (re-search-forward diff-hunk-header-re nil nil count) |
| 270 | (error (error "Can't find next hunk"))) | 290 | (error (error "Can't find next hunk"))) |
| 271 | (goto-char (match-beginning 0)))) | 291 | (goto-char (match-beginning 0)) |
| 292 | (diff-recenter))) | ||
| 272 | 293 | ||
| 273 | (defun diff-prev-hunk (&optional count) | 294 | (defun diff-prev-hunk (&optional count) |
| 274 | "Move to previous (COUNT'th) hunk." | 295 | "Move to previous (COUNT'th) hunk." |
| @@ -288,7 +309,8 @@ when editing big diffs)." | |||
| 288 | (condition-case () | 309 | (condition-case () |
| 289 | (re-search-forward diff-file-header-re nil nil count) | 310 | (re-search-forward diff-file-header-re nil nil count) |
| 290 | (error (error "Can't find next file"))) | 311 | (error (error "Can't find next file"))) |
| 291 | (goto-char (match-beginning 0)))) | 312 | (goto-char (match-beginning 0)) |
| 313 | (diff-recenter))) | ||
| 292 | 314 | ||
| 293 | (defun diff-prev-file (&optional count) | 315 | (defun diff-prev-file (&optional count) |
| 294 | "Move to (COUNT'th) previous file header." | 316 | "Move to (COUNT'th) previous file header." |
| @@ -351,9 +373,34 @@ If the prefix ARG is given, restrict the view to the current file instead." | |||
| 351 | ;;;; jump to other buffers | 373 | ;;;; jump to other buffers |
| 352 | ;;;; | 374 | ;;;; |
| 353 | 375 | ||
| 376 | (defvar diff-remembered-files-alist nil) | ||
| 377 | |||
| 354 | (defun diff-filename-drop-dir (file) | 378 | (defun diff-filename-drop-dir (file) |
| 355 | (when (string-match "/" file) (substring file (match-end 0)))) | 379 | (when (string-match "/" file) (substring file (match-end 0)))) |
| 356 | 380 | ||
| 381 | (defun diff-merge-strings (ancestor from to) | ||
| 382 | "Merge the diff between ANCESTOR and FROM into TO. | ||
| 383 | Returns the merged string if successful or nil otherwise. | ||
| 384 | If ANCESTOR = FROM, returns TO. | ||
| 385 | If ANCESTOR = TO, returns FROM. | ||
| 386 | The heuristic is simplistic and only really works for cases | ||
| 387 | like \(diff-merge-strings \"b/foo\" \"b/bar\" \"/a/c/foo\")." | ||
| 388 | ;; Ideally, we want: | ||
| 389 | ;; AMB ANB CMD -> CND | ||
| 390 | ;; but that's ambiguous if `foo' or `bar' is empty: | ||
| 391 | ;; a/foo a/foo1 b/foo.c -> b/foo1.c but not 1b/foo.c or b/foo.c1 | ||
| 392 | (let ((str (concat ancestor " /|/ " from " /|/ " to))) | ||
| 393 | (when (and (string-match (concat | ||
| 394 | "\\`\\(.*?\\)\\(.*\\)\\(.*\\) /|/ " | ||
| 395 | "\\1\\(.*\\)\\3 /|/ " | ||
| 396 | "\\(.*\\(\\2\\).*\\)\\'") str) | ||
| 397 | (equal to (match-string 5 str))) | ||
| 398 | (concat (substring str (match-beginning 5) (match-beginning 6)) | ||
| 399 | (match-string 4 str) | ||
| 400 | (substring str (match-end 6) (match-end 5)))))) | ||
| 401 | |||
| 402 | |||
| 403 | |||
| 357 | (defun diff-find-file-name (&optional old) | 404 | (defun diff-find-file-name (&optional old) |
| 358 | "Return the file corresponding to the current patch. | 405 | "Return the file corresponding to the current patch. |
| 359 | Non-nil OLD means that we want the old file." | 406 | Non-nil OLD means that we want the old file." |
| @@ -378,26 +425,33 @@ Non-nil OLD means that we want the old file." | |||
| 378 | (when (re-search-backward "^diff \\(-\\S-+ +\\)*\\(\\S-+\\)\\( +\\(\\S-+\\)\\)?" nil t) | 425 | (when (re-search-backward "^diff \\(-\\S-+ +\\)*\\(\\S-+\\)\\( +\\(\\S-+\\)\\)?" nil t) |
| 379 | (list (if old (match-string 2) (match-string 4)) | 426 | (list (if old (match-string 2) (match-string 4)) |
| 380 | (if old (match-string 4) (match-string 2)))))) | 427 | (if old (match-string 4) (match-string 2)))))) |
| 381 | (fs (delq nil fs)) | 428 | (fs (delq nil fs))) |
| 382 | (file | ||
| 383 | ;; look for each file in turn. If none found, try again but | ||
| 384 | ;; ignoring the first level of directory, ... | ||
| 385 | (do* ((files fs (delq nil (mapcar 'diff-filename-drop-dir files))) | ||
| 386 | (file nil nil)) | ||
| 387 | ((or (null files) | ||
| 388 | (setq file (do* ((files files (cdr files)) | ||
| 389 | (file (car files) (car files))) | ||
| 390 | ((or (null file) (file-exists-p file)) | ||
| 391 | file)))) | ||
| 392 | file)))) | ||
| 393 | (or | 429 | (or |
| 394 | file | 430 | ;; use any previously used preference |
| 431 | (cdr (assoc fs diff-remembered-files-alist)) | ||
| 432 | ;; try to be clever and use previous choices as an inspiration | ||
| 433 | (dolist (rf diff-remembered-files-alist) | ||
| 434 | (let ((newfile (diff-merge-strings (caar rf) (car fs) (cdr rf)))) | ||
| 435 | (if (and newfile (file-exists-p newfile)) (return newfile)))) | ||
| 436 | ;; look for each file in turn. If none found, try again but | ||
| 437 | ;; ignoring the first level of directory, ... | ||
| 438 | (do* ((files fs (delq nil (mapcar 'diff-filename-drop-dir files))) | ||
| 439 | (file nil nil)) | ||
| 440 | ((or (null files) | ||
| 441 | (setq file (do* ((files files (cdr files)) | ||
| 442 | (file (car files) (car files))) | ||
| 443 | ((or (null file) (file-exists-p file)) | ||
| 444 | file)))) | ||
| 445 | file)) | ||
| 446 | ;; <foo>.rej patches implicitly apply to <foo> | ||
| 395 | (and (string-match "\\.rej\\'" (or buffer-file-name "")) | 447 | (and (string-match "\\.rej\\'" (or buffer-file-name "")) |
| 396 | (let ((file (substring buffer-file-name 0 (match-beginning 0)))) | 448 | (let ((file (substring buffer-file-name 0 (match-beginning 0)))) |
| 397 | (when (file-exists-p file) file))) | 449 | (when (file-exists-p file) file))) |
| 398 | ;; FIXME: use a more informative prompt | 450 | ;; if all else fails, ask the user |
| 399 | (let ((file (read-file-name "File: " nil (first fs) nil (first fs)))) | 451 | (let ((file (read-file-name (format "Use file %s: " (or (first fs) "")) |
| 400 | ;; FIXME: remember for the next invocation | 452 | nil (first fs) t (first fs)))) |
| 453 | (set (make-local-variable 'diff-remembered-files-alist) | ||
| 454 | (cons (cons fs file) diff-remembered-files-alist)) | ||
| 401 | file))))) | 455 | file))))) |
| 402 | 456 | ||
| 403 | (defun diff-goto-source (&optional other-file) | 457 | (defun diff-goto-source (&optional other-file) |
| @@ -760,10 +814,9 @@ See `after-change-functions' for the meaning of BEG, END and LEN." | |||
| 760 | ;;;; The main function | 814 | ;;;; The main function |
| 761 | ;;;; | 815 | ;;;; |
| 762 | 816 | ||
| 763 | ;;(autoload 'diff-mode "diff-mode" "Major mode for viewing context diffs." t) | ||
| 764 | ;;;###autoload | 817 | ;;;###autoload |
| 765 | (defun diff-mode () | 818 | (defun diff-mode () |
| 766 | "Major mode for viewing context diffs. | 819 | "Major mode for viewing/editing context diffs. |
| 767 | Supports unified and context diffs as well as (to a lesser extent) normal diffs. | 820 | Supports unified and context diffs as well as (to a lesser extent) normal diffs. |
| 768 | When the buffer is read-only, the ESC prefix is not necessary. | 821 | When the buffer is read-only, the ESC prefix is not necessary. |
| 769 | This mode runs `diff-mode-hook'. | 822 | This mode runs `diff-mode-hook'. |
| @@ -789,10 +842,10 @@ This mode runs `diff-mode-hook'. | |||
| 789 | (if (not diff-update-on-the-fly-flag) | 842 | (if (not diff-update-on-the-fly-flag) |
| 790 | (add-hook 'write-contents-hooks 'diff-write-contents-hooks) | 843 | (add-hook 'write-contents-hooks 'diff-write-contents-hooks) |
| 791 | (make-local-variable 'diff-unhandled-changes) | 844 | (make-local-variable 'diff-unhandled-changes) |
| 792 | (make-local-hook 'after-change-functions) | 845 | (add-hook (make-local-hook 'after-change-functions) |
| 793 | (add-hook 'after-change-functions 'diff-after-change-function nil t) | 846 | 'diff-after-change-function nil t) |
| 794 | (make-local-hook 'post-command-hook) | 847 | (add-hook (make-local-hook 'post-command-hook) |
| 795 | (add-hook 'post-command-hook 'diff-post-command-hook nil t)) | 848 | 'diff-post-command-hook nil t)) |
| 796 | ;; Neat trick from Dave Love to add more bindings in read-only mode: | 849 | ;; Neat trick from Dave Love to add more bindings in read-only mode: |
| 797 | (add-to-list (make-local-variable 'minor-mode-map-alist) | 850 | (add-to-list (make-local-variable 'minor-mode-map-alist) |
| 798 | (cons 'buffer-read-only diff-mode-shared-map)) | 851 | (cons 'buffer-read-only diff-mode-shared-map)) |
| @@ -800,13 +853,20 @@ This mode runs `diff-mode-hook'. | |||
| 800 | (run-hooks 'diff-mode-hook)) | 853 | (run-hooks 'diff-mode-hook)) |
| 801 | 854 | ||
| 802 | ;;;###autoload | 855 | ;;;###autoload |
| 803 | (add-to-list 'auto-mode-alist '("\\.\\(diffs?\\|patch\\|rej\\)\\'" . diff-mode)) | 856 | (define-minor-mode diff-minor-mode |
| 857 | "Minor mode for viewing/editing context diffs. | ||
| 858 | \\{diff-minor-mode-map}" | ||
| 859 | nil " Diff" nil | ||
| 860 | ;; FIXME: setup font-lock | ||
| 861 | ;; FIXME: setup change hooks | ||
| 862 | ) | ||
| 863 | |||
| 804 | 864 | ||
| 805 | ;; provide the package | 865 | ;; provide the package |
| 806 | (provide 'diff-mode) | 866 | (provide 'diff-mode) |
| 807 | 867 | ||
| 808 | ;;; Change Log: | 868 | ;;; Change Log: |
| 809 | ;; diff-mode.el,v | 869 | ;; $Log: diff-mode.el,v $ |
| 810 | ;; Revision 1.11 1999/10/09 23:38:29 monnier | 870 | ;; Revision 1.11 1999/10/09 23:38:29 monnier |
| 811 | ;; (diff-mode-load-hook): dropped. | 871 | ;; (diff-mode-load-hook): dropped. |
| 812 | ;; (auto-mode-alist): also catch *.diffs. | 872 | ;; (auto-mode-alist): also catch *.diffs. |