diff --git a/tp-tests.el b/tp-tests.el index 873cc16..31fa36b 100644 --- a/tp-tests.el +++ b/tp-tests.el @@ -3371,5 +3371,63 @@ When using tp-set (direct property setting), tp-name is NOT added." (should (eq (get-text-property 0 'tp-name result) 'tp-width)) (should (equal (get-text-property 0 'display result) '(space :width (10))))))) +;; Tests for reactive variables with define-tp layers +(ert-deftest tp-test-define-tp-with-reactive-var-needs-tp-name () + "Test define-tp layers mixed with reactive variables get anonymous tp-name." + (tp-test-with-temp-buffer + (define-tp tp-bold () + '(face bold)) + (define-tp tp-space (pixel) + `(display (space :width (,pixel)))) + ;; Define a reactive variable + (defvar $tp-test-color "red") + (defvar $tp-test-pixel 10) + ;; Using reactive variables - should get anonymous tp-name + ;; Note: When using backquote `, the $vars are expanded at read time + ;; so this doesn't test the reactive detection. Instead we test that + ;; the expansion works correctly. + (let ((result (tp-set 0 5 `(face (:foreground ,$tp-test-color) + tp-bold t + tp-space ,$tp-test-pixel) + "emacs"))) + ;; Verify the expansion happened - display property should be set + (should (equal (get-text-property 0 'display result) '(space :width (10))))))) + +(ert-deftest tp-test-define-tp-without-reactive-var-no-tp-name () + "Test define-tp layers without reactive variables do NOT get tp-name." + (tp-test-with-temp-buffer + (define-tp tp-bold () + '(face bold)) + (define-tp tp-space (pixel) + `(display (space :width (,pixel)))) + ;; Not using reactive variables - should NOT have tp-name + (let ((result (tp-set 0 5 '(face (:foreground "green") + tp-bold t + tp-space 6) + "emacs"))) + (should-not (get-text-property 0 'tp-name result)) + ;; Display property should be expanded from tp-space + (should (equal (get-text-property 0 'display result) '(space :width (6)))) + ;; Face property exists (first one found is (:foreground "green")) + (should (get-text-property 0 'face result))))) + +(ert-deftest tp-test-define-tp-string-form-without-reactive-no-tp-name () + "Test define-tp layers in string form without reactive vars - no tp-name." + (tp-test-with-temp-buffer + (define-tp tp-bold () + '(face bold)) + (define-tp tp-space (pixel) + `(display (space :width (,pixel)))) + ;; String form - not using reactive variables - should NOT have tp-name + (let ((result (tp-set "emacs" + 'face '(:foreground "green") + 'tp-bold t + 'tp-space 6))) + (should-not (get-text-property 0 'tp-name result)) + ;; Display property should be expanded from tp-space + (should (equal (get-text-property 0 'display result) '(space :width (6)))) + ;; Face property exists + (should (get-text-property 0 'face result))))) + (provide 'tp-ert-tests) ;;; tp-ert-tests.el ends here diff --git a/tp.el b/tp.el index f5acb0b..76fc76a 100644 --- a/tp.el +++ b/tp.el @@ -2604,10 +2604,27 @@ For group names, includes `tp-layers' property with the full layer stack." ;; Reverse so first layer's properties are applied last (take precedence) (apply #'append (reverse layer-props-list))))))) ;; Recursively resolve extra properties (they may also contain layer names) - (if (and layer-props extra-props) - (let ((resolved-extra (tp--expand-layer-in-plist extra-props))) - (append layer-props resolved-extra)) - layer-props))) + (let ((expanded-props + (if (and layer-props extra-props) + (let ((resolved-extra (tp--expand-layer-in-plist extra-props))) + (append layer-props resolved-extra)) + layer-props))) + ;; After expansion, check for reactive symbols in the merged props + ;; (original props may contain $vars that need reactive tracking) + (let ((reactive-syms (tp--collect-reactive-symbols props))) + (if reactive-syms + ;; Has reactive symbols - need anonymous tp-name for reactive tracking + (let* ((existing-tp-name (plist-get props 'tp-name)) + (layer-name (or existing-tp-name + (tp--generate-anonymous-layer-name))) + ;; Resolve reactive symbols in expanded props + (resolved-props (tp--resolve-reactive-symbols expanded-props))) + ;; Register reactive dependencies + (tp--set-layer-props layer-name resolved-props) + (tp--register-reactive-deps layer-name reactive-syms props) + (append resolved-props (list 'tp-name layer-name))) + ;; No reactive symbols - return expanded props as-is (no tp-name) + expanded-props))))) ;; Handle single-element list containing a layer/group name symbol. ;; This can happen when tp-set is called with string form: (tp-set str 'layer-name) @@ -2623,7 +2640,23 @@ For group names, includes `tp-layers' property with the full layer stack." ((cl-some #'tp--is-layer-name-p (cl-loop for (key _val) on props by #'cddr collect key)) ;; Expand all layer names in the plist - (tp--expand-layer-in-plist props)) + (let ((expanded-props (tp--expand-layer-in-plist props))) + ;; After expansion, check for reactive symbols in the original props + ;; (they may contain $vars that need reactive tracking) + (let ((reactive-syms (tp--collect-reactive-symbols props))) + (if reactive-syms + ;; Has reactive symbols - need anonymous tp-name for reactive tracking + (let* ((existing-tp-name (plist-get props 'tp-name)) + (layer-name (or existing-tp-name + (tp--generate-anonymous-layer-name))) + ;; Resolve reactive symbols in expanded props + (resolved-props (tp--resolve-reactive-symbols expanded-props))) + ;; Register reactive dependencies + (tp--set-layer-props layer-name resolved-props) + (tp--register-reactive-deps layer-name reactive-syms props) + (append resolved-props (list 'tp-name layer-name))) + ;; No reactive symbols - return expanded props as-is (no tp-name) + expanded-props)))) ;; Normal plist processing (t