X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/31ca4639ad1bfaa355a3f30ef92eb977bd2c6b78..2bfa3d3e1fb347ba76bddf77f3e288049635821d:/src/undo.c diff --git a/src/undo.c b/src/undo.c index 9b763984d7..66b038e6ef 100644 --- a/src/undo.c +++ b/src/undo.c @@ -1,5 +1,6 @@ /* undo handling for GNU Emacs. - Copyright (C) 1990, 1993-1994, 2000-2012 Free Software Foundation, Inc. + Copyright (C) 1990, 1993-1994, 2000-2014 Free Software Foundation, + Inc. This file is part of GNU Emacs. @@ -18,8 +19,9 @@ along with GNU Emacs. If not, see . */ #include -#include + #include "lisp.h" +#include "character.h" #include "buffer.h" #include "commands.h" #include "window.h" @@ -53,7 +55,7 @@ static Lisp_Object pending_boundary; static void record_point (ptrdiff_t pt) { - int at_boundary; + bool at_boundary; /* Don't record position of pt when undo_inhibit_record_point holds. */ if (undo_inhibit_record_point) @@ -73,27 +75,8 @@ record_point (ptrdiff_t pt) Fundo_boundary (); last_undo_buffer = current_buffer; - if (CONSP (BVAR (current_buffer, undo_list))) - { - /* Set AT_BOUNDARY to 1 only when we have nothing other than - marker adjustment before undo boundary. */ - - Lisp_Object tail = BVAR (current_buffer, undo_list), elt; - - while (1) - { - if (NILP (tail)) - elt = Qnil; - else - elt = XCAR (tail); - if (NILP (elt) || ! (CONSP (elt) && MARKERP (XCAR (elt)))) - break; - tail = XCDR (tail); - } - at_boundary = NILP (elt); - } - else - at_boundary = 1; + at_boundary = ! CONSP (BVAR (current_buffer, undo_list)) + || NILP (XCAR (BVAR (current_buffer, undo_list))); if (MODIFF <= SAVE_MODIFF) record_first_change (); @@ -103,8 +86,9 @@ record_point (ptrdiff_t pt) if (at_boundary && current_buffer == last_boundary_buffer && last_boundary_position != pt) - BVAR (current_buffer, undo_list) - = Fcons (make_number (last_boundary_position), BVAR (current_buffer, undo_list)); + bset_undo_list (current_buffer, + Fcons (make_number (last_boundary_position), + BVAR (current_buffer, undo_list))); } /* Record an insertion that just happened or is about to happen, @@ -140,15 +124,65 @@ record_insert (ptrdiff_t beg, ptrdiff_t length) XSETFASTINT (lbeg, beg); XSETINT (lend, beg + length); - BVAR (current_buffer, undo_list) = Fcons (Fcons (lbeg, lend), - BVAR (current_buffer, undo_list)); + bset_undo_list (current_buffer, + Fcons (Fcons (lbeg, lend), BVAR (current_buffer, undo_list))); } -/* Record that a deletion is about to take place, - of the characters in STRING, at location BEG. */ +/* Record the fact that markers in the region of FROM, TO are about to + be adjusted. This is done only when a marker points within text + being deleted, because that's the only case where an automatic + marker adjustment won't be inverted automatically by undoing the + buffer modification. */ + +static void +record_marker_adjustments (ptrdiff_t from, ptrdiff_t to) +{ + Lisp_Object marker; + register struct Lisp_Marker *m; + register ptrdiff_t charpos, adjustment; + + /* Allocate a cons cell to be the undo boundary after this command. */ + if (NILP (pending_boundary)) + pending_boundary = Fcons (Qnil, Qnil); + + if (current_buffer != last_undo_buffer) + Fundo_boundary (); + last_undo_buffer = current_buffer; + + for (m = BUF_MARKERS (current_buffer); m; m = m->next) + { + charpos = m->charpos; + eassert (charpos <= Z); + + if (from <= charpos && charpos <= to) + { + /* insertion_type nil markers will end up at the beginning of + the re-inserted text after undoing a deletion, and must be + adjusted to move them to the correct place. + + insertion_type t markers will automatically move forward + upon re-inserting the deleted text, so we have to arrange + for them to move backward to the correct position. */ + adjustment = (m->insertion_type ? to : from) - charpos; + + if (adjustment) + { + XSETMISC (marker, m); + bset_undo_list + (current_buffer, + Fcons (Fcons (marker, make_number (adjustment)), + BVAR (current_buffer, undo_list))); + } + } + } +} + +/* Record that a deletion is about to take place, of the characters in + STRING, at location BEG. Optionally record adjustments for markers + in the region STRING occupies in the current buffer. */ void -record_delete (ptrdiff_t beg, Lisp_Object string) +record_delete (ptrdiff_t beg, Lisp_Object string, bool record_markers) { Lisp_Object sbeg; @@ -166,32 +200,15 @@ record_delete (ptrdiff_t beg, Lisp_Object string) record_point (beg); } - BVAR (current_buffer, undo_list) - = Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list)); -} - -/* Record the fact that MARKER is about to be adjusted by ADJUSTMENT. - This is done only when a marker points within text being deleted, - because that's the only case where an automatic marker adjustment - won't be inverted automatically by undoing the buffer modification. */ - -void -record_marker_adjustment (Lisp_Object marker, ptrdiff_t adjustment) -{ - if (EQ (BVAR (current_buffer, undo_list), Qt)) - return; + /* primitive-undo assumes marker adjustments are recorded + immediately before the deletion is recorded. See bug 16818 + discussion. */ + if (record_markers) + record_marker_adjustments (beg, beg + SCHARS (string)); - /* Allocate a cons cell to be the undo boundary after this command. */ - if (NILP (pending_boundary)) - pending_boundary = Fcons (Qnil, Qnil); - - if (current_buffer != last_undo_buffer) - Fundo_boundary (); - last_undo_buffer = current_buffer; - - BVAR (current_buffer, undo_list) - = Fcons (Fcons (marker, make_number (adjustment)), - BVAR (current_buffer, undo_list)); + bset_undo_list + (current_buffer, + Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list))); } /* Record that a replacement is about to take place, @@ -201,7 +218,7 @@ record_marker_adjustment (Lisp_Object marker, ptrdiff_t adjustment) void record_change (ptrdiff_t beg, ptrdiff_t length) { - record_delete (beg, make_buffer_string (beg, beg + length, 1)); + record_delete (beg, make_buffer_string (beg, beg + length, 1), false); record_insert (beg, length); } @@ -224,9 +241,9 @@ record_first_change (void) if (base_buffer->base_buffer) base_buffer = base_buffer->base_buffer; - BVAR (current_buffer, undo_list) = - Fcons (Fcons (Qt, INTEGER_TO_CONS (base_buffer->modtime)), - BVAR (current_buffer, undo_list)); + bset_undo_list (current_buffer, + Fcons (Fcons (Qt, Fvisited_file_modtime ()), + BVAR (current_buffer, undo_list))); } /* Record a change in property PROP (whose old value was VAL) @@ -239,7 +256,7 @@ record_property_change (ptrdiff_t beg, ptrdiff_t length, { Lisp_Object lbeg, lend, entry; struct buffer *obuf = current_buffer, *buf = XBUFFER (buffer); - int boundary = 0; + bool boundary = 0; if (EQ (BVAR (buf, undo_list), Qt)) return; @@ -264,7 +281,8 @@ record_property_change (ptrdiff_t beg, ptrdiff_t length, XSETINT (lbeg, beg); XSETINT (lend, beg + length); entry = Fcons (Qnil, Fcons (prop, Fcons (value, Fcons (lbeg, lend)))); - BVAR (current_buffer, undo_list) = Fcons (entry, BVAR (current_buffer, undo_list)); + bset_undo_list (current_buffer, + Fcons (entry, BVAR (current_buffer, undo_list))); current_buffer = obuf; } @@ -287,11 +305,12 @@ but another undo command will undo to the previous boundary. */) /* If we have preallocated the cons cell to use here, use that one. */ XSETCDR (pending_boundary, BVAR (current_buffer, undo_list)); - BVAR (current_buffer, undo_list) = pending_boundary; + bset_undo_list (current_buffer, pending_boundary); pending_boundary = Qnil; } else - BVAR (current_buffer, undo_list) = Fcons (Qnil, BVAR (current_buffer, undo_list)); + bset_undo_list (current_buffer, + Fcons (Qnil, BVAR (current_buffer, undo_list))); } last_boundary_position = PT; last_boundary_buffer = current_buffer; @@ -309,15 +328,13 @@ truncate_undo_list (struct buffer *b) Lisp_Object list; Lisp_Object prev, next, last_boundary; EMACS_INT size_so_far = 0; - - /* Make sure that calling undo-outer-limit-function - won't cause another GC. */ - ptrdiff_t count = inhibit_garbage_collection (); + dynwind_begin (); + static const size_t sizeof_cons = sizeof (scm_t_cell); /* Make the buffer current to get its local values of variables such as undo_limit. Also so that Vundo_outer_limit_function can tell which buffer to operate on. */ - record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ()); + record_unwind_current_buffer (); set_buffer_internal (b); list = BVAR (b, undo_list); @@ -330,7 +347,7 @@ truncate_undo_list (struct buffer *b) if (CONSP (next) && NILP (XCAR (next))) { /* Add in the space occupied by this element and its chain link. */ - size_so_far += sizeof (struct Lisp_Cons); + size_so_far += sizeof_cons; /* Advance to next element. */ prev = next; @@ -349,10 +366,10 @@ truncate_undo_list (struct buffer *b) elt = XCAR (next); /* Add in the space occupied by this element and its chain link. */ - size_so_far += sizeof (struct Lisp_Cons); + size_so_far += sizeof_cons; if (CONSP (elt)) { - size_so_far += sizeof (struct Lisp_Cons); + size_so_far += sizeof_cons; if (STRINGP (XCAR (elt))) size_so_far += (sizeof (struct Lisp_String) - 1 + SCHARS (XCAR (elt))); @@ -378,7 +395,7 @@ truncate_undo_list (struct buffer *b) { /* The function is responsible for making any desired changes in buffer-undo-list. */ - unbind_to (count, Qnil); + dynwind_end (); return; } /* That function probably used the minibuffer, and if so, that @@ -410,10 +427,10 @@ truncate_undo_list (struct buffer *b) } /* Add in the space occupied by this element and its chain link. */ - size_so_far += sizeof (struct Lisp_Cons); + size_so_far += sizeof_cons; if (CONSP (elt)) { - size_so_far += sizeof (struct Lisp_Cons); + size_so_far += sizeof_cons; if (STRINGP (XCAR (elt))) size_so_far += (sizeof (struct Lisp_String) - 1 + SCHARS (XCAR (elt))); @@ -432,218 +449,17 @@ truncate_undo_list (struct buffer *b) XSETCDR (last_boundary, Qnil); /* There's nothing we decided to keep, so clear it out. */ else - BVAR (b, undo_list) = Qnil; - - unbind_to (count, Qnil); -} + bset_undo_list (b, Qnil); -static void user_error (const char*) NO_RETURN; -static void user_error (const char *msg) -{ - xsignal1 (Quser_error, build_string (msg)); + dynwind_end (); } -DEFUN ("primitive-undo", Fprimitive_undo, Sprimitive_undo, 2, 2, 0, - doc: /* Undo N records from the front of the list LIST. -Return what remains of the list. */) - (Lisp_Object n, Lisp_Object list) -{ - struct gcpro gcpro1, gcpro2; - Lisp_Object next; - ptrdiff_t count = SPECPDL_INDEX (); - register EMACS_INT arg; - Lisp_Object oldlist; - int did_apply = 0; - -#if 0 /* This is a good feature, but would make undo-start - unable to do what is expected. */ - Lisp_Object tem; - - /* If the head of the list is a boundary, it is the boundary - preceding this command. Get rid of it and don't count it. */ - tem = Fcar (list); - if (NILP (tem)) - list = Fcdr (list); -#endif - - CHECK_NUMBER (n); - arg = XINT (n); - next = Qnil; - GCPRO2 (next, list); - /* I don't think we need to gcpro oldlist, as we use it only - to check for EQ. ++kfs */ - - /* In a writable buffer, enable undoing read-only text that is so - because of text properties. */ - if (NILP (BVAR (current_buffer, read_only))) - specbind (Qinhibit_read_only, Qt); - - /* Don't let `intangible' properties interfere with undo. */ - specbind (Qinhibit_point_motion_hooks, Qt); - - oldlist = BVAR (current_buffer, undo_list); - - while (arg > 0) - { - while (CONSP (list)) - { - next = XCAR (list); - list = XCDR (list); - /* Exit inner loop at undo boundary. */ - if (NILP (next)) - break; - /* Handle an integer by setting point to that value. */ - if (INTEGERP (next)) - SET_PT (clip_to_bounds (BEGV, XINT (next), ZV)); - else if (CONSP (next)) - { - Lisp_Object car, cdr; - - car = XCAR (next); - cdr = XCDR (next); - if (EQ (car, Qt)) - { - /* Element (t high . low) records previous modtime. */ - struct buffer *base_buffer = current_buffer; - time_t mod_time; - CONS_TO_INTEGER (cdr, time_t, mod_time); - - if (current_buffer->base_buffer) - base_buffer = current_buffer->base_buffer; - - /* If this records an obsolete save - (not matching the actual disk file) - then don't mark unmodified. */ - if (mod_time != base_buffer->modtime) - continue; -#ifdef CLASH_DETECTION - Funlock_buffer (); -#endif /* CLASH_DETECTION */ - Fset_buffer_modified_p (Qnil); - } - else if (EQ (car, Qnil)) - { - /* Element (nil PROP VAL BEG . END) is property change. */ - Lisp_Object beg, end, prop, val; - - prop = Fcar (cdr); - cdr = Fcdr (cdr); - val = Fcar (cdr); - cdr = Fcdr (cdr); - beg = Fcar (cdr); - end = Fcdr (cdr); - - if (XINT (beg) < BEGV || XINT (end) > ZV) - user_error ("Changes to be undone are outside visible portion of buffer"); - Fput_text_property (beg, end, prop, val, Qnil); - } - else if (INTEGERP (car) && INTEGERP (cdr)) - { - /* Element (BEG . END) means range was inserted. */ - - if (XINT (car) < BEGV - || XINT (cdr) > ZV) - user_error ("Changes to be undone are outside visible portion of buffer"); - /* Set point first thing, so that undoing this undo - does not send point back to where it is now. */ - Fgoto_char (car); - Fdelete_region (car, cdr); - } - else if (EQ (car, Qapply)) - { - /* Element (apply FUN . ARGS) means call FUN to undo. */ - struct buffer *save_buffer = current_buffer; - - car = Fcar (cdr); - cdr = Fcdr (cdr); - if (INTEGERP (car)) - { - /* Long format: (apply DELTA START END FUN . ARGS). */ - Lisp_Object delta = car; - Lisp_Object start = Fcar (cdr); - Lisp_Object end = Fcar (Fcdr (cdr)); - Lisp_Object start_mark = Fcopy_marker (start, Qnil); - Lisp_Object end_mark = Fcopy_marker (end, Qt); - - cdr = Fcdr (Fcdr (cdr)); - apply1 (Fcar (cdr), Fcdr (cdr)); - - /* Check that the function did what the entry said it - would do. */ - if (!EQ (start, Fmarker_position (start_mark)) - || (XINT (delta) + XINT (end) - != marker_position (end_mark))) - error ("Changes to be undone by function different than announced"); - Fset_marker (start_mark, Qnil, Qnil); - Fset_marker (end_mark, Qnil, Qnil); - } - else - apply1 (car, cdr); - - if (save_buffer != current_buffer) - error ("Undo function switched buffer"); - did_apply = 1; - } - else if (STRINGP (car) && INTEGERP (cdr)) - { - /* Element (STRING . POS) means STRING was deleted. */ - Lisp_Object membuf; - EMACS_INT pos = XINT (cdr); - - membuf = car; - if (pos < 0) - { - if (-pos < BEGV || -pos > ZV) - user_error ("Changes to be undone are outside visible portion of buffer"); - SET_PT (-pos); - Finsert (1, &membuf); - } - else - { - if (pos < BEGV || pos > ZV) - user_error ("Changes to be undone are outside visible portion of buffer"); - SET_PT (pos); - - /* Now that we record marker adjustments - (caused by deletion) for undo, - we should always insert after markers, - so that undoing the marker adjustments - put the markers back in the right place. */ - Finsert (1, &membuf); - SET_PT (pos); - } - } - else if (MARKERP (car) && INTEGERP (cdr)) - { - /* (MARKER . INTEGER) means a marker MARKER - was adjusted by INTEGER. */ - if (XMARKER (car)->buffer) - Fset_marker (car, - make_number (marker_position (car) - XINT (cdr)), - Fmarker_buffer (car)); - } - } - } - arg--; - } - - - /* Make sure an apply entry produces at least one undo entry, - so the test in `undo' for continuing an undo series - will work right. */ - if (did_apply - && EQ (oldlist, BVAR (current_buffer, undo_list))) - BVAR (current_buffer, undo_list) - = Fcons (list3 (Qapply, Qcdr, Qnil), BVAR (current_buffer, undo_list)); - - UNGCPRO; - return unbind_to (count, list); -} - void syms_of_undo (void) { +#include "undo.x" + DEFSYM (Qinhibit_read_only, "inhibit-read-only"); DEFSYM (Qapply, "apply"); @@ -653,9 +469,6 @@ syms_of_undo (void) last_undo_buffer = NULL; last_boundary_buffer = NULL; - defsubr (&Sprimitive_undo); - defsubr (&Sundo_boundary); - DEFVAR_INT ("undo-limit", undo_limit, doc: /* Keep no more undo information once it exceeds this size. This limit is applied when garbage collection happens.