Fix tp-text reactive variable synchronization when initial value is nil

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-28 10:29:03 +00:00
parent 842482245b
commit 094f8f0f88
3 changed files with 75 additions and 0 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ dash.el
# Backup files # Backup files
*~ *~
\#*\# \#*\#
dash.el

View File

@ -3044,6 +3044,49 @@ preserving the native text property behavior."
;; Cleanup ;; Cleanup
(makunbound 'tp-test-reactive-text)))) (makunbound 'tp-test-reactive-text))))
(ert-deftest tp-test-tp-text-reactive-nil-initializes-variable ()
"Test tp-text with nil reactive variable initializes the variable to source text.
When tp-text is bound to a reactive variable and that variable is nil,
the source text should be used and the reactive variable should be updated."
(tp-test-with-temp-buffer
(defvar tp-test-text-var nil "Test variable for tp-text initialization.")
(setq tp-test-text-var nil)
(unwind-protect
(progn
;; Define layer with tp-text bound to a reactive variable
(tp-define-layer 'test-init-text-layer
:props '(face bold tp-text $tp-test-text-var))
;; Apply layer to string - variable is nil, so source text should be used
(let ((result (tp-set "2" 'test-init-text-layer)))
;; Result should be the source text "2"
(should (equal result "2"))
;; tp-test-text-var should now be "2"
(should (equal tp-test-text-var "2"))
;; tp-text property should be "2"
(should (equal (tp-at 0 'tp-text result) "2")))
;; Now set the variable to a different value and test again
(setq tp-test-text-var "18")
;; Redefine layer to reset resolved props
(tp-define-layer 'test-init-text-layer
:props '(face bold tp-text $tp-test-text-var))
(let ((result (tp-set "2" 'test-init-text-layer)))
;; Result should be the variable value "18", not source "2"
(should (equal result "18"))
;; Variable should remain "18"
(should (equal tp-test-text-var "18"))))
;; Cleanup
(makunbound 'tp-test-text-var))))
(ert-deftest tp-test-tp-text-direct-string-uses-specified-text ()
"Test tp-text with direct string value uses that string, not source text.
When tp-text is set directly to a string (not a reactive variable),
the inserted text should be that string, not the source text."
(let ((result (tp-set "2" 'tp-text "23")))
;; Result should be "23", not "2"
(should (equal result "23"))
;; tp-text property should be "23"
(should (equal (tp-at 0 'tp-text result) "23"))))
(ert-deftest tp-test-tp-text-reactive-computed () (ert-deftest tp-test-tp-text-reactive-computed ()
"Test tp-text with computed reactive variable." "Test tp-text with computed reactive variable."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer

31
tp.el
View File

@ -576,6 +576,26 @@ WHERE specifies which buffers to update:
;;; Layer 4: Reactive Text (tp-text property) ;;; Layer 4: Reactive Text (tp-text property)
;;;============================================================================ ;;;============================================================================
(defun tp--find-tp-text-reactive-var (layer-name)
"Find the reactive variable symbol used for tp-text in LAYER-NAME.
Returns the variable symbol (e.g., tp-test-counter) if tp-text uses a
reactive variable (e.g., $tp-test-counter), or nil if not found.
Searches through `tp-reactive-deps' to find the original reactive props."
(catch 'found
(dolist (dep tp-reactive-deps)
(let* ((var-sym (car dep))
(layer-entry (assoc layer-name (cdr dep))))
(when layer-entry
(let ((reactive-props (cdr layer-entry)))
;; Check if tp-text in reactive-props uses this variable
(when (plist-member reactive-props 'tp-text)
(let ((tp-text-val (plist-get reactive-props 'tp-text)))
;; Check if tp-text-val is a reactive symbol for this variable
(when (and (tp--reactive-symbol-p tp-text-val)
(eq (tp--reactive-var-symbol tp-text-val) var-sym))
(throw 'found var-sym))))))))
nil))
(defun tp--update-reactive-text (layer-name &optional where) (defun tp--update-reactive-text (layer-name &optional where)
"Update text regions that have tp-text property with LAYER-NAME applied. "Update text regions that have tp-text property with LAYER-NAME applied.
This is called when a reactive variable bound to tp-text changes. This is called when a reactive variable bound to tp-text changes.
@ -644,6 +664,17 @@ NEW-OBJECT is the new string object (only different for strings with tp-text)."
(with-current-buffer object (with-current-buffer object
(buffer-substring-no-properties start end)) (buffer-substring-no-properties start end))
(buffer-substring-no-properties start end))))) (buffer-substring-no-properties start end)))))
;; If tp-text uses a reactive variable, update that variable to match
;; This ensures the reactive variable and buffer text stay in sync
(when-let ((layer-name (plist-get props 'tp-name)))
(when-let ((reactive-var (tp--find-tp-text-reactive-var layer-name)))
;; Update the reactive variable with the current text (buffer-local)
(set reactive-var current-text)
;; Also update the layer definition so future accesses see the new value
(let ((layer-props (cdr (assoc layer-name tp-layer-alist))))
(when layer-props
(setf (cdr (assoc layer-name tp-layer-alist))
(plist-put layer-props 'tp-text current-text))))))
(list (plist-put props 'tp-text current-text) end object))) (list (plist-put props 'tp-text current-text) end object)))
;; tp-text has a string value - replace the text in the region ;; tp-text has a string value - replace the text in the region
((stringp tp-text-val) ((stringp tp-text-val)