aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMauro Aranda2025-01-17 17:12:08 -0300
committerEli Zaretskii2025-02-05 15:08:28 +0200
commit85113fcda97970bc2468f409278e27d6570fc76f (patch)
treed91a3ca509cf9f26075d3c39b592a3e86cc6fc98
parenta1f2f5995d69db646f58a7203ab6208556f0df4b (diff)
downloademacs-85113fcda97970bc2468f409278e27d6570fc76f.tar.gz
emacs-85113fcda97970bc2468f409278e27d6570fc76f.zip
Prepare markers for insertions inside of a widget
Recreating child widgets without recreating the parent widget may lead to situations where the parent widget doesn't cover its children or buttons entirely anymore. This bug manifests as a faulty fontification of children or buttons, for example. (Bug#69941) * lisp/wid-edit.el (widget--prepare-markers-for-inside-insertion) (widget--prepare-markers-for-outside-insertion): New functions. (widget-default-create): Use them. * test/lisp/wid-edit-tests.el (widget-test-insertion-at-parent-markers) (widget-test-insertion-at-parent-markers-2): New tests.
-rw-r--r--lisp/wid-edit.el49
-rw-r--r--test/lisp/wid-edit-tests.el51
2 files changed, 98 insertions, 2 deletions
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 7260f60196a..a1d4c4850ae 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -1730,6 +1730,49 @@ The value of the :type attribute should be an unconverted widget type."
1730 (call-interactively 1730 (call-interactively
1731 (widget-get widget :complete-function)))))))) 1731 (widget-get widget :complete-function))))))))
1732 1732
1733(defun widget--prepare-markers-for-inside-insertion (widget)
1734 "Prepare the WIDGET's parent for insertions inside it, if necessary.
1735
1736Usually, the :from marker has type t, while the :to marker has type nil.
1737When recreating a child or a button inside a composite widget right at these
1738markers, they have to be changed to nil and t respectively,
1739so that the WIDGET's parent (if any), properly contains all of its
1740recreated children and buttons.
1741
1742Prepares also the markers of the WIDGET's grandparent, if necessary.
1743
1744Returns a list of the markers that had its type changed, for later resetting."
1745 (let* ((parent (widget-get widget :parent))
1746 (parent-from-marker (and parent (widget-get parent :from)))
1747 (parent-to-marker (and parent (widget-get parent :to)))
1748 (lst nil)
1749 (pos (point)))
1750 (when (and parent-from-marker
1751 (eq pos (marker-position parent-from-marker))
1752 (marker-insertion-type parent-from-marker))
1753 (set-marker-insertion-type parent-from-marker nil)
1754 (push (cons parent-from-marker t) lst))
1755 (when (and parent-to-marker
1756 (eq pos (marker-position parent-to-marker))
1757 (not (marker-insertion-type parent-to-marker)))
1758 (set-marker-insertion-type parent-to-marker t)
1759 (push (cons parent-to-marker nil) lst))
1760 (when lst
1761 (nconc lst (widget--prepare-markers-for-inside-insertion parent)))))
1762
1763(defun widget--revert-markers-for-outside-insertion (markers)
1764 "Revert MARKERS for insertions that do not belong to a widget.
1765
1766MARKERS is a list of the form (MARKER . NEW-TYPE), as returned by
1767`widget--prepare-markers-for-inside-insertion' and this function sets MARKER
1768to NEW-TYPE.
1769
1770Coupled with `widget--prepare-parent-for-inside-insertion', this has the effect
1771of setting markers back to the type needed for insertions that do not belong
1772to a given widget."
1773 (dolist (marker markers)
1774 (set-marker-insertion-type (car marker) (cdr marker))))
1775
1733(defun widget-default-create (widget) 1776(defun widget-default-create (widget)
1734 "Create WIDGET at point in the current buffer." 1777 "Create WIDGET at point in the current buffer."
1735 (widget-specify-insert 1778 (widget-specify-insert
@@ -1737,7 +1780,8 @@ The value of the :type attribute should be an unconverted widget type."
1737 button-begin button-end 1780 button-begin button-end
1738 sample-begin sample-end 1781 sample-begin sample-end
1739 doc-begin doc-end 1782 doc-begin doc-end
1740 value-pos) 1783 value-pos
1784 (markers (widget--prepare-markers-for-inside-insertion widget)))
1741 (insert (widget-get widget :format)) 1785 (insert (widget-get widget :format))
1742 (goto-char from) 1786 (goto-char from)
1743 ;; Parse escapes in format. 1787 ;; Parse escapes in format.
@@ -1797,7 +1841,8 @@ The value of the :type attribute should be an unconverted widget type."
1797 (widget-specify-doc widget doc-begin doc-end)) 1841 (widget-specify-doc widget doc-begin doc-end))
1798 (when value-pos 1842 (when value-pos
1799 (goto-char value-pos) 1843 (goto-char value-pos)
1800 (widget-apply widget :value-create))) 1844 (widget-apply widget :value-create))
1845 (widget--revert-markers-for-outside-insertion markers))
1801 (let ((from (point-min-marker)) 1846 (let ((from (point-min-marker))
1802 (to (point-max-marker))) 1847 (to (point-max-marker)))
1803 (set-marker-insertion-type from t) 1848 (set-marker-insertion-type from t)
diff --git a/test/lisp/wid-edit-tests.el b/test/lisp/wid-edit-tests.el
index c18e6d14c4c..e34aa64f8d1 100644
--- a/test/lisp/wid-edit-tests.el
+++ b/test/lisp/wid-edit-tests.el
@@ -430,4 +430,55 @@ return nil, even with a non-nil bubblep argument."
430 (should-not (overlay-buffer field-overlay)) 430 (should-not (overlay-buffer field-overlay))
431 (should-not (overlay-buffer field-end-overlay))))) 431 (should-not (overlay-buffer field-end-overlay)))))
432 432
433;; The following two tests are for Bug#69941. Markers need to be prepared
434;; against "inside" insertions at them. That is, a recreated child should
435;; still be covered by the parent's :from and :to markers.
436(ert-deftest widget-test-insertion-at-parent-markers ()
437 "Test that recreating a child keeps the parent's markers covering it.
438
439Test the most common situation, where only one parent needs to be adjusted."
440 (with-temp-buffer
441 (let* ((group (widget-create 'group
442 :format "%v"
443 '(item :value 1 :format "%v")))
444 (item (car (widget-get group :children)))
445 (ofrom (marker-position (widget-get group :from)))
446 (oto (marker-position (widget-get group :to))))
447 (widget-insert "\n")
448 (widget-setup)
449 ;; Change item, without recreating the group. This causes changes
450 ;; right at the :from and :to markers, and if they don't have
451 ;; the right type, the group's :from-:to span won't include its
452 ;; child, the item widget, anymore.
453 (widget-value-set item 2)
454 ;; The positions should be the same as they were when the group
455 ;; widget was first created.
456 (should (= ofrom (widget-get group :from)))
457 (should (= oto (widget-get group :to))))))
458
459(ert-deftest widget-test-insertion-at-parent-markers-2 ()
460 "Test that recreating a child keeps the parent's marker covering it.
461
462Test the uncommon situation in which we might need to prepare the grandparent's
463markers (and so on) as well."
464 (with-temp-buffer
465 (let* ((group (widget-create '(group
466 :format "%v"
467 (group
468 :format "%v"
469 (item :value 1 :format "%v")))))
470 (group2 (car (widget-get group :children)))
471 (item (car (widget-get group2 :children)))
472 (ofrom (marker-position (widget-get group :from)))
473 (oto (marker-position (widget-get group :to)))
474 (ofrom2 (marker-position (widget-get group2 :from)))
475 (oto2 (marker-position (widget-get group2 :to))))
476 (widget-insert "\n")
477 (widget-setup)
478 (widget-value-set item 2)
479 (should (= ofrom (widget-get group :from)))
480 (should (= oto (widget-get group :to)))
481 (should (= ofrom2 (widget-get group2 :from)))
482 (should (= oto2 (widget-get group2 :to))))))
483
433;;; wid-edit-tests.el ends here 484;;; wid-edit-tests.el ends here