Fix tp-reset and tp-set to preserve embedded text properties from tp-text

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-01-07 09:22:32 +00:00
parent 39f38018a4
commit 148b255456
2 changed files with 30 additions and 29 deletions

View File

@ -3082,8 +3082,9 @@ text content but different properties, the properties should be updated."
;;; ============================================================
(ert-deftest tp-test-tp-text-with-embedded-properties-string ()
"Test that tp-set with tp-text overrides embedded properties on strings."
;; When tp-text is a propertized string and tp-set is used, props override embedded
"Test that tp-set with tp-text preserves embedded properties on strings."
;; When tp-text is a propertized string, embedded properties should be preserved
;; (props still override embedded if there's a conflict)
(let* ((propertized-text (copy-sequence "Hello"))
(_ (put-text-property 0 5 'custom-prop 'embedded-value propertized-text))
(result (tp-set "X" 'tp-text propertized-text 'face 'bold)))
@ -3091,8 +3092,8 @@ text content but different properties, the properties should be updated."
(should (equal result "Hello"))
;; The face property from props should be applied
(should (equal (tp-at 0 'face result) 'bold))
;; tp-set overrides - embedded custom-prop should NOT be present
(should (null (tp-at 0 'custom-prop result)))))
;; tp-set now preserves embedded props
(should (equal (tp-at 0 'custom-prop result) 'embedded-value))))
(ert-deftest tp-test-tp-add-with-embedded-properties-string ()
"Test that tp-add with tp-text merges embedded properties on strings."
@ -3108,15 +3109,15 @@ text content but different properties, the properties should be updated."
(should (equal (tp-at 0 'custom-prop result) 'embedded-value))))
(ert-deftest tp-test-tp-text-with-embedded-face-string ()
"Test that tp-set with tp-text overrides embedded face on strings."
"Test that tp-set with tp-text preserves embedded face on strings."
(let* ((propertized-text (copy-sequence "Hello"))
(_ (put-text-property 0 5 'face 'italic propertized-text))
;; Set tp-text with its own face, and also specify help-echo
(result (tp-set "X" 'tp-text propertized-text 'help-echo "tip")))
;; The text content should be from tp-text
(should (equal result "Hello"))
;; tp-set overrides - embedded face should NOT be present (no face in props)
(should (null (tp-at 0 'face result)))
;; tp-set now preserves embedded face
(should (equal (tp-at 0 'face result) 'italic))
;; The help-echo from props should be applied
(should (equal (tp-at 0 'help-echo result) "tip"))))
@ -3134,7 +3135,7 @@ text content but different properties, the properties should be updated."
(should (equal (tp-at 0 'help-echo result) "tip"))))
(ert-deftest tp-test-tp-text-with-embedded-properties-buffer ()
"Test that tp-set with tp-text overrides embedded properties in buffers."
"Test that tp-set with tp-text preserves embedded properties in buffers."
(tp-test-with-temp-buffer
(insert "Original")
(let* ((propertized-text (copy-sequence "New"))
@ -3144,12 +3145,12 @@ text content but different properties, the properties should be updated."
(should (equal (buffer-substring-no-properties 1 4) "New"))
;; The face from props should be applied
(should (equal (tp-at 1 'face) 'bold))
;; tp-set overrides embedded props - custom-prop should NOT be present
(should (null (tp-at 1 'custom-prop))))))
;; tp-set now preserves embedded props
(should (equal (tp-at 1 'custom-prop) 'embedded-value)))))
(ert-deftest tp-test-tp-text-with-mixed-properties ()
"Test that tp-set with tp-text overrides embedded properties."
;; tp-set should override embedded props, not merge them
"Test that tp-set with tp-text preserves embedded properties."
;; tp-set now preserves embedded props (props still take precedence for conflicts)
(let* ((propertized-text (copy-sequence "ABCD"))
;; Set a property at position 0
(_ (put-text-property 0 4 'region-type 'start propertized-text))
@ -3159,11 +3160,11 @@ text content but different properties, the properties should be updated."
;; The face from props should be applied uniformly
(should (equal (tp-at 0 'face result) 'bold))
(should (equal (tp-at 3 'face result) 'bold))
;; tp-set overrides embedded props - region-type should NOT be present
(should (null (tp-at 0 'region-type result)))))
;; tp-set now preserves embedded props
(should (equal (tp-at 0 'region-type result) 'start))))
(ert-deftest tp-test-tp-reset-with-embedded-properties ()
"Test that tp-reset ignores embedded text properties entirely."
"Test that tp-reset preserves embedded text properties from tp-text."
(let* ((propertized-text (copy-sequence "Test"))
(_ (put-text-property 0 4 'custom-prop 'value propertized-text))
(result (tp-reset "X" 'tp-text propertized-text 'face 'bold)))
@ -3171,8 +3172,8 @@ text content but different properties, the properties should be updated."
(should (equal result "Test"))
;; The face from props should be applied
(should (equal (tp-at 0 'face result) 'bold))
;; tp-reset ignores embedded props - custom-prop should NOT be present
(should (null (tp-at 0 'custom-prop result)))))
;; tp-reset now preserves embedded props from tp-text
(should (equal (tp-at 0 'custom-prop result) 'value))))
(ert-deftest tp-test-tp-add-with-embedded-properties ()
"Test that tp-add with embedded text properties preserves them."

24
tp.el
View File

@ -1147,9 +1147,10 @@ If tp-text is a string different from current text, replace the text.
When PRESERVE-PROPS is non-nil, existing text properties are preserved
on the replaced text (used by tp-set and tp-add).
MERGE-MODE controls how embedded text properties in tp-text are handled:
:merge - embedded properties are merged with props (tp-add behavior)
:override or nil - props override embedded properties (tp-set behavior)
:reset - embedded properties are ignored, only props used (tp-reset behavior)
All modes now preserve embedded text properties from tp-text.
:merge - embedded properties are merged with props, with face merging (tp-add)
:override or nil - props take precedence over embedded properties (tp-set)
:reset - props take precedence over embedded properties (tp-reset)
Returns (PROPS NEW-END NEW-OBJECT) where PROPS is the updated props,
NEW-END is the new end position after any text replacement, and
NEW-OBJECT is the new string object (only different for strings with tp-text)."
@ -1197,15 +1198,14 @@ NEW-OBJECT is the new string object (only different for strings with tp-text)."
tp-text-val))
tp-text-val))
;; Handle embedded text properties based on merge-mode:
;; :merge - merge embedded props with props (tp-add)
;; :override/nil - props take precedence, don't merge (tp-set)
;; :reset - ignore embedded props entirely (tp-reset)
;; All modes now preserve embedded text properties from tp-text.
;; :merge - merge embedded props with props, with face merging (tp-add)
;; :override/nil - props take precedence over embedded props (tp-set)
;; :reset - props take precedence over embedded props (tp-reset)
;; In all cases, use tp--merge-string-props-into-plist which handles
;; props taking precedence and special face merging when needed.
(result-props
(if (eq merge-mode :merge)
;; tp-add: merge embedded properties with props
(tp--merge-string-props-into-plist final-text props)
;; tp-set/:override or tp-reset: just use props as-is
props)))
(tp--merge-string-props-into-plist final-text props)))
(if (stringp object)
;; For strings: create a new string with tp-text content
;; Strip properties - result-props will be applied by the caller
@ -1425,7 +1425,7 @@ Returns: For buffers, (START . END) cons. For strings, the result string."
(defun tp-reset (start-or-string &optional end-or-prop props-or-val &rest rest)
"Completely replace all text properties with PROPS.
Like `tp-set' but replaces ALL existing properties.
For tp-text, embedded text properties are ignored - only props are used.
For tp-text, embedded text properties are preserved (props override if there's a conflict).
**String Modification Behavior:**
- Entire string form (tp-reset STRING ...): Returns a NEW propertized string