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

From: Pip Cet <[email-masked]>
Date: Sat, 09 Aug 2025 08:55:38 +0000

Gerd Möllmann <[email-masked]> writes:

> Ó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
>
> =>
>
> 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

Hmm. I'm seeing different results: "only" five markers between "a"
insertions.

> 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.
>
> Adding Stef.

Well, compact_undo_list is a nop with HAVE_MPS:

static Lisp_Object
compact_undo_list (Lisp_Object list)
{
#ifndef HAVE_MPS
  Lisp_Object tail, *prev = &list;

  for (tail = list; CONSP (tail); tail = XCDR (tail))
    {
      if (CONSP (XCAR (tail))
	  && MARKERP (XCAR (XCAR (tail)))
	  && !vectorlike_marked_p (&XMARKER (XCAR (XCAR (tail)))->header))
	*prev = XCDR (tail);
      else
	prev = xcdr_addr (tail);
    }
#endif
  return list;
}

We still truncate undo lists, but we don't "compact" them, i.e. treat
them as weak structures and remove markers that are referred to only in
undo lists. Correct?

The "obvious" fix would be to use a weak hash table to associate undo
information with markers rather than keeping them in the undo
list. However, that sounds quite hard to do, and it might make undo a
lot more expensive.

Pip