diff options
| author | Richard M. Stallman | 1998-03-14 08:19:27 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1998-03-14 08:19:27 +0000 |
| commit | 65627aad683a648e510b5dd204f6b18086b99054 (patch) | |
| tree | 449de265edbd78bf15fb901e2c8b52d396f93df5 | |
| parent | 5cfee3ac9a36df3b974c77ad03710702127d82cf (diff) | |
| download | emacs-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.el | 187 |
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. |
| 799 | Repeat this command to undo more changes. | 799 | Repeat this command to undo more changes. |
| 800 | A numeric argument serves as a repeat count." | 800 | A numeric argument serves as a repeat count. |
| 801 | (interactive "*p") | 801 | |
| 802 | Just C-u as argument requests selective undo, | ||
| 803 | limited to changes within the current region. | ||
| 804 | Likewise 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. | ||
| 833 | The 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. |
| 840 | Call `undo-start' to get ready to undo recent changes, | 839 | Call `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. | ||
| 857 | The next call to `undo-more' will undo the most recently made change. | ||
| 858 | If BEG and END are specified, then only undo elements | ||
| 859 | that apply to text between BEG and END are used; other undo elements | ||
| 860 | are 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. | ||
| 872 | The elements come from `buffer-undo-list', but we keep only | ||
| 873 | the elements inside this region, and discard those outside this region. | ||
| 874 | If we find an element that crosses an edge of this region, | ||
| 875 | we 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. | ||
| 943 | If 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. | ||
| 979 | This assumes we have already decided that UNDO-ELT | ||
| 980 | is 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 | ||