Subject: bug#69941: 30.0.50; Faulty fontification of radio button widgets

From: Stefan Monnier <[email-masked]>
Date: Thu, 16 Jan 2025 23:28:02 -0500

> I attach a patch with a first idea (based on Stefan's suggestions),
> where we "protect" the parent's markers, inside widget-default-create.
> This is so we can try to catch almost all situations where this bug
> might arise.

I can't judge whether `widget-default-create` covers all the cases, but
other than that, it's looking good (well, I find this "set and later reset"
to be rather ugly/brittle, but that's probably as good as it gets given
what we have).

> +(defun widget--protect-parent-markers (widget)
> +  "Protect the :from and :to markers of the WIDGET's parent, if necessary.
> +
> +Usually, the :from marker has type t, while the :to marker has type nil.
> +When creating a child or a button inside a composite widget, they have to be
> +changed to nil and t respectively, so that the WIDGET's parent (if any),
> +properly contains all of its children and buttons."
> +  (let* ((parent (widget-get widget :parent))
> +         (parent-from-marker (and parent (widget-get parent :from)))
> +         (parent-to-marker (and parent (widget-get parent :to)))
> +         (pos (point)))
> +    (when (and parent-from-marker
> +               (eq pos (marker-position parent-from-marker)))
> +      (set-marker-insertion-type parent-from-marker nil))
> +    (when (and parent-to-marker
> +               (eq pos (marker-position parent-to-marker)))
> +      (set-marker-insertion-type parent-to-marker t))))
> +
> +(defun widget--unprotect-parent-markers (widget)
> +  "Unprotect the :from and :to markers of the WIDGET's parent, if necessary.
> +
> +Changes the :from marker type back to t and the :to marker type back to nil."
> +  (let* ((parent (widget-get widget :parent))
> +         (parent-from-marker (and parent (widget-get parent :from)))
> +         (parent-to-marker (and parent (widget-get parent :to))))
> +    (when parent-from-marker
> +      (set-marker-insertion-type parent-from-marker t))
> +    (when parent-to-marker
> +      (set-marker-insertion-type parent-to-marker nil))))

I'm not a big fan of the name "protect" here, to be honest.
Maybe `widget--prepare-parent-for-insertion`?

> It doesn't catch _all_, in theory, since it might happen
> that a grandparent widget doesn't adjust its markers, but maybe that's
> unusual enough that we can get away with it.

We can probably handle that case easily by just adding to
`widget--protect-parent-markers` a recursive call if either the :from or
the :to marker had to be adjusted.  The only difficulty I see is making
sure we adjust all the makers in the "unprotect" case that we
protected earlier.  Maybe we should make
`widget--protect-parent-markers` return a list of (MARKER
. PREVIOUS-TYPE) so the `widget--unprotect-parent-markers`
would turn into something like (mapcar (apply-partially #'apply
#'set-marker-insertion-type) ...).
IIUC that list would be empty in the vast majority of cases.

        Stefan