diff options
| author | Stefan Monnier | 2000-08-16 19:56:10 +0000 |
|---|---|---|
| committer | Stefan Monnier | 2000-08-16 19:56:10 +0000 |
| commit | 027ac3f8db2a1837cb53d84130d07d0a7ea4574b (patch) | |
| tree | fa3b24ccb73b89cdde4d84f6b22840b83e8463a3 | |
| parent | 239e21e2242a8883778032d5ad9cfbab0a72cecf (diff) | |
| download | emacs-027ac3f8db2a1837cb53d84130d07d0a7ea4574b.tar.gz emacs-027ac3f8db2a1837cb53d84130d07d0a7ea4574b.zip | |
(diff-mode-map): Bind diff-apply-hunk.
(diff-find-source-location): New fun, extracted from diff-goto-source.
(diff-goto-source): Use it.
(diff-next-complex-hunk, diff-filter-lines): New function.
(diff-apply-hunk): New command.
| -rw-r--r-- | lisp/diff-mode.el | 219 |
1 files changed, 130 insertions, 89 deletions
diff --git a/lisp/diff-mode.el b/lisp/diff-mode.el index 8db4887beb8..1775a0165b3 100644 --- a/lisp/diff-mode.el +++ b/lisp/diff-mode.el | |||
| @@ -4,7 +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 | ;; Revision: $Id: diff-mode.el,v 1.7 2000/05/10 22:12:46 monnier Exp $ | 7 | ;; Revision: $Id: diff-mode.el,v 1.8 2000/06/05 07:30:09 monnier Exp $ |
| 8 | 8 | ||
| 9 | ;; This file is part of GNU Emacs. | 9 | ;; This file is part of GNU Emacs. |
| 10 | 10 | ||
| @@ -40,21 +40,25 @@ | |||
| 40 | 40 | ||
| 41 | ;; Bugs: | 41 | ;; Bugs: |
| 42 | 42 | ||
| 43 | ;; - reverse doesn't work with normal diffs. | 43 | ;; - Reverse doesn't work with normal diffs. |
| 44 | ;; - (nitpick) the mark is not always quite right in diff-goto-source. | 44 | ;; - (nitpick) The mark is not always quite right in diff-goto-source. |
| 45 | ;; - diff-apply-hunk only works on unified diffs. | ||
| 45 | 46 | ||
| 46 | ;; Todo: | 47 | ;; Todo: |
| 47 | 48 | ||
| 48 | ;; - spice up the minor-mode with font-lock support | 49 | ;; - Add change-log support. |
| 49 | ;; - improve narrowed-view support | 50 | ;; - Spice up the minor-mode with font-lock support. |
| 50 | ;; - improve the `compile' support (?) | 51 | ;; - Improve narrowed-view support. |
| 51 | ;; - recognize pcl-cvs' special string for `cvs-execute-single' | 52 | ;; - Improve the `compile' support (?). |
| 52 | ;; - support for # comments in context->unified | 53 | ;; - Recognize pcl-cvs' special string for `cvs-execute-single'. |
| 53 | ;; - diff-apply-hunk | 54 | ;; - Support for # comments in context->unified. |
| 54 | ;; - do a fuzzy search in diff-goto-source | 55 | ;; - Do a fuzzy search in diff-goto-source. |
| 55 | ;; - allow diff.el to use diff-mode | 56 | ;; - Allow diff.el to use diff-mode. |
| 56 | ;; - imenu support | 57 | ;; This mostly means ability to jump from half-hunk to half-hunk |
| 57 | ;; - handle `diff -b' output in context->unified | 58 | ;; in context (and normal) diffs and to jump to the corresponding |
| 59 | ;; (i.e. new or old) file. | ||
| 60 | ;; - imenu support. | ||
| 61 | ;; - Handle `diff -b' output in context->unified. | ||
| 58 | 62 | ||
| 59 | ;;; Code: | 63 | ;;; Code: |
| 60 | 64 | ||
| @@ -93,18 +97,18 @@ when editing big diffs)." | |||
| 93 | ;;;; | 97 | ;;;; |
| 94 | 98 | ||
| 95 | (easy-mmode-defmap diff-mode-shared-map | 99 | (easy-mmode-defmap diff-mode-shared-map |
| 96 | '(;; from Pavel Machek's patch-mode | 100 | '(;; From Pavel Machek's patch-mode. |
| 97 | ("n" . diff-hunk-next) | 101 | ("n" . diff-hunk-next) |
| 98 | ("N" . diff-file-next) | 102 | ("N" . diff-file-next) |
| 99 | ("p" . diff-hunk-prev) | 103 | ("p" . diff-hunk-prev) |
| 100 | ("P" . diff-file-prev) | 104 | ("P" . diff-file-prev) |
| 101 | ("k" . diff-hunk-kill) | 105 | ("k" . diff-hunk-kill) |
| 102 | ("K" . diff-file-kill) | 106 | ("K" . diff-file-kill) |
| 103 | ;; from compilation-minor-mode | 107 | ;; From compilation-minor-mode. |
| 104 | ("}" . diff-file-next) | 108 | ("}" . diff-file-next) |
| 105 | ("{" . diff-file-prev) | 109 | ("{" . diff-file-prev) |
| 106 | ("\C-m" . diff-goto-source) | 110 | ("\C-m" . diff-goto-source) |
| 107 | ;; from XEmacs' diff-mode | 111 | ;; From XEmacs' diff-mode. |
| 108 | ("W" . widen) | 112 | ("W" . widen) |
| 109 | ;;("." . diff-goto-source) ;display-buffer | 113 | ;;("." . diff-goto-source) ;display-buffer |
| 110 | ;;("f" . diff-goto-source) ;find-file | 114 | ;;("f" . diff-goto-source) ;find-file |
| @@ -116,7 +120,7 @@ when editing big diffs)." | |||
| 116 | ;;("q" . diff-quit) | 120 | ;;("q" . diff-quit) |
| 117 | (" " . scroll-up) | 121 | (" " . scroll-up) |
| 118 | ("\177" . scroll-down) | 122 | ("\177" . scroll-down) |
| 119 | ;; our very own bindings | 123 | ;; Our very own bindings. |
| 120 | ("A" . diff-ediff-patch) | 124 | ("A" . diff-ediff-patch) |
| 121 | ("r" . diff-restrict-view) | 125 | ("r" . diff-restrict-view) |
| 122 | ("R" . diff-reverse-direction) | 126 | ("R" . diff-reverse-direction) |
| @@ -126,8 +130,10 @@ when editing big diffs)." | |||
| 126 | 130 | ||
| 127 | (easy-mmode-defmap diff-mode-map | 131 | (easy-mmode-defmap diff-mode-map |
| 128 | `(("\e" . ,diff-mode-shared-map) | 132 | `(("\e" . ,diff-mode-shared-map) |
| 129 | ;; from compilation-minor-mode | 133 | ;; From compilation-minor-mode. |
| 130 | ("\C-c\C-c" . diff-goto-source)) | 134 | ("\C-c\C-c" . diff-goto-source) |
| 135 | ;; Misc operations. | ||
| 136 | ("\C-cda" . diff-apply-hunk)) | ||
| 131 | "Keymap for `diff-mode'. See also `diff-mode-shared-map'.") | 137 | "Keymap for `diff-mode'. See also `diff-mode-shared-map'.") |
| 132 | 138 | ||
| 133 | (easy-menu-define diff-mode-menu diff-mode-map | 139 | (easy-menu-define diff-mode-menu diff-mode-map |
| @@ -420,42 +426,51 @@ Non-nil OLD means that we want the old file." | |||
| 420 | (cons (cons fs file) diff-remembered-files-alist)) | 426 | (cons (cons fs file) diff-remembered-files-alist)) |
| 421 | file))))) | 427 | file))))) |
| 422 | 428 | ||
| 429 | (defun diff-find-source-location (&optional other-file) | ||
| 430 | "Find out (FILE LINE SPAN)." | ||
| 431 | (save-excursion | ||
| 432 | (diff-beginning-of-hunk) | ||
| 433 | (let* ((old (if (not other-file) diff-jump-to-old-file-flag | ||
| 434 | (not diff-jump-to-old-file-flag))) | ||
| 435 | ;; Find the location specification. | ||
| 436 | (loc (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?")) | ||
| 437 | (error "Can't find the hunk header") | ||
| 438 | (if old (match-string 1) | ||
| 439 | (if (match-end 3) (match-string 3) | ||
| 440 | (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t) | ||
| 441 | (error "Can't find the hunk separator")) | ||
| 442 | (match-string 1))))) | ||
| 443 | ;; Extract the actual line number. | ||
| 444 | (lines (if (string-match "^\\([0-9]*\\),\\([0-9]*\\)" loc) | ||
| 445 | (cons (string-to-number (match-string 1 loc)) | ||
| 446 | (string-to-number (match-string 2 loc))) | ||
| 447 | (cons (string-to-number loc) nil))) | ||
| 448 | (file (diff-find-file-name old)) | ||
| 449 | (line (car lines)) | ||
| 450 | (span (if (or (null (cdr lines)) (< (cdr lines) 0)) 0 | ||
| 451 | ;; Bad hack. | ||
| 452 | (if (< (cdr lines) line) (cdr lines) | ||
| 453 | (- (cdr lines) line))))) | ||
| 454 | ;; Update the user preference if he so wished. | ||
| 455 | (when (> (prefix-numeric-value other-file) 8) | ||
| 456 | (setq diff-jump-to-old-file-flag old)) | ||
| 457 | (if (null file) (error "Can't find the file") | ||
| 458 | (list file line span))))) | ||
| 459 | |||
| 423 | (defun diff-goto-source (&optional other-file) | 460 | (defun diff-goto-source (&optional other-file) |
| 424 | "Jump to the corresponding source line. | 461 | "Jump to the corresponding source line. |
| 425 | `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg | 462 | `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg |
| 426 | is give) determines whether to jump to the old or the new file. | 463 | is give) determines whether to jump to the old or the new file. |
| 427 | If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument]) | 464 | If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument]) |
| 428 | then `diff-jump-to-old-file-flag' is also set, for the next invokations." | 465 | then `diff-jump-to-old-file-flag' is also set, for the next invocations." |
| 429 | (interactive "P") | 466 | (interactive "P") |
| 430 | (save-excursion | 467 | (save-excursion |
| 431 | (let ((old (if (not other-file) diff-jump-to-old-file-flag | 468 | (let ((loc (diff-find-source-location other-file))) |
| 432 | (not diff-jump-to-old-file-flag)))) | 469 | (pop-to-buffer (find-file-noselect (car loc))) |
| 433 | (when (> (prefix-numeric-value other-file) 8) | 470 | (ignore-errors |
| 434 | (setq diff-jump-to-old-file-flag old)) | 471 | (goto-line (+ (cadr loc) (caddr loc))) |
| 435 | (diff-beginning-of-hunk) | 472 | (push-mark (point) t t) |
| 436 | (let* ((loc (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?")) | 473 | (goto-line (cadr loc)))))) |
| 437 | (error "Can't find the hunk header") | ||
| 438 | (if old (match-string 1) | ||
| 439 | (if (match-end 3) (match-string 3) | ||
| 440 | (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t) | ||
| 441 | (error "Can't find the hunk separator")) | ||
| 442 | (match-string 1))))) | ||
| 443 | (lines (if (string-match "^\\([0-9]*\\),\\([0-9]*\\)" loc) | ||
| 444 | (cons (string-to-number (match-string 1 loc)) | ||
| 445 | (string-to-number (match-string 2 loc))) | ||
| 446 | (cons (string-to-number loc) nil))) | ||
| 447 | (file (diff-find-file-name old))) | ||
| 448 | (unless file (error "Can't find the file")) | ||
| 449 | (pop-to-buffer (find-file-noselect file)) | ||
| 450 | (let* ((line (car lines)) | ||
| 451 | (span (if (or (null (cdr lines)) (< (cdr lines) 0)) 0 | ||
| 452 | (if (< (cdr lines) line) (cdr lines) | ||
| 453 | (- (cdr lines) line))))) | ||
| 454 | (ignore-errors | ||
| 455 | (goto-line line) | ||
| 456 | (forward-line span) | ||
| 457 | (push-mark (point) t t) | ||
| 458 | (goto-line line))))))) | ||
| 459 | 474 | ||
| 460 | 475 | ||
| 461 | (defun diff-ediff-patch () | 476 | (defun diff-ediff-patch () |
| @@ -836,50 +851,76 @@ This mode runs `diff-mode-hook'. | |||
| 836 | 'diff-post-command-hook nil t))) | 851 | 'diff-post-command-hook nil t))) |
| 837 | 852 | ||
| 838 | 853 | ||
| 854 | ;;; | ||
| 855 | ;;; Misc operations that have proved useful at some point. | ||
| 856 | ;;; | ||
| 857 | |||
| 858 | (defun diff-next-complex-hunk () | ||
| 859 | "Jump to the next \"complex\" hunk. | ||
| 860 | \"Complex\" is approximated by \"the hunk changes the number of lines\". | ||
| 861 | Only works for unified diffs." | ||
| 862 | (interactive) | ||
| 863 | (while | ||
| 864 | (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@" | ||
| 865 | nil t) | ||
| 866 | (equal (match-string 1) (match-string 2))))) | ||
| 867 | |||
| 868 | |||
| 869 | (defun diff-filter-lines (char) | ||
| 870 | (goto-char (point-min)) | ||
| 871 | (while (not (eobp)) | ||
| 872 | (if (eq (char-after) char) | ||
| 873 | (delete-region (point) (progn (forward-line 1) (point))) | ||
| 874 | (delete-char 1) | ||
| 875 | (forward-line 1)))) | ||
| 876 | |||
| 877 | (defun diff-apply-hunk (&optional reverse) | ||
| 878 | "Apply the current hunk. | ||
| 879 | With a prefix argument, REVERSE the hunk. | ||
| 880 | FIXME: Only works for unified diffs." | ||
| 881 | (interactive "P") | ||
| 882 | (save-excursion | ||
| 883 | (let ((loc (diff-find-source-location nil))) | ||
| 884 | (diff-beginning-of-hunk) | ||
| 885 | (unless (looking-at diff-hunk-header-re) (error "Help! Mom!")) | ||
| 886 | (goto-char (1+ (match-end 0))) | ||
| 887 | ;; Extract the SRC and DEST strings. | ||
| 888 | (let ((text (buffer-substring (point) (progn (diff-end-of-hunk) (point)))) | ||
| 889 | src dest) | ||
| 890 | (with-temp-buffer | ||
| 891 | (insert text) | ||
| 892 | (diff-filter-lines ?+) | ||
| 893 | (setq src (buffer-string)) | ||
| 894 | (erase-buffer) | ||
| 895 | (insert text) | ||
| 896 | (diff-filter-lines ?-) | ||
| 897 | (setq dest (buffer-string))) | ||
| 898 | ;; Exchange the two strings if we're reversing the patch. | ||
| 899 | (if reverse (let ((tmp src)) (setq src dest) (setq dest tmp))) | ||
| 900 | ;; Look for SRC in the file. | ||
| 901 | (pop-to-buffer (find-file-noselect (car loc))) | ||
| 902 | (goto-line (cadr loc)) | ||
| 903 | (let* ((pos (point)) | ||
| 904 | (forw (and (search-forward src nil t) | ||
| 905 | (match-beginning 0))) | ||
| 906 | (back (and (goto-char (+ pos (length src))) | ||
| 907 | (search-backward src nil t) | ||
| 908 | (match-beginning 0)))) | ||
| 909 | ;; Choose the closest match. | ||
| 910 | (setq pos (if (and forw back) | ||
| 911 | (if (> (- forw pos) (- pos back)) back forw) | ||
| 912 | (or back forw))) | ||
| 913 | (unless pos (error "Can't find the text to patch")) | ||
| 914 | ;; Do it! | ||
| 915 | (goto-char pos) | ||
| 916 | (delete-char (length src)) | ||
| 917 | (insert dest)))))) | ||
| 918 | |||
| 919 | |||
| 839 | ;; provide the package | 920 | ;; provide the package |
| 840 | (provide 'diff-mode) | 921 | (provide 'diff-mode) |
| 841 | 922 | ||
| 842 | ;;; Change Log: | 923 | ;;; Old Change Log from when diff-mode wasn't part of Emacs: |
| 843 | ;; $Log: diff-mode.el,v $ | ||
| 844 | ;; Revision 1.7 2000/05/10 22:12:46 monnier | ||
| 845 | ;; (diff-font-lock-keywords): Recognize comments. | ||
| 846 | ;; (diff-font-lock-defaults): Explicitly turn off multiline. | ||
| 847 | ;; (diff-end-of-hunk): Handle comments and fix end-of-buffer bug. | ||
| 848 | ;; (diff-ediff-patch): Fix call to ediff-patch-file. | ||
| 849 | ;; (diff-end-of-file, diff-reverse-direction, diff-fixup-modifs): | ||
| 850 | ;; Handle comments. | ||
| 851 | ;; | ||
| 852 | ;; Revision 1.6 2000/03/21 16:59:17 monnier | ||
| 853 | ;; (diff-mode-*-map): use `easy-mmode-defmap'. | ||
| 854 | ;; (diff-end-of-hunk): Return the end position for use in | ||
| 855 | ;; `easy-mmode-define-navigation'. | ||
| 856 | ;; (diff-recenter): Remove. | ||
| 857 | ;; (diff-(next|prev)-*): Rename `diff-*-(prev|next)' and defined in terms | ||
| 858 | ;; of `easy-mmode-define-navigation'. | ||
| 859 | ;; (diff-kill-*): Rename `diff-*-kill' (for consistency with the | ||
| 860 | ;; previous renaming) and fix to use new names. | ||
| 861 | ;; (diff-merge-strings): Use \n as separator: simpler, faster. | ||
| 862 | ;; (diff-mode): Use `define-derived-mode'. | ||
| 863 | ;; | ||
| 864 | ;; Revision 1.5 2000/02/07 02:01:07 monnier | ||
| 865 | ;; (diff-kill-junk): New interactive function. | ||
| 866 | ;; (diff-reverse-direction): Use delete-and-extract-region. | ||
| 867 | ;; (diff-post-command-hook): Restrict the area so that the hook also works | ||
| 868 | ;; outside of any diff hunk. This is necessary for the minor-mode. | ||
| 869 | ;; (diff-mode): Use toggle-read-only and minor-mode-overriding-map-alist. | ||
| 870 | ;; (diff-minor-mode): Setup the hooks for header-hunk rewriting. | ||
| 871 | ;; | ||
| 872 | ;; Revision 1.4 1999/12/07 07:04:03 monnier | ||
| 873 | ;; * diff-mode.el (diff-mode-shared-map): fset'd and doc change. | ||
| 874 | ;; (diff-minor-mode, diff-minor-mode-prefix, diff-minor-mode-map): | ||
| 875 | ;; New code to support the minor mode version. | ||
| 876 | ;; (diff-recenter): New function. | ||
| 877 | ;; (diff-next-hunk, diff-next-file): Use it. | ||
| 878 | ;; (diff-remembered-files-alist): New var. | ||
| 879 | ;; (diff-merge-strings): New function. | ||
| 880 | ;; (diff-find-file-name): Make it smarter and use the user's input more. | ||
| 881 | ;; (diff-mode): Cosmetic changes. | ||
| 882 | ;; | ||
| 883 | ;; Revision 1.11 1999/10/09 23:38:29 monnier | 924 | ;; Revision 1.11 1999/10/09 23:38:29 monnier |
| 884 | ;; (diff-mode-load-hook): dropped. | 925 | ;; (diff-mode-load-hook): dropped. |
| 885 | ;; (auto-mode-alist): also catch *.diffs. | 926 | ;; (auto-mode-alist): also catch *.diffs. |