Subject: Re: bug#79200: 31.0.50; Duplicated elements for '#<marker at' in buffer-undo-list

From: Alan Mackenzie <[email-masked]>
Date: Sat, 9 Aug 2025 16:00:13 +0000

Hello, everybody.

On Sat, Aug 09, 2025 at 10:29:15 +0200, Gerd Möllmann wrote:
> Óscar Fuentes <[email-masked]> writes:

> > emacs -Q

> > write in *scratch* some lines with random words:

> > arins
> > arseina
> > aierni

> > iosrn nioni
> > ionugfw

> > sronuf

> > Now undo a few of those lines (C-_), move the cursor and C-_ again to
> > redo.

> Interesting. I come from a slightly different angle, that in my mac+igc
> port I had sometimes only 3 undos before I got "no more undo
> information". and I thought I had messed up something.

> My cimpler recipe is

> 1. Make a new buffer x.el
> 2. Type an a
> 3. undo
> 4. C-h v buffer-undo-list

I have an equally simple recipe, which suggests undo is not the culprit.
Note step 3.

1. emacs -Q
2. Type asdf.
3. M-: buffer-undo-list.  This gives:

    (nil (148 . 152) (t . 0) nil (1 . 148) (t . 0))

4. Type <backspace>.
5. M-: buffer-undo-list.  This gives:

    (nil (#("f" 0 1 (fontified t)) . -151) (#<marker at 151 in *scratch*>
    . -1) (#<marker at 151 in *scratch*> . -1) (#<marker at 151 in
    *scratch*> . -1) (#<marker at 151 in *scratch*> . -1) (#<marker at
    151 in *scratch*> . -1) (#<marker at 15\1 in *scratch*> . -1)
    (#<marker at 151 in *scratch*> . -1) (#<marker at 151 in *scratch*> .
    -1) (#<marker at 151 in *scratch*> . -1) 152 ...)

  [ That enforced dumning down of the output by truncation is most
  annoying.  I haven't found a way to get a complete output. ]

Unlike other people here, I see this in the emacs-30 branch, too.

> => 

> Value:
> (nil ("a" . 1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) (#<marker at 1 in x.el> . -1)
>      (#<marker at 1 in x.el> . -1) nil (1 . 2) (t . 0))
> Local in buffer x.el; global value is nil

> So far I can't see what is the cause. I would have guessed something in
> or around primitive-undo, but that hasn't changed forever.

My theory is that the cause is general poor marker hygiene.  In the elisp
manual, there is (or was) a recommendation that after finishing use of a
marker, one should remove it from the buffer's list of markers with

    (move-marker foo nil)

(or the C equivalent).  There are lots of places (probably several
hundred) where this is not done.

I tried gdb with a break point on Fpoint_marker, but every hit I got
(about 15) was from save-excursion, which does clean up the marker it
uses for point.

I tried disabling inessential features like font-lock-mode, and setting
post-self-insert-hook to nil.  This made no essential difference fo
buffer-undo-list.

[ .... ]

-- 
Alan Mackenzie (Nuremberg, Germany).