|
|
1.1 root 1: /* undo handling for GNU Emacs.
2: Copyright (C) 1990 Free Software Foundation, Inc.
3:
4: This file is part of GNU Emacs.
5:
6: GNU Emacs is free software; you can redistribute it and/or modify
7: it under the terms of the GNU General Public License as published by
8: the Free Software Foundation; either version 1, or (at your option)
9: any later version.
10:
11: GNU Emacs is distributed in the hope that it will be useful,
12: but WITHOUT ANY WARRANTY; without even the implied warranty of
13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14: GNU General Public License for more details.
15:
16: You should have received a copy of the GNU General Public License
17: along with GNU Emacs; see the file COPYING. If not, write to
18: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19:
20:
21: #include "config.h"
22: #include "lisp.h"
23: #include "buffer.h"
24:
25: /* Last buffer for which undo information was recorded. */
26: Lisp_Object last_undo_buffer;
27:
28: /* Record an insertion that just happened or is about to happen,
29: for LENGTH characters at position BEG.
30: (It is possible to record an insertion before or after the fact
31: because we don't need to record the contents.) */
32:
33: record_insert (beg, length)
34: Lisp_Object beg, length;
35: {
36: Lisp_Object lbeg, lend;
37:
38: if (current_buffer != XBUFFER (last_undo_buffer))
39: Fundo_boundary ();
40: XSET (last_undo_buffer, Lisp_Buffer, current_buffer);
41:
42: if (EQ (current_buffer->undo_list, Qt))
43: return;
44: if (MODIFF <= current_buffer->save_modified)
45: record_first_change ();
46:
47: /* If this is following another insertion and consecutive with it
48: in the buffer, combine the two. */
49: if (XTYPE (current_buffer->undo_list) == Lisp_Cons)
50: {
51: Lisp_Object elt;
52: elt = XCONS (current_buffer->undo_list)->car;
53: if (XTYPE (elt) == Lisp_Cons
54: && XTYPE (XCONS (elt)->car) == Lisp_Int
55: && XTYPE (XCONS (elt)->cdr) == Lisp_Int
56: && XINT (XCONS (elt)->cdr) == beg)
57: {
58: XSETINT (XCONS (elt)->cdr, beg + length);
59: return;
60: }
61: }
62:
63: XFASTINT (lbeg) = beg;
64: XFASTINT (lend) = beg + length;
65: current_buffer->undo_list = Fcons (Fcons (lbeg, lend), current_buffer->undo_list);
66: }
67:
68: /* Record that a deletion is about to take place,
69: for LENGTH characters at location BEG. */
70:
71: record_delete (beg, length)
72: int beg, length;
73: {
74: Lisp_Object lbeg, llength, lend, sbeg;
75:
76: if (current_buffer != XBUFFER (last_undo_buffer))
77: Fundo_boundary ();
78: XSET (last_undo_buffer, Lisp_Buffer, current_buffer);
79:
80: if (EQ (current_buffer->undo_list, Qt))
81: return;
82: if (MODIFF <= current_buffer->save_modified)
83: record_first_change ();
84:
85: if (point == beg + length)
86: XSET (sbeg, Lisp_Int, -beg);
87: else
88: XFASTINT (sbeg) = beg;
89: XFASTINT (lbeg) = beg;
90: XFASTINT (llength) = length;
91: XFASTINT (lend) = beg + length;
92: current_buffer->undo_list = Fcons (Fcons (Fbuffer_substring (lbeg, lend), sbeg),
93: current_buffer->undo_list);
94: }
95:
96: /* Record that a replacement is about to take place,
97: for LENGTH characters at location BEG.
98: The replacement does not change the number of characters. */
99:
100: record_change (beg, length)
101: int beg, length;
102: {
103: record_delete (beg, length);
104: record_insert (beg, length);
105: }
106:
107: /* Record that an unmodified buffer is about to be changed.
108: Record the file modification date so that when undoing this entry
109: we can tell whether it is obsolete because the file was saved again. */
110:
111: record_first_change ()
112: {
113: Lisp_Object high, low;
114: XFASTINT (high) = (current_buffer->modtime >> 16) & 0xffff;
115: XFASTINT (low) = current_buffer->modtime & 0xffff;
116: current_buffer->undo_list = Fcons (Fcons (Qt, Fcons (high, low)), current_buffer->undo_list);
117: }
118:
119: DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0,
120: "Mark a boundary between units of undo.\n\
121: An undo command will stop at this point,\n\
122: but another undo command will undo to the previous boundary.")
123: ()
124: {
125: Lisp_Object tem;
126: if (EQ (current_buffer->undo_list, Qt))
127: return Qnil;
128: tem = Fcar (current_buffer->undo_list);
129: if (!NULL (tem))
130: current_buffer->undo_list = Fcons (Qnil, current_buffer->undo_list);
131: return Qnil;
132: }
133:
134: /* At garbage collection time, make an undo list shorter at the end,
135: returning the truncated list.
136: MINSIZE and MAXSIZE are the limits on size allowed, as described below.
137: In practice, these are the values of undo-threshold and
138: undo-high-threshold. */
139:
140: Lisp_Object
141: truncate_undo_list (list, minsize, maxsize)
142: Lisp_Object list;
143: int minsize, maxsize;
144: {
145: Lisp_Object prev, next, save_prev;
146: int size_so_far = 0;
147:
148: prev = Qnil;
149: next = list;
150: save_prev = Qnil;
151:
152: while (XTYPE (next) == Lisp_Cons)
153: {
154: Lisp_Object elt;
155: elt = XCONS (next)->car;
156:
157: /* When we get to a boundary, decide whether to truncate
158: either before or after it. The lower threshold, MINSIZE,
159: tells us to truncate after it. If its size pushes past
160: the higher threshold MAXSIZE as well, we truncate before it. */
161: if (NULL (elt))
162: {
163: if (size_so_far > maxsize)
164: break;
165: save_prev = prev;
166: if (size_so_far > minsize)
167: break;
168: }
169:
170: /* Add in the space occupied by this element and its chain link. */
171: size_so_far += 8;
172: if (XTYPE (elt) == Lisp_Cons)
173: {
174: size_so_far += 8;
175: if (XTYPE (XCONS (elt)->car) == Lisp_String)
176: size_so_far += 6 + XSTRING (XCONS (elt)->car)->size;
177: }
178:
179: /* Advance to next element. */
180: prev = next;
181: next = XCONS (next)->cdr;
182: }
183:
184: /* If we scanned the whole list, it is short enough; don't change it. */
185: if (NULL (next))
186: return list;
187:
188: /* Truncate at the boundary where we decided to truncate. */
189: if (!NULL (save_prev))
190: {
191: XCONS (save_prev)->cdr = Qnil;
192: return list;
193: }
194: else
195: return Qnil;
196: }
197:
198: DEFUN ("primitive-undo", Fprimitive_undo, Sprimitive_undo, 2, 2, 0,
199: "Undo N records from the front of the list LIST.\n\
200: Return what remains of the list.")
201: (count, list)
202: Lisp_Object count, list;
203: {
204: register int arg = XINT (count);
205: #if 0 /* This is a good feature, but would make undo-start
206: unable to do what is expected. */
207: Lisp_Object tem;
208:
209: /* If the head of the list is a boundary, it is the boundary
210: preceding this command. Get rid of it and don't count it. */
211: tem = Fcar (list);
212: if (NULL (tem))
213: list = Fcdr (list);
214: #endif
215:
216: while (arg > 0)
217: {
218: while (1)
219: {
220: Lisp_Object next, car, cdr;
221: next = Fcar (list);
222: list = Fcdr (list);
223: if (NULL (next))
224: break;
225: car = Fcar (next);
226: cdr = Fcdr (next);
227: if (EQ (car, Qt))
228: {
229: Lisp_Object high, low;
230: int mod_time;
231: high = Fcar (cdr);
232: low = Fcdr (cdr);
233: mod_time = (high << 16) + low;
234: /* If this records an obsolete save
235: (not matching the actual disk file)
236: then don't mark unmodified. */
237: if (mod_time != current_buffer->modtime)
238: break;
239: #ifdef CLASH_DETECTION
240: Funlock_buffer ();
241: #endif /* CLASH_DETECTION */
242: Fset_buffer_modified_p (Qnil);
243: }
244: else if (XTYPE (car) == Lisp_Int && XTYPE (cdr) == Lisp_Int)
245: {
246: Lisp_Object end;
247: if (XINT (car) < BEGV
248: || XINT (cdr) > ZV)
249: error ("Changes to be undone are outside visible portion of buffer");
250: Fdelete_region (car, cdr);
251: Fgoto_char (car);
252: }
253: else if (XTYPE (car) == Lisp_String && XTYPE (cdr) == Lisp_Int)
254: {
255: Lisp_Object membuf;
256: int pos = XINT (cdr);
257: membuf = car;
258: if (pos < 0)
259: {
260: if (-pos < BEGV || -pos > ZV)
261: error ("Changes to be undone are outside visible portion of buffer");
262: SET_PT (-pos);
263: Finsert (1, &membuf);
264: }
265: else
266: {
267: if (pos < BEGV || pos > ZV)
268: error ("Changes to be undone are outside visible portion of buffer");
269: SET_PT (pos);
270: /* The idea here is to leave mark after this text,
271: which will be the desirable thing if undoing C-w. */
272: Finsert_before_markers (1, &membuf);
273: SET_PT (pos);
274: }
275: }
276: }
277: arg--;
278: }
279:
280: return list;
281: }
282:
283: syms_of_undo ()
284: {
285: defsubr (&Sprimitive_undo);
286: defsubr (&Sundo_boundary);
287: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.