diff options
| author | Eli Zaretskii | 2013-09-05 11:01:04 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2013-09-05 11:01:04 +0300 |
| commit | 41306318777a942420bc4feadbfacf662ea179dc (patch) | |
| tree | 669e5cca02f95d6064ce73c0d3fbbf91b8c8b563 /src/undo.c | |
| parent | 141f1ff7a40cda10f0558e891dd196a943a5082e (diff) | |
| parent | 257b3b03cb1cff917e0b3b7832ad3eab5b59f257 (diff) | |
| download | emacs-41306318777a942420bc4feadbfacf662ea179dc.tar.gz emacs-41306318777a942420bc4feadbfacf662ea179dc.zip | |
Merge from trunk after a lot of time.
Diffstat (limited to 'src/undo.c')
| -rw-r--r-- | src/undo.c | 249 |
1 files changed, 26 insertions, 223 deletions
diff --git a/src/undo.c b/src/undo.c index 9b763984d7f..234b8510f0a 100644 --- a/src/undo.c +++ b/src/undo.c | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | /* undo handling for GNU Emacs. | 1 | /* undo handling for GNU Emacs. |
| 2 | Copyright (C) 1990, 1993-1994, 2000-2012 Free Software Foundation, Inc. | 2 | Copyright (C) 1990, 1993-1994, 2000-2013 Free Software Foundation, |
| 3 | Inc. | ||
| 3 | 4 | ||
| 4 | This file is part of GNU Emacs. | 5 | This file is part of GNU Emacs. |
| 5 | 6 | ||
| @@ -18,8 +19,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 18 | 19 | ||
| 19 | 20 | ||
| 20 | #include <config.h> | 21 | #include <config.h> |
| 21 | #include <setjmp.h> | 22 | |
| 22 | #include "lisp.h" | 23 | #include "lisp.h" |
| 24 | #include "character.h" | ||
| 23 | #include "buffer.h" | 25 | #include "buffer.h" |
| 24 | #include "commands.h" | 26 | #include "commands.h" |
| 25 | #include "window.h" | 27 | #include "window.h" |
| @@ -103,8 +105,9 @@ record_point (ptrdiff_t pt) | |||
| 103 | if (at_boundary | 105 | if (at_boundary |
| 104 | && current_buffer == last_boundary_buffer | 106 | && current_buffer == last_boundary_buffer |
| 105 | && last_boundary_position != pt) | 107 | && last_boundary_position != pt) |
| 106 | BVAR (current_buffer, undo_list) | 108 | bset_undo_list (current_buffer, |
| 107 | = Fcons (make_number (last_boundary_position), BVAR (current_buffer, undo_list)); | 109 | Fcons (make_number (last_boundary_position), |
| 110 | BVAR (current_buffer, undo_list))); | ||
| 108 | } | 111 | } |
| 109 | 112 | ||
| 110 | /* Record an insertion that just happened or is about to happen, | 113 | /* Record an insertion that just happened or is about to happen, |
| @@ -140,8 +143,8 @@ record_insert (ptrdiff_t beg, ptrdiff_t length) | |||
| 140 | 143 | ||
| 141 | XSETFASTINT (lbeg, beg); | 144 | XSETFASTINT (lbeg, beg); |
| 142 | XSETINT (lend, beg + length); | 145 | XSETINT (lend, beg + length); |
| 143 | BVAR (current_buffer, undo_list) = Fcons (Fcons (lbeg, lend), | 146 | bset_undo_list (current_buffer, |
| 144 | BVAR (current_buffer, undo_list)); | 147 | Fcons (Fcons (lbeg, lend), BVAR (current_buffer, undo_list))); |
| 145 | } | 148 | } |
| 146 | 149 | ||
| 147 | /* Record that a deletion is about to take place, | 150 | /* Record that a deletion is about to take place, |
| @@ -166,8 +169,9 @@ record_delete (ptrdiff_t beg, Lisp_Object string) | |||
| 166 | record_point (beg); | 169 | record_point (beg); |
| 167 | } | 170 | } |
| 168 | 171 | ||
| 169 | BVAR (current_buffer, undo_list) | 172 | bset_undo_list |
| 170 | = Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list)); | 173 | (current_buffer, |
| 174 | Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list))); | ||
| 171 | } | 175 | } |
| 172 | 176 | ||
| 173 | /* Record the fact that MARKER is about to be adjusted by ADJUSTMENT. | 177 | /* Record the fact that MARKER is about to be adjusted by ADJUSTMENT. |
| @@ -189,9 +193,10 @@ record_marker_adjustment (Lisp_Object marker, ptrdiff_t adjustment) | |||
| 189 | Fundo_boundary (); | 193 | Fundo_boundary (); |
| 190 | last_undo_buffer = current_buffer; | 194 | last_undo_buffer = current_buffer; |
| 191 | 195 | ||
| 192 | BVAR (current_buffer, undo_list) | 196 | bset_undo_list |
| 193 | = Fcons (Fcons (marker, make_number (adjustment)), | 197 | (current_buffer, |
| 194 | BVAR (current_buffer, undo_list)); | 198 | Fcons (Fcons (marker, make_number (adjustment)), |
| 199 | BVAR (current_buffer, undo_list))); | ||
| 195 | } | 200 | } |
| 196 | 201 | ||
| 197 | /* Record that a replacement is about to take place, | 202 | /* Record that a replacement is about to take place, |
| @@ -224,9 +229,9 @@ record_first_change (void) | |||
| 224 | if (base_buffer->base_buffer) | 229 | if (base_buffer->base_buffer) |
| 225 | base_buffer = base_buffer->base_buffer; | 230 | base_buffer = base_buffer->base_buffer; |
| 226 | 231 | ||
| 227 | BVAR (current_buffer, undo_list) = | 232 | bset_undo_list (current_buffer, |
| 228 | Fcons (Fcons (Qt, INTEGER_TO_CONS (base_buffer->modtime)), | 233 | Fcons (Fcons (Qt, Fvisited_file_modtime ()), |
| 229 | BVAR (current_buffer, undo_list)); | 234 | BVAR (current_buffer, undo_list))); |
| 230 | } | 235 | } |
| 231 | 236 | ||
| 232 | /* Record a change in property PROP (whose old value was VAL) | 237 | /* Record a change in property PROP (whose old value was VAL) |
| @@ -264,7 +269,8 @@ record_property_change (ptrdiff_t beg, ptrdiff_t length, | |||
| 264 | XSETINT (lbeg, beg); | 269 | XSETINT (lbeg, beg); |
| 265 | XSETINT (lend, beg + length); | 270 | XSETINT (lend, beg + length); |
| 266 | entry = Fcons (Qnil, Fcons (prop, Fcons (value, Fcons (lbeg, lend)))); | 271 | entry = Fcons (Qnil, Fcons (prop, Fcons (value, Fcons (lbeg, lend)))); |
| 267 | BVAR (current_buffer, undo_list) = Fcons (entry, BVAR (current_buffer, undo_list)); | 272 | bset_undo_list (current_buffer, |
| 273 | Fcons (entry, BVAR (current_buffer, undo_list))); | ||
| 268 | 274 | ||
| 269 | current_buffer = obuf; | 275 | current_buffer = obuf; |
| 270 | } | 276 | } |
| @@ -287,11 +293,12 @@ but another undo command will undo to the previous boundary. */) | |||
| 287 | /* If we have preallocated the cons cell to use here, | 293 | /* If we have preallocated the cons cell to use here, |
| 288 | use that one. */ | 294 | use that one. */ |
| 289 | XSETCDR (pending_boundary, BVAR (current_buffer, undo_list)); | 295 | XSETCDR (pending_boundary, BVAR (current_buffer, undo_list)); |
| 290 | BVAR (current_buffer, undo_list) = pending_boundary; | 296 | bset_undo_list (current_buffer, pending_boundary); |
| 291 | pending_boundary = Qnil; | 297 | pending_boundary = Qnil; |
| 292 | } | 298 | } |
| 293 | else | 299 | else |
| 294 | BVAR (current_buffer, undo_list) = Fcons (Qnil, BVAR (current_buffer, undo_list)); | 300 | bset_undo_list (current_buffer, |
| 301 | Fcons (Qnil, BVAR (current_buffer, undo_list))); | ||
| 295 | } | 302 | } |
| 296 | last_boundary_position = PT; | 303 | last_boundary_position = PT; |
| 297 | last_boundary_buffer = current_buffer; | 304 | last_boundary_buffer = current_buffer; |
| @@ -317,7 +324,7 @@ truncate_undo_list (struct buffer *b) | |||
| 317 | /* Make the buffer current to get its local values of variables such | 324 | /* Make the buffer current to get its local values of variables such |
| 318 | as undo_limit. Also so that Vundo_outer_limit_function can | 325 | as undo_limit. Also so that Vundo_outer_limit_function can |
| 319 | tell which buffer to operate on. */ | 326 | tell which buffer to operate on. */ |
| 320 | record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ()); | 327 | record_unwind_current_buffer (); |
| 321 | set_buffer_internal (b); | 328 | set_buffer_internal (b); |
| 322 | 329 | ||
| 323 | list = BVAR (b, undo_list); | 330 | list = BVAR (b, undo_list); |
| @@ -432,214 +439,11 @@ truncate_undo_list (struct buffer *b) | |||
| 432 | XSETCDR (last_boundary, Qnil); | 439 | XSETCDR (last_boundary, Qnil); |
| 433 | /* There's nothing we decided to keep, so clear it out. */ | 440 | /* There's nothing we decided to keep, so clear it out. */ |
| 434 | else | 441 | else |
| 435 | BVAR (b, undo_list) = Qnil; | 442 | bset_undo_list (b, Qnil); |
| 436 | 443 | ||
| 437 | unbind_to (count, Qnil); | 444 | unbind_to (count, Qnil); |
| 438 | } | 445 | } |
| 439 | 446 | ||
| 440 | static void user_error (const char*) NO_RETURN; | ||
| 441 | static void user_error (const char *msg) | ||
| 442 | { | ||
| 443 | xsignal1 (Quser_error, build_string (msg)); | ||
| 444 | } | ||
| 445 | |||
| 446 | |||
| 447 | DEFUN ("primitive-undo", Fprimitive_undo, Sprimitive_undo, 2, 2, 0, | ||
| 448 | doc: /* Undo N records from the front of the list LIST. | ||
| 449 | Return what remains of the list. */) | ||
| 450 | (Lisp_Object n, Lisp_Object list) | ||
| 451 | { | ||
| 452 | struct gcpro gcpro1, gcpro2; | ||
| 453 | Lisp_Object next; | ||
| 454 | ptrdiff_t count = SPECPDL_INDEX (); | ||
| 455 | register EMACS_INT arg; | ||
| 456 | Lisp_Object oldlist; | ||
| 457 | int did_apply = 0; | ||
| 458 | |||
| 459 | #if 0 /* This is a good feature, but would make undo-start | ||
| 460 | unable to do what is expected. */ | ||
| 461 | Lisp_Object tem; | ||
| 462 | |||
| 463 | /* If the head of the list is a boundary, it is the boundary | ||
| 464 | preceding this command. Get rid of it and don't count it. */ | ||
| 465 | tem = Fcar (list); | ||
| 466 | if (NILP (tem)) | ||
| 467 | list = Fcdr (list); | ||
| 468 | #endif | ||
| 469 | |||
| 470 | CHECK_NUMBER (n); | ||
| 471 | arg = XINT (n); | ||
| 472 | next = Qnil; | ||
| 473 | GCPRO2 (next, list); | ||
| 474 | /* I don't think we need to gcpro oldlist, as we use it only | ||
| 475 | to check for EQ. ++kfs */ | ||
| 476 | |||
| 477 | /* In a writable buffer, enable undoing read-only text that is so | ||
| 478 | because of text properties. */ | ||
| 479 | if (NILP (BVAR (current_buffer, read_only))) | ||
| 480 | specbind (Qinhibit_read_only, Qt); | ||
| 481 | |||
| 482 | /* Don't let `intangible' properties interfere with undo. */ | ||
| 483 | specbind (Qinhibit_point_motion_hooks, Qt); | ||
| 484 | |||
| 485 | oldlist = BVAR (current_buffer, undo_list); | ||
| 486 | |||
| 487 | while (arg > 0) | ||
| 488 | { | ||
| 489 | while (CONSP (list)) | ||
| 490 | { | ||
| 491 | next = XCAR (list); | ||
| 492 | list = XCDR (list); | ||
| 493 | /* Exit inner loop at undo boundary. */ | ||
| 494 | if (NILP (next)) | ||
| 495 | break; | ||
| 496 | /* Handle an integer by setting point to that value. */ | ||
| 497 | if (INTEGERP (next)) | ||
| 498 | SET_PT (clip_to_bounds (BEGV, XINT (next), ZV)); | ||
| 499 | else if (CONSP (next)) | ||
| 500 | { | ||
| 501 | Lisp_Object car, cdr; | ||
| 502 | |||
| 503 | car = XCAR (next); | ||
| 504 | cdr = XCDR (next); | ||
| 505 | if (EQ (car, Qt)) | ||
| 506 | { | ||
| 507 | /* Element (t high . low) records previous modtime. */ | ||
| 508 | struct buffer *base_buffer = current_buffer; | ||
| 509 | time_t mod_time; | ||
| 510 | CONS_TO_INTEGER (cdr, time_t, mod_time); | ||
| 511 | |||
| 512 | if (current_buffer->base_buffer) | ||
| 513 | base_buffer = current_buffer->base_buffer; | ||
| 514 | |||
| 515 | /* If this records an obsolete save | ||
| 516 | (not matching the actual disk file) | ||
| 517 | then don't mark unmodified. */ | ||
| 518 | if (mod_time != base_buffer->modtime) | ||
| 519 | continue; | ||
| 520 | #ifdef CLASH_DETECTION | ||
| 521 | Funlock_buffer (); | ||
| 522 | #endif /* CLASH_DETECTION */ | ||
| 523 | Fset_buffer_modified_p (Qnil); | ||
| 524 | } | ||
| 525 | else if (EQ (car, Qnil)) | ||
| 526 | { | ||
| 527 | /* Element (nil PROP VAL BEG . END) is property change. */ | ||
| 528 | Lisp_Object beg, end, prop, val; | ||
| 529 | |||
| 530 | prop = Fcar (cdr); | ||
| 531 | cdr = Fcdr (cdr); | ||
| 532 | val = Fcar (cdr); | ||
| 533 | cdr = Fcdr (cdr); | ||
| 534 | beg = Fcar (cdr); | ||
| 535 | end = Fcdr (cdr); | ||
| 536 | |||
| 537 | if (XINT (beg) < BEGV || XINT (end) > ZV) | ||
| 538 | user_error ("Changes to be undone are outside visible portion of buffer"); | ||
| 539 | Fput_text_property (beg, end, prop, val, Qnil); | ||
| 540 | } | ||
| 541 | else if (INTEGERP (car) && INTEGERP (cdr)) | ||
| 542 | { | ||
| 543 | /* Element (BEG . END) means range was inserted. */ | ||
| 544 | |||
| 545 | if (XINT (car) < BEGV | ||
| 546 | || XINT (cdr) > ZV) | ||
| 547 | user_error ("Changes to be undone are outside visible portion of buffer"); | ||
| 548 | /* Set point first thing, so that undoing this undo | ||
| 549 | does not send point back to where it is now. */ | ||
| 550 | Fgoto_char (car); | ||
| 551 | Fdelete_region (car, cdr); | ||
| 552 | } | ||
| 553 | else if (EQ (car, Qapply)) | ||
| 554 | { | ||
| 555 | /* Element (apply FUN . ARGS) means call FUN to undo. */ | ||
| 556 | struct buffer *save_buffer = current_buffer; | ||
| 557 | |||
| 558 | car = Fcar (cdr); | ||
| 559 | cdr = Fcdr (cdr); | ||
| 560 | if (INTEGERP (car)) | ||
| 561 | { | ||
| 562 | /* Long format: (apply DELTA START END FUN . ARGS). */ | ||
| 563 | Lisp_Object delta = car; | ||
| 564 | Lisp_Object start = Fcar (cdr); | ||
| 565 | Lisp_Object end = Fcar (Fcdr (cdr)); | ||
| 566 | Lisp_Object start_mark = Fcopy_marker (start, Qnil); | ||
| 567 | Lisp_Object end_mark = Fcopy_marker (end, Qt); | ||
| 568 | |||
| 569 | cdr = Fcdr (Fcdr (cdr)); | ||
| 570 | apply1 (Fcar (cdr), Fcdr (cdr)); | ||
| 571 | |||
| 572 | /* Check that the function did what the entry said it | ||
| 573 | would do. */ | ||
| 574 | if (!EQ (start, Fmarker_position (start_mark)) | ||
| 575 | || (XINT (delta) + XINT (end) | ||
| 576 | != marker_position (end_mark))) | ||
| 577 | error ("Changes to be undone by function different than announced"); | ||
| 578 | Fset_marker (start_mark, Qnil, Qnil); | ||
| 579 | Fset_marker (end_mark, Qnil, Qnil); | ||
| 580 | } | ||
| 581 | else | ||
| 582 | apply1 (car, cdr); | ||
| 583 | |||
| 584 | if (save_buffer != current_buffer) | ||
| 585 | error ("Undo function switched buffer"); | ||
| 586 | did_apply = 1; | ||
| 587 | } | ||
| 588 | else if (STRINGP (car) && INTEGERP (cdr)) | ||
| 589 | { | ||
| 590 | /* Element (STRING . POS) means STRING was deleted. */ | ||
| 591 | Lisp_Object membuf; | ||
| 592 | EMACS_INT pos = XINT (cdr); | ||
| 593 | |||
| 594 | membuf = car; | ||
| 595 | if (pos < 0) | ||
| 596 | { | ||
| 597 | if (-pos < BEGV || -pos > ZV) | ||
| 598 | user_error ("Changes to be undone are outside visible portion of buffer"); | ||
| 599 | SET_PT (-pos); | ||
| 600 | Finsert (1, &membuf); | ||
| 601 | } | ||
| 602 | else | ||
| 603 | { | ||
| 604 | if (pos < BEGV || pos > ZV) | ||
| 605 | user_error ("Changes to be undone are outside visible portion of buffer"); | ||
| 606 | SET_PT (pos); | ||
| 607 | |||
| 608 | /* Now that we record marker adjustments | ||
| 609 | (caused by deletion) for undo, | ||
| 610 | we should always insert after markers, | ||
| 611 | so that undoing the marker adjustments | ||
| 612 | put the markers back in the right place. */ | ||
| 613 | Finsert (1, &membuf); | ||
| 614 | SET_PT (pos); | ||
| 615 | } | ||
| 616 | } | ||
| 617 | else if (MARKERP (car) && INTEGERP (cdr)) | ||
| 618 | { | ||
| 619 | /* (MARKER . INTEGER) means a marker MARKER | ||
| 620 | was adjusted by INTEGER. */ | ||
| 621 | if (XMARKER (car)->buffer) | ||
| 622 | Fset_marker (car, | ||
| 623 | make_number (marker_position (car) - XINT (cdr)), | ||
| 624 | Fmarker_buffer (car)); | ||
| 625 | } | ||
| 626 | } | ||
| 627 | } | ||
| 628 | arg--; | ||
| 629 | } | ||
| 630 | |||
| 631 | |||
| 632 | /* Make sure an apply entry produces at least one undo entry, | ||
| 633 | so the test in `undo' for continuing an undo series | ||
| 634 | will work right. */ | ||
| 635 | if (did_apply | ||
| 636 | && EQ (oldlist, BVAR (current_buffer, undo_list))) | ||
| 637 | BVAR (current_buffer, undo_list) | ||
| 638 | = Fcons (list3 (Qapply, Qcdr, Qnil), BVAR (current_buffer, undo_list)); | ||
| 639 | |||
| 640 | UNGCPRO; | ||
| 641 | return unbind_to (count, list); | ||
| 642 | } | ||
| 643 | 447 | ||
| 644 | void | 448 | void |
| 645 | syms_of_undo (void) | 449 | syms_of_undo (void) |
| @@ -653,7 +457,6 @@ syms_of_undo (void) | |||
| 653 | last_undo_buffer = NULL; | 457 | last_undo_buffer = NULL; |
| 654 | last_boundary_buffer = NULL; | 458 | last_boundary_buffer = NULL; |
| 655 | 459 | ||
| 656 | defsubr (&Sprimitive_undo); | ||
| 657 | defsubr (&Sundo_boundary); | 460 | defsubr (&Sundo_boundary); |
| 658 | 461 | ||
| 659 | DEFVAR_INT ("undo-limit", undo_limit, | 462 | DEFVAR_INT ("undo-limit", undo_limit, |