(verify_overlay_modification): GCPRO tail and overlay.
[bpt/emacs.git] / src / undo.c
CommitLineData
c6953be1 1/* undo handling for GNU Emacs.
3a22ee35 2 Copyright (C) 1990, 1993, 1994 Free Software Foundation, Inc.
c6953be1
JB
3
4This file is part of GNU Emacs.
5
6GNU Emacs is distributed in the hope that it will be useful,
7but WITHOUT ANY WARRANTY. No author or distributor
8accepts responsibility to anyone for the consequences of using it
9or for whether it serves any particular purpose or works at all,
10unless he says so in writing. Refer to the GNU Emacs General Public
11License for full details.
12
13Everyone is granted permission to copy, modify and redistribute
14GNU Emacs, but only under the conditions described in the
15GNU Emacs General Public License. A copy of this license is
16supposed to have been given to you along with GNU Emacs so you
17can know your rights and responsibilities. It should be in a
18file named COPYING. Among other things, the copyright notice
19and this notice must be preserved on all copies. */
20
21
18160b98 22#include <config.h>
c6953be1
JB
23#include "lisp.h"
24#include "buffer.h"
4e665715 25#include "commands.h"
c6953be1
JB
26
27/* Last buffer for which undo information was recorded. */
28Lisp_Object last_undo_buffer;
29
f87a68b3
RS
30Lisp_Object Qinhibit_read_only;
31
c58632fc
RS
32/* The first time a command records something for undo.
33 it also allocates the undo-boundary object
34 which will be added to the list at the end of the command.
35 This ensures we can't run out of space while trying to make
36 an undo-boundary. */
37Lisp_Object pending_boundary;
38
c6953be1
JB
39/* Record an insertion that just happened or is about to happen,
40 for LENGTH characters at position BEG.
41 (It is possible to record an insertion before or after the fact
42 because we don't need to record the contents.) */
43
44record_insert (beg, length)
45 Lisp_Object beg, length;
46{
47 Lisp_Object lbeg, lend;
48
bdbe6f28
RS
49 if (EQ (current_buffer->undo_list, Qt))
50 return;
51
c58632fc
RS
52 /* Allocate a cons cell to be the undo boundary after this command. */
53 if (NILP (pending_boundary))
54 pending_boundary = Fcons (Qnil, Qnil);
55
c6953be1
JB
56 if (current_buffer != XBUFFER (last_undo_buffer))
57 Fundo_boundary ();
58 XSET (last_undo_buffer, Lisp_Buffer, current_buffer);
59
c6953be1
JB
60 if (MODIFF <= current_buffer->save_modified)
61 record_first_change ();
62
63 /* If this is following another insertion and consecutive with it
64 in the buffer, combine the two. */
65 if (XTYPE (current_buffer->undo_list) == Lisp_Cons)
66 {
67 Lisp_Object elt;
68 elt = XCONS (current_buffer->undo_list)->car;
69 if (XTYPE (elt) == Lisp_Cons
70 && XTYPE (XCONS (elt)->car) == Lisp_Int
71 && XTYPE (XCONS (elt)->cdr) == Lisp_Int
213861c7 72 && XINT (XCONS (elt)->cdr) == XINT (beg))
c6953be1 73 {
213861c7 74 XSETINT (XCONS (elt)->cdr, XINT (beg) + XINT (length));
c6953be1
JB
75 return;
76 }
77 }
78
213861c7
JB
79 lbeg = beg;
80 XSET (lend, Lisp_Int, XINT (beg) + XINT (length));
81 current_buffer->undo_list = Fcons (Fcons (lbeg, lend),
82 current_buffer->undo_list);
c6953be1
JB
83}
84
85/* Record that a deletion is about to take place,
86 for LENGTH characters at location BEG. */
87
88record_delete (beg, length)
89 int beg, length;
90{
91 Lisp_Object lbeg, lend, sbeg;
e7a8b791 92 int at_boundary;
c6953be1 93
bdbe6f28
RS
94 if (EQ (current_buffer->undo_list, Qt))
95 return;
96
c58632fc
RS
97 /* Allocate a cons cell to be the undo boundary after this command. */
98 if (NILP (pending_boundary))
99 pending_boundary = Fcons (Qnil, Qnil);
100
c6953be1
JB
101 if (current_buffer != XBUFFER (last_undo_buffer))
102 Fundo_boundary ();
103 XSET (last_undo_buffer, Lisp_Buffer, current_buffer);
104
e7a8b791
RS
105 at_boundary = (CONSP (current_buffer->undo_list)
106 && NILP (XCONS (current_buffer->undo_list)->car));
107
c6953be1
JB
108 if (MODIFF <= current_buffer->save_modified)
109 record_first_change ();
110
111 if (point == beg + length)
112 XSET (sbeg, Lisp_Int, -beg);
113 else
114 XFASTINT (sbeg) = beg;
115 XFASTINT (lbeg) = beg;
116 XFASTINT (lend) = beg + length;
350bce56 117
e7a8b791
RS
118 /* If we are just after an undo boundary, and
119 point wasn't at start of deleted range, record where it was. */
120 if (at_boundary
121 && last_point_position != XFASTINT (sbeg)
122 && current_buffer == XBUFFER (last_point_position_buffer))
350bce56 123 current_buffer->undo_list
4e665715 124 = Fcons (make_number (last_point_position), current_buffer->undo_list);
350bce56 125
c6953be1
JB
126 current_buffer->undo_list
127 = Fcons (Fcons (Fbuffer_substring (lbeg, lend), sbeg),
128 current_buffer->undo_list);
129}
130
131/* Record that a replacement is about to take place,
132 for LENGTH characters at location BEG.
133 The replacement does not change the number of characters. */
134
135record_change (beg, length)
136 int beg, length;
137{
138 record_delete (beg, length);
139 record_insert (beg, length);
140}
141\f
142/* Record that an unmodified buffer is about to be changed.
143 Record the file modification date so that when undoing this entry
144 we can tell whether it is obsolete because the file was saved again. */
145
146record_first_change ()
147{
148 Lisp_Object high, low;
0736cafe
RS
149
150 if (EQ (current_buffer->undo_list, Qt))
151 return;
152
153 if (current_buffer != XBUFFER (last_undo_buffer))
154 Fundo_boundary ();
155 XSET (last_undo_buffer, Lisp_Buffer, current_buffer);
156
c6953be1
JB
157 XFASTINT (high) = (current_buffer->modtime >> 16) & 0xffff;
158 XFASTINT (low) = current_buffer->modtime & 0xffff;
159 current_buffer->undo_list = Fcons (Fcons (Qt, Fcons (high, low)), current_buffer->undo_list);
160}
161
da9319d5
RS
162/* Record a change in property PROP (whose old value was VAL)
163 for LENGTH characters starting at position BEG in BUFFER. */
164
165record_property_change (beg, length, prop, value, buffer)
166 int beg, length;
167 Lisp_Object prop, value, buffer;
168{
169 Lisp_Object lbeg, lend, entry;
170 struct buffer *obuf = current_buffer;
171 int boundary = 0;
172
0736cafe 173 if (EQ (XBUFFER (buffer)->undo_list, Qt))
bdbe6f28
RS
174 return;
175
c58632fc
RS
176 /* Allocate a cons cell to be the undo boundary after this command. */
177 if (NILP (pending_boundary))
178 pending_boundary = Fcons (Qnil, Qnil);
179
da9319d5
RS
180 if (!EQ (buffer, last_undo_buffer))
181 boundary = 1;
182 last_undo_buffer = buffer;
183
da9319d5
RS
184 /* Switch temporarily to the buffer that was changed. */
185 current_buffer = XBUFFER (buffer);
186
187 if (boundary)
188 Fundo_boundary ();
189
190 if (MODIFF <= current_buffer->save_modified)
191 record_first_change ();
192
193 XSET (lbeg, Lisp_Int, beg);
194 XSET (lend, Lisp_Int, beg + length);
195 entry = Fcons (Qnil, Fcons (prop, Fcons (value, Fcons (lbeg, lend))));
196 current_buffer->undo_list = Fcons (entry, current_buffer->undo_list);
197
198 current_buffer = obuf;
199}
200
c6953be1
JB
201DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0,
202 "Mark a boundary between units of undo.\n\
203An undo command will stop at this point,\n\
204but another undo command will undo to the previous boundary.")
205 ()
206{
207 Lisp_Object tem;
208 if (EQ (current_buffer->undo_list, Qt))
209 return Qnil;
210 tem = Fcar (current_buffer->undo_list);
265a9e55 211 if (!NILP (tem))
c58632fc
RS
212 {
213 /* One way or another, cons nil onto the front of the undo list. */
214 if (!NILP (pending_boundary))
215 {
216 /* If we have preallocated the cons cell to use here,
217 use that one. */
218 XCONS (pending_boundary)->cdr = current_buffer->undo_list;
219 current_buffer->undo_list = pending_boundary;
220 pending_boundary = Qnil;
221 }
222 else
223 current_buffer->undo_list = Fcons (Qnil, current_buffer->undo_list);
224 }
c6953be1
JB
225 return Qnil;
226}
227
228/* At garbage collection time, make an undo list shorter at the end,
229 returning the truncated list.
230 MINSIZE and MAXSIZE are the limits on size allowed, as described below.
f06cd136
JB
231 In practice, these are the values of undo-limit and
232 undo-strong-limit. */
c6953be1
JB
233
234Lisp_Object
235truncate_undo_list (list, minsize, maxsize)
236 Lisp_Object list;
237 int minsize, maxsize;
238{
239 Lisp_Object prev, next, last_boundary;
240 int size_so_far = 0;
241
242 prev = Qnil;
243 next = list;
244 last_boundary = Qnil;
245
246 /* Always preserve at least the most recent undo record.
181a18b1
JB
247 If the first element is an undo boundary, skip past it.
248
249 Skip, skip, skip the undo, skip, skip, skip the undo,
07627b5d
JB
250 Skip, skip, skip the undo, skip to the undo bound'ry.
251 (Get it? "Skip to my Loo?") */
c6953be1 252 if (XTYPE (next) == Lisp_Cons
213861c7 253 && NILP (XCONS (next)->car))
c6953be1
JB
254 {
255 /* Add in the space occupied by this element and its chain link. */
256 size_so_far += sizeof (struct Lisp_Cons);
257
258 /* Advance to next element. */
259 prev = next;
260 next = XCONS (next)->cdr;
261 }
262 while (XTYPE (next) == Lisp_Cons
213861c7 263 && ! NILP (XCONS (next)->car))
c6953be1
JB
264 {
265 Lisp_Object elt;
266 elt = XCONS (next)->car;
267
268 /* Add in the space occupied by this element and its chain link. */
269 size_so_far += sizeof (struct Lisp_Cons);
270 if (XTYPE (elt) == Lisp_Cons)
271 {
272 size_so_far += sizeof (struct Lisp_Cons);
273 if (XTYPE (XCONS (elt)->car) == Lisp_String)
274 size_so_far += (sizeof (struct Lisp_String) - 1
275 + XSTRING (XCONS (elt)->car)->size);
276 }
277
278 /* Advance to next element. */
279 prev = next;
280 next = XCONS (next)->cdr;
281 }
282 if (XTYPE (next) == Lisp_Cons)
283 last_boundary = prev;
284
285 while (XTYPE (next) == Lisp_Cons)
286 {
287 Lisp_Object elt;
288 elt = XCONS (next)->car;
289
290 /* When we get to a boundary, decide whether to truncate
291 either before or after it. The lower threshold, MINSIZE,
292 tells us to truncate after it. If its size pushes past
293 the higher threshold MAXSIZE as well, we truncate before it. */
265a9e55 294 if (NILP (elt))
c6953be1
JB
295 {
296 if (size_so_far > maxsize)
297 break;
298 last_boundary = prev;
299 if (size_so_far > minsize)
300 break;
301 }
302
303 /* Add in the space occupied by this element and its chain link. */
304 size_so_far += sizeof (struct Lisp_Cons);
305 if (XTYPE (elt) == Lisp_Cons)
306 {
307 size_so_far += sizeof (struct Lisp_Cons);
308 if (XTYPE (XCONS (elt)->car) == Lisp_String)
309 size_so_far += (sizeof (struct Lisp_String) - 1
310 + XSTRING (XCONS (elt)->car)->size);
311 }
312
313 /* Advance to next element. */
314 prev = next;
315 next = XCONS (next)->cdr;
316 }
317
318 /* If we scanned the whole list, it is short enough; don't change it. */
265a9e55 319 if (NILP (next))
c6953be1
JB
320 return list;
321
322 /* Truncate at the boundary where we decided to truncate. */
265a9e55 323 if (!NILP (last_boundary))
c6953be1
JB
324 {
325 XCONS (last_boundary)->cdr = Qnil;
326 return list;
327 }
328 else
329 return Qnil;
330}
331\f
332DEFUN ("primitive-undo", Fprimitive_undo, Sprimitive_undo, 2, 2, 0,
333 "Undo N records from the front of the list LIST.\n\
334Return what remains of the list.")
063fb61f
RS
335 (n, list)
336 Lisp_Object n, list;
c6953be1 337{
f87a68b3 338 int count = specpdl_ptr - specpdl;
063fb61f 339 register int arg = XINT (n);
c6953be1
JB
340#if 0 /* This is a good feature, but would make undo-start
341 unable to do what is expected. */
342 Lisp_Object tem;
343
344 /* If the head of the list is a boundary, it is the boundary
345 preceding this command. Get rid of it and don't count it. */
346 tem = Fcar (list);
265a9e55 347 if (NILP (tem))
c6953be1
JB
348 list = Fcdr (list);
349#endif
350
f87a68b3
RS
351 /* Don't let read-only properties interfere with undo. */
352 if (NILP (current_buffer->read_only))
353 specbind (Qinhibit_read_only, Qt);
354
c6953be1
JB
355 while (arg > 0)
356 {
357 while (1)
358 {
350bce56 359 Lisp_Object next;
c6953be1
JB
360 next = Fcar (list);
361 list = Fcdr (list);
350bce56 362 /* Exit inner loop at undo boundary. */
265a9e55 363 if (NILP (next))
c6953be1 364 break;
350bce56
RS
365 /* Handle an integer by setting point to that value. */
366 if (XTYPE (next) == Lisp_Int)
367 SET_PT (clip_to_bounds (BEGV, XINT (next), ZV));
368 else if (XTYPE (next) == Lisp_Cons)
c6953be1 369 {
350bce56
RS
370 Lisp_Object car, cdr;
371
372 car = Fcar (next);
373 cdr = Fcdr (next);
374 if (EQ (car, Qt))
c6953be1 375 {
350bce56
RS
376 /* Element (t high . low) records previous modtime. */
377 Lisp_Object high, low;
378 int mod_time;
379
380 high = Fcar (cdr);
381 low = Fcdr (cdr);
d8552b2f 382 mod_time = (XFASTINT (high) << 16) + XFASTINT (low);
350bce56
RS
383 /* If this records an obsolete save
384 (not matching the actual disk file)
385 then don't mark unmodified. */
386 if (mod_time != current_buffer->modtime)
387 break;
e6dd6080 388#ifdef CLASH_DETECTION
350bce56 389 Funlock_buffer ();
e6dd6080 390#endif /* CLASH_DETECTION */
350bce56 391 Fset_buffer_modified_p (Qnil);
c6953be1 392 }
d8552b2f
RS
393#ifdef USE_TEXT_PROPERTIES
394 else if (EQ (car, Qnil))
da9319d5 395 {
d8552b2f 396 /* Element (nil prop val beg . end) is property change. */
da9319d5
RS
397 Lisp_Object beg, end, prop, val;
398
399 prop = Fcar (cdr);
400 cdr = Fcdr (cdr);
401 val = Fcar (cdr);
402 cdr = Fcdr (cdr);
403 beg = Fcar (cdr);
404 end = Fcdr (cdr);
405
406 Fput_text_property (beg, end, prop, val, Qnil);
407 }
d8552b2f 408#endif /* USE_TEXT_PROPERTIES */
350bce56 409 else if (XTYPE (car) == Lisp_Int && XTYPE (cdr) == Lisp_Int)
c6953be1 410 {
350bce56
RS
411 /* Element (BEG . END) means range was inserted. */
412 Lisp_Object end;
413
414 if (XINT (car) < BEGV
415 || XINT (cdr) > ZV)
c6953be1 416 error ("Changes to be undone are outside visible portion of buffer");
f28f04cc
RS
417 /* Set point first thing, so that undoing this undo
418 does not send point back to where it is now. */
350bce56 419 Fgoto_char (car);
f28f04cc 420 Fdelete_region (car, cdr);
350bce56
RS
421 }
422 else if (XTYPE (car) == Lisp_String && XTYPE (cdr) == Lisp_Int)
423 {
424 /* Element (STRING . POS) means STRING was deleted. */
425 Lisp_Object membuf;
426 int pos = XINT (cdr);
427
428 membuf = car;
429 if (pos < 0)
430 {
431 if (-pos < BEGV || -pos > ZV)
432 error ("Changes to be undone are outside visible portion of buffer");
433 SET_PT (-pos);
434 Finsert (1, &membuf);
435 }
436 else
437 {
438 if (pos < BEGV || pos > ZV)
439 error ("Changes to be undone are outside visible portion of buffer");
440 SET_PT (pos);
441
442 /* Insert before markers so that if the mark is
443 currently on the boundary of this deletion, it
444 ends up on the other side of the now-undeleted
445 text from point. Since undo doesn't even keep
446 track of the mark, this isn't really necessary,
447 but it may lead to better behavior in certain
448 situations. */
449 Finsert_before_markers (1, &membuf);
450 SET_PT (pos);
451 }
c6953be1
JB
452 }
453 }
454 }
455 arg--;
456 }
457
f87a68b3 458 return unbind_to (count, list);
c6953be1
JB
459}
460
461syms_of_undo ()
462{
f87a68b3
RS
463 Qinhibit_read_only = intern ("inhibit-read-only");
464 staticpro (&Qinhibit_read_only);
465
c58632fc
RS
466 pending_boundary = Qnil;
467 staticpro (&pending_boundary);
468
c6953be1
JB
469 defsubr (&Sprimitive_undo);
470 defsubr (&Sundo_boundary);
471}