aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard M. Stallman1998-03-14 08:19:27 +0000
committerRichard M. Stallman1998-03-14 08:19:27 +0000
commit65627aad683a648e510b5dd204f6b18086b99054 (patch)
tree449de265edbd78bf15fb901e2c8b52d396f93df5
parent5cfee3ac9a36df3b974c77ad03710702127d82cf (diff)
downloademacs-65627aad683a648e510b5dd204f6b18086b99054.tar.gz
emacs-65627aad683a648e510b5dd204f6b18086b99054.zip
Implement selective undo (by Paul Flinders).
(undo-copy-list, undo-copy-list-1): New functions. (undo-make-selective-list, undo-delta): New functions. (undo-elt-in-region, undo-elt-crosses-region): New functions. (undo-adjusted-markers): New defvar. (undo-start): New args BEG and END. (undo): If arg or active region, pass args to undo-start.
-rw-r--r--lisp/simple.el187
1 files changed, 174 insertions, 13 deletions
diff --git a/lisp/simple.el b/lisp/simple.el
index 62a6ea2e877..be60e3c8f73 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -797,8 +797,12 @@ Get previous element of history which is a completion of minibuffer contents."
797(defun undo (&optional arg) 797(defun undo (&optional arg)
798 "Undo some previous changes. 798 "Undo some previous changes.
799Repeat this command to undo more changes. 799Repeat this command to undo more changes.
800A numeric argument serves as a repeat count." 800A numeric argument serves as a repeat count.
801 (interactive "*p") 801
802Just C-u as argument requests selective undo,
803limited to changes within the current region.
804Likewise in Transient Mark mode when the mark is active."
805 (interactive "*P")
802 ;; If we don't get all the way thru, make last-command indicate that 806 ;; If we don't get all the way thru, make last-command indicate that
803 ;; for the following command. 807 ;; for the following command.
804 (setq this-command t) 808 (setq this-command t)
@@ -807,9 +811,11 @@ A numeric argument serves as a repeat count."
807 (or (eq (selected-window) (minibuffer-window)) 811 (or (eq (selected-window) (minibuffer-window))
808 (message "Undo!")) 812 (message "Undo!"))
809 (or (eq last-command 'undo) 813 (or (eq last-command 'undo)
810 (progn (undo-start) 814 (progn (if (or arg (and transient-mark-mode mark-active))
815 (undo-start (region-beginning) (region-end))
816 (undo-start))
811 (undo-more 1))) 817 (undo-more 1)))
812 (undo-more (or arg 1)) 818 (undo-more (if arg (prefix-numeric-value arg) 1))
813 ;; Don't specify a position in the undo record for the undo command. 819 ;; Don't specify a position in the undo record for the undo command.
814 ;; Instead, undoing this should move point to where the change is. 820 ;; Instead, undoing this should move point to where the change is.
815 (let ((tail buffer-undo-list) 821 (let ((tail buffer-undo-list)
@@ -828,13 +834,6 @@ A numeric argument serves as a repeat count."
828(defvar pending-undo-list nil 834(defvar pending-undo-list nil
829 "Within a run of consecutive undo commands, list remaining to be undone.") 835 "Within a run of consecutive undo commands, list remaining to be undone.")
830 836
831(defun undo-start ()
832 "Set `pending-undo-list' to the front of the undo list.
833The next call to `undo-more' will undo the most recently made change."
834 (if (eq buffer-undo-list t)
835 (error "No undo information in this buffer"))
836 (setq pending-undo-list buffer-undo-list))
837
838(defun undo-more (count) 837(defun undo-more (count)
839 "Undo back N undo-boundaries beyond what was already undone recently. 838 "Undo back N undo-boundaries beyond what was already undone recently.
840Call `undo-start' to get ready to undo recent changes, 839Call `undo-start' to get ready to undo recent changes,
@@ -843,6 +842,168 @@ then call `undo-more' one or more times to undo them."
843 (error "No further undo information")) 842 (error "No further undo information"))
844 (setq pending-undo-list (primitive-undo count pending-undo-list))) 843 (setq pending-undo-list (primitive-undo count pending-undo-list)))
845 844
845;; Deep copy of a list
846(defun undo-copy-list (list)
847 "Make a copy of undo list LIST."
848 (mapcar 'undo-copy-list-1 list))
849
850(defun undo-copy-list-1 (elt)
851 (if (consp elt)
852 (cons (car elt) (undo-copy-list-1 (cdr elt)))
853 elt))
854
855(defun undo-start (&optional beg end)
856 "Set `pending-undo-list' to the front of the undo list.
857The next call to `undo-more' will undo the most recently made change.
858If BEG and END are specified, then only undo elements
859that apply to text between BEG and END are used; other undo elements
860are ignored. If BEG and END are nil, all undo elements are used."
861 (if (eq buffer-undo-list t)
862 (error "No undo information in this buffer"))
863 (setq pending-undo-list
864 (if (and beg end (not (= beg end)))
865 (undo-make-selective-list (min beg end) (max beg end))
866 buffer-undo-list)))
867
868(defvar undo-adjusted-markers)
869
870(defun undo-make-selective-list (start end)
871 "Return a list of undo elements for the region START to END.
872The elements come from `buffer-undo-list', but we keep only
873the elements inside this region, and discard those outside this region.
874If we find an element that crosses an edge of this region,
875we stop and ignore all further elements."
876 (let ((undo-list-copy (undo-copy-list buffer-undo-list))
877 (undo-list (list nil))
878 undo-adjusted-markers
879 some-rejected
880 undo-elt undo-elt temp-undo-list delta)
881 (while undo-list-copy
882 (setq undo-elt (car undo-list-copy))
883 (let ((keep-this
884 (cond ((and (consp undo-elt) (eq (car undo-elt) t))
885 ;; This is a "was unmodified" element.
886 ;; Keep it if we have kept everything thus far.
887 (not some-rejected))
888 (t
889 (undo-elt-in-region undo-elt start end)))))
890 (if keep-this
891 (progn
892 (setq end (+ end (cdr (undo-delta undo-elt))))
893 ;; Don't put two nils together in the list
894 (if (not (and (eq (car undo-list) nil)
895 (eq undo-elt nil)))
896 (setq undo-list (cons undo-elt undo-list))))
897 (if (undo-elt-crosses-region undo-elt start end)
898 (setq undo-list-copy nil)
899 (setq some-rejected t)
900 (setq temp-undo-list (cdr undo-list-copy))
901 (setq delta (undo-delta undo-elt))
902
903 (when (/= (cdr delta) 0)
904 (let ((position (car delta))
905 (offset (cdr delta)))
906
907 ;; Loop down the earlier events adjusting their buffer positions
908 ;; to reflect the fact that a change to the buffer isn't being
909 ;; undone. We only need to process those element types which
910 ;; undo-elt-in-region will return as being in the region since
911 ;; only those types can ever get into the output
912
913 (while temp-undo-list
914 (setq undo-elt (car temp-undo-list))
915 (cond ((integerp undo-elt)
916 (if (>= undo-elt position)
917 (setcar temp-undo-list (- undo-elt offset))))
918 ((atom undo-elt) nil)
919 ((stringp (car undo-elt))
920 ;; (TEXT . POSITION)
921 (let ((text-pos (abs (cdr undo-elt)))
922 (point-at-end (< (cdr undo-elt) 0 )))
923 (if (>= text-pos position)
924 (setcdr undo-elt (* (if point-at-end -1 1)
925 (- text-pos offset))))))
926 ((integerp (car undo-elt))
927 ;; (BEGIN . END)
928 (when (>= (car undo-elt) position)
929 (setcar undo-elt (- (car undo-elt) offset))
930 (setcdr undo-elt (- (cdr undo-elt) offset))))
931 ((null (car undo-elt))
932 ;; (nil PROPERTY VALUE BEG . END)
933 (let ((tail (nthcdr 3 undo-elt)))
934 (when (>= (car tail) position)
935 (setcar tail (- (car tail) offset))
936 (setcdr tail (- (cdr tail) offset))))))
937 (setq temp-undo-list (cdr temp-undo-list))))))))
938 (setq undo-list-copy (cdr undo-list-copy)))
939 (nreverse undo-list)))
940
941(defun undo-elt-in-region (undo-elt start end)
942 "Determine whether UNDO-ELT falls inside the region START ... END.
943If it crosses the edge, we return nil."
944 (cond ((integerp undo-elt)
945 (and (>= undo-elt start)
946 (< undo-elt end)))
947 ((eq undo-elt nil)
948 t)
949 ((atom undo-elt)
950 nil)
951 ((stringp (car undo-elt))
952 ;; (TEXT . POSITION)
953 (and (>= (abs (cdr undo-elt)) start)
954 (< (abs (cdr undo-elt)) end)))
955 ((and (consp undo-elt) (markerp (car undo-elt)))
956 ;; This is a marker-adjustment element (MARKER . ADJUSTMENT).
957 ;; See if MARKER is inside the region.
958 (let ((alist-elt (assq (car undo-elt) undo-adjusted-markers)))
959 (unless alist-elt
960 (setq alist-elt (cons (car undo-elt)
961 (marker-position (car undo-elt))))
962 (setq undo-adjusted-markers
963 (cons alist-elt undo-adjusted-markers)))
964 (and (cdr alist-elt)
965 (>= (cdr alist-elt) start)
966 (< (cdr alist-elt) end))))
967 ((null (car undo-elt))
968 ;; (nil PROPERTY VALUE BEG . END)
969 (let ((tail (nthcdr 3 undo-elt)))
970 (and (>= (car tail) start)
971 (< (cdr tail) end))))
972 ((integerp (car undo-elt))
973 ;; (BEGIN . END)
974 (and (>= (car undo-elt) start)
975 (< (cdr undo-elt) end)))))
976
977(defun undo-elt-crosses-region (undo-elt start end)
978 "Test whether UNDO-ELT crosses one edge of that region START ... END.
979This assumes we have already decided that UNDO-ELT
980is not *inside* the region START...END."
981 (cond ((atom undo-elt) nil)
982 ((null (car undo-elt))
983 ;; (nil PROPERTY VALUE BEG . END)
984 (let ((tail (nthcdr 3 undo-elt)))
985 (not (or (< (car tail) end)
986 (> (cdr tail) start)))))
987 ((integerp (car undo-elt))
988 ;; (BEGIN . END)
989 (not (or (< (car undo-elt) end)
990 (> (cdr undo-elt) start))))))
991
992;; Return the first affected buffer position and the delta for an undo element
993;; delta is defined as the change in subsequent buffer positions if we *did*
994;; the undo.
995(defun undo-delta (undo-elt)
996 (if (consp undo-elt)
997 (cond ((stringp (car undo-elt))
998 ;; (TEXT . POSITION)
999 (cons (abs (cdr undo-elt)) (length (car undo-elt))))
1000 ((integerp (car undo-elt))
1001 ;; (BEGIN . END)
1002 (cons (car undo-elt) (- (car undo-elt) (cdr undo-elt))))
1003 (t
1004 '(0 . 0)))
1005 '(0 . 0)))
1006
846(defvar shell-command-history nil 1007(defvar shell-command-history nil
847 "History list for some commands that read shell commands.") 1008 "History list for some commands that read shell commands.")
848 1009
@@ -934,7 +1095,7 @@ In either case, the output is inserted after point (leaving mark after it)."
934 )) 1095 ))
935 (shell-command-on-region (point) (point) command output-buffer) 1096 (shell-command-on-region (point) (point) command output-buffer)
936 )))))) 1097 ))))))
937 1098
938;; We have a sentinel to prevent insertion of a termination message 1099;; We have a sentinel to prevent insertion of a termination message
939;; in the buffer itself. 1100;; in the buffer itself.
940(defun shell-command-sentinel (process signal) 1101(defun shell-command-sentinel (process signal)
@@ -1072,7 +1233,7 @@ If it is nil, error output is mingled with regular output."
1072 (if (and error-file (file-exists-p error-file)) 1233 (if (and error-file (file-exists-p error-file))
1073 (save-excursion 1234 (save-excursion
1074 (set-buffer (get-buffer-create error-buffer)) 1235 (set-buffer (get-buffer-create error-buffer))
1075 ;; Do no formatting while reading error file, for fear of looping. 1236 ;; Do no formatting while reading error file, for fear of looping.
1076 (format-insert-file error-file nil) 1237 (format-insert-file error-file nil)
1077 (delete-file error-file))))) 1238 (delete-file error-file)))))
1078 1239