diff options
| author | Jim Porter | 2025-05-28 09:55:58 -0700 |
|---|---|---|
| committer | Jim Porter | 2025-06-10 22:09:26 -0700 |
| commit | 4a3c8e6e1df44b187b7286747e363232e8b4e0ea (patch) | |
| tree | 415ffb4422e3457bb37ad8e15e892b74b448a405 | |
| parent | 7416595e2fc0ff676ef98a139328722ac9220ca0 (diff) | |
| download | emacs-4a3c8e6e1df44b187b7286747e363232e8b4e0ea.tar.gz emacs-4a3c8e6e1df44b187b7286747e363232e8b4e0ea.zip | |
Don't delete in-place when replacing a display property
When calling 'add-display-text-property' on a region of text that
already contains PROP, we first delete the old display specification
from the region. If the region's 'display' property is a list of
display specifications, we need to avoid destructively modifying the
list; other regions of text could be using the same list object. (For a
'display' property that's a vector or a single display spec, this
doesn't matter since we first make a new list in the code.)
In addition, be more careful when working with a display property like
((margin ...) ...). This is a single display specification, not a list
of display specs.
* lisp/emacs-lisp/subr-x.el (add-display-text-property): Don't delete
in-place for list values. Handle (margin ...) display specification
type correctly.
* test/lisp/emacs-lisp/subr-x-tests.el
(subr-x-test-add-display-text-property): Update test.
| -rw-r--r-- | lisp/emacs-lisp/subr-x.el | 20 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/subr-x-tests.el | 37 |
2 files changed, 44 insertions, 13 deletions
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index eaa8119ead7..4d56c67be5b 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el | |||
| @@ -438,22 +438,32 @@ this defaults to the current buffer." | |||
| 438 | (put-text-property sub-start sub-end 'display (list prop value) | 438 | (put-text-property sub-start sub-end 'display (list prop value) |
| 439 | object) | 439 | object) |
| 440 | ;; We have old properties. | 440 | ;; We have old properties. |
| 441 | (let ((vector nil)) | 441 | (let (type) |
| 442 | ;; Make disp into a list. | 442 | ;; Make disp into a list. |
| 443 | (setq disp | 443 | (setq disp |
| 444 | (cond | 444 | (cond |
| 445 | ((vectorp disp) | 445 | ((vectorp disp) |
| 446 | (setq vector t) | 446 | (setq type 'vector) |
| 447 | (seq-into disp 'list)) | 447 | (seq-into disp 'list)) |
| 448 | ((not (consp (car disp))) | 448 | ((or (not (consp (car-safe disp))) |
| 449 | ;; If disp looks like ((margin ...) ...), that's | ||
| 450 | ;; still a single display specification. | ||
| 451 | (eq (caar disp) 'margin)) | ||
| 452 | (setq type 'scalar) | ||
| 449 | (list disp)) | 453 | (list disp)) |
| 450 | (t | 454 | (t |
| 455 | (setq type 'list) | ||
| 451 | disp))) | 456 | disp))) |
| 452 | ;; Remove any old instances. | 457 | ;; Remove any old instances. |
| 453 | (when-let* ((old (assoc prop disp))) | 458 | (when-let* ((old (assoc prop disp))) |
| 454 | (setq disp (delete old disp))) | 459 | ;; If the property value was a list, don't modify the |
| 460 | ;; original value in place; it could be used by other | ||
| 461 | ;; regions of text. | ||
| 462 | (setq disp (if (eq type 'list) | ||
| 463 | (remove old disp) | ||
| 464 | (delete old disp)))) | ||
| 455 | (setq disp (cons (list prop value) disp)) | 465 | (setq disp (cons (list prop value) disp)) |
| 456 | (when vector | 466 | (when (eq type 'vector) |
| 457 | (setq disp (seq-into disp 'vector))) | 467 | (setq disp (seq-into disp 'vector))) |
| 458 | ;; Finally update the range. | 468 | ;; Finally update the range. |
| 459 | (put-text-property sub-start sub-end 'display disp object))) | 469 | (put-text-property sub-start sub-end 'display disp object))) |
diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index f6675637fef..5ffbe64ae40 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el | |||
| @@ -696,18 +696,39 @@ | |||
| 696 | (insert "Foo bar zot gazonk") | 696 | (insert "Foo bar zot gazonk") |
| 697 | (add-display-text-property 4 8 'height 2.0) | 697 | (add-display-text-property 4 8 'height 2.0) |
| 698 | (add-display-text-property 2 12 'raise 0.5) | 698 | (add-display-text-property 2 12 'raise 0.5) |
| 699 | (should (equal (get-text-property 2 'display) '(raise 0.5))) | 699 | (add-display-text-property 6 10 'height 1.0) |
| 700 | (should (equal (get-text-property 5 'display) | 700 | (should (equal-including-properties |
| 701 | '((raise 0.5) (height 2.0)))) | 701 | (buffer-string) |
| 702 | (should (equal (get-text-property 9 'display) '(raise 0.5)))) | 702 | #("Foo bar zot gazonk" |
| 703 | 1 3 (display (raise 0.5)) | ||
| 704 | 3 5 (display ((raise 0.5) (height 2.0))) | ||
| 705 | 5 9 (display ((height 1.0) (raise 0.5))) | ||
| 706 | 9 11 (display (raise 0.5)))))) | ||
| 703 | (with-temp-buffer | 707 | (with-temp-buffer |
| 704 | (insert "Foo bar zot gazonk") | 708 | (insert "Foo bar zot gazonk") |
| 705 | (put-text-property 4 8 'display [(height 2.0)]) | 709 | (put-text-property 4 8 'display [(height 2.0)]) |
| 706 | (add-display-text-property 2 12 'raise 0.5) | 710 | (add-display-text-property 2 12 'raise 0.5) |
| 707 | (should (equal (get-text-property 2 'display) '(raise 0.5))) | 711 | (add-display-text-property 6 10 'height 1.0) |
| 708 | (should (equal (get-text-property 5 'display) | 712 | (should (equal-including-properties |
| 709 | [(raise 0.5) (height 2.0)])) | 713 | (buffer-string) |
| 710 | (should (equal (get-text-property 9 'display) '(raise 0.5)))) | 714 | #("Foo bar zot gazonk" |
| 715 | 1 3 (display (raise 0.5)) | ||
| 716 | 3 5 (display [(raise 0.5) (height 2.0)]) | ||
| 717 | 5 7 (display [(height 1.0) (raise 0.5)]) | ||
| 718 | 7 9 (display ((height 1.0) (raise 0.5))) | ||
| 719 | 9 11 (display (raise 0.5)))))) | ||
| 720 | (with-temp-buffer | ||
| 721 | (insert "Foo bar zot gazonk") | ||
| 722 | (add-display-text-property 4 8 '(margin nil) "Hi") | ||
| 723 | (add-display-text-property 2 12 'raise 0.5) | ||
| 724 | (add-display-text-property 6 10 '(margin nil) "Bye") | ||
| 725 | (should (equal-including-properties | ||
| 726 | (buffer-string) | ||
| 727 | #("Foo bar zot gazonk" | ||
| 728 | 1 3 (display (raise 0.5)) | ||
| 729 | 3 5 (display ((raise 0.5) ((margin nil) "Hi"))) | ||
| 730 | 5 9 (display (((margin nil) "Bye") (raise 0.5))) | ||
| 731 | 9 11 (display (raise 0.5)))))) | ||
| 711 | (with-temp-buffer | 732 | (with-temp-buffer |
| 712 | (should (equal-including-properties | 733 | (should (equal-including-properties |
| 713 | (let ((str (copy-sequence "some useless string"))) | 734 | (let ((str (copy-sequence "some useless string"))) |