aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiles Bader2000-09-11 13:51:21 +0000
committerMiles Bader2000-09-11 13:51:21 +0000
commit7530b6da4e245c83407efdddfa87b496246ab80d (patch)
tree51cba920e2b6b35a3d07f981f3e341f187698ba1
parentf74b07059d4bd017536a77a792b0575d6c2f1a1b (diff)
downloademacs-7530b6da4e245c83407efdddfa87b496246ab80d.tar.gz
emacs-7530b6da4e245c83407efdddfa87b496246ab80d.zip
(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.
-rw-r--r--lisp/ChangeLog16
-rw-r--r--lisp/diff-mode.el269
2 files changed, 226 insertions, 59 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 0bdb3585496..01244908d57 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,19 @@
12000-09-11 Miles Bader <miles@lsi.nec.co.jp>
2
3 * diff-mode.el (diff-apply-hunk): Function basically rewritten.
4 Now understands non-unified diffs. Some functionality moved into
5 `diff-hunk-text' and `diff-find-text'. Add OTHER-FILE, DRY-RUN,
6 POPUP, and NOERROR arguments. If DRY-RUN is true, don't actually
7 modify anything. Only reposition point in the patched file if the
8 patch succeeds. Only pop up another window if POPUP is true.
9 Emit a message describing what happened if successful, and at what
10 line-offset. Automatically detect reversed hunks and do something
11 appropriate.
12 (diff-hunk-text, diff-find-text): New functions.
13 (diff-filter-lines): Function removed.
14 (diff-test-hunk): New function.
15 (diff-goto-source): Rewritten in terms of diff-apply-hunk.
16
12000-09-10 Dave Love <fx@gnu.org> 172000-09-10 Dave Love <fx@gnu.org>
2 18
3 * textmodes/tildify.el: Minor doc/commentary fixes. 19 * textmodes/tildify.el: Minor doc/commentary fixes.
diff --git a/lisp/diff-mode.el b/lisp/diff-mode.el
index b737754f3db..5e5d4d7e625 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.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
@@ -42,7 +42,6 @@
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
@@ -471,21 +470,6 @@ Non-nil OLD means that we want the old file."
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 472
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
477is give) determines whether to jump to the old or the new file.
478If 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
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")
@@ -896,57 +880,224 @@ Only works for unified diffs."
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
885the 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
910With a prefix argument, REVERSE the hunk. 953(defun diff-find-text (text line)
911FIXME: Only works for unified diffs." 954 "Return the buffer position of the nearest occurance of TEXT to line LINE.
912 (interactive "P") 955If 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)) 970By 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
972patched instead (some commands, such as `diff-goto-source' can change
973the value of this variable when given an appropriate prefix argument).
974
975With a prefix argument, REVERSE the hunk.
976If OTHER-FILE is non-nil, patch the old file by default, and reverse the
977 sense of `diff-jump-to-old-file-flag'.
978If DRY-RUN is non-nil, don't actually modify anything, just see whether
979 it's possible to do so.
980If POPUP is non-nil, pop up the patched file in another window; if POPUP
981 is `select' then select the new window too.
982If 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
985Return values are `t' if the hunk was sucessfully applied (or could be
986applied, in the case where DRY-RUN was non-nil), `reversed' if the hunk
987was applied backwards, or nil if the hunk couldn't be found and NOERROR
988was 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)))))
1079
949 1080
1081(defun diff-test-hunk (&optional reverse)
1082 "See whether it's possible to apply the current hunk.
1083With 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
1090is give) determines whether to jump to the old or the new file.
1091If 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)