diff options
| author | Mauro Aranda | 2025-01-17 17:12:08 -0300 |
|---|---|---|
| committer | Eli Zaretskii | 2025-02-05 15:08:28 +0200 |
| commit | 85113fcda97970bc2468f409278e27d6570fc76f (patch) | |
| tree | d91a3ca509cf9f26075d3c39b592a3e86cc6fc98 | |
| parent | a1f2f5995d69db646f58a7203ab6208556f0df4b (diff) | |
| download | emacs-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.el | 49 | ||||
| -rw-r--r-- | test/lisp/wid-edit-tests.el | 51 |
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 | |||
| 1736 | Usually, the :from marker has type t, while the :to marker has type nil. | ||
| 1737 | When recreating a child or a button inside a composite widget right at these | ||
| 1738 | markers, they have to be changed to nil and t respectively, | ||
| 1739 | so that the WIDGET's parent (if any), properly contains all of its | ||
| 1740 | recreated children and buttons. | ||
| 1741 | |||
| 1742 | Prepares also the markers of the WIDGET's grandparent, if necessary. | ||
| 1743 | |||
| 1744 | Returns 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 | |||
| 1766 | MARKERS is a list of the form (MARKER . NEW-TYPE), as returned by | ||
| 1767 | `widget--prepare-markers-for-inside-insertion' and this function sets MARKER | ||
| 1768 | to NEW-TYPE. | ||
| 1769 | |||
| 1770 | Coupled with `widget--prepare-parent-for-inside-insertion', this has the effect | ||
| 1771 | of setting markers back to the type needed for insertions that do not belong | ||
| 1772 | to 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 | |||
| 439 | Test 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 | |||
| 462 | Test the uncommon situation in which we might need to prepare the grandparent's | ||
| 463 | markers (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 |