Subject: Re: bug#16818: Acknowledgement (Undo in region after markers in undo history relocated)

From: Stefan <[email-masked]>
Date: Mon, 17 Mar 2014 20:02:43 -0400

>> The primitive-undo fix should be safe enough for 24.4, so if you
>> want to code this up and install it, feel free.
> I have attached the patch for this, which I'll install if nothing
> comes up from review.

Thanks.  A few comments below.

--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2229,6 +2229,7 @@
         (did-apply nil)
         (next nil))
     (while (> arg 0)
+      (let (del-pos)
       (while (setq next (pop list))     ;Exit inner loop at undo boundary.
         ;; Handle an integer by setting point to that value.
         (pcase next
@@ -2289,6 +2290,7 @@
            (when (let ((apos (abs pos)))
                    (or (< apos (point-min)) (> apos (point-max))))
              (error "Changes to be undone are outside visible portion of buffer"))
+             (setq del-pos pos)
            (if (< pos 0)
                (progn
                  (goto-char (- pos))
@@ -2303,11 +2305,14 @@
              (goto-char pos)))
           ;; (MARKER . OFFSET) means a marker MARKER was adjusted by OFFSET.
           (`(,(and marker (pred markerp)) . ,(and offset (pred integerp)))
-           (when (marker-buffer marker)
+             (when (and del-pos
+                        (integerp (marker-position marker))
+                        (= del-pos marker)
+                        (marker-buffer marker))
              (set-marker marker
                          (- marker offset)
                          (marker-buffer marker))))
-          (_ (error "Unrecognized entry in undo list %S" next))))
+            (_ (error "Unrecognized entry in undo list %S" next)))))
       (setq arg (1- arg)))
     ;; Make sure an apply entry produces at least one undo entry,
     ;; so the test in `undo' for continuing an undo series

The "move-after" markers will be at (+ del-pos (length inserted-text))
rather than at del-pos.  Also, a lone (MARKER . OFFSET) entry will
arbitrarily use some unrelated earlier del-pos.  Admittedly, such lone
(MARKER . OFFSET) entries should/will never happen, but I'd rather we
catch them and emit a warning than silently do something arbitrary.

IOW, I think we should only change the entry corresponding to a deletion
such that it directly handles all the immediately following
marker-adjustments (it can check them before doing the insertion, hence
using del-pos without having to care about insert-after vs
insert-before).  Then the current handling of marker-adjustments can be
changed to emit a warning.

> @@ -2351,18 +2354,30 @@ If we find an element that crosses an edge of this region,
>  we stop and ignore all further elements."
>    (let ((undo-list-copy (undo-copy-list buffer-undo-list))
>  	(undo-list (list nil))
> -	undo-adjusted-markers
> +        ;; The position of a deletion record (TEXT . POSITION) of the
> +        ;; current change group.
> +        ;;
> +        ;; This is used to check that marker adjustmenets are in the
> +        ;; region.  Bug 16818 describes why the marker's position is
> +        ;; not suitable.
> +        del-pos
>  	some-rejected
>  	undo-elt temp-undo-list delta)
>      (while undo-list-copy
>        (setq undo-elt (car undo-list-copy))
> +      ;; Update del-pos
> +      (if undo-elt
> +          (when (and (consp undo-elt) (stringp (car undo-elt)))
> +            (setq del-pos (cdr undo-elt)))
> +        ;; Undo boundary means new change group, so unset del-pos
> +        (setq del-pos nil))
>        (let ((keep-this
>  	     (cond ((and (consp undo-elt) (eq (car undo-elt) t))
>  		    ;; This is a "was unmodified" element.
>  		    ;; Keep it if we have kept everything thus far.
>  		    (not some-rejected))
>  		   (t
> -		    (undo-elt-in-region undo-elt start end)))))
> +		    (undo-elt-in-region undo-elt start end del-pos)))))
>  	(if keep-this
>  	    (progn
>  	      (setq end (+ end (cdr (undo-delta undo-elt))))

I'd have the same comment here, but if we emit a warning for sole
marker-adjustments in the "non-region" code, we don't really have to
worry about them here.

But this is also affected by the issue of "move-after" markers where
`del-pos' is not sufficient info since those markers won't be at del-pos
any more if they were at del-pos before we inserted the deleted text.

        Stefan