Port better to POSIX hosts lacking _setjmp.
[bpt/emacs.git] / src / undo.c
CommitLineData
c6953be1 1/* undo handling for GNU Emacs.
acaf905b 2 Copyright (C) 1990, 1993-1994, 2000-2012 Free Software Foundation, Inc.
c6953be1
JB
3
4This file is part of GNU Emacs.
5
9ec0b715 6GNU Emacs is free software: you can redistribute it and/or modify
3b7ad313 7it under the terms of the GNU General Public License as published by
9ec0b715
GM
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
3b7ad313 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
9ec0b715 17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
c6953be1
JB
18
19
18160b98 20#include <config.h>
0328b6de 21
c6953be1 22#include "lisp.h"
e5560ff7 23#include "character.h"
c6953be1 24#include "buffer.h"
4e665715 25#include "commands.h"
91e25f5e 26#include "window.h"
c6953be1
JB
27
28/* Last buffer for which undo information was recorded. */
4591d6cb 29/* BEWARE: This is not traced by the GC, so never dereference it! */
2b96acb7 30static struct buffer *last_undo_buffer;
4591d6cb
SM
31
32/* Position of point last time we inserted a boundary. */
2b96acb7 33static struct buffer *last_boundary_buffer;
d311d28c 34static ptrdiff_t last_boundary_position;
c6953be1 35
f87a68b3
RS
36Lisp_Object Qinhibit_read_only;
37
49be18c9
KS
38/* Marker for function call undo list elements. */
39
40Lisp_Object Qapply;
41
c58632fc
RS
42/* The first time a command records something for undo.
43 it also allocates the undo-boundary object
44 which will be added to the list at the end of the command.
45 This ensures we can't run out of space while trying to make
46 an undo-boundary. */
2b96acb7 47static Lisp_Object pending_boundary;
c58632fc 48
6396140a 49/* Record point as it was at beginning of this command (if necessary)
8abe0f97 50 and prepare the undo info for recording a change.
6396140a
SM
51 PT is the position of point that will naturally occur as a result of the
52 undo record that will be added just after this command terminates. */
c6953be1 53
6396140a 54static void
d311d28c 55record_point (ptrdiff_t pt)
c6953be1 56{
6396140a 57 int at_boundary;
bdbe6f28 58
4591d6cb 59 /* Don't record position of pt when undo_inhibit_record_point holds. */
8abe0f97
MR
60 if (undo_inhibit_record_point)
61 return;
62
c58632fc
RS
63 /* Allocate a cons cell to be the undo boundary after this command. */
64 if (NILP (pending_boundary))
65 pending_boundary = Fcons (Qnil, Qnil);
66
3ecc1163
MR
67 if ((current_buffer != last_undo_buffer)
68 /* Don't call Fundo_boundary for the first change. Otherwise we
69 risk overwriting last_boundary_position in Fundo_boundary with
70 PT of the current buffer and as a consequence not insert an
71 undo boundary because last_boundary_position will equal pt in
72 the test at the end of the present function (Bug#731). */
73 && (MODIFF > SAVE_MODIFF))
c6953be1 74 Fundo_boundary ();
4591d6cb 75 last_undo_buffer = current_buffer;
c6953be1 76
4b4deea2 77 if (CONSP (BVAR (current_buffer, undo_list)))
6396140a
SM
78 {
79 /* Set AT_BOUNDARY to 1 only when we have nothing other than
80 marker adjustment before undo boundary. */
81
4b4deea2 82 Lisp_Object tail = BVAR (current_buffer, undo_list), elt;
6396140a
SM
83
84 while (1)
85 {
86 if (NILP (tail))
87 elt = Qnil;
88 else
89 elt = XCAR (tail);
90 if (NILP (elt) || ! (CONSP (elt) && MARKERP (XCAR (elt))))
91 break;
92 tail = XCDR (tail);
93 }
94 at_boundary = NILP (elt);
95 }
96 else
97 at_boundary = 1;
98
ad9cdce4 99 if (MODIFF <= SAVE_MODIFF)
c6953be1
JB
100 record_first_change ();
101
177c0ea7 102 /* If we are just after an undo boundary, and
6396140a
SM
103 point wasn't at start of deleted range, record where it was. */
104 if (at_boundary
4591d6cb
SM
105 && current_buffer == last_boundary_buffer
106 && last_boundary_position != pt)
39eb03f1
PE
107 bset_undo_list (current_buffer,
108 Fcons (make_number (last_boundary_position),
109 BVAR (current_buffer, undo_list)));
6396140a
SM
110}
111
112/* Record an insertion that just happened or is about to happen,
113 for LENGTH characters at position BEG.
114 (It is possible to record an insertion before or after the fact
115 because we don't need to record the contents.) */
116
117void
d311d28c 118record_insert (ptrdiff_t beg, ptrdiff_t length)
6396140a
SM
119{
120 Lisp_Object lbeg, lend;
121
4b4deea2 122 if (EQ (BVAR (current_buffer, undo_list), Qt))
6396140a
SM
123 return;
124
125 record_point (beg);
126
c6953be1
JB
127 /* If this is following another insertion and consecutive with it
128 in the buffer, combine the two. */
4b4deea2 129 if (CONSP (BVAR (current_buffer, undo_list)))
c6953be1
JB
130 {
131 Lisp_Object elt;
4b4deea2 132 elt = XCAR (BVAR (current_buffer, undo_list));
38c0d37c 133 if (CONSP (elt)
c1d497be
KR
134 && INTEGERP (XCAR (elt))
135 && INTEGERP (XCDR (elt))
136 && XINT (XCDR (elt)) == beg)
c6953be1 137 {
f3fbd155 138 XSETCDR (elt, make_number (beg + length));
c6953be1
JB
139 return;
140 }
141 }
142
53480e99
KH
143 XSETFASTINT (lbeg, beg);
144 XSETINT (lend, beg + length);
39eb03f1
PE
145 bset_undo_list (current_buffer,
146 Fcons (Fcons (lbeg, lend), BVAR (current_buffer, undo_list)));
c6953be1
JB
147}
148
149/* Record that a deletion is about to take place,
e928d437 150 of the characters in STRING, at location BEG. */
c6953be1 151
ff1aa840 152void
d311d28c 153record_delete (ptrdiff_t beg, Lisp_Object string)
c6953be1 154{
e928d437 155 Lisp_Object sbeg;
c6953be1 156
4b4deea2 157 if (EQ (BVAR (current_buffer, undo_list), Qt))
bdbe6f28
RS
158 return;
159
d5db4077 160 if (PT == beg + SCHARS (string))
cbc1b668 161 {
6396140a
SM
162 XSETINT (sbeg, -beg);
163 record_point (PT);
cbc1b668
KH
164 }
165 else
6396140a
SM
166 {
167 XSETFASTINT (sbeg, beg);
168 record_point (beg);
169 }
350bce56 170
39eb03f1
PE
171 bset_undo_list
172 (current_buffer,
173 Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list)));
c6953be1
JB
174}
175
714bced9
RS
176/* Record the fact that MARKER is about to be adjusted by ADJUSTMENT.
177 This is done only when a marker points within text being deleted,
178 because that's the only case where an automatic marker adjustment
179 won't be inverted automatically by undoing the buffer modification. */
180
ff1aa840 181void
d311d28c 182record_marker_adjustment (Lisp_Object marker, ptrdiff_t adjustment)
714bced9 183{
4b4deea2 184 if (EQ (BVAR (current_buffer, undo_list), Qt))
714bced9
RS
185 return;
186
187 /* Allocate a cons cell to be the undo boundary after this command. */
188 if (NILP (pending_boundary))
189 pending_boundary = Fcons (Qnil, Qnil);
190
4591d6cb 191 if (current_buffer != last_undo_buffer)
714bced9 192 Fundo_boundary ();
4591d6cb 193 last_undo_buffer = current_buffer;
714bced9 194
39eb03f1
PE
195 bset_undo_list
196 (current_buffer,
197 Fcons (Fcons (marker, make_number (adjustment)),
198 BVAR (current_buffer, undo_list)));
714bced9
RS
199}
200
c6953be1
JB
201/* Record that a replacement is about to take place,
202 for LENGTH characters at location BEG.
e928d437 203 The replacement must not change the number of characters. */
c6953be1 204
ff1aa840 205void
d311d28c 206record_change (ptrdiff_t beg, ptrdiff_t length)
c6953be1 207{
e928d437 208 record_delete (beg, make_buffer_string (beg, beg + length, 1));
c6953be1
JB
209 record_insert (beg, length);
210}
211\f
212/* Record that an unmodified buffer is about to be changed.
213 Record the file modification date so that when undoing this entry
214 we can tell whether it is obsolete because the file was saved again. */
215
90dd3e4f 216void
971de7fb 217record_first_change (void)
c6953be1 218{
ad9cdce4 219 struct buffer *base_buffer = current_buffer;
0736cafe 220
4b4deea2 221 if (EQ (BVAR (current_buffer, undo_list), Qt))
0736cafe
RS
222 return;
223
4591d6cb 224 if (current_buffer != last_undo_buffer)
0736cafe 225 Fundo_boundary ();
4591d6cb 226 last_undo_buffer = current_buffer;
0736cafe 227
ad9cdce4
RS
228 if (base_buffer->base_buffer)
229 base_buffer = base_buffer->base_buffer;
230
39eb03f1
PE
231 bset_undo_list
232 (current_buffer,
233 Fcons (Fcons (Qt, make_lisp_time (base_buffer->modtime)),
234 BVAR (current_buffer, undo_list)));
c6953be1
JB
235}
236
da9319d5
RS
237/* Record a change in property PROP (whose old value was VAL)
238 for LENGTH characters starting at position BEG in BUFFER. */
239
90dd3e4f 240void
d311d28c 241record_property_change (ptrdiff_t beg, ptrdiff_t length,
c8a66ab8
EZ
242 Lisp_Object prop, Lisp_Object value,
243 Lisp_Object buffer)
da9319d5
RS
244{
245 Lisp_Object lbeg, lend, entry;
4591d6cb 246 struct buffer *obuf = current_buffer, *buf = XBUFFER (buffer);
da9319d5
RS
247 int boundary = 0;
248
4b4deea2 249 if (EQ (BVAR (buf, undo_list), Qt))
bdbe6f28
RS
250 return;
251
c58632fc
RS
252 /* Allocate a cons cell to be the undo boundary after this command. */
253 if (NILP (pending_boundary))
254 pending_boundary = Fcons (Qnil, Qnil);
255
4591d6cb 256 if (buf != last_undo_buffer)
da9319d5 257 boundary = 1;
4591d6cb 258 last_undo_buffer = buf;
da9319d5 259
da9319d5 260 /* Switch temporarily to the buffer that was changed. */
4591d6cb 261 current_buffer = buf;
da9319d5
RS
262
263 if (boundary)
264 Fundo_boundary ();
265
ad9cdce4 266 if (MODIFF <= SAVE_MODIFF)
da9319d5
RS
267 record_first_change ();
268
552bdbcf
KH
269 XSETINT (lbeg, beg);
270 XSETINT (lend, beg + length);
da9319d5 271 entry = Fcons (Qnil, Fcons (prop, Fcons (value, Fcons (lbeg, lend))));
39eb03f1
PE
272 bset_undo_list (current_buffer,
273 Fcons (entry, BVAR (current_buffer, undo_list)));
da9319d5
RS
274
275 current_buffer = obuf;
276}
277
a7ca3326 278DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0,
8c1a1077
PJ
279 doc: /* Mark a boundary between units of undo.
280An undo command will stop at this point,
281but another undo command will undo to the previous boundary. */)
5842a27b 282 (void)
c6953be1
JB
283{
284 Lisp_Object tem;
4b4deea2 285 if (EQ (BVAR (current_buffer, undo_list), Qt))
c6953be1 286 return Qnil;
4b4deea2 287 tem = Fcar (BVAR (current_buffer, undo_list));
265a9e55 288 if (!NILP (tem))
c58632fc
RS
289 {
290 /* One way or another, cons nil onto the front of the undo list. */
291 if (!NILP (pending_boundary))
292 {
293 /* If we have preallocated the cons cell to use here,
294 use that one. */
4b4deea2 295 XSETCDR (pending_boundary, BVAR (current_buffer, undo_list));
39eb03f1 296 bset_undo_list (current_buffer, pending_boundary);
c58632fc
RS
297 pending_boundary = Qnil;
298 }
299 else
39eb03f1
PE
300 bset_undo_list (current_buffer,
301 Fcons (Qnil, BVAR (current_buffer, undo_list)));
c58632fc 302 }
4591d6cb
SM
303 last_boundary_position = PT;
304 last_boundary_buffer = current_buffer;
c6953be1
JB
305 return Qnil;
306}
307
308/* At garbage collection time, make an undo list shorter at the end,
137e23ea
RS
309 returning the truncated list. How this is done depends on the
310 variables undo-limit, undo-strong-limit and undo-outer-limit.
311 In some cases this works by calling undo-outer-limit-function. */
312
313void
971de7fb 314truncate_undo_list (struct buffer *b)
c6953be1 315{
137e23ea 316 Lisp_Object list;
c6953be1 317 Lisp_Object prev, next, last_boundary;
d311d28c 318 EMACS_INT size_so_far = 0;
c6953be1 319
137e23ea
RS
320 /* Make sure that calling undo-outer-limit-function
321 won't cause another GC. */
d311d28c 322 ptrdiff_t count = inhibit_garbage_collection ();
137e23ea
RS
323
324 /* Make the buffer current to get its local values of variables such
325 as undo_limit. Also so that Vundo_outer_limit_function can
326 tell which buffer to operate on. */
66322887 327 record_unwind_current_buffer ();
137e23ea
RS
328 set_buffer_internal (b);
329
4b4deea2 330 list = BVAR (b, undo_list);
137e23ea 331
c6953be1
JB
332 prev = Qnil;
333 next = list;
334 last_boundary = Qnil;
335
137e23ea 336 /* If the first element is an undo boundary, skip past it. */
c1d497be 337 if (CONSP (next) && NILP (XCAR (next)))
c6953be1
JB
338 {
339 /* Add in the space occupied by this element and its chain link. */
340 size_so_far += sizeof (struct Lisp_Cons);
341
342 /* Advance to next element. */
343 prev = next;
c1d497be 344 next = XCDR (next);
c6953be1 345 }
e3d5ca1e 346
137e23ea
RS
347 /* Always preserve at least the most recent undo record
348 unless it is really horribly big.
349
350 Skip, skip, skip the undo, skip, skip, skip the undo,
351 Skip, skip, skip the undo, skip to the undo bound'ry. */
352
c1d497be 353 while (CONSP (next) && ! NILP (XCAR (next)))
c6953be1
JB
354 {
355 Lisp_Object elt;
c1d497be 356 elt = XCAR (next);
c6953be1
JB
357
358 /* Add in the space occupied by this element and its chain link. */
359 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 360 if (CONSP (elt))
c6953be1
JB
361 {
362 size_so_far += sizeof (struct Lisp_Cons);
c1d497be 363 if (STRINGP (XCAR (elt)))
c6953be1 364 size_so_far += (sizeof (struct Lisp_String) - 1
d5db4077 365 + SCHARS (XCAR (elt)));
c6953be1
JB
366 }
367
368 /* Advance to next element. */
369 prev = next;
c1d497be 370 next = XCDR (next);
c6953be1 371 }
e3d5ca1e 372
137e23ea
RS
373 /* If by the first boundary we have already passed undo_outer_limit,
374 we're heading for memory full, so offer to clear out the list. */
81c1cf71
RS
375 if (INTEGERP (Vundo_outer_limit)
376 && size_so_far > XINT (Vundo_outer_limit)
137e23ea
RS
377 && !NILP (Vundo_outer_limit_function))
378 {
4591d6cb
SM
379 Lisp_Object tem;
380 struct buffer *temp = last_undo_buffer;
137e23ea
RS
381
382 /* Normally the function this calls is undo-outer-limit-truncate. */
88fde92a
KR
383 tem = call1 (Vundo_outer_limit_function, make_number (size_so_far));
384 if (! NILP (tem))
137e23ea
RS
385 {
386 /* The function is responsible for making
387 any desired changes in buffer-undo-list. */
388 unbind_to (count, Qnil);
389 return;
390 }
391 /* That function probably used the minibuffer, and if so, that
392 changed last_undo_buffer. Change it back so that we don't
393 force next change to make an undo boundary here. */
394 last_undo_buffer = temp;
395 }
396
38c0d37c 397 if (CONSP (next))
c6953be1
JB
398 last_boundary = prev;
399
137e23ea 400 /* Keep additional undo data, if it fits in the limits. */
38c0d37c 401 while (CONSP (next))
c6953be1
JB
402 {
403 Lisp_Object elt;
c1d497be 404 elt = XCAR (next);
c6953be1
JB
405
406 /* When we get to a boundary, decide whether to truncate
137e23ea 407 either before or after it. The lower threshold, undo_limit,
c6953be1 408 tells us to truncate after it. If its size pushes past
137e23ea 409 the higher threshold undo_strong_limit, we truncate before it. */
265a9e55 410 if (NILP (elt))
c6953be1 411 {
137e23ea 412 if (size_so_far > undo_strong_limit)
c6953be1
JB
413 break;
414 last_boundary = prev;
137e23ea 415 if (size_so_far > undo_limit)
c6953be1
JB
416 break;
417 }
418
419 /* Add in the space occupied by this element and its chain link. */
420 size_so_far += sizeof (struct Lisp_Cons);
38c0d37c 421 if (CONSP (elt))
c6953be1
JB
422 {
423 size_so_far += sizeof (struct Lisp_Cons);
c1d497be 424 if (STRINGP (XCAR (elt)))
c6953be1 425 size_so_far += (sizeof (struct Lisp_String) - 1
d5db4077 426 + SCHARS (XCAR (elt)));
c6953be1
JB
427 }
428
429 /* Advance to next element. */
430 prev = next;
c1d497be 431 next = XCDR (next);
c6953be1
JB
432 }
433
434 /* If we scanned the whole list, it is short enough; don't change it. */
265a9e55 435 if (NILP (next))
137e23ea 436 ;
c6953be1 437 /* Truncate at the boundary where we decided to truncate. */
137e23ea
RS
438 else if (!NILP (last_boundary))
439 XSETCDR (last_boundary, Qnil);
440 /* There's nothing we decided to keep, so clear it out. */
c6953be1 441 else
39eb03f1 442 bset_undo_list (b, Qnil);
137e23ea
RS
443
444 unbind_to (count, Qnil);
c6953be1 445}
71873e2b 446
845ca893
PE
447static _Noreturn void
448user_error (const char *msg)
71873e2b
SM
449{
450 xsignal1 (Quser_error, build_string (msg));
451}
452
c6953be1
JB
453\f
454DEFUN ("primitive-undo", Fprimitive_undo, Sprimitive_undo, 2, 2, 0,
8c1a1077
PJ
455 doc: /* Undo N records from the front of the list LIST.
456Return what remains of the list. */)
5842a27b 457 (Lisp_Object n, Lisp_Object list)
c6953be1 458{
de65837b
KH
459 struct gcpro gcpro1, gcpro2;
460 Lisp_Object next;
d311d28c
PE
461 ptrdiff_t count = SPECPDL_INDEX ();
462 register EMACS_INT arg;
4ac03187
KS
463 Lisp_Object oldlist;
464 int did_apply = 0;
177c0ea7 465
c6953be1
JB
466#if 0 /* This is a good feature, but would make undo-start
467 unable to do what is expected. */
468 Lisp_Object tem;
469
470 /* If the head of the list is a boundary, it is the boundary
471 preceding this command. Get rid of it and don't count it. */
472 tem = Fcar (list);
265a9e55 473 if (NILP (tem))
c6953be1
JB
474 list = Fcdr (list);
475#endif
476
b7826503 477 CHECK_NUMBER (n);
de65837b
KH
478 arg = XINT (n);
479 next = Qnil;
480 GCPRO2 (next, list);
4ac03187
KS
481 /* I don't think we need to gcpro oldlist, as we use it only
482 to check for EQ. ++kfs */
de65837b 483
38d56be3
GM
484 /* In a writable buffer, enable undoing read-only text that is so
485 because of text properties. */
4b4deea2 486 if (NILP (BVAR (current_buffer, read_only)))
f87a68b3
RS
487 specbind (Qinhibit_read_only, Qt);
488
8c757fd7
GM
489 /* Don't let `intangible' properties interfere with undo. */
490 specbind (Qinhibit_point_motion_hooks, Qt);
491
4b4deea2 492 oldlist = BVAR (current_buffer, undo_list);
4ac03187 493
c6953be1
JB
494 while (arg > 0)
495 {
c3b09bbf 496 while (CONSP (list))
c6953be1 497 {
c3b09bbf
SM
498 next = XCAR (list);
499 list = XCDR (list);
350bce56 500 /* Exit inner loop at undo boundary. */
265a9e55 501 if (NILP (next))
c6953be1 502 break;
350bce56 503 /* Handle an integer by setting point to that value. */
38c0d37c 504 if (INTEGERP (next))
350bce56 505 SET_PT (clip_to_bounds (BEGV, XINT (next), ZV));
38c0d37c 506 else if (CONSP (next))
c6953be1 507 {
350bce56
RS
508 Lisp_Object car, cdr;
509
c3b09bbf
SM
510 car = XCAR (next);
511 cdr = XCDR (next);
350bce56 512 if (EQ (car, Qt))
c6953be1 513 {
d35af63c
PE
514 /* Element (t . TIME) records previous modtime.
515 Preserve any flag of NONEXISTENT_MODTIME_NSECS or
516 UNKNOWN_MODTIME_NSECS. */
ad9cdce4 517 struct buffer *base_buffer = current_buffer;
d35af63c
PE
518 EMACS_TIME mod_time;
519
520 if (CONSP (cdr)
521 && CONSP (XCDR (cdr))
522 && CONSP (XCDR (XCDR (cdr)))
523 && CONSP (XCDR (XCDR (XCDR (cdr))))
524 && INTEGERP (XCAR (XCDR (XCDR (XCDR (cdr)))))
525 && XINT (XCAR (XCDR (XCDR (XCDR (cdr))))) < 0)
e9a9ae03
PE
526 mod_time =
527 (make_emacs_time
528 (0, XINT (XCAR (XCDR (XCDR (XCDR (cdr))))) / 1000));
d35af63c 529 else
31571fd7 530 mod_time = lisp_time_argument (cdr);
ad9cdce4
RS
531
532 if (current_buffer->base_buffer)
533 base_buffer = current_buffer->base_buffer;
534
350bce56
RS
535 /* If this records an obsolete save
536 (not matching the actual disk file)
537 then don't mark unmodified. */
d35af63c 538 if (EMACS_TIME_NE (mod_time, base_buffer->modtime))
103dcb38 539 continue;
e6dd6080 540#ifdef CLASH_DETECTION
350bce56 541 Funlock_buffer ();
e6dd6080 542#endif /* CLASH_DETECTION */
350bce56 543 Fset_buffer_modified_p (Qnil);
c6953be1 544 }
d8552b2f 545 else if (EQ (car, Qnil))
da9319d5 546 {
6887bce5 547 /* Element (nil PROP VAL BEG . END) is property change. */
da9319d5
RS
548 Lisp_Object beg, end, prop, val;
549
550 prop = Fcar (cdr);
551 cdr = Fcdr (cdr);
552 val = Fcar (cdr);
553 cdr = Fcdr (cdr);
554 beg = Fcar (cdr);
555 end = Fcdr (cdr);
556
9e568684 557 if (XINT (beg) < BEGV || XINT (end) > ZV)
71873e2b 558 user_error ("Changes to be undone are outside visible portion of buffer");
da9319d5
RS
559 Fput_text_property (beg, end, prop, val, Qnil);
560 }
38c0d37c 561 else if (INTEGERP (car) && INTEGERP (cdr))
c6953be1 562 {
350bce56 563 /* Element (BEG . END) means range was inserted. */
350bce56
RS
564
565 if (XINT (car) < BEGV
566 || XINT (cdr) > ZV)
71873e2b 567 user_error ("Changes to be undone are outside visible portion of buffer");
f28f04cc
RS
568 /* Set point first thing, so that undoing this undo
569 does not send point back to where it is now. */
350bce56 570 Fgoto_char (car);
f28f04cc 571 Fdelete_region (car, cdr);
350bce56 572 }
49be18c9 573 else if (EQ (car, Qapply))
6887bce5 574 {
3419757d 575 /* Element (apply FUN . ARGS) means call FUN to undo. */
a7a39468
KS
576 struct buffer *save_buffer = current_buffer;
577
49be18c9 578 car = Fcar (cdr);
3419757d 579 cdr = Fcdr (cdr);
49be18c9
KS
580 if (INTEGERP (car))
581 {
3419757d
SM
582 /* Long format: (apply DELTA START END FUN . ARGS). */
583 Lisp_Object delta = car;
584 Lisp_Object start = Fcar (cdr);
585 Lisp_Object end = Fcar (Fcdr (cdr));
586 Lisp_Object start_mark = Fcopy_marker (start, Qnil);
587 Lisp_Object end_mark = Fcopy_marker (end, Qt);
588
589 cdr = Fcdr (Fcdr (cdr));
590 apply1 (Fcar (cdr), Fcdr (cdr));
591
592 /* Check that the function did what the entry said it
593 would do. */
594 if (!EQ (start, Fmarker_position (start_mark))
595 || (XINT (delta) + XINT (end)
596 != marker_position (end_mark)))
597 error ("Changes to be undone by function different than announced");
598 Fset_marker (start_mark, Qnil, Qnil);
599 Fset_marker (end_mark, Qnil, Qnil);
49be18c9 600 }
3419757d
SM
601 else
602 apply1 (car, cdr);
a7a39468
KS
603
604 if (save_buffer != current_buffer)
605 error ("Undo function switched buffer");
4ac03187 606 did_apply = 1;
6887bce5 607 }
38c0d37c 608 else if (STRINGP (car) && INTEGERP (cdr))
350bce56
RS
609 {
610 /* Element (STRING . POS) means STRING was deleted. */
611 Lisp_Object membuf;
c8a66ab8 612 EMACS_INT pos = XINT (cdr);
350bce56
RS
613
614 membuf = car;
615 if (pos < 0)
616 {
617 if (-pos < BEGV || -pos > ZV)
71873e2b 618 user_error ("Changes to be undone are outside visible portion of buffer");
350bce56
RS
619 SET_PT (-pos);
620 Finsert (1, &membuf);
621 }
622 else
623 {
624 if (pos < BEGV || pos > ZV)
71873e2b 625 user_error ("Changes to be undone are outside visible portion of buffer");
350bce56
RS
626 SET_PT (pos);
627
b2adc409
RS
628 /* Now that we record marker adjustments
629 (caused by deletion) for undo,
630 we should always insert after markers,
631 so that undoing the marker adjustments
632 put the markers back in the right place. */
633 Finsert (1, &membuf);
350bce56
RS
634 SET_PT (pos);
635 }
c6953be1 636 }
714bced9
RS
637 else if (MARKERP (car) && INTEGERP (cdr))
638 {
639 /* (MARKER . INTEGER) means a marker MARKER
640 was adjusted by INTEGER. */
641 if (XMARKER (car)->buffer)
642 Fset_marker (car,
643 make_number (marker_position (car) - XINT (cdr)),
644 Fmarker_buffer (car));
645 }
c6953be1
JB
646 }
647 }
648 arg--;
649 }
650
4ac03187
KS
651
652 /* Make sure an apply entry produces at least one undo entry,
653 so the test in `undo' for continuing an undo series
654 will work right. */
655 if (did_apply
4b4deea2 656 && EQ (oldlist, BVAR (current_buffer, undo_list)))
39eb03f1
PE
657 bset_undo_list
658 (current_buffer,
659 Fcons (list3 (Qapply, Qcdr, Qnil), BVAR (current_buffer, undo_list)));
4ac03187 660
de65837b 661 UNGCPRO;
f87a68b3 662 return unbind_to (count, list);
c6953be1 663}
6887bce5 664\f
dfcf069d 665void
971de7fb 666syms_of_undo (void)
c6953be1 667{
cd3520a4
JB
668 DEFSYM (Qinhibit_read_only, "inhibit-read-only");
669 DEFSYM (Qapply, "apply");
49be18c9 670
c58632fc
RS
671 pending_boundary = Qnil;
672 staticpro (&pending_boundary);
673
4591d6cb
SM
674 last_undo_buffer = NULL;
675 last_boundary_buffer = NULL;
676
c6953be1
JB
677 defsubr (&Sprimitive_undo);
678 defsubr (&Sundo_boundary);
137e23ea 679
29208e82 680 DEFVAR_INT ("undo-limit", undo_limit,
137e23ea
RS
681 doc: /* Keep no more undo information once it exceeds this size.
682This limit is applied when garbage collection happens.
683When a previous command increases the total undo list size past this
684value, the earlier commands that came before it are forgotten.
685
686The size is counted as the number of bytes occupied,
687which includes both saved text and other data. */);
2159bd06 688 undo_limit = 80000;
137e23ea 689
29208e82 690 DEFVAR_INT ("undo-strong-limit", undo_strong_limit,
137e23ea
RS
691 doc: /* Don't keep more than this much size of undo information.
692This limit is applied when garbage collection happens.
693When a previous command increases the total undo list size past this
694value, that command and the earlier commands that came before it are forgotten.
695However, the most recent buffer-modifying command's undo info
696is never discarded for this reason.
697
698The size is counted as the number of bytes occupied,
699which includes both saved text and other data. */);
2159bd06 700 undo_strong_limit = 120000;
137e23ea 701
29208e82 702 DEFVAR_LISP ("undo-outer-limit", Vundo_outer_limit,
137e23ea
RS
703 doc: /* Outer limit on size of undo information for one command.
704At garbage collection time, if the current command has produced
62d776fd
LT
705more than this much undo information, it discards the info and displays
706a warning. This is a last-ditch limit to prevent memory overflow.
137e23ea 707
62d776fd
LT
708The size is counted as the number of bytes occupied, which includes
709both saved text and other data. A value of nil means no limit. In
710this case, accumulating one huge undo entry could make Emacs crash as
711a result of memory overflow.
137e23ea
RS
712
713In fact, this calls the function which is the value of
714`undo-outer-limit-function' with one argument, the size.
715The text above describes the behavior of the function
716that variable usually specifies. */);
2159bd06 717 Vundo_outer_limit = make_number (12000000);
137e23ea 718
29208e82 719 DEFVAR_LISP ("undo-outer-limit-function", Vundo_outer_limit_function,
137e23ea
RS
720 doc: /* Function to call when an undo list exceeds `undo-outer-limit'.
721This function is called with one argument, the current undo list size
722for the most recent command (since the last undo boundary).
723If the function returns t, that means truncation has been fully handled.
724If it returns nil, the other forms of truncation are done.
725
726Garbage collection is inhibited around the call to this function,
727so it must make sure not to do a lot of consing. */);
728 Vundo_outer_limit_function = Qnil;
8abe0f97 729
29208e82 730 DEFVAR_BOOL ("undo-inhibit-record-point", undo_inhibit_record_point,
8abe0f97
MR
731 doc: /* Non-nil means do not record `point' in `buffer-undo-list'. */);
732 undo_inhibit_record_point = 0;
c6953be1 733}