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