Mercurial > emacs
comparison lisp/diff-mode.el @ 31538:46aca282e6b0
(diff-apply-hunk): Function basically rewritten. Now understands
non-unified diffs. Some functionality moved into `diff-hunk-text' and
`diff-find-text'. Add OTHER-FILE, DRY-RUN, POPUP, and NOERROR
arguments. If DRY-RUN is true, don't actually modify anything. Only
reposition point in the patched file if the patch succeeds. Only pop
up another window if POPUP is true. Emit a message describing what
happened if successful, and at what line-offset. Automatically detect
reversed hunks and do something appropriate.
(diff-hunk-text, diff-find-text): New functions.
(diff-filter-lines): Function removed.
(diff-test-hunk): New function.
(diff-goto-source): Rewritten in terms of diff-apply-hunk.
author | Miles Bader <miles@gnu.org> |
---|---|
date | Mon, 11 Sep 2000 13:49:38 +0000 |
parents | d58461428926 |
children | 74d7a9c42c2a |
comparison
equal
deleted
inserted
replaced
31537:6f330e666e31 | 31538:46aca282e6b0 |
---|---|
2 | 2 |
3 ;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. | 3 ;; Copyright (C) 1998, 1999, 2000 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.9 2000/08/16 19:56:10 monnier Exp $ | 7 ;; Revision: $Id: diff-mode.el,v 1.11 2000/09/07 20:14:27 fx 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 |
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. | |
46 | 45 |
47 ;; Todo: | 46 ;; Todo: |
48 | 47 |
49 ;; - Add change-log support. | 48 ;; - Add change-log support. |
50 ;; - Spice up the minor-mode with font-lock support. | 49 ;; - Spice up the minor-mode with font-lock support. |
468 ;; Update the user preference if he so wished. | 467 ;; Update the user preference if he so wished. |
469 (when (> (prefix-numeric-value other-file) 8) | 468 (when (> (prefix-numeric-value other-file) 8) |
470 (setq diff-jump-to-old-file-flag old)) | 469 (setq diff-jump-to-old-file-flag old)) |
471 (if (null file) (error "Can't find the file") | 470 (if (null file) (error "Can't find the file") |
472 (list file line span))))) | 471 (list file line span))))) |
473 | |
474 (defun diff-goto-source (&optional other-file) | |
475 "Jump to the corresponding source line. | |
476 `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg | |
477 is give) determines whether to jump to the old or the new file. | |
478 If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument]) | |
479 then `diff-jump-to-old-file-flag' is also set, for the next invocations." | |
480 (interactive "P") | |
481 (save-excursion | |
482 (let ((loc (diff-find-source-location other-file))) | |
483 (pop-to-buffer (find-file-noselect (car loc))) | |
484 (ignore-errors | |
485 (goto-line (+ (cadr loc) (caddr loc))) | |
486 (push-mark (point) t t) | |
487 (goto-line (cadr loc)))))) | |
488 | 472 |
489 (defun diff-mouse-goto-source (event) | 473 (defun diff-mouse-goto-source (event) |
490 "Run `diff-goto-source' for the diff at a mouse click." | 474 "Run `diff-goto-source' for the diff at a mouse click." |
491 (interactive "e") | 475 (interactive "e") |
492 (save-excursion | 476 (save-excursion |
894 (while | 878 (while |
895 (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@" | 879 (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@" |
896 nil t) | 880 nil t) |
897 (equal (match-string 1) (match-string 2))))) | 881 (equal (match-string 1) (match-string 2))))) |
898 | 882 |
899 | 883 (defun diff-hunk-text (hunk dest) |
900 (defun diff-filter-lines (char) | 884 "Returns the literal source text from HUNK, if DEST is nil, otherwise |
885 the destination text." | |
886 (with-current-buffer "foo" | |
887 (erase-buffer) | |
888 (insert hunk) | |
889 (goto-char (point-min)) | |
890 (let ((src nil) | |
891 (dst nil) | |
892 (divider nil) | |
893 (num-pfx-chars 2)) | |
894 (cond ((looking-at "^@@") | |
895 ;; unified diff | |
896 (setq num-pfx-chars 1) | |
897 (forward-line 1) | |
898 (setq src (point) dst (point))) | |
899 ((looking-at "^\\*\\*") | |
900 ;; context diff | |
901 (forward-line 2) | |
902 (setq src (point)) | |
903 (re-search-forward "^--- " nil t) | |
904 (forward-line 0) | |
905 (setq divider (point)) | |
906 (forward-line 1) | |
907 (setq dst (point))) | |
908 ((looking-at "^[0-9]+a[0-9,]+$") | |
909 ;; normal diff, insert | |
910 (forward-line 1) | |
911 (setq dst (point))) | |
912 ((looking-at "^[0-9,]+d[0-9]+$") | |
913 ;; normal diff, delete | |
914 (forward-line 1) | |
915 (setq src (point))) | |
916 ((looking-at "^[0-9,]+c[0-9,]+$") | |
917 ;; normal diff, change | |
918 (forward-line 1) | |
919 (setq src (point)) | |
920 (re-search-forward "^---$" nil t) | |
921 (forward-line 0) | |
922 (setq divider (point)) | |
923 (forward-line 1) | |
924 (setq dst (point))) | |
925 (t | |
926 (error "Unknown diff hunk type"))) | |
927 | |
928 (if (if dest (null dst) (null src)) | |
929 ;; Implied empty text | |
930 "" | |
931 | |
932 ;; Explicit text | |
933 | |
934 ;; Delete unused text region | |
935 (let ((keep (if dest dst src)) | |
936 (kill (or divider (if dest src dst)))) | |
937 (when (and kill (> kill keep)) | |
938 (delete-region kill (point-max))) | |
939 (delete-region (point-min) keep)) | |
940 | |
941 ;; Remove line-prefix characters, and unneeded lines (for | |
942 ;; unified diffs). | |
943 (let ((kill-char (if dest ?- ?+))) | |
901 (goto-char (point-min)) | 944 (goto-char (point-min)) |
902 (while (not (eobp)) | 945 (while (not (eobp)) |
903 (if (eq (char-after) char) | 946 (if (eq (char-after) kill-char) |
904 (delete-region (point) (progn (forward-line 1) (point))) | 947 (delete-region (point) (progn (forward-line 1) (point))) |
905 (delete-char 1) | 948 (delete-char num-pfx-chars) |
906 (forward-line 1)))) | 949 (forward-line 1)))) |
907 | 950 |
908 (defun diff-apply-hunk (&optional reverse) | 951 (buffer-substring-no-properties (point-min) (point-max)))))) |
909 "Apply the current hunk. | 952 |
910 With a prefix argument, REVERSE the hunk. | 953 (defun diff-find-text (text line) |
911 FIXME: Only works for unified diffs." | 954 "Return the buffer position of the nearest occurance of TEXT to line LINE. |
912 (interactive "P") | 955 If TEXT isn't found, nil is returned." |
913 (save-excursion | 956 (goto-line line) |
914 (let ((loc (diff-find-source-location nil))) | 957 (let* ((orig (point)) |
915 (diff-beginning-of-hunk) | 958 (forw (and (search-forward text nil t) |
916 (unless (looking-at diff-hunk-header-re) (error "Help! Mom!")) | |
917 (goto-char (1+ (match-end 0))) | |
918 ;; Extract the SRC and DEST strings. | |
919 (let ((text (buffer-substring (point) (progn (diff-end-of-hunk) (point)))) | |
920 src dest) | |
921 (with-temp-buffer | |
922 (insert text) | |
923 (diff-filter-lines ?+) | |
924 (setq src (buffer-string)) | |
925 (erase-buffer) | |
926 (insert text) | |
927 (diff-filter-lines ?-) | |
928 (setq dest (buffer-string))) | |
929 ;; Exchange the two strings if we're reversing the patch. | |
930 (if reverse (let ((tmp src)) (setq src dest) (setq dest tmp))) | |
931 ;; Look for SRC in the file. | |
932 (pop-to-buffer (find-file-noselect (car loc))) | |
933 (goto-line (cadr loc)) | |
934 (let* ((pos (point)) | |
935 (forw (and (search-forward src nil t) | |
936 (match-beginning 0))) | 959 (match-beginning 0))) |
937 (back (and (goto-char (+ pos (length src))) | 960 (back (and (goto-char (+ orig (length text))) |
938 (search-backward src nil t) | 961 (search-backward text nil t) |
939 (match-beginning 0)))) | 962 (match-beginning 0)))) |
940 ;; Choose the closest match. | 963 ;; Choose the closest match. |
941 (setq pos (if (and forw back) | 964 (if (and forw back) |
942 (if (> (- forw pos) (- pos back)) back forw) | 965 (if (> (- forw orig) (- orig back)) back forw) |
943 (or back forw))) | 966 (or back forw)))) |
944 (unless pos (error "Can't find the text to patch")) | 967 |
945 ;; Do it! | 968 (defun diff-apply-hunk (&optional reverse other-file dry-run popup noerror) |
946 (goto-char pos) | 969 "Apply the current hunk to the source file. |
947 (delete-char (length src)) | 970 By default, the new source file is patched, but if the variable |
948 (insert dest)))))) | 971 `diff-jump-to-old-file-flag' is non-nil, then the old source file is |
972 patched instead (some commands, such as `diff-goto-source' can change | |
973 the value of this variable when given an appropriate prefix argument). | |
974 | |
975 With a prefix argument, REVERSE the hunk. | |
976 If OTHER-FILE is non-nil, patch the old file by default, and reverse the | |
977 sense of `diff-jump-to-old-file-flag'. | |
978 If DRY-RUN is non-nil, don't actually modify anything, just see whether | |
979 it's possible to do so. | |
980 If POPUP is non-nil, pop up the patched file in another window; if POPUP | |
981 is `select' then select the new window too. | |
982 If NOERROR is non-nil, then no error is signaled in the case where the hunk | |
983 cannot be found in the source file (other errors may still be signaled). | |
984 | |
985 Return values are `t' if the hunk was sucessfully applied (or could be | |
986 applied, in the case where DRY-RUN was non-nil), `reversed' if the hunk | |
987 was applied backwards, or nil if the hunk couldn't be found and NOERROR | |
988 was non-nil." | |
989 (interactive (list current-prefix-arg nil nil t)) | |
990 | |
991 (when other-file | |
992 ;; OTHER-FILE inverts the sense of the hunk | |
993 (setq reverse (not reverse))) | |
994 (when diff-jump-to-old-file-flag | |
995 ;; The global variable `diff-jump-to-old-file-flag' inverts the | |
996 ;; sense of OTHER-FILE (in `diff-find-source-location') | |
997 (setq reverse (not reverse))) | |
998 | |
999 (let* ((loc (diff-find-source-location other-file)) | |
1000 (buf (find-file-noselect (car loc))) | |
1001 (patch-line (cadr loc)) | |
1002 (hunk | |
1003 (save-excursion | |
1004 (diff-beginning-of-hunk) | |
1005 (unless (looking-at diff-hunk-header-re) | |
1006 (error "Malformed hunk")) | |
1007 (buffer-substring (point) (progn (diff-end-of-hunk) (point))))) | |
1008 (src (diff-hunk-text hunk reverse)) | |
1009 (dst (diff-hunk-text hunk (not reverse))) | |
1010 (pos | |
1011 (with-current-buffer buf (diff-find-text src patch-line))) | |
1012 (reversed-pos | |
1013 (and (null pos) | |
1014 (with-current-buffer buf (diff-find-text dst patch-line))))) | |
1015 | |
1016 (when (and reversed-pos popup) | |
1017 ;; A reversed patch was detected, perhaps apply it in reverse | |
1018 ;; (this is only done in `interactive' mode, when POPUP is non-nil). | |
1019 (if (or dry-run | |
1020 (save-window-excursion | |
1021 (pop-to-buffer buf) | |
1022 (goto-char reversed-pos) | |
1023 (if reverse | |
1024 (y-or-n-p | |
1025 "Hunk hasn't been applied yet, so can't reverse it; apply it now? ") | |
1026 (y-or-n-p "Hunk has already been applied; undo it? ")))) | |
1027 | |
1028 ;; Set up things to reverse the diff | |
1029 (let ((swap dst)) | |
1030 (setq pos reversed-pos) | |
1031 (setq src dst) | |
1032 (setq dst swap)) | |
1033 | |
1034 ;; The user has chosen not to apply the reversed hunk, but we | |
1035 ;; don't want to given an error message, so set things up so | |
1036 ;; nothing else gets done down below | |
1037 (message "(Nothing done)") | |
1038 (setq noerror t))) | |
1039 | |
1040 (if (null pos) | |
1041 ;; POS is nil, so we couldn't find the source text. | |
1042 (unless noerror | |
1043 (error "Can't find the text to patch")) | |
1044 | |
1045 (let ((reversed (if reversed-pos (not reverse) reverse))) | |
1046 (unless dry-run | |
1047 ;; Apply the hunk | |
1048 (with-current-buffer buf | |
1049 (goto-char pos) | |
1050 (delete-char (length src)) | |
1051 (insert dst))) | |
1052 | |
1053 (when popup | |
1054 ;; Show a message describing what was done | |
1055 (let ((real-line | |
1056 (1+ (with-current-buffer buf (count-lines (point-min) pos)))) | |
1057 (msg | |
1058 (if dry-run | |
1059 (if reversed "already applied" "not yet applied") | |
1060 (if reversed "undone" "applied")))) | |
1061 (cond ((= real-line patch-line) | |
1062 (message "Hunk %s" msg)) | |
1063 ((= real-line (1+ patch-line)) | |
1064 (message "Hunk %s at offset 1 line" msg)) | |
1065 (t | |
1066 (message "Hunk %s at offset %d lines" | |
1067 msg | |
1068 (- real-line patch-line))))) | |
1069 | |
1070 ;; Display BUF in a window, and maybe select it | |
1071 (cond ((eq popup 'select) | |
1072 (pop-to-buffer buf) | |
1073 (goto-char pos)) | |
1074 (t | |
1075 (set-window-point (display-buffer buf) pos)))) | |
1076 | |
1077 ;; Return an appropriate indicator of success | |
1078 (if reversed 'reversed t))))) | |
949 | 1079 |
1080 | |
1081 (defun diff-test-hunk (&optional reverse) | |
1082 "See whether it's possible to apply the current hunk. | |
1083 With a prefix argument, REVERSE the hunk." | |
1084 (interactive "P") | |
1085 (diff-apply-hunk reverse nil t t)) | |
1086 | |
1087 (defun diff-goto-source (&optional other-file) | |
1088 "Jump to the corresponding source line. | |
1089 `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg | |
1090 is give) determines whether to jump to the old or the new file. | |
1091 If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument]) | |
1092 then `diff-jump-to-old-file-flag' is also set, for the next invocations." | |
1093 (interactive "P") | |
1094 (or (diff-apply-hunk nil other-file t 'select t) | |
1095 ;; couldn't actually find the hunk, just obey the hunk line number | |
1096 (let ((loc (diff-find-source-location other-file))) | |
1097 (find-file-other-window (car loc)) | |
1098 (goto-line (cadr loc)) | |
1099 (error "Hunk text not found")))) | |
1100 | |
950 | 1101 |
951 ;; provide the package | 1102 ;; provide the package |
952 (provide 'diff-mode) | 1103 (provide 'diff-mode) |
953 | 1104 |
954 ;;; Old Change Log from when diff-mode wasn't part of Emacs: | 1105 ;;; Old Change Log from when diff-mode wasn't part of Emacs: |