use dynwind_begin and dynwind_end
[bpt/emacs.git] / src / undo.c
index 63edc8e..66b038e 100644 (file)
@@ -1,5 +1,5 @@
 /* undo handling for GNU Emacs.
-   Copyright (C) 1990, 1993-1994, 2000-2013 Free Software Foundation,
+   Copyright (C) 1990, 1993-1994, 2000-2014 Free Software Foundation,
    Inc.
 
 This file is part of GNU Emacs.
@@ -55,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)
@@ -75,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 ();
@@ -147,11 +128,61 @@ record_insert (ptrdiff_t beg, ptrdiff_t length)
                  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;
 
@@ -169,34 +200,15 @@ record_delete (ptrdiff_t beg, Lisp_Object string)
       record_point (beg);
     }
 
-  bset_undo_list
-    (current_buffer,
-     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;
-
-  /* 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;
+  /* 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));
 
   bset_undo_list
     (current_buffer,
-     Fcons (Fcons (marker, make_number (adjustment)),
-           BVAR (current_buffer, undo_list)));
+     Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list)));
 }
 
 /* Record that a replacement is about to take place,
@@ -206,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);
 }
 \f
@@ -229,10 +241,9 @@ record_first_change (void)
   if (base_buffer->base_buffer)
     base_buffer = base_buffer->base_buffer;
 
-  bset_undo_list
-    (current_buffer,
-     Fcons (Fcons (Qt, make_lisp_time (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)
@@ -245,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;
@@ -317,10 +328,8 @@ 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
@@ -338,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;
@@ -357,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)));
@@ -386,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
@@ -418,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)));
@@ -442,19 +451,15 @@ truncate_undo_list (struct buffer *b)
   else
     bset_undo_list (b, Qnil);
 
-  unbind_to (count, Qnil);
-}
-
-static _Noreturn void
-user_error (const char *msg)
-{
-  xsignal1 (Quser_error, build_string (msg));
+  dynwind_end ();
 }
 
 \f
 void
 syms_of_undo (void)
 {
+#include "undo.x"
+
   DEFSYM (Qinhibit_read_only, "inhibit-read-only");
   DEFSYM (Qapply, "apply");
 
@@ -464,8 +469,6 @@ syms_of_undo (void)
   last_undo_buffer = NULL;
   last_boundary_buffer = NULL;
 
-  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.