(fix_submap_inheritance, get_keyelt, store_in_keymap,
[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
3b7ad313
EN
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
c6953be1 11GNU Emacs is distributed in the hope that it will be useful,
3b7ad313
EN
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
c6953be1
JB
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
90dd3e4f 44void
c6953be1 45record_insert (beg, length)
53480e99 46 int beg, length;
c6953be1
JB
47{
48 Lisp_Object lbeg, lend;
49
bdbe6f28
RS
50 if (EQ (current_buffer->undo_list, Qt))
51 return;
52
c58632fc
RS
53 /* Allocate a cons cell to be the undo boundary after this command. */
54 if (NILP (pending_boundary))
55 pending_boundary = Fcons (Qnil, Qnil);
56
c6953be1
JB
57 if (current_buffer != XBUFFER (last_undo_buffer))
58 Fundo_boundary ();
552bdbcf 59 XSETBUFFER (last_undo_buffer, current_buffer);
c6953be1 60
ad9cdce4 61 if (MODIFF <= SAVE_MODIFF)
c6953be1
JB
62 record_first_change ();
63
64 /* If this is following another insertion and consecutive with it
65 in the buffer, combine the two. */
38c0d37c 66 if (CONSP (current_buffer->undo_list))
c6953be1
JB
67 {
68 Lisp_Object elt;
69 elt = XCONS (current_buffer->undo_list)->car;
38c0d37c
KH
70 if (CONSP (elt)
71 && INTEGERP (XCONS (elt)->car)
72 && INTEGERP (XCONS (elt)->cdr)
53480e99 73 && XINT (XCONS (elt)->cdr) == beg)
c6953be1 74 {
53480e99 75 XSETINT (XCONS (elt)->cdr, beg + length);
c6953be1
JB
76 return;
77 }
78 }
79
53480e99
KH
80 XSETFASTINT (lbeg, beg);
81 XSETINT (lend, beg + length);
213861c7
JB
82 current_buffer->undo_list = Fcons (Fcons (lbeg, lend),
83 current_buffer->undo_list);
c6953be1
JB
84}
85
86/* Record that a deletion is about to take place,
e928d437 87 of the characters in STRING, at location BEG. */
c6953be1 88
ff1aa840 89void
e928d437
RS
90record_delete (beg, string)
91 int beg;
92 Lisp_Object string;
c6953be1 93{
e928d437 94 Lisp_Object sbeg;
e7a8b791 95 int at_boundary;
c6953be1 96
bdbe6f28
RS
97 if (EQ (current_buffer->undo_list, Qt))
98 return;
99
c58632fc
RS
100 /* Allocate a cons cell to be the undo boundary after this command. */
101 if (NILP (pending_boundary))
102 pending_boundary = Fcons (Qnil, Qnil);
103
c6953be1
JB
104 if (current_buffer != XBUFFER (last_undo_buffer))
105 Fundo_boundary ();
552bdbcf 106 XSETBUFFER (last_undo_buffer, current_buffer);
c6953be1 107
e7a8b791
RS
108 at_boundary = (CONSP (current_buffer->undo_list)
109 && NILP (XCONS (current_buffer->undo_list)->car));
110
ad9cdce4 111 if (MODIFF <= SAVE_MODIFF)
c6953be1
JB
112 record_first_change ();
113
e928d437 114 if (PT == beg + XSTRING (string)->size)
552bdbcf 115 XSETINT (sbeg, -beg);
c6953be1 116 else
28b2b116 117 XSETFASTINT (sbeg, beg);
350bce56 118
e7a8b791
RS
119 /* If we are just after an undo boundary, and
120 point wasn't at start of deleted range, record where it was. */
121 if (at_boundary
122 && last_point_position != XFASTINT (sbeg)
123 && current_buffer == XBUFFER (last_point_position_buffer))
350bce56 124 current_buffer->undo_list
4e665715 125 = Fcons (make_number (last_point_position), current_buffer->undo_list);
350bce56 126
c6953be1 127 current_buffer->undo_list
e928d437 128 = Fcons (Fcons (string, sbeg), current_buffer->undo_list);
c6953be1
JB
129}
130
714bced9
RS
131/* Record the fact that MARKER is about to be adjusted by ADJUSTMENT.
132 This is done only when a marker points within text being deleted,
133 because that's the only case where an automatic marker adjustment
134 won't be inverted automatically by undoing the buffer modification. */
135
ff1aa840 136void
714bced9
RS
137record_marker_adjustment (marker, adjustment)
138 Lisp_Object marker;
139 int adjustment;
140{
141 if (EQ (current_buffer->undo_list, Qt))
142 return;
143
144 /* Allocate a cons cell to be the undo boundary after this command. */
145 if (NILP (pending_boundary))
146 pending_boundary = Fcons (Qnil, Qnil);
147
148 if (current_buffer != XBUFFER (last_undo_buffer))
149 Fundo_boundary ();
150 XSETBUFFER (last_undo_buffer, current_buffer);
151
152 current_buffer->undo_list
153 = Fcons (Fcons (marker, make_number (adjustment)),
154 current_buffer->undo_list);
155}
156
c6953be1
JB
157/* Record that a replacement is about to take place,
158 for LENGTH characters at location BEG.
e928d437 159 The replacement must not change the number of characters. */
c6953be1 160
ff1aa840 161void
c6953be1
JB
162record_change (beg, length)
163 int beg, length;
164{
e928d437 165 record_delete (beg, make_buffer_string (beg, beg + length, 1));
c6953be1
JB
166 record_insert (beg, length);
167}
168\f
169/* Record that an unmodified buffer is about to be changed.
170 Record the file modification date so that when undoing this entry
171 we can tell whether it is obsolete because the file was saved again. */
172
90dd3e4f 173void
c6953be1
JB
174record_first_change ()
175{
176 Lisp_Object high, low;
ad9cdce4 177 struct buffer *base_buffer = current_buffer;
0736cafe
RS
178
179 if (EQ (current_buffer->undo_list, Qt))
180 return;
181
182 if (current_buffer != XBUFFER (last_undo_buffer))
183 Fundo_boundary ();
552bdbcf 184 XSETBUFFER (last_undo_buffer, current_buffer);
0736cafe 185
ad9cdce4
RS
186 if (base_buffer->base_buffer)
187 base_buffer = base_buffer->base_buffer;
188
189 XSETFASTINT (high, (base_buffer->modtime >> 16) & 0xffff);
190 XSETFASTINT (low, base_buffer->modtime & 0xffff);
c6953be1
JB
191 current_buffer->undo_list = Fcons (Fcons (Qt, Fcons (high, low)), current_buffer->undo_list);
192}
193
da9319d5
RS
194/* Record a change in property PROP (whose old value was VAL)
195 for LENGTH characters starting at position BEG in BUFFER. */
196
90dd3e4f 197void
da9319d5
RS
198record_property_change (beg, length, prop, value, buffer)
199 int beg, length;
200 Lisp_Object prop, value, buffer;
201{
202 Lisp_Object lbeg, lend, entry;
203 struct buffer *obuf = current_buffer;
204 int boundary = 0;
205
0736cafe 206 if (EQ (XBUFFER (buffer)->undo_list, Qt))
bdbe6f28
RS
207 return;
208
c58632fc
RS
209 /* Allocate a cons cell to be the undo boundary after this command. */
210 if (NILP (pending_boundary))
211 pending_boundary = Fcons (Qnil, Qnil);
212
da9319d5
RS
213 if (!EQ (buffer, last_undo_buffer))
214 boundary = 1;
215 last_undo_buffer = buffer;
216
da9319d5
RS
217 /* Switch temporarily to the buffer that was changed. */
218 current_buffer = XBUFFER (buffer);
219
220 if (boundary)
221 Fundo_boundary ();
222
ad9cdce4 223 if (MODIFF <= SAVE_MODIFF)
da9319d5
RS
224 record_first_change ();
225
552bdbcf
KH
226 XSETINT (lbeg, beg);
227 XSETINT (lend, beg + length);
da9319d5
RS
228 entry = Fcons (Qnil, Fcons (prop, Fcons (value, Fcons (lbeg, lend))));
229 current_buffer->undo_list = Fcons (entry, current_buffer->undo_list);
230
231 current_buffer = obuf;
232}
233
c6953be1
JB
234DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0,
235 "Mark a boundary between units of undo.\n\
236An undo command will stop at this point,\n\
237but another undo command will undo to the previous boundary.")
238 ()
239{
240 Lisp_Object tem;
241 if (EQ (current_buffer->undo_list, Qt))
242 return Qnil;
243 tem = Fcar (current_buffer->undo_list);
265a9e55 244 if (!NILP (tem))
c58632fc
RS
245 {
246 /* One way or another, cons nil onto the front of the undo list. */
247 if (!NILP (pending_boundary))
248 {
249 /* If we have preallocated the cons cell to use here,
250 use that one. */
251 XCONS (pending_boundary)->cdr = current_buffer->undo_list;
252 current_buffer->undo_list = pending_boundary;
253 pending_boundary = Qnil;
254 }
255 else
256 current_buffer->undo_list = Fcons (Qnil, current_buffer->undo_list);
257 }
c6953be1
JB
258 return Qnil;
259}
260
261/* At garbage collection time, make an undo list shorter at the end,
262 returning the truncated list.
263 MINSIZE and MAXSIZE are the limits on size allowed, as described below.
f06cd136
JB
264 In practice, these are the values of undo-limit and
265 undo-strong-limit. */
c6953be1
JB
266
267Lisp_Object
268truncate_undo_list (list, minsize, maxsize)
269 Lisp_Object list;
270 int minsize, maxsize;
271{
272 Lisp_Object prev, next, last_boundary;
273 int size_so_far = 0;
274
275 prev = Qnil;
276 next = list;
277 last_boundary = Qnil;
278
279 /* Always preserve at least the most recent undo record.
181a18b1
JB
280 If the first element is an undo boundary, skip past it.
281
282 Skip, skip, skip the undo, skip, skip, skip the undo,
07627b5d
JB
283 Skip, skip, skip the undo, skip to the undo bound'ry.
284 (Get it? "Skip to my Loo?") */
38c0d37c 285 if (CONSP (next) && NILP (XCONS (next)->car))
c6953be1
JB
286 {
287 /* Add in the space occupied by this element and its chain link. */
288 size_so_far += sizeof (struct Lisp_Cons);
289
290 /* Advance to next element. */
291 prev = next;
292 next = XCONS (next)->cdr;
293 }
38c0d37c 294 while (CONSP (next) && ! NILP (XCONS (next)->car))
c6953be1
JB
295 {
296 Lisp_Object elt;
297 elt = XCONS (next)->car;
298
299 /* Add in the space occupied by this element and its chain link. */
300 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 301 if (CONSP (elt))
c6953be1
JB
302 {
303 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 304 if (STRINGP (XCONS (elt)->car))
c6953be1
JB
305 size_so_far += (sizeof (struct Lisp_String) - 1
306 + XSTRING (XCONS (elt)->car)->size);
307 }
308
309 /* Advance to next element. */
310 prev = next;
311 next = XCONS (next)->cdr;
312 }
38c0d37c 313 if (CONSP (next))
c6953be1
JB
314 last_boundary = prev;
315
38c0d37c 316 while (CONSP (next))
c6953be1
JB
317 {
318 Lisp_Object elt;
319 elt = XCONS (next)->car;
320
321 /* When we get to a boundary, decide whether to truncate
322 either before or after it. The lower threshold, MINSIZE,
323 tells us to truncate after it. If its size pushes past
324 the higher threshold MAXSIZE as well, we truncate before it. */
265a9e55 325 if (NILP (elt))
c6953be1
JB
326 {
327 if (size_so_far > maxsize)
328 break;
329 last_boundary = prev;
330 if (size_so_far > minsize)
331 break;
332 }
333
334 /* Add in the space occupied by this element and its chain link. */
335 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 336 if (CONSP (elt))
c6953be1
JB
337 {
338 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 339 if (STRINGP (XCONS (elt)->car))
c6953be1
JB
340 size_so_far += (sizeof (struct Lisp_String) - 1
341 + XSTRING (XCONS (elt)->car)->size);
342 }
343
344 /* Advance to next element. */
345 prev = next;
346 next = XCONS (next)->cdr;
347 }
348
349 /* If we scanned the whole list, it is short enough; don't change it. */
265a9e55 350 if (NILP (next))
c6953be1
JB
351 return list;
352
353 /* Truncate at the boundary where we decided to truncate. */
265a9e55 354 if (!NILP (last_boundary))
c6953be1
JB
355 {
356 XCONS (last_boundary)->cdr = Qnil;
357 return list;
358 }
359 else
360 return Qnil;
361}
362\f
363DEFUN ("primitive-undo", Fprimitive_undo, Sprimitive_undo, 2, 2, 0,
364 "Undo N records from the front of the list LIST.\n\
365Return what remains of the list.")
063fb61f
RS
366 (n, list)
367 Lisp_Object n, list;
c6953be1 368{
de65837b
KH
369 struct gcpro gcpro1, gcpro2;
370 Lisp_Object next;
f87a68b3 371 int count = specpdl_ptr - specpdl;
de65837b 372 register int arg;
c6953be1
JB
373#if 0 /* This is a good feature, but would make undo-start
374 unable to do what is expected. */
375 Lisp_Object tem;
376
377 /* If the head of the list is a boundary, it is the boundary
378 preceding this command. Get rid of it and don't count it. */
379 tem = Fcar (list);
265a9e55 380 if (NILP (tem))
c6953be1
JB
381 list = Fcdr (list);
382#endif
383
de65837b
KH
384 CHECK_NUMBER (n, 0);
385 arg = XINT (n);
386 next = Qnil;
387 GCPRO2 (next, list);
388
f87a68b3
RS
389 /* Don't let read-only properties interfere with undo. */
390 if (NILP (current_buffer->read_only))
391 specbind (Qinhibit_read_only, Qt);
392
c6953be1
JB
393 while (arg > 0)
394 {
395 while (1)
396 {
c6953be1
JB
397 next = Fcar (list);
398 list = Fcdr (list);
350bce56 399 /* Exit inner loop at undo boundary. */
265a9e55 400 if (NILP (next))
c6953be1 401 break;
350bce56 402 /* Handle an integer by setting point to that value. */
38c0d37c 403 if (INTEGERP (next))
350bce56 404 SET_PT (clip_to_bounds (BEGV, XINT (next), ZV));
38c0d37c 405 else if (CONSP (next))
c6953be1 406 {
350bce56
RS
407 Lisp_Object car, cdr;
408
409 car = Fcar (next);
410 cdr = Fcdr (next);
411 if (EQ (car, Qt))
c6953be1 412 {
350bce56
RS
413 /* Element (t high . low) records previous modtime. */
414 Lisp_Object high, low;
415 int mod_time;
ad9cdce4 416 struct buffer *base_buffer = current_buffer;
350bce56
RS
417
418 high = Fcar (cdr);
419 low = Fcdr (cdr);
d8552b2f 420 mod_time = (XFASTINT (high) << 16) + XFASTINT (low);
ad9cdce4
RS
421
422 if (current_buffer->base_buffer)
423 base_buffer = current_buffer->base_buffer;
424
350bce56
RS
425 /* If this records an obsolete save
426 (not matching the actual disk file)
427 then don't mark unmodified. */
ad9cdce4 428 if (mod_time != base_buffer->modtime)
103dcb38 429 continue;
e6dd6080 430#ifdef CLASH_DETECTION
350bce56 431 Funlock_buffer ();
e6dd6080 432#endif /* CLASH_DETECTION */
350bce56 433 Fset_buffer_modified_p (Qnil);
c6953be1 434 }
d8552b2f
RS
435#ifdef USE_TEXT_PROPERTIES
436 else if (EQ (car, Qnil))
da9319d5 437 {
d8552b2f 438 /* Element (nil prop val beg . end) is property change. */
da9319d5
RS
439 Lisp_Object beg, end, prop, val;
440
441 prop = Fcar (cdr);
442 cdr = Fcdr (cdr);
443 val = Fcar (cdr);
444 cdr = Fcdr (cdr);
445 beg = Fcar (cdr);
446 end = Fcdr (cdr);
447
448 Fput_text_property (beg, end, prop, val, Qnil);
449 }
d8552b2f 450#endif /* USE_TEXT_PROPERTIES */
38c0d37c 451 else if (INTEGERP (car) && INTEGERP (cdr))
c6953be1 452 {
350bce56
RS
453 /* Element (BEG . END) means range was inserted. */
454 Lisp_Object end;
455
456 if (XINT (car) < BEGV
457 || XINT (cdr) > ZV)
c6953be1 458 error ("Changes to be undone are outside visible portion of buffer");
f28f04cc
RS
459 /* Set point first thing, so that undoing this undo
460 does not send point back to where it is now. */
350bce56 461 Fgoto_char (car);
f28f04cc 462 Fdelete_region (car, cdr);
350bce56 463 }
38c0d37c 464 else if (STRINGP (car) && INTEGERP (cdr))
350bce56
RS
465 {
466 /* Element (STRING . POS) means STRING was deleted. */
467 Lisp_Object membuf;
468 int pos = XINT (cdr);
469
470 membuf = car;
471 if (pos < 0)
472 {
473 if (-pos < BEGV || -pos > ZV)
474 error ("Changes to be undone are outside visible portion of buffer");
475 SET_PT (-pos);
476 Finsert (1, &membuf);
477 }
478 else
479 {
480 if (pos < BEGV || pos > ZV)
481 error ("Changes to be undone are outside visible portion of buffer");
482 SET_PT (pos);
483
b2adc409
RS
484 /* Now that we record marker adjustments
485 (caused by deletion) for undo,
486 we should always insert after markers,
487 so that undoing the marker adjustments
488 put the markers back in the right place. */
489 Finsert (1, &membuf);
350bce56
RS
490 SET_PT (pos);
491 }
c6953be1 492 }
714bced9
RS
493 else if (MARKERP (car) && INTEGERP (cdr))
494 {
495 /* (MARKER . INTEGER) means a marker MARKER
496 was adjusted by INTEGER. */
497 if (XMARKER (car)->buffer)
498 Fset_marker (car,
499 make_number (marker_position (car) - XINT (cdr)),
500 Fmarker_buffer (car));
501 }
c6953be1
JB
502 }
503 }
504 arg--;
505 }
506
de65837b 507 UNGCPRO;
f87a68b3 508 return unbind_to (count, list);
c6953be1
JB
509}
510
511syms_of_undo ()
512{
f87a68b3
RS
513 Qinhibit_read_only = intern ("inhibit-read-only");
514 staticpro (&Qinhibit_read_only);
515
c58632fc
RS
516 pending_boundary = Qnil;
517 staticpro (&pending_boundary);
518
c6953be1
JB
519 defsubr (&Sprimitive_undo);
520 defsubr (&Sundo_boundary);
521}