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

From: Gerd Möllmann <[email-masked]>
Date: Thu, 14 Aug 2025 13:25:39 +0200

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

> Alan Mackenzie <[email-masked]> writes:
>
>> I more wonder why those limits are so low.  It's a long time since PCs'
>> RAM capacity was measured in mere megabytes.
>
> That, and then the fact that I can produce very long undo list, as long
> as they don't exceed the size in bytes limits. And these lists can
> contain a lot of markers.
>
> (defun my-test-undo-list ()
>   (interactive)
>   (with-current-buffer (get-buffer-create "undo-list-test.txt")
>     (erase-buffer)
>     (setf buffer-undo-list nil)
>     (dotimes (i 1000)
>       (dotimes (j 10)
> 	(insert "abcd")
> 	(dotimes (i 5) (point-marker))
> 	(backward-delete-char 1)
> 	(undo-boundary)
> 	(insert "\n")))
>     '(igc-collect)
>     (message "undo-list length %d (%d)" (length buffer-undo-list) n-compacted)
>     '(kill-buffer (current-buffer))))
>
> The above produces an undo list with length 80000.
>
> I think it might make sense to limit that to some reasonable number.

@Oscar: the patch below adds such a variable. My gut feeling is that
might well resolve the whole problem, without the other stuff, i.e.
applied directly to feature/igc. Sorry for the back and forth.

>From 2f2a1ea607938dea4584aba6b0823aab36493fed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= <[email-masked]>
Date: Thu, 14 Aug 2025 13:17:06 +0200
Subject: [PATCH] New variable undo-group-limit

* src/undo.c (truncate_undo_list): Use undo_group_limit.
(syms_of_undo): Add DEFVAR_INT.
---
 src/undo.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/undo.c b/src/undo.c
index 8702d937ed5..6046b769d4d 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -376,6 +376,7 @@ truncate_undo_list (struct buffer *b)
     last_boundary = prev;
 
   /* Keep additional undo data, if it fits in the limits.  */
+  ptrdiff_t ngroups = 0;
   while (CONSP (next))
     {
       Lisp_Object elt;
@@ -387,6 +388,8 @@ truncate_undo_list (struct buffer *b)
 	 the higher threshold undo_strong_limit, we truncate before it.  */
       if (NILP (elt))
 	{
+	  if (++ngroups > undo_group_limit)
+	    break;
 	  if (size_so_far > undo_strong_limit)
 	    break;
 	  last_boundary = prev;
@@ -461,6 +464,9 @@ syms_of_undo (void)
 which includes both saved text and other data.  */);
   undo_strong_limit = 240000;
 
+  DEFVAR_INT ("undo-group-limit", undo_group_limit, doc: /* */);
+  undo_group_limit = 200;
+
   DEFVAR_LISP ("undo-outer-limit", Vundo_outer_limit,
 	      doc: /* Outer limit on size of undo information for one command.
 At garbage collection time, if the current command has produced
-- 
2.50.1