Optimize tp--replace-reactive-text-in-buffer to preserve all original text properties

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-01-08 02:58:45 +00:00
parent e383882a74
commit 3a0f774c70

47
tp.el
View File

@ -1093,20 +1093,34 @@ it will be applied to the text before updating."
(save-excursion (save-excursion
(tp--replace-reactive-text-in-buffer layer-name new-text props))))))))))) (tp--replace-reactive-text-in-buffer layer-name new-text props)))))))))))
(defvar tp-preserve-external-properties '(keymap mouse-face cursor pointer help-echo) (defun tp--merge-props-with-base (base-props new-props)
"List of text properties to preserve during reactive text updates. "Merge NEW-PROPS into BASE-PROPS, with NEW-PROPS taking precedence.
These properties are typically set by external libraries (like twidget's event For face properties (face, font-lock-face, mouse-face), uses `tp--merge-face-values'
system) and should not be overwritten when tp.el updates reactive text. to properly merge face values. For other properties, NEW-PROPS values override.
Properties in this list will be preserved from the buffer during updates, BASE-PROPS are the original properties, NEW-PROPS are the new properties to apply.
while other properties will be replaced with the new values.") Returns the merged plist."
(if (null base-props)
new-props
(let ((result (copy-sequence new-props)))
(cl-loop for (key val) on base-props by #'cddr
do (let ((new-val (plist-get result key)))
(if (plist-member result key)
;; New props has this key - merge if face property
(when (memq key '(face font-lock-face mouse-face))
(setq result
(plist-put result key
(tp--merge-face-values val new-val))))
;; New props doesn't have this key - add from base
(setq result (plist-put result key val)))))
result)))
(defun tp--replace-reactive-text-in-buffer (layer-name new-text props) (defun tp--replace-reactive-text-in-buffer (layer-name new-text props)
"Replace text in current buffer for reactive text with LAYER-NAME. "Replace text in current buffer for reactive text with LAYER-NAME.
NEW-TEXT is the new text to replace with. NEW-TEXT is the new text to replace with.
PROPS are the properties to apply to the new text. PROPS are the properties to apply to the new text.
+Text properties embedded in NEW-TEXT are merged with PROPS. Text properties embedded in NEW-TEXT are merged with PROPS.
+Properties listed in `tp-preserve-external-properties' that were applied All original text properties are preserved and merged with the new properties,
+by other libraries are preserved during the update." with new properties taking precedence over original properties."
(goto-char (point-min)) (goto-char (point-min))
(let ((match (text-property-search-forward 'tp-name layer-name t)) (let ((match (text-property-search-forward 'tp-name layer-name t))
;; Merge embedded text properties from new-text into props ;; Merge embedded text properties from new-text into props
@ -1115,17 +1129,10 @@ PROPS are the properties to apply to the new text.
(let* ((m-start (prop-match-beginning match)) (let* ((m-start (prop-match-beginning match))
(m-end (prop-match-end match)) (m-end (prop-match-end match))
(old-text (buffer-substring-no-properties m-start m-end)) (old-text (buffer-substring-no-properties m-start m-end))
;; Preserve only specific external properties ;; Get ALL existing properties from the original text
(existing-props (text-properties-at m-start)) (existing-props (text-properties-at m-start))
(preserved-props nil)) ;; Merge: existing props as base, merged-props as new (takes precedence)
;; Extract only the explicitly listed external properties to preserve (final-props (tp--merge-props-with-base existing-props merged-props)))
(dolist (prop-name tp-preserve-external-properties)
(let ((val (plist-get existing-props prop-name)))
(when (and val (not (plist-member merged-props prop-name)))
(setq preserved-props
(plist-put preserved-props prop-name val)))))
;; Combine merged-props with preserved external props
(let ((final-props (append merged-props preserved-props)))
(if (equal old-text (substring-no-properties new-text)) (if (equal old-text (substring-no-properties new-text))
;; Text content is the same, but properties may differ ;; Text content is the same, but properties may differ
;; Use set-text-properties to replace with final properties ;; Use set-text-properties to replace with final properties
@ -1136,7 +1143,7 @@ PROPS are the properties to apply to the new text.
(insert (substring-no-properties new-text)) (insert (substring-no-properties new-text))
;; Apply final properties ;; Apply final properties
(let ((new-end (+ m-start (length new-text)))) (let ((new-end (+ m-start (length new-text))))
(set-text-properties m-start new-end final-props))))) (set-text-properties m-start new-end final-props))))
;; Search for next match ;; Search for next match
(setq match (text-property-search-forward 'tp-name layer-name t))))) (setq match (text-property-search-forward 'tp-name layer-name t)))))