emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] emacs-24 r116855: Undo in region after markers in undo his


From: Barry O'Reilly
Subject: [Emacs-diffs] emacs-24 r116855: Undo in region after markers in undo history relocated
Date: Tue, 25 Mar 2014 02:30:28 +0000
User-agent: Bazaar (2.6b2)

------------------------------------------------------------
revno: 116855
revision-id: address@hidden
parent: address@hidden
fixes bug: http://debbugs.gnu.org/16818
committer: Barry O'Reilly <address@hidden>
branch nick: emacs-24
timestamp: Mon 2014-03-24 22:47:39 -0400
message:
  Undo in region after markers in undo history relocated
  
  * simple.el (primitive-undo): Only process marker adjustments
  validated against their corresponding (TEXT . POS).  Issue warning
  for lone marker adjustments in undo history.  (Bug#16818)
  (undo-make-selective-list): Add marker adjustments to selective
  undo list based on whether their corresponding (TEXT . POS) is in
  the region.  Remove variable adjusted-markers, which was unused
  and only non nil during undo-make-selective-list.
  (undo-elt-in-region): Return nil when passed a marker adjustment
  and explain in function doc.
  
  Have (MARKER . ADJUSTMENT) undo records always be immediately
  after their corresponding (TEXT . POS) record in undo list.
  (Bug#16818)
  * lisp.h (record-delete): New arg record_markers.
  (record_marker_adjustment): No longer needed outside undo.c.
  * insdel.c (adjust_markers_for_delete): Move calculation of marker
  adjustments to undo.c's record_marker_adjustments.  Note that
  fileio.c's decide_coding_unwind is another caller to
  adjust_markers_for_delete.  Because it has undo list bound to t,
  it does not rely on adjust_markers_for_delete to record marker
  adjustments.
  (del_range_2): Swap call to record_delete and
  adjust_markers_for_delete so as undo marker adjustments are
  recorded before current deletion's adjustments, as before.
  (adjust_after_replace):
  (replace_range): Pass value for new record_markers arg to
  delete_record.
  * undo.c (record_marker_adjustment): Renamed to
  record_marker_adjustments and made static.
  (record_delete): Check record_markers arg and call
  record_marker_adjustments.
  (record_change): Pass value for new record_markers arg to
  delete_record.
  (record_point): at_boundary calculation no longer needs to account
  for marker adjustments.
  
  * undo-tests.el (undo-test-marker-adjustment-nominal):
  (undo-test-region-t-marker): New tests of marker adjustments.
  (undo-test-marker-adjustment-moved):
  (undo-test-region-mark-adjustment): New tests to demonstrate
  bug#16818, which fail without the fix.
  
  * markers.texi (Moving Marker Positions): The 2014-03-02 doc
  change mentioning undo's inability to handle relocated markers no
  longer applies.  See bug#16818.
  * text.texi (Undo): Expand documentation of (TEXT . POS) and
  (MARKER . ADJUSTMENT) undo elements.
modified:
  doc/lispref/ChangeLog          changelog-20091113204419-o5vbwnq5f7feedwu-6155
  doc/lispref/markers.texi       
markers.texi-20091113204419-o5vbwnq5f7feedwu-6198
  doc/lispref/text.texi          text.texi-20091113204419-o5vbwnq5f7feedwu-6215
  lisp/ChangeLog                 changelog-20091113204419-o5vbwnq5f7feedwu-1432
  lisp/simple.el                 simple.el-20091113204419-o5vbwnq5f7feedwu-403
  src/ChangeLog                  changelog-20091113204419-o5vbwnq5f7feedwu-1438
  src/insdel.c                   insdel.c-20091113204419-o5vbwnq5f7feedwu-175
  src/lisp.h                     lisp.h-20091113204419-o5vbwnq5f7feedwu-253
  src/undo.c                     undo.c-20091113204419-o5vbwnq5f7feedwu-206
  test/ChangeLog                 changelog-20091113204419-o5vbwnq5f7feedwu-8588
  test/automated/undo-tests.el   undotests.el-20130108184711-brlnvjau5r1ardib-1
=== modified file 'doc/lispref/ChangeLog'
--- a/doc/lispref/ChangeLog     2014-03-22 19:54:48 +0000
+++ b/doc/lispref/ChangeLog     2014-03-25 02:47:39 +0000
@@ -1,3 +1,11 @@
+2014-03-24  Barry O'Reilly  <address@hidden>
+
+       * markers.texi (Moving Marker Positions): The 2014-03-02 doc
+       change mentioning undo's inability to handle relocated markers no
+       longer applies.  See bug#16818.
+       * text.texi (Undo): Expand documentation of (TEXT . POS) and
+       (MARKER . ADJUSTMENT) undo elements.
+
 2014-03-22  Glenn Morris  <address@hidden>
 
        * commands.texi (Defining Commands): List interactive-only values.

=== modified file 'doc/lispref/markers.texi'
--- a/doc/lispref/markers.texi  2014-03-02 17:49:02 +0000
+++ b/doc/lispref/markers.texi  2014-03-25 02:47:39 +0000
@@ -344,12 +344,10 @@
 @section Moving Marker Positions
 
   This section describes how to change the position of an existing
-marker.  When you do this, be sure you know how the marker is used
-outside of your program.  For example, moving a marker to an unrelated
-new position can cause undo to later adjust the marker incorrectly.
-Often when you wish to relocate a marker to an unrelated position, it
-is preferable to make a new marker and set the prior one to point
-nowhere.
+marker.  When you do this, be sure you know whether the marker is used
+outside of your program, and, if so, what effects will result from
+moving it---otherwise, confusing things may happen in other parts of
+Emacs.
 
 @defun set-marker marker position &optional buffer
 This function moves @var{marker} to @var{position}

=== modified file 'doc/lispref/text.texi'
--- a/doc/lispref/text.texi     2014-03-18 21:14:36 +0000
+++ b/doc/lispref/text.texi     2014-03-25 02:47:39 +0000
@@ -1270,7 +1270,8 @@
 The deleted text itself is the string @var{text}.  The place to
 reinsert it is @code{(abs @var{position})}.  If @var{position} is
 positive, point was at the beginning of the deleted text, otherwise it
-was at the end.
+was at the end.  Zero or more (@var{marker} . @var{adjustment})
+elements follow immediately after this element.
 
 @item (t . @var{time-flag})
 This kind of element indicates that an unmodified buffer became
@@ -1296,8 +1297,10 @@
 @item (@var{marker} . @var{adjustment})
 This kind of element records the fact that the marker @var{marker} was
 relocated due to deletion of surrounding text, and that it moved
address@hidden character positions.  Undoing this element moves
address@hidden @minus{} @var{adjustment} characters.
address@hidden character positions.  If the marker's location is
+consistent with the (@var{text} . @var{position}) element preceding it
+in the undo list, then undoing this element moves @var{marker}
address@hidden @var{adjustment} characters.
 
 @item (apply @var{funname} . @var{args})
 This is an extensible undo item, which is undone by calling

=== modified file 'lisp/ChangeLog'
--- a/lisp/ChangeLog    2014-03-24 22:38:06 +0000
+++ b/lisp/ChangeLog    2014-03-25 02:47:39 +0000
@@ -1,3 +1,15 @@
+2014-03-24  Barry O'Reilly  <address@hidden>
+
+       * simple.el (primitive-undo): Only process marker adjustments
+       validated against their corresponding (TEXT . POS).  Issue warning
+       for lone marker adjustments in undo history.  (Bug#16818)
+       (undo-make-selective-list): Add marker adjustments to selective
+       undo list based on whether their corresponding (TEXT . POS) is in
+       the region.  Remove variable adjusted-markers, which was unused
+       and only non nil during undo-make-selective-list.
+       (undo-elt-in-region): Return nil when passed a marker adjustment
+       and explain in function doc.
+
 2014-03-24  Dmitry Gutov  <address@hidden>
 
        * emacs-lisp/package.el (package--add-to-archive-contents):

=== modified file 'lisp/simple.el'
--- a/lisp/simple.el    2014-03-23 22:30:47 +0000
+++ b/lisp/simple.el    2014-03-25 02:47:39 +0000
@@ -2289,24 +2289,41 @@
            (when (let ((apos (abs pos)))
                    (or (< apos (point-min)) (> apos (point-max))))
              (error "Changes to be undone are outside visible portion of 
buffer"))
-           (if (< pos 0)
-               (progn
-                 (goto-char (- pos))
-                 (insert string))
-             (goto-char 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.
-             (insert string)
-             (goto-char pos)))
+           (let (valid-marker-adjustments)
+             ;; Check that marker adjustments which were recorded
+             ;; with the (STRING . POS) record are still valid, ie
+             ;; the markers haven't moved.  We check their validity
+             ;; before reinserting the string so as we don't need to
+             ;; mind marker insertion-type.
+             (while (and (markerp (car-safe (car list)))
+                         (integerp (cdr-safe (car list))))
+               (let* ((marker-adj (pop list))
+                      (m (car marker-adj)))
+                 (and (eq (marker-buffer m) (current-buffer))
+                      (= pos m)
+                      (push marker-adj valid-marker-adjustments))))
+             ;; Insert string and adjust point
+             (if (< pos 0)
+                 (progn
+                   (goto-char (- pos))
+                   (insert string))
+               (goto-char pos)
+               (insert string)
+               (goto-char pos))
+             ;; Adjust the valid marker adjustments
+             (dolist (adj valid-marker-adjustments)
+               (set-marker (car adj)
+                           (- (car adj) (cdr adj))))))
           ;; (MARKER . OFFSET) means a marker MARKER was adjusted by OFFSET.
           (`(,(and marker (pred markerp)) . ,(and offset (pred integerp)))
-           (when (marker-buffer marker)
-             (set-marker marker
-                         (- marker offset)
-                         (marker-buffer marker))))
+           (warn "Encountered %S entry in undo list with no matching (TEXT . 
POS) entry"
+                 next)
+           ;; Even though these elements are not expected in the undo
+           ;; list, adjust them to be conservative for the 24.4
+           ;; release.  (Bug#16818)
+           (set-marker marker
+                       (- marker offset)
+                       (marker-buffer marker)))
           (_ (error "Unrecognized entry in undo list %S" next))))
       (setq arg (1- arg)))
     ;; Make sure an apply entry produces at least one undo entry,
@@ -2341,8 +2358,6 @@
            (undo-make-selective-list (min beg end) (max beg end))
          buffer-undo-list)))
 
-(defvar undo-adjusted-markers)
-
 (defun undo-make-selective-list (start end)
   "Return a list of undo elements for the region START to END.
 The elements come from `buffer-undo-list', but we keep only
@@ -2351,7 +2366,6 @@
 we stop and ignore all further elements."
   (let ((undo-list-copy (undo-copy-list buffer-undo-list))
        (undo-list (list nil))
-       undo-adjusted-markers
        some-rejected
        undo-elt temp-undo-list delta)
     (while undo-list-copy
@@ -2361,15 +2375,30 @@
                    ;; This is a "was unmodified" element.
                    ;; Keep it if we have kept everything thus far.
                    (not some-rejected))
+                   ;; Skip over marker adjustments, instead relying on
+                   ;; finding them after (TEXT . POS) elements
+                   ((markerp (car-safe undo-elt))
+                    nil)
                   (t
                    (undo-elt-in-region undo-elt start end)))))
        (if keep-this
            (progn
              (setq end (+ end (cdr (undo-delta undo-elt))))
              ;; Don't put two nils together in the list
-             (if (not (and (eq (car undo-list) nil)
-                           (eq undo-elt nil)))
-                 (setq undo-list (cons undo-elt undo-list))))
+             (when (not (and (eq (car undo-list) nil)
+                              (eq undo-elt nil)))
+                (setq undo-list (cons undo-elt undo-list))
+                ;; If (TEXT . POS), "keep" its subsequent (MARKER
+                ;; . ADJUSTMENT) whose markers haven't moved.
+                (when (and (stringp (car-safe undo-elt))
+                           (integerp (cdr-safe undo-elt)))
+                  (let ((list-i (cdr undo-list-copy)))
+                    (while (markerp (car-safe (car list-i)))
+                      (let* ((adj-elt (pop list-i))
+                             (m (car adj-elt)))
+                        (and (eq (marker-buffer m) (current-buffer))
+                             (= (cdr undo-elt) m)
+                             (push adj-elt undo-list))))))))
          (if (undo-elt-crosses-region undo-elt start end)
              (setq undo-list-copy nil)
            (setq some-rejected t)
@@ -2417,7 +2446,12 @@
 
 (defun undo-elt-in-region (undo-elt start end)
   "Determine whether UNDO-ELT falls inside the region START ... END.
-If it crosses the edge, we return nil."
+If it crosses the edge, we return nil.
+
+Generally this function is not useful for determining
+whether (MARKER . ADJUSTMENT) undo elements are in the region,
+because markers can be arbitrarily relocated.  Instead, pass the
+marker adjustment's corresponding (TEXT . POS) element."
   (cond ((integerp undo-elt)
         (and (>= undo-elt start)
              (<= undo-elt end)))
@@ -2430,17 +2464,8 @@
         (and (>= (abs (cdr undo-elt)) start)
              (<= (abs (cdr undo-elt)) end)))
        ((and (consp undo-elt) (markerp (car undo-elt)))
-        ;; This is a marker-adjustment element (MARKER . ADJUSTMENT).
-        ;; See if MARKER is inside the region.
-        (let ((alist-elt (assq (car undo-elt) undo-adjusted-markers)))
-          (unless alist-elt
-            (setq alist-elt (cons (car undo-elt)
-                                  (marker-position (car undo-elt))))
-            (setq undo-adjusted-markers
-                  (cons alist-elt undo-adjusted-markers)))
-          (and (cdr alist-elt)
-               (>= (cdr alist-elt) start)
-               (<= (cdr alist-elt) end))))
+        ;; (MARKER . ADJUSTMENT)
+         (<= start (car undo-elt) end))
        ((null (car undo-elt))
         ;; (nil PROPERTY VALUE BEG . END)
         (let ((tail (nthcdr 3 undo-elt)))

=== modified file 'src/ChangeLog'
--- a/src/ChangeLog     2014-03-24 08:50:17 +0000
+++ b/src/ChangeLog     2014-03-25 02:47:39 +0000
@@ -1,3 +1,31 @@
+2014-03-24  Barry O'Reilly  <address@hidden>
+
+       Have (MARKER . ADJUSTMENT) undo records always be immediately
+       after their corresponding (TEXT . POS) record in undo list.
+       (Bug#16818)
+       * lisp.h (record-delete): New arg record_markers.
+       (record_marker_adjustment): No longer needed outside undo.c.
+       * insdel.c (adjust_markers_for_delete): Move calculation of marker
+       adjustments to undo.c's record_marker_adjustments.  Note that
+       fileio.c's decide_coding_unwind is another caller to
+       adjust_markers_for_delete.  Because it has undo list bound to t,
+       it does not rely on adjust_markers_for_delete to record marker
+       adjustments.
+       (del_range_2): Swap call to record_delete and
+       adjust_markers_for_delete so as undo marker adjustments are
+       recorded before current deletion's adjustments, as before.
+       (adjust_after_replace):
+       (replace_range): Pass value for new record_markers arg to
+       delete_record.
+       * undo.c (record_marker_adjustment): Renamed to
+       record_marker_adjustments and made static.
+       (record_delete): Check record_markers arg and call
+       record_marker_adjustments.
+       (record_change): Pass value for new record_markers arg to
+       delete_record.
+       (record_point): at_boundary calculation no longer needs to account
+       for marker adjustments.
+
 2014-03-24  Martin Rudalics  <address@hidden>
 
        * w32term.c (x_set_window_size): Refine fix from 2014-03-14

=== modified file 'src/insdel.c'
--- a/src/insdel.c      2014-01-01 17:44:48 +0000
+++ b/src/insdel.c      2014-03-25 02:47:39 +0000
@@ -233,34 +233,9 @@
       /* Here's the case where a marker is inside text being deleted.  */
       else if (charpos > from)
        {
-         if (! m->insertion_type)
-           { /* Normal 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.  */
-             XSETMISC (marker, m);
-             record_marker_adjustment (marker, from - charpos);
-           }
-         else if (charpos < to)
-           { /* Before-insertion 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.  */
-             XSETMISC (marker, m);
-             record_marker_adjustment (marker, to - charpos);
-           }
          m->charpos = from;
          m->bytepos = from_byte;
        }
-      /* Here's the case where a before-insertion marker is immediately
-        before the deleted region.  */
-      else if (charpos == from && m->insertion_type)
-       {
-         /* Undoing the change uses normal insertion, which will
-            incorrectly make MARKER move forward, so we arrange for it
-            to then move backward to the correct place at the beginning
-            of the deleted region.  */
-         XSETMISC (marker, m);
-         record_marker_adjustment (marker, to - from);
-       }
     }
 }
 
@@ -1219,7 +1194,7 @@
                               from + len, from_byte + len_byte, 0);
 
   if (nchars_del > 0)
-    record_delete (from, prev_text);
+    record_delete (from, prev_text, false);
   record_insert (from, len);
 
   if (len > nchars_del)
@@ -1384,7 +1359,7 @@
   if (!NILP (deletion))
     {
       record_insert (from + SCHARS (deletion), inschars);
-      record_delete (from, deletion);
+      record_delete (from, deletion, false);
     }
 
   GAP_SIZE -= outgoing_insbytes;
@@ -1716,13 +1691,14 @@
   else
     deletion = Qnil;
 
-  /* Relocate all markers pointing into the new, larger gap
-     to point at the end of the text before the gap.
-     Do this before recording the deletion,
-     so that undo handles this after reinserting the text.  */
+  /* Record marker adjustments, and text deletion into undo
+     history.  */
+  record_delete (from, deletion, true);
+
+  /* Relocate all markers pointing into the new, larger gap to point
+     at the end of the text before the gap.  */
   adjust_markers_for_delete (from, from_byte, to, to_byte);
 
-  record_delete (from, deletion);
   MODIFF++;
   CHARS_MODIFF = MODIFF;
 

=== modified file 'src/lisp.h'
--- a/src/lisp.h        2014-02-28 21:45:34 +0000
+++ b/src/lisp.h        2014-03-25 02:47:39 +0000
@@ -4198,9 +4198,8 @@
 extern Lisp_Object Qapply;
 extern Lisp_Object Qinhibit_read_only;
 extern void truncate_undo_list (struct buffer *);
-extern void record_marker_adjustment (Lisp_Object, ptrdiff_t);
 extern void record_insert (ptrdiff_t, ptrdiff_t);
-extern void record_delete (ptrdiff_t, Lisp_Object);
+extern void record_delete (ptrdiff_t, Lisp_Object, bool);
 extern void record_first_change (void);
 extern void record_change (ptrdiff_t, ptrdiff_t);
 extern void record_property_change (ptrdiff_t, ptrdiff_t,

=== modified file 'src/undo.c'
--- a/src/undo.c        2014-01-01 07:43:34 +0000
+++ b/src/undo.c        2014-03-25 02:47:39 +0000
@@ -75,27 +75,8 @@
     Fundo_boundary ();
   last_undo_buffer = current_buffer;
 
-  if (CONSP (BVAR (current_buffer, undo_list)))
-    {
-      /* Set AT_BOUNDARY 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 @@
                  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,36 +200,17 @@
       record_point (beg);
     }
 
+  /* 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 (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;
-
-  bset_undo_list
-    (current_buffer,
-     Fcons (Fcons (marker, make_number (adjustment)),
-           BVAR (current_buffer, undo_list)));
-}
-
 /* Record that a replacement is about to take place,
    for LENGTH characters at location BEG.
    The replacement must not change the number of characters.  */
@@ -206,7 +218,7 @@
 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);
 }
 

=== modified file 'test/ChangeLog'
--- a/test/ChangeLog    2014-03-23 06:00:18 +0000
+++ b/test/ChangeLog    2014-03-25 02:47:39 +0000
@@ -1,3 +1,11 @@
+2014-03-24  Barry O'Reilly  <address@hidden>
+
+       * undo-tests.el (undo-test-marker-adjustment-nominal):
+       (undo-test-region-t-marker): New tests of marker adjustments.
+       (undo-test-marker-adjustment-moved):
+       (undo-test-region-mark-adjustment): New tests to demonstrate
+       bug#16818, which fail without the fix.
+
 2014-03-23  Daniel Colascione  <address@hidden>
 
        * automated/cl-lib.el (cl-lib-keyword-names-versus-values): New

=== modified file 'test/automated/undo-tests.el'
--- a/test/automated/undo-tests.el      2014-03-02 17:37:32 +0000
+++ b/test/automated/undo-tests.el      2014-03-25 02:47:39 +0000
@@ -268,6 +268,104 @@
     (should (string= (buffer-string)
                      "This sentence corrupted?aaa"))))
 
+(ert-deftest undo-test-marker-adjustment-nominal ()
+  "Test nominal behavior of marker adjustments."
+  (with-temp-buffer
+    (buffer-enable-undo)
+    (insert "abcdefg")
+    (undo-boundary)
+    (let ((m (make-marker)))
+      (set-marker m 2 (current-buffer))
+      (goto-char (point-min))
+      (delete-forward-char 3)
+      (undo-boundary)
+      (should (= (point-min) (marker-position m)))
+      (undo)
+      (undo-boundary)
+      (should (= 2 (marker-position m))))))
+
+(ert-deftest undo-test-region-t-marker ()
+  "Test undo in region containing marker with t insertion-type."
+  (with-temp-buffer
+    (buffer-enable-undo)
+    (transient-mark-mode 1)
+    (insert "abcdefg")
+    (undo-boundary)
+    (let ((m (make-marker)))
+      (set-marker-insertion-type m t)
+      (set-marker m (point-min) (current-buffer)) ; m at a
+      (goto-char (+ 2 (point-min)))
+      (push-mark (point) t t)
+      (setq mark-active t)
+      (goto-char (point-min))
+      (delete-forward-char 1) ;; delete region covering "ab"
+      (undo-boundary)
+      (should (= (point-min) (marker-position m)))
+      ;; Resurrect "ab". m's insertion type means the reinsertion
+      ;; moves it forward 2, and then the marker adjustment returns it
+      ;; to its rightful place.
+      (undo)
+      (undo-boundary)
+      (should (= (point-min) (marker-position m))))))
+
+(ert-deftest undo-test-marker-adjustment-moved ()
+  "Test marker adjustment behavior when the marker moves.
+Demonstrates bug 16818."
+  (with-temp-buffer
+    (buffer-enable-undo)
+    (insert "abcdefghijk")
+    (undo-boundary)
+    (let ((m (make-marker)))
+      (set-marker m 2 (current-buffer)) ; m at b
+      (goto-char (point-min))
+      (delete-forward-char 3) ; m at d
+      (undo-boundary)
+      (set-marker m 4) ; m at g
+      (undo)
+      (undo-boundary)
+      ;; m still at g, but shifted 3 because deletion undone
+      (should (= 7 (marker-position m))))))
+
+(ert-deftest undo-test-region-mark-adjustment ()
+  "Test that the mark's marker adjustment in undo history doesn't
+obstruct undo in region from finding the correct change group.
+Demonstrates bug 16818."
+  (with-temp-buffer
+    (buffer-enable-undo)
+    (transient-mark-mode 1)
+    (insert "First line\n")
+    (insert "Second line\n")
+    (undo-boundary)
+
+    (goto-char (point-min))
+    (insert "aaa")
+    (undo-boundary)
+
+    (undo)
+    (undo-boundary)
+
+    (goto-char (point-max))
+    (insert "bbb")
+    (undo-boundary)
+
+    (push-mark (point) t t)
+    (setq mark-active t)
+    (goto-char (- (point) 3))
+    (delete-forward-char 1)
+    (undo-boundary)
+
+    (insert "bbb")
+    (undo-boundary)
+
+    (goto-char (point-min))
+    (push-mark (point) t t)
+    (setq mark-active t)
+    (goto-char (+ (point) 3))
+    (undo)
+    (undo-boundary)
+
+    (should (string= (buffer-string) "aaaFirst line\nSecond line\nbbb"))))
+
 (defun undo-test-all (&optional interactive)
   "Run all tests for \\[undo]."
   (interactive "p")


reply via email to

[Prev in Thread] Current Thread [Next in Thread]