Update FSF's ddress in preamble
[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)
53480e99 45 int beg, length;
c6953be1
JB
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 ();
552bdbcf 58 XSETBUFFER (last_undo_buffer, current_buffer);
c6953be1 59
ad9cdce4 60 if (MODIFF <= SAVE_MODIFF)
c6953be1
JB
61 record_first_change ();
62
63 /* If this is following another insertion and consecutive with it
64 in the buffer, combine the two. */
38c0d37c 65 if (CONSP (current_buffer->undo_list))
c6953be1
JB
66 {
67 Lisp_Object elt;
68 elt = XCONS (current_buffer->undo_list)->car;
38c0d37c
KH
69 if (CONSP (elt)
70 && INTEGERP (XCONS (elt)->car)
71 && INTEGERP (XCONS (elt)->cdr)
53480e99 72 && XINT (XCONS (elt)->cdr) == beg)
c6953be1 73 {
53480e99 74 XSETINT (XCONS (elt)->cdr, beg + length);
c6953be1
JB
75 return;
76 }
77 }
78
53480e99
KH
79 XSETFASTINT (lbeg, beg);
80 XSETINT (lend, beg + length);
213861c7
JB
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 ();
552bdbcf 103 XSETBUFFER (last_undo_buffer, current_buffer);
c6953be1 104
e7a8b791
RS
105 at_boundary = (CONSP (current_buffer->undo_list)
106 && NILP (XCONS (current_buffer->undo_list)->car));
107
ad9cdce4 108 if (MODIFF <= SAVE_MODIFF)
c6953be1
JB
109 record_first_change ();
110
111 if (point == beg + length)
552bdbcf 112 XSETINT (sbeg, -beg);
c6953be1 113 else
28b2b116
KH
114 XSETFASTINT (sbeg, beg);
115 XSETFASTINT (lbeg, beg);
116 XSETFASTINT (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;
ad9cdce4 149 struct buffer *base_buffer = current_buffer;
0736cafe
RS
150
151 if (EQ (current_buffer->undo_list, Qt))
152 return;
153
154 if (current_buffer != XBUFFER (last_undo_buffer))
155 Fundo_boundary ();
552bdbcf 156 XSETBUFFER (last_undo_buffer, current_buffer);
0736cafe 157
ad9cdce4
RS
158 if (base_buffer->base_buffer)
159 base_buffer = base_buffer->base_buffer;
160
161 XSETFASTINT (high, (base_buffer->modtime >> 16) & 0xffff);
162 XSETFASTINT (low, base_buffer->modtime & 0xffff);
c6953be1
JB
163 current_buffer->undo_list = Fcons (Fcons (Qt, Fcons (high, low)), current_buffer->undo_list);
164}
165
da9319d5
RS
166/* Record a change in property PROP (whose old value was VAL)
167 for LENGTH characters starting at position BEG in BUFFER. */
168
169record_property_change (beg, length, prop, value, buffer)
170 int beg, length;
171 Lisp_Object prop, value, buffer;
172{
173 Lisp_Object lbeg, lend, entry;
174 struct buffer *obuf = current_buffer;
175 int boundary = 0;
176
0736cafe 177 if (EQ (XBUFFER (buffer)->undo_list, Qt))
bdbe6f28
RS
178 return;
179
c58632fc
RS
180 /* Allocate a cons cell to be the undo boundary after this command. */
181 if (NILP (pending_boundary))
182 pending_boundary = Fcons (Qnil, Qnil);
183
da9319d5
RS
184 if (!EQ (buffer, last_undo_buffer))
185 boundary = 1;
186 last_undo_buffer = buffer;
187
da9319d5
RS
188 /* Switch temporarily to the buffer that was changed. */
189 current_buffer = XBUFFER (buffer);
190
191 if (boundary)
192 Fundo_boundary ();
193
ad9cdce4 194 if (MODIFF <= SAVE_MODIFF)
da9319d5
RS
195 record_first_change ();
196
552bdbcf
KH
197 XSETINT (lbeg, beg);
198 XSETINT (lend, beg + length);
da9319d5
RS
199 entry = Fcons (Qnil, Fcons (prop, Fcons (value, Fcons (lbeg, lend))));
200 current_buffer->undo_list = Fcons (entry, current_buffer->undo_list);
201
202 current_buffer = obuf;
203}
204
c6953be1
JB
205DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0,
206 "Mark a boundary between units of undo.\n\
207An undo command will stop at this point,\n\
208but another undo command will undo to the previous boundary.")
209 ()
210{
211 Lisp_Object tem;
212 if (EQ (current_buffer->undo_list, Qt))
213 return Qnil;
214 tem = Fcar (current_buffer->undo_list);
265a9e55 215 if (!NILP (tem))
c58632fc
RS
216 {
217 /* One way or another, cons nil onto the front of the undo list. */
218 if (!NILP (pending_boundary))
219 {
220 /* If we have preallocated the cons cell to use here,
221 use that one. */
222 XCONS (pending_boundary)->cdr = current_buffer->undo_list;
223 current_buffer->undo_list = pending_boundary;
224 pending_boundary = Qnil;
225 }
226 else
227 current_buffer->undo_list = Fcons (Qnil, current_buffer->undo_list);
228 }
c6953be1
JB
229 return Qnil;
230}
231
232/* At garbage collection time, make an undo list shorter at the end,
233 returning the truncated list.
234 MINSIZE and MAXSIZE are the limits on size allowed, as described below.
f06cd136
JB
235 In practice, these are the values of undo-limit and
236 undo-strong-limit. */
c6953be1
JB
237
238Lisp_Object
239truncate_undo_list (list, minsize, maxsize)
240 Lisp_Object list;
241 int minsize, maxsize;
242{
243 Lisp_Object prev, next, last_boundary;
244 int size_so_far = 0;
245
246 prev = Qnil;
247 next = list;
248 last_boundary = Qnil;
249
250 /* Always preserve at least the most recent undo record.
181a18b1
JB
251 If the first element is an undo boundary, skip past it.
252
253 Skip, skip, skip the undo, skip, skip, skip the undo,
07627b5d
JB
254 Skip, skip, skip the undo, skip to the undo bound'ry.
255 (Get it? "Skip to my Loo?") */
38c0d37c 256 if (CONSP (next) && NILP (XCONS (next)->car))
c6953be1
JB
257 {
258 /* Add in the space occupied by this element and its chain link. */
259 size_so_far += sizeof (struct Lisp_Cons);
260
261 /* Advance to next element. */
262 prev = next;
263 next = XCONS (next)->cdr;
264 }
38c0d37c 265 while (CONSP (next) && ! NILP (XCONS (next)->car))
c6953be1
JB
266 {
267 Lisp_Object elt;
268 elt = XCONS (next)->car;
269
270 /* Add in the space occupied by this element and its chain link. */
271 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 272 if (CONSP (elt))
c6953be1
JB
273 {
274 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 275 if (STRINGP (XCONS (elt)->car))
c6953be1
JB
276 size_so_far += (sizeof (struct Lisp_String) - 1
277 + XSTRING (XCONS (elt)->car)->size);
278 }
279
280 /* Advance to next element. */
281 prev = next;
282 next = XCONS (next)->cdr;
283 }
38c0d37c 284 if (CONSP (next))
c6953be1
JB
285 last_boundary = prev;
286
38c0d37c 287 while (CONSP (next))
c6953be1
JB
288 {
289 Lisp_Object elt;
290 elt = XCONS (next)->car;
291
292 /* When we get to a boundary, decide whether to truncate
293 either before or after it. The lower threshold, MINSIZE,
294 tells us to truncate after it. If its size pushes past
295 the higher threshold MAXSIZE as well, we truncate before it. */
265a9e55 296 if (NILP (elt))
c6953be1
JB
297 {
298 if (size_so_far > maxsize)
299 break;
300 last_boundary = prev;
301 if (size_so_far > minsize)
302 break;
303 }
304
305 /* Add in the space occupied by this element and its chain link. */
306 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 307 if (CONSP (elt))
c6953be1
JB
308 {
309 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 310 if (STRINGP (XCONS (elt)->car))
c6953be1
JB
311 size_so_far += (sizeof (struct Lisp_String) - 1
312 + XSTRING (XCONS (elt)->car)->size);
313 }
314
315 /* Advance to next element. */
316 prev = next;
317 next = XCONS (next)->cdr;
318 }
319
320 /* If we scanned the whole list, it is short enough; don't change it. */
265a9e55 321 if (NILP (next))
c6953be1
JB
322 return list;
323
324 /* Truncate at the boundary where we decided to truncate. */
265a9e55 325 if (!NILP (last_boundary))
c6953be1
JB
326 {
327 XCONS (last_boundary)->cdr = Qnil;
328 return list;
329 }
330 else
331 return Qnil;
332}
333\f
334DEFUN ("primitive-undo", Fprimitive_undo, Sprimitive_undo, 2, 2, 0,
335 "Undo N records from the front of the list LIST.\n\
336Return what remains of the list.")
063fb61f
RS
337 (n, list)
338 Lisp_Object n, list;
c6953be1 339{
de65837b
KH
340 struct gcpro gcpro1, gcpro2;
341 Lisp_Object next;
f87a68b3 342 int count = specpdl_ptr - specpdl;
de65837b 343 register int arg;
c6953be1
JB
344#if 0 /* This is a good feature, but would make undo-start
345 unable to do what is expected. */
346 Lisp_Object tem;
347
348 /* If the head of the list is a boundary, it is the boundary
349 preceding this command. Get rid of it and don't count it. */
350 tem = Fcar (list);
265a9e55 351 if (NILP (tem))
c6953be1
JB
352 list = Fcdr (list);
353#endif
354
de65837b
KH
355 CHECK_NUMBER (n, 0);
356 arg = XINT (n);
357 next = Qnil;
358 GCPRO2 (next, list);
359
f87a68b3
RS
360 /* Don't let read-only properties interfere with undo. */
361 if (NILP (current_buffer->read_only))
362 specbind (Qinhibit_read_only, Qt);
363
c6953be1
JB
364 while (arg > 0)
365 {
366 while (1)
367 {
c6953be1
JB
368 next = Fcar (list);
369 list = Fcdr (list);
350bce56 370 /* Exit inner loop at undo boundary. */
265a9e55 371 if (NILP (next))
c6953be1 372 break;
350bce56 373 /* Handle an integer by setting point to that value. */
38c0d37c 374 if (INTEGERP (next))
350bce56 375 SET_PT (clip_to_bounds (BEGV, XINT (next), ZV));
38c0d37c 376 else if (CONSP (next))
c6953be1 377 {
350bce56
RS
378 Lisp_Object car, cdr;
379
380 car = Fcar (next);
381 cdr = Fcdr (next);
382 if (EQ (car, Qt))
c6953be1 383 {
350bce56
RS
384 /* Element (t high . low) records previous modtime. */
385 Lisp_Object high, low;
386 int mod_time;
ad9cdce4 387 struct buffer *base_buffer = current_buffer;
350bce56
RS
388
389 high = Fcar (cdr);
390 low = Fcdr (cdr);
d8552b2f 391 mod_time = (XFASTINT (high) << 16) + XFASTINT (low);
ad9cdce4
RS
392
393 if (current_buffer->base_buffer)
394 base_buffer = current_buffer->base_buffer;
395
350bce56
RS
396 /* If this records an obsolete save
397 (not matching the actual disk file)
398 then don't mark unmodified. */
ad9cdce4 399 if (mod_time != base_buffer->modtime)
103dcb38 400 continue;
e6dd6080 401#ifdef CLASH_DETECTION
350bce56 402 Funlock_buffer ();
e6dd6080 403#endif /* CLASH_DETECTION */
350bce56 404 Fset_buffer_modified_p (Qnil);
c6953be1 405 }
d8552b2f
RS
406#ifdef USE_TEXT_PROPERTIES
407 else if (EQ (car, Qnil))
da9319d5 408 {
d8552b2f 409 /* Element (nil prop val beg . end) is property change. */
da9319d5
RS
410 Lisp_Object beg, end, prop, val;
411
412 prop = Fcar (cdr);
413 cdr = Fcdr (cdr);
414 val = Fcar (cdr);
415 cdr = Fcdr (cdr);
416 beg = Fcar (cdr);
417 end = Fcdr (cdr);
418
419 Fput_text_property (beg, end, prop, val, Qnil);
420 }
d8552b2f 421#endif /* USE_TEXT_PROPERTIES */
38c0d37c 422 else if (INTEGERP (car) && INTEGERP (cdr))
c6953be1 423 {
350bce56
RS
424 /* Element (BEG . END) means range was inserted. */
425 Lisp_Object end;
426
427 if (XINT (car) < BEGV
428 || XINT (cdr) > ZV)
c6953be1 429 error ("Changes to be undone are outside visible portion of buffer");
f28f04cc
RS
430 /* Set point first thing, so that undoing this undo
431 does not send point back to where it is now. */
350bce56 432 Fgoto_char (car);
f28f04cc 433 Fdelete_region (car, cdr);
350bce56 434 }
38c0d37c 435 else if (STRINGP (car) && INTEGERP (cdr))
350bce56
RS
436 {
437 /* Element (STRING . POS) means STRING was deleted. */
438 Lisp_Object membuf;
439 int pos = XINT (cdr);
440
441 membuf = car;
442 if (pos < 0)
443 {
444 if (-pos < BEGV || -pos > ZV)
445 error ("Changes to be undone are outside visible portion of buffer");
446 SET_PT (-pos);
447 Finsert (1, &membuf);
448 }
449 else
450 {
451 if (pos < BEGV || pos > ZV)
452 error ("Changes to be undone are outside visible portion of buffer");
453 SET_PT (pos);
454
455 /* Insert before markers so that if the mark is
456 currently on the boundary of this deletion, it
457 ends up on the other side of the now-undeleted
458 text from point. Since undo doesn't even keep
459 track of the mark, this isn't really necessary,
460 but it may lead to better behavior in certain
461 situations. */
462 Finsert_before_markers (1, &membuf);
463 SET_PT (pos);
464 }
c6953be1
JB
465 }
466 }
467 }
468 arg--;
469 }
470
de65837b 471 UNGCPRO;
f87a68b3 472 return unbind_to (count, list);
c6953be1
JB
473}
474
475syms_of_undo ()
476{
f87a68b3
RS
477 Qinhibit_read_only = intern ("inhibit-read-only");
478 staticpro (&Qinhibit_read_only);
479
c58632fc
RS
480 pending_boundary = Qnil;
481 staticpro (&pending_boundary);
482
c6953be1
JB
483 defsubr (&Sprimitive_undo);
484 defsubr (&Sundo_boundary);
485}