comparison lisp/diff-mode.el @ 31795:868648c4a36b

* diff-mode.el (diff-add-log-file-name, diff-current-defun): New funs. (diff-mode): Add support for add-log.el. (diff-hunk-text): Use char offsets rather than line offsets. (diff-find-source-location): Replace LINE with line-offset (nil if not found) and always set POS to a meaningful position. Adapt to the new char-offsets. (diff-apply-hunk): Drop support for the unused `select' POPUP. Adapt to the new diff-find-source-location. (diff-goto-source): Adapt to the new diff-find-source-location.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Wed, 20 Sep 2000 22:36:23 +0000
parents 181947e98152
children e23774b771e1
comparison
equal deleted inserted replaced
31794:7e8ab579609b 31795:868648c4a36b
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.18 2000/09/20 06:40:30 miles Exp $ 7 ;; Revision: $Id: diff-mode.el,v 1.19 2000/09/20 16:56:13 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
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 45
46 ;; Todo: 46 ;; Todo:
47 47
48 ;; - Add change-log support.
49 ;; - Spice up the minor-mode with font-lock support. 48 ;; - Spice up the minor-mode with font-lock support.
50 ;; - Improve narrowed-view support. 49 ;; - Improve narrowed-view support.
51 ;; - Improve the `compile' support (?). 50 ;; - Improve the `compile' support (?).
52 ;; - Recognize pcl-cvs' special string for `cvs-execute-single'. 51 ;; - Recognize pcl-cvs' special string for `cvs-execute-single'.
53 ;; - Support for # comments in context->unified. 52 ;; - Support for # comments in context->unified.
451 "Run `diff-goto-source' for the diff at a mouse click." 450 "Run `diff-goto-source' for the diff at a mouse click."
452 (interactive "e") 451 (interactive "e")
453 (save-excursion 452 (save-excursion
454 (mouse-set-point event) 453 (mouse-set-point event)
455 (diff-goto-source))) 454 (diff-goto-source)))
455
456 (defun diff-add-log-file-name (log-file)
457 "File name to use in add-log for the hunk at point.
458 For use in `add-log-file-name-function'.
459 LOG-FILE should be the path to the ChangeLog file."
460 (add-log-file-name (expand-file-name (diff-find-file-name)) log-file))
461
456 462
457 (defun diff-ediff-patch () 463 (defun diff-ediff-patch ()
458 "Call `ediff-patch-file' on the current buffer." 464 "Call `ediff-patch-file' on the current buffer."
459 (interactive) 465 (interactive)
460 (condition-case err 466 (condition-case err
823 'diff-after-change-function nil t) 829 'diff-after-change-function nil t)
824 (add-hook (make-local-hook 'post-command-hook) 830 (add-hook (make-local-hook 'post-command-hook)
825 'diff-post-command-hook nil t)) 831 'diff-post-command-hook nil t))
826 ;; Neat trick from Dave Love to add more bindings in read-only mode: 832 ;; Neat trick from Dave Love to add more bindings in read-only mode:
827 (add-to-list (make-local-variable 'minor-mode-overriding-map-alist) 833 (add-to-list (make-local-variable 'minor-mode-overriding-map-alist)
828 (cons 'buffer-read-only diff-mode-shared-map))) 834 (cons 'buffer-read-only diff-mode-shared-map))
835 ;; add-log support
836 (set (make-local-variable 'add-log-current-defun-function)
837 'diff-current-defun)
838 (set (make-local-variable 'add-log-file-name-function)
839 'diff-add-log-file-name))
829 840
830 ;;;###autoload 841 ;;;###autoload
831 (define-minor-mode diff-minor-mode 842 (define-minor-mode diff-minor-mode
832 "Minor mode for viewing/editing context diffs. 843 "Minor mode for viewing/editing context diffs.
833 \\{diff-minor-mode-map}" 844 \\{diff-minor-mode-map}"
855 (while 866 (while
856 (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@" 867 (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@"
857 nil t) 868 nil t)
858 (equal (match-string 1) (match-string 2))))) 869 (equal (match-string 1) (match-string 2)))))
859 870
860 (defun diff-hunk-text (hunk destp &optional line-offset) 871 (defun diff-hunk-text (hunk destp &optional char-offset)
861 "Returns the literal source text from HUNK. 872 "Returns the literal source text from HUNK.
862 if DESTP is nil return the source, otherwise the destination text. 873 if DESTP is nil return the source, otherwise the destination text.
863 If LINE-OFFSET is non-nil, it should be a line-offset in 874 If CHAR-OFFSET is non-nil, it should be a char-offset in
864 HUNK, and instead of a string, a cons cell is returned whose car is the 875 HUNK, and instead of a string, a cons cell is returned whose car is the
865 appropriate text, and whose cdr is the corresponding line-offset in that text." 876 appropriate text, and whose cdr is the corresponding char-offset in that text."
866 (with-temp-buffer 877 (with-temp-buffer
867 (insert hunk) 878 (insert hunk)
868 (goto-char (point-min)) 879 (goto-char (point-min))
869 (let ((src-pos nil) 880 (let ((src-pos nil)
870 (dst-pos nil) 881 (dst-pos nil)
908 (setq dst-pos (point))) 919 (setq dst-pos (point)))
909 (t 920 (t
910 (error "Unknown diff hunk type"))) 921 (error "Unknown diff hunk type")))
911 (if (if destp (null dst-pos) (null src-pos)) 922 (if (if destp (null dst-pos) (null src-pos))
912 ;; Implied empty text 923 ;; Implied empty text
913 (if line-offset '("" . 0) "") 924 (if char-offset '("" . 0) "")
914 925
915 (when line-offset 926 (when char-offset (goto-char char-offset))
916 (goto-char (point-min))
917 (forward-line line-offset))
918 927
919 ;; Get rid of anything except the desired text. 928 ;; Get rid of anything except the desired text.
920 (save-excursion 929 (save-excursion
921 ;; Delete unused text region 930 ;; Delete unused text region
922 (let ((keep (if destp dst-pos src-pos)) 931 (let ((keep (if destp dst-pos src-pos))
932 (delete-region (point) (progn (forward-line 1) (point))) 941 (delete-region (point) (progn (forward-line 1) (point)))
933 (delete-char num-pfx-chars) 942 (delete-char num-pfx-chars)
934 (forward-line 1))))) 943 (forward-line 1)))))
935 944
936 (let ((text (buffer-substring-no-properties (point-min) (point-max)))) 945 (let ((text (buffer-substring-no-properties (point-min) (point-max))))
937 (if line-offset 946 (if char-offset (cons text (point)) text))))))
938 (cons text (count-lines (point-min) (point)))
939 text))))))
940 947
941 (defun diff-find-text (text) 948 (defun diff-find-text (text)
942 "Return the buffer position of the nearest occurance of TEXT. 949 "Return the buffer position of the nearest occurrence of TEXT.
943 If TEXT isn't found, nil is returned." 950 If TEXT isn't found, nil is returned."
944 (let* ((orig (point)) 951 (let* ((orig (point))
945 (forw (and (search-forward text nil t) 952 (forw (and (search-forward text nil t)
946 (match-beginning 0))) 953 (match-beginning 0)))
947 (back (and (goto-char (+ orig (length text))) 954 (back (and (goto-char (+ orig (length text)))
951 (if (and forw back) 958 (if (and forw back)
952 (if (> (- forw orig) (- orig back)) back forw) 959 (if (> (- forw orig) (- orig back)) back forw)
953 (or back forw)))) 960 (or back forw))))
954 961
955 (defun diff-find-source-location (&optional other-file reverse) 962 (defun diff-find-source-location (&optional other-file reverse)
956 "Find out (BUF LINE POS SRC DST SWITCHED)." 963 "Find out (BUF LINE-OFFSET POS SRC DST SWITCHED)."
957 (save-excursion 964 (save-excursion
958 (let* ((old (if (not other-file) diff-jump-to-old-file-flag 965 (let* ((old (if (not other-file) diff-jump-to-old-file-flag
959 (not diff-jump-to-old-file-flag))) 966 (not diff-jump-to-old-file-flag)))
960 (orig-point (point)) 967 (hunk-char-offset
961 (hunk-line-offset 968 (- (point) (progn (diff-beginning-of-hunk) (point))))
962 (progn (diff-beginning-of-hunk) (count-lines (point) orig-point)))
963 ;; Find the location specification. 969 ;; Find the location specification.
964 (line (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?")) 970 (line (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?"))
965 (error "Can't find the hunk header") 971 (error "Can't find the hunk header")
966 (if old (match-string 1) 972 (if old (match-string 1)
967 (if (match-end 3) (match-string 3) 973 (if (match-end 3) (match-string 3)
970 (match-string 1))))) 976 (match-string 1)))))
971 (file (or (diff-find-file-name old) (error "Can't find the file"))) 977 (file (or (diff-find-file-name old) (error "Can't find the file")))
972 (buf (find-file-noselect file)) 978 (buf (find-file-noselect file))
973 (hunk 979 (hunk
974 (buffer-substring (point) (progn (diff-end-of-hunk) (point)))) 980 (buffer-substring (point) (progn (diff-end-of-hunk) (point))))
975 (old (diff-hunk-text hunk reverse hunk-line-offset)) 981 (old (diff-hunk-text hunk reverse hunk-char-offset))
976 (new (diff-hunk-text hunk (not reverse) hunk-line-offset))) 982 (new (diff-hunk-text hunk (not reverse) hunk-char-offset)))
977 ;; Update the user preference if he so wished. 983 ;; Update the user preference if he so wished.
978 (when (> (prefix-numeric-value other-file) 8) 984 (when (> (prefix-numeric-value other-file) 8)
979 (setq diff-jump-to-old-file-flag old)) 985 (setq diff-jump-to-old-file-flag old))
980 (with-current-buffer buf 986 (with-current-buffer buf
981 (goto-line (string-to-number line)) 987 (goto-line (string-to-number line))
982 (let* ((orig-pos (point)) 988 (let* ((orig-pos (point))
983 (pos (diff-find-text (car old))) 989 (pos (diff-find-text (car old)))
984 (switched nil)) 990 (switched nil))
985 (when (null pos) 991 (when (null pos)
986 (setq pos (diff-find-text (car new)) switched t)) 992 (setq pos (diff-find-text (car new)) switched t))
987 (list* buf (string-to-number line) pos 993 (cons buf
988 (if switched (list new old t) (list old new)))))))) 994 (nconc
995 (if pos (list (count-lines orig-pos pos) pos) (list nil orig-pos))
996 (if switched (list new old t) (list old new)))))))))
989 997
990 (defun diff-apply-hunk (&optional reverse other-file dry-run popup noerror) 998 (defun diff-apply-hunk (&optional reverse other-file dry-run popup noerror)
991 "Apply the current hunk to the source file. 999 "Apply the current hunk to the source file.
992 By default, the new source file is patched, but if the variable 1000 By default, the new source file is patched, but if the variable
993 `diff-jump-to-old-file-flag' is non-nil, then the old source file is 1001 `diff-jump-to-old-file-flag' is non-nil, then the old source file is
997 With a prefix argument, REVERSE the hunk. 1005 With a prefix argument, REVERSE the hunk.
998 If OTHER-FILE is non-nil, patch the old file by default, and reverse the 1006 If OTHER-FILE is non-nil, patch the old file by default, and reverse the
999 sense of `diff-jump-to-old-file-flag'. 1007 sense of `diff-jump-to-old-file-flag'.
1000 If DRY-RUN is non-nil, don't actually modify anything, just see whether 1008 If DRY-RUN is non-nil, don't actually modify anything, just see whether
1001 it's possible to do so. 1009 it's possible to do so.
1002 If POPUP is non-nil, pop up the patched file in another window; if POPUP 1010 If POPUP is non-nil, pop up the patched file in another window.
1003 is `select' then select the new window too.
1004 If NOERROR is non-nil, then no error is signaled in the case where the hunk 1011 If NOERROR is non-nil, then no error is signaled in the case where the hunk
1005 cannot be found in the source file (other errors may still be signaled). 1012 cannot be found in the source file (other errors may still be signaled).
1006 1013
1007 Return values are t if the hunk was sucessfully applied (or could be 1014 Return values are t if the hunk was sucessfully applied (or could be
1008 applied, in the case where DRY-RUN was non-nil), `reversed' if the hunk 1015 applied, in the case where DRY-RUN was non-nil), `reversed' if the hunk
1016 (when diff-jump-to-old-file-flag 1023 (when diff-jump-to-old-file-flag
1017 ;; The global variable `diff-jump-to-old-file-flag' inverts the 1024 ;; The global variable `diff-jump-to-old-file-flag' inverts the
1018 ;; sense of OTHER-FILE (in `diff-find-source-location') 1025 ;; sense of OTHER-FILE (in `diff-find-source-location')
1019 (setq reverse (not reverse))) 1026 (setq reverse (not reverse)))
1020 1027
1021 (destructuring-bind (buf patch-line pos old new &optional switched) 1028 (destructuring-bind (buf line-offset pos old new &optional switched)
1022 (diff-find-source-location other-file reverse) 1029 (diff-find-source-location other-file reverse)
1023 1030
1024 (when (and pos switched popup) 1031 (when (and line-offset switched popup)
1025 ;; A reversed patch was detected, perhaps apply it in reverse 1032 ;; A reversed patch was detected, perhaps apply it in reverse
1026 ;; (this is only done in `interactive' mode, when POPUP is non-nil). 1033 ;; (this is only done in `interactive' mode, when POPUP is non-nil).
1027 (if (or dry-run 1034 (if (or dry-run
1028 (save-window-excursion 1035 (save-window-excursion
1029 (pop-to-buffer buf) 1036 (pop-to-buffer buf)
1030 (goto-char pos) 1037 (goto-char (+ pos (cdr old)))
1031 (forward-line (cdr old))
1032 (if reverse 1038 (if reverse
1033 (y-or-n-p 1039 (y-or-n-p
1034 "Hunk hasn't been applied yet, so can't reverse it; apply it now? ") 1040 "Hunk hasn't been applied yet, so can't reverse it; apply it now? ")
1035 (y-or-n-p "Hunk has already been applied; undo it? ")))) 1041 (y-or-n-p "Hunk has already been applied; undo it? "))))
1036 1042
1037 nil 1043 nil
1038 ;; The user has chosen not to apply the reversed hunk, but we 1044 ;; The user has chosen not to apply the reversed hunk, but we
1039 ;; don't want to given an error message, so set things up so 1045 ;; don't want to given an error message, so set things up so
1040 ;; nothing else gets done down below 1046 ;; nothing else gets done down below
1041 (setq pos nil) 1047 (setq line-offset nil)
1042 (message "(Nothing done)") 1048 (message "(Nothing done)")
1043 (setq noerror t))) 1049 (setq noerror t)))
1044 1050
1045 (if (null pos) 1051 (if (null line-offset)
1046 ;; POS is nil, so we couldn't find the source text. 1052 ;; LINE-OFFSET is nil, so we couldn't find the source text.
1047 (unless noerror 1053 (funcall (if noerror 'message 'error) "Can't find the text to patch")
1048 (error "Can't find the text to patch"))
1049 1054
1050 (let ((reversed (if switched (not reverse) reverse))) 1055 (let ((reversed (if switched (not reverse) reverse)))
1051 (unless dry-run 1056 (unless dry-run
1052 ;; Apply the hunk 1057 ;; Apply the hunk
1053 (with-current-buffer buf 1058 (with-current-buffer buf
1056 (insert (car new)))) 1061 (insert (car new))))
1057 1062
1058 (when popup 1063 (when popup
1059 (with-current-buffer buf 1064 (with-current-buffer buf
1060 ;; Show a message describing what was done 1065 ;; Show a message describing what was done
1061 (let ((real-line (1+ (count-lines (point-min) pos))) 1066 (let ((msg
1062 (msg
1063 (if dry-run 1067 (if dry-run
1064 (if reversed "already applied" "not yet applied") 1068 (if reversed "already applied" "not yet applied")
1065 (if reversed "undone" "applied")))) 1069 (if reversed "undone" "applied"))))
1066 (cond ((= real-line patch-line) 1070 (message (cond ((= line-offset 0) "Hunk %s")
1067 (message "Hunk %s" msg)) 1071 ((= line-offset 1) "Hunk %s at offset %d line")
1068 ((= real-line (1+ patch-line)) 1072 (t "Hunk %s at offset %d lines"))
1069 (message "Hunk %s at offset 1 line" msg)) 1073 msg line-offset))
1070 (t 1074
1071 (message "Hunk %s at offset %d lines" 1075 ;; fixup POS to reflect the hunk char offset
1072 msg 1076 (goto-char (+ pos (cdr (if dry-run old new))))
1073 (- real-line patch-line)))))
1074
1075 ;; fixup POS to reflect the hunk line offset
1076 (goto-char pos)
1077 (forward-line (cdr (if dry-run old new)))
1078 (setq pos (point))) 1077 (setq pos (point)))
1079 1078
1080 ;; Display BUF in a window, and maybe select it 1079 ;; Display BUF in a window, and maybe select it
1081 (let ((win (display-buffer buf))) 1080 (let ((win (display-buffer buf)))
1082 (set-window-point win pos) 1081 (set-window-point win pos)))
1083 (when (eq popup 'select)
1084 (select-window win))))
1085 1082
1086 ;; Return an appropriate indicator of success 1083 ;; Return an appropriate indicator of success
1087 (if reversed 'reversed t))))) 1084 (if reversed 'reversed t)))))
1088 1085
1089 1086
1094 (diff-apply-hunk reverse nil t t)) 1091 (diff-apply-hunk reverse nil t t))
1095 1092
1096 (defun diff-goto-source (&optional other-file) 1093 (defun diff-goto-source (&optional other-file)
1097 "Jump to the corresponding source line. 1094 "Jump to the corresponding source line.
1098 `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg 1095 `diff-jump-to-old-file-flag' (or its opposite if the OTHER-FILE prefix arg
1099 is give) determines whether to jump to the old or the new file. 1096 is given) determines whether to jump to the old or the new file.
1100 If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument]) 1097 If the prefix arg is bigger than 8 (for example with \\[universal-argument] \\[universal-argument])
1101 then `diff-jump-to-old-file-flag' is also set, for the next invocations." 1098 then `diff-jump-to-old-file-flag' is also set, for the next invocations."
1102 (interactive "P") 1099 (interactive "P")
1103 (destructuring-bind (buf patch-line pos src &rest ignore) 1100 (destructuring-bind (buf line-offset pos src dst &optional switched)
1104 (diff-find-source-location other-file) 1101 (diff-find-source-location other-file)
1105 (pop-to-buffer buf) 1102 (pop-to-buffer buf)
1106 (if (null pos) 1103 (goto-char (+ pos (cdr src)))
1107 (progn 1104 (if (null line-offset) (message "Hunk text not found"))))
1108 (goto-line patch-line) 1105
1109 (message "Hunk text not found")) 1106 (defun diff-current-defun ()
1110 (goto-char pos) 1107 (destructuring-bind (buf line-offset pos src dst &optional switched)
1111 (forward-line (cdr src))))) 1108 (diff-find-source-location)
1112 1109 (save-excursion
1113 1110 (beginning-of-line)
1111 (or (when (memq (char-after) '(?< ?-))
1112 (let ((old (if switched dst src)))
1113 (with-temp-buffer
1114 (insert (car old))
1115 (goto-char (cdr old))
1116 (funcall (with-current-buffer buf major-mode))
1117 (add-log-current-defun))))
1118 (with-current-buffer buf
1119 (goto-char (+ pos (cdr src)))
1120 (add-log-current-defun))))))
1114 1121
1115 ;; provide the package 1122 ;; provide the package
1116 (provide 'diff-mode) 1123 (provide 'diff-mode)
1117 1124
1118 ;;; Old Change Log from when diff-mode wasn't part of Emacs: 1125 ;;; Old Change Log from when diff-mode wasn't part of Emacs: