Subject: Re: bug#42801: 26.3; customize-apropos shows bogus State buttons after saving

From: Mauro Aranda <[email-masked]>
Date: Tue, 11 Aug 2020 10:47:59 -0300

tags 42801 patch
quit

Szunti <[email-masked]> writes:

> 1) M-x customize-apropos RET inhibit RET
> 2) Toggled Inhibit Startup Screen's value:
> Stepped with TAB-s to Inhibit Startup Screen's arrow, RET to open the
> details, another TAB onto Toggle button and RET
> 3) Then typed C-x C-s, confirmed with yes RET
>
> After this the customization buffer refreshed and all options despite
> closed, got a State button with STANDARD value.
>
> S-TAB thrice to get onto the State button of the previous option
> (Inhibit Startup Echo Area Message) and after RET on the State button,
> the echo area tells,
>
> Symbol’s function definition is void: nil

Thanks for the bug report.

I can reproduce this bug in Emacs 26.3.  On current master, the bug
manifests
differently: we don't get the State buttons, but we do get the message:
Symbol's function definition is void: nil

The difference lies in `custom-variable-modified-p' bugging out before we
draw
the bogus State buttons for all options, because it is not supposed to
handle
hidden widgets.

The bug is that Custom-save recalculates the state and redraws all widgets,
even the hidden ones.  I propose the attached patch, that changes
Custom-save to only act on edited widgets in the buffer.
From a659dcb694f8eb890d33f3ee1e5972ea7455ee64 Mon Sep 17 00:00:00 2001
From: Mauro Aranda <[email-masked]>
Date: Tue, 11 Aug 2020 09:36:10 -0300
Subject: [PATCH] Make sure we only act on edited widgets in Custom-save

* lisp/cus-edit.el (Custom-save): Only act on edited widgets in the
  buffer.  If we attempt to redraw all widgets, we confuse
  custom-variable-modified-p, or we end up drawing State buttons for
  all options, including the hidden ones.  (Bug#42801)
---
 lisp/cus-edit.el | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index 16695967df..c8cc6b800b 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -801,16 +801,19 @@ Custom-save
 If a setting was edited and set before, this saves it.  If a
 setting was merely edited before, this sets it then saves it."
   (interactive)
-  (when (custom-command-apply
-	 (lambda (child)
-	   (when (memq (widget-get child :custom-state)
-		       '(modified set changed rogue))
-	     (widget-apply child :custom-mark-to-save)))
-	 "Save all settings in this buffer? " t)
-    ;; Save changes to buffer and redraw.
-    (custom-save-all)
-    (dolist (child custom-options)
-      (widget-apply child :custom-state-set-and-redraw))))
+  (let (edited-widgets)
+    (when (custom-command-apply
+	   (lambda (child)
+	     (when (memq (widget-get child :custom-state)
+		         '(modified set changed rogue))
+               (push child edited-widgets)
+	       (widget-apply child :custom-mark-to-save)))
+	   "Save all settings in this buffer? " t)
+      ;; Save changes to buffer.
+      (custom-save-all)
+      ;; Redraw and recalculate the state when necessary.
+      (dolist (widget edited-widgets)
+        (widget-apply widget :custom-state-set-and-redraw)))))
 
 (defun custom-reset (_widget &optional event)
   "Select item from reset menu."
-- 
2.28.0