feat: :data supports initial values with cons cell format

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-20 11:08:37 +00:00
parent ac0e163e66
commit be3cc6c147
2 changed files with 70 additions and 25 deletions

View File

@ -2654,5 +2654,31 @@ Returns list of (START END VALUE) intervals."
(ignore-errors (makunbound 'tp-test-dc-last)) (ignore-errors (makunbound 'tp-test-dc-last))
(ignore-errors (makunbound 'tp-test-dc-full))))) (ignore-errors (makunbound 'tp-test-dc-full)))))
(ert-deftest tp-test-data-with-initial-values ()
"Test that :data supports initial values with cons cell format."
(tp-test-with-temp-buffer
(unwind-protect
(progn
;; Define layer with :data having initial values
(tp-define-layer test-data-init-layer
:props (face (:foreground $tp-test-init-color) help-echo $tp-test-init-name)
:data ((tp-test-init-color . "blue")
(tp-test-init-name . "Initial Name")
tp-test-init-other))
;; Check initial values
(should (equal tp-test-init-color "blue"))
(should (equal tp-test-init-name "Initial Name"))
(should (equal tp-test-init-other nil))
;; Apply layer to text
(insert "Hello World")
(tp-set 1 6 'test-data-init-layer)
;; Check text properties have initial values
(should (equal (plist-get (get-text-property 1 'face) :foreground) "blue"))
(should (equal (get-text-property 1 'help-echo) "Initial Name")))
;; Cleanup
(ignore-errors (makunbound 'tp-test-init-color))
(ignore-errors (makunbound 'tp-test-init-name))
(ignore-errors (makunbound 'tp-test-init-other)))))
(provide 'tp-ert-tests) (provide 'tp-ert-tests)
;;; tp-ert-tests.el ends here ;;; tp-ert-tests.el ends here

49
tp.el
View File

@ -351,16 +351,25 @@ Sets the global variables to their computed values."
(when val (when val
(set var-sym val))))) (set var-sym val)))))
(defun tp--data-var-symbol (data-entry)
"Extract the variable symbol from DATA-ENTRY.
DATA-ENTRY can be a symbol or a cons cell (SYMBOL . INITIAL-VALUE)."
(if (consp data-entry)
(car data-entry)
data-entry))
(defun tp--register-layer-data (layer-name data-vars) (defun tp--register-layer-data (layer-name data-vars)
"Register DATA-VARS for LAYER-NAME. "Register DATA-VARS for LAYER-NAME.
DATA-VARS is a list of variable symbols defined via :data. DATA-VARS is a list of variable symbols or cons cells (SYMBOL . INITIAL-VALUE).
Also adds variable watchers so changes to data vars trigger computed updates." Also adds variable watchers so changes to data vars trigger computed updates."
(when data-vars (when data-vars
;; Extract just the symbols for storage
(let ((var-symbols (mapcar #'tp--data-var-symbol data-vars)))
(if (assoc layer-name tp-layer-data) (if (assoc layer-name tp-layer-data)
(setf (cdr (assoc layer-name tp-layer-data)) data-vars) (setf (cdr (assoc layer-name tp-layer-data)) var-symbols)
(push (cons layer-name data-vars) tp-layer-data)) (push (cons layer-name var-symbols) tp-layer-data))
;; Add watchers for data variables ;; Add watchers for data variables
(dolist (var-sym data-vars) (dolist (var-sym var-symbols)
(let ((existing (assoc var-sym tp-reactive-deps))) (let ((existing (assoc var-sym tp-reactive-deps)))
(if existing (if existing
;; Add this layer to existing dependencies (with nil props since data vars don't have direct props) ;; Add this layer to existing dependencies (with nil props since data vars don't have direct props)
@ -369,7 +378,7 @@ Also adds variable watchers so changes to data vars trigger computed updates."
(push (cons layer-name nil) (cdr existing)))) (push (cons layer-name nil) (cdr existing))))
;; Create new dependency entry and add watcher ;; Create new dependency entry and add watcher
(push (cons var-sym (list (cons layer-name nil))) tp-reactive-deps) (push (cons var-sym (list (cons layer-name nil))) tp-reactive-deps)
(add-variable-watcher var-sym #'tp--reactive-variable-watcher)))))) (add-variable-watcher var-sym #'tp--reactive-variable-watcher)))))))
(defun tp--unregister-layer-data (layer-name) (defun tp--unregister-layer-data (layer-name)
"Unregister data variables for LAYER-NAME." "Unregister data variables for LAYER-NAME."
@ -377,13 +386,17 @@ Also adds variable watchers so changes to data vars trigger computed updates."
(defun tp--ensure-reactive-variables (var-symbols) (defun tp--ensure-reactive-variables (var-symbols)
"Ensure all VAR-SYMBOLS are defined as global variables. "Ensure all VAR-SYMBOLS are defined as global variables.
If a variable is not bound, define it with nil as initial value." VAR-SYMBOLS can be a list of symbols or cons cells (SYMBOL . INITIAL-VALUE).
If a variable is not bound, define it with the initial value (nil if not specified)."
(dolist (sym var-symbols) (dolist (sym var-symbols)
(let ((var-sym (if (tp--reactive-symbol-p sym) (let* ((is-cons (and (consp sym) (not (tp--reactive-symbol-p sym))))
(tp--reactive-var-symbol sym) (var-sym (cond
sym))) (is-cons (car sym))
((tp--reactive-symbol-p sym) (tp--reactive-var-symbol sym))
(t sym)))
(initial-val (if is-cons (cdr sym) nil)))
(unless (boundp var-sym) (unless (boundp var-sym)
(set var-sym nil))))) (set var-sym initial-val)))))
(defun tp--update-layer-regions (layer-name) (defun tp--update-layer-regions (layer-name)
"Update all text regions that have LAYER-NAME applied. "Update all text regions that have LAYER-NAME applied.
@ -2022,10 +2035,15 @@ The layer is stored in `tp-layer-alist'."
(computed-vars (when compute (mapcar #'car compute))) (computed-vars (when compute (mapcar #'car compute)))
;; All variables that need to be reactive ;; All variables that need to be reactive
(all-reactive-syms (delete-dups (append reactive-syms))) (all-reactive-syms (delete-dups (append reactive-syms)))
;; All variables to ensure are defined (including data) ;; Variables from :props that need to be defined (without initial values)
(props-vars (mapcar #'tp--reactive-var-symbol reactive-syms))
;; All variables to ensure are defined:
;; - :data entries (may have initial values as cons cells)
;; - :props reactive symbols (no initial values)
;; - :compute variable names (no initial values)
(all-vars-to-define (delete-dups (all-vars-to-define (delete-dups
(append (mapcar #'tp--reactive-var-symbol reactive-syms) (append data
data props-vars
computed-vars)))) computed-vars))))
(if (or all-reactive-syms data compute) (if (or all-reactive-syms data compute)
;; Has reactive features - register dependencies and resolve at runtime ;; Has reactive features - register dependencies and resolve at runtime
@ -2206,9 +2224,10 @@ and the group itself is stored in `tp-layer-groups'."
(reactive-syms (tp--collect-reactive-symbols props)) (reactive-syms (tp--collect-reactive-symbols props))
(computed-vars (when compute (mapcar #'car compute))) (computed-vars (when compute (mapcar #'car compute)))
(all-reactive-syms (delete-dups reactive-syms)) (all-reactive-syms (delete-dups reactive-syms))
(props-vars (mapcar #'tp--reactive-var-symbol reactive-syms))
(all-vars-to-define (delete-dups (all-vars-to-define (delete-dups
(append (mapcar #'tp--reactive-var-symbol reactive-syms) (append data
data props-vars
computed-vars)))) computed-vars))))
(if (or all-reactive-syms data compute) (if (or all-reactive-syms data compute)
;; Has reactive features - register dependencies and resolve at runtime ;; Has reactive features - register dependencies and resolve at runtime