(call-interactively
(widget-get widget :complete-function))))))))
+(defun widget--prepare-markers-for-inside-insertion (widget)
+ "Prepare the WIDGET's parent for insertions inside it, if necessary.
+
+Usually, the :from marker has type t, while the :to marker has type nil.
+When recreating a child or a button inside a composite widget right at these
+markers, they have to be changed to nil and t respectively,
+so that the WIDGET's parent (if any), properly contains all of its
+recreated children and buttons.
+
+Prepares also the markers of the WIDGET's grandparent, if necessary.
+
+Returns a list of the markers that had its type changed, for later resetting."
+ (let* ((parent (widget-get widget :parent))
+ (parent-from-marker (and parent (widget-get parent :from)))
+ (parent-to-marker (and parent (widget-get parent :to)))
+ (lst nil)
+ (pos (point)))
+ (when (and parent-from-marker
+ (eq pos (marker-position parent-from-marker))
+ (marker-insertion-type parent-from-marker))
+ (set-marker-insertion-type parent-from-marker nil)
+ (push (cons parent-from-marker t) lst))
+ (when (and parent-to-marker
+ (eq pos (marker-position parent-to-marker))
+ (not (marker-insertion-type parent-to-marker)))
+ (set-marker-insertion-type parent-to-marker t)
+ (push (cons parent-to-marker nil) lst))
+ (when lst
+ (nconc lst (widget--prepare-markers-for-inside-insertion parent)))))
+
+(defun widget--revert-markers-for-outside-insertion (markers)
+ "Revert MARKERS for insertions that do not belong to a widget.
+
+MARKERS is a list of the form (MARKER . NEW-TYPE), as returned by
+`widget--prepare-markers-for-inside-insertion' and this function sets MARKER
+to NEW-TYPE.
+
+Coupled with `widget--prepare-parent-for-inside-insertion', this has the effect
+of setting markers back to the type needed for insertions that do not belong
+to a given widget."
+ (dolist (marker markers)
+ (set-marker-insertion-type (car marker) (cdr marker))))
+
(defun widget-default-create (widget)
"Create WIDGET at point in the current buffer."
(widget-specify-insert
button-begin button-end
sample-begin sample-end
doc-begin doc-end
- value-pos)
+ value-pos
+ (markers (widget--prepare-markers-for-inside-insertion widget)))
(insert (widget-get widget :format))
(goto-char from)
;; Parse escapes in format.
(widget-specify-doc widget doc-begin doc-end))
(when value-pos
(goto-char value-pos)
- (widget-apply widget :value-create)))
+ (widget-apply widget :value-create))
+ (widget--revert-markers-for-outside-insertion markers))
(let ((from (point-min-marker))
(to (point-max-marker)))
(set-marker-insertion-type from t)
(should-not (overlay-buffer field-overlay))
(should-not (overlay-buffer field-end-overlay)))))
+;; The following two tests are for Bug#69941. Markers need to be prepared
+;; against "inside" insertions at them. That is, a recreated child should
+;; still be covered by the parent's :from and :to markers.
+(ert-deftest widget-test-insertion-at-parent-markers ()
+ "Test that recreating a child keeps the parent's markers covering it.
+
+Test the most common situation, where only one parent needs to be adjusted."
+ (with-temp-buffer
+ (let* ((group (widget-create 'group
+ :format "%v"
+ '(item :value 1 :format "%v")))
+ (item (car (widget-get group :children)))
+ (ofrom (marker-position (widget-get group :from)))
+ (oto (marker-position (widget-get group :to))))
+ (widget-insert "\n")
+ (widget-setup)
+ ;; Change item, without recreating the group. This causes changes
+ ;; right at the :from and :to markers, and if they don't have
+ ;; the right type, the group's :from-:to span won't include its
+ ;; child, the item widget, anymore.
+ (widget-value-set item 2)
+ ;; The positions should be the same as they were when the group
+ ;; widget was first created.
+ (should (= ofrom (widget-get group :from)))
+ (should (= oto (widget-get group :to))))))
+
+(ert-deftest widget-test-insertion-at-parent-markers-2 ()
+ "Test that recreating a child keeps the parent's marker covering it.
+
+Test the uncommon situation in which we might need to prepare the grandparent's
+markers (and so on) as well."
+ (with-temp-buffer
+ (let* ((group (widget-create '(group
+ :format "%v"
+ (group
+ :format "%v"
+ (item :value 1 :format "%v")))))
+ (group2 (car (widget-get group :children)))
+ (item (car (widget-get group2 :children)))
+ (ofrom (marker-position (widget-get group :from)))
+ (oto (marker-position (widget-get group :to)))
+ (ofrom2 (marker-position (widget-get group2 :from)))
+ (oto2 (marker-position (widget-get group2 :to))))
+ (widget-insert "\n")
+ (widget-setup)
+ (widget-value-set item 2)
+ (should (= ofrom (widget-get group :from)))
+ (should (= oto (widget-get group :to)))
+ (should (= ofrom2 (widget-get group2 :from)))
+ (should (= oto2 (widget-get group2 :to))))))
+
;;; wid-edit-tests.el ends here