aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Porter2025-05-28 09:55:58 -0700
committerJim Porter2025-06-10 22:09:26 -0700
commit4a3c8e6e1df44b187b7286747e363232e8b4e0ea (patch)
tree415ffb4422e3457bb37ad8e15e892b74b448a405
parent7416595e2fc0ff676ef98a139328722ac9220ca0 (diff)
downloademacs-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.el20
-rw-r--r--test/lisp/emacs-lisp/subr-x-tests.el37
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")))