From aafbeff1a45e575ab86a34c82e3eb37ffdd0f9bc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:19:21 +0000 Subject: [PATCH] Remove tp-name from direct property setting, keep for layer stack and reactive Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com> --- tp-tests.el | 165 ++++++++++++++++++++++++++++++---------------------- tp.el | 120 +++++++++++++++++++++++--------------- 2 files changed, 169 insertions(+), 116 deletions(-) diff --git a/tp-tests.el b/tp-tests.el index 475d140..8a0b119 100644 --- a/tp-tests.el +++ b/tp-tests.el @@ -253,10 +253,15 @@ '(face underline))))) (ert-deftest tp-test-layer-props () - "Test tp-layer-props returns properties with tp-name." + "Test tp-layer-props returns properties, and tp-name when requested." (tp-test-with-temp-buffer (tp-define-layer 'my-layer '(face bold)) + ;; Without tp-name (default for direct property setting) (let ((props (tp-layer-props 'my-layer))) + (should (eq (plist-get props 'face) 'bold)) + (should-not (plist-get props 'tp-name))) + ;; With tp-name (for layer stack functions) + (let ((props (tp-layer-props 'my-layer t))) (should (eq (plist-get props 'face) 'bold)) (should (eq (plist-get props 'tp-name) 'my-layer))))) @@ -2036,7 +2041,8 @@ Returns list of (START END VALUE) intervals." ;;; ============================================================ (ert-deftest tp-test-set-with-layer-name () - "Test tp-set accepts a layer name defined by define-tp." + "Test tp-set accepts a layer name defined by tp-define-layer. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "Hello World") (tp-define-layer 'my-style '(face bold help-echo "tip")) @@ -2044,19 +2050,20 @@ Returns list of (START END VALUE) intervals." (tp-set 1 6 'my-style) (should (eq (tp-at 1 'face) 'bold)) (should (equal (tp-at 1 'help-echo) "tip")) - ;; tp-name should be preserved for reactive text property support - (should (eq (tp-at 1 'tp-name) 'my-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (tp-at 1 'tp-name)))) (ert-deftest tp-test-set-with-layer-name-on-string () - "Test tp-set accepts a layer name on string." + "Test tp-set accepts a layer name on string. +When using tp-set (direct property setting), tp-name is NOT added." (let ((str (copy-sequence "Hello World"))) (setq tp-layer-alist nil) (setq tp-layer-groups nil) (tp-define-layer 'my-style '(face italic)) (tp-set 0 5 'my-style str) (should (eq (get-text-property 0 'face str) 'italic)) - ;; tp-name should be preserved for reactive text property support - (should (eq (get-text-property 0 'tp-name str) 'my-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (get-text-property 0 'tp-name str)))) (ert-deftest tp-test-set-entire-string-with-layer-name () "Test tp-set with layer name on entire string (string form). @@ -2073,7 +2080,8 @@ incorrectly generate an anonymous tp-name instead of using the layer name." (should (equal (plist-get (get-text-property 0 'face str) :background) "blue"))))) (ert-deftest tp-test-reset-with-layer-name () - "Test tp-reset accepts a layer name defined by define-tp." + "Test tp-reset accepts a layer name defined by tp-define-layer. +When using tp-reset (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "Hello World") (tp-set 1 6 '(mouse-face highlight)) @@ -2082,11 +2090,12 @@ incorrectly generate an anonymous tp-name instead of using the layer name." (tp-reset 1 6 'my-style) (should (eq (tp-at 1 'face) 'underline)) (should (null (tp-at 1 'mouse-face))) - ;; tp-name should be preserved - (should (eq (tp-at 1 'tp-name) 'my-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (tp-at 1 'tp-name)))) (ert-deftest tp-test-add-with-layer-name () - "Test tp-add accepts a layer name defined by define-tp." + "Test tp-add accepts a layer name defined by tp-define-layer. +When using tp-add (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "Hello World") (tp-set 1 6 '(help-echo "existing")) @@ -2095,11 +2104,12 @@ incorrectly generate an anonymous tp-name instead of using the layer name." (tp-add 1 6 'my-style) (should (eq (tp-at 1 'face) 'bold)) (should (equal (tp-at 1 'help-echo) "existing")) - ;; tp-name should be preserved - (should (eq (tp-at 1 'tp-name) 'my-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (tp-at 1 'tp-name)))) (ert-deftest tp-test-match-set-with-layer-name () - "Test tp-match-set accepts a layer name." + "Test tp-match-set accepts a layer name. +When using tp-match-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "Hello World Hello") (tp-define-layer 'match-style '(face bold help-echo "matched")) @@ -2107,11 +2117,12 @@ incorrectly generate an anonymous tp-name instead of using the layer name." (should (eq (tp-at 1 'face) 'bold)) (should (equal (tp-at 1 'help-echo) "matched")) (should (eq (tp-at 13 'face) 'bold)) - ;; tp-name should be preserved - (should (eq (tp-at 1 'tp-name) 'match-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (tp-at 1 'tp-name)))) (ert-deftest tp-test-match-set-with-layer-name-on-string () - "Test tp-match-set accepts a layer name on string." + "Test tp-match-set accepts a layer name on string. +When using tp-match-set (direct property setting), tp-name is NOT added." (let ((str (copy-sequence "Hello World Hello"))) (setq tp-layer-alist nil) (setq tp-layer-groups nil) @@ -2119,8 +2130,8 @@ incorrectly generate an anonymous tp-name instead of using the layer name." (tp-match-set "Hello" 'match-style str) (should (eq (get-text-property 0 'face str) 'italic)) (should (eq (get-text-property 12 'face str) 'italic)) - ;; tp-name should be preserved - (should (eq (get-text-property 0 'tp-name str) 'match-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (get-text-property 0 'tp-name str)))) (ert-deftest tp-test-match-reset-with-layer-name () "Test tp-match-reset accepts a layer name." @@ -2173,7 +2184,8 @@ incorrectly generate an anonymous tp-name instead of using the layer name." (should (null (tp-at 5 'mouse-face))))) (ert-deftest tp-test-regexp-add-with-layer-name () - "Test tp-regexp-add accepts a layer name." + "Test tp-regexp-add accepts a layer name. +When using tp-regexp-add (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "abc 123 def 456") (tp-set 5 8 '(help-echo "original")) @@ -2181,42 +2193,44 @@ incorrectly generate an anonymous tp-name instead of using the layer name." (tp-regexp-add "[0-9]+" 'number-style) (should (eq (tp-at 5 'face) 'bold)) (should (equal (tp-at 5 'help-echo) "original")) - ;; tp-name should be preserved - (should (eq (tp-at 5 'tp-name) 'number-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (tp-at 5 'tp-name)))) (ert-deftest tp-test-set-with-group-name () - "Test tp-set accepts a group name defined by define-tp-group." + "Test tp-set accepts a group name defined by define-tp-group. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "Hello World") (tp-define-layer-group 'my-group '("style" . (face bold help-echo "grouped"))) - ;; Use group name - should include tp-name for top layer + ;; Use group name (tp-set 1 6 'my-group) (should (eq (tp-at 1 'face) 'bold)) (should (equal (tp-at 1 'help-echo) "grouped")) - ;; tp-name should be preserved for the top layer - (should (eq (tp-at 1 'tp-name) 'my-group-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (tp-at 1 'tp-name)))) (ert-deftest tp-test-set-with-group-name-multiple-layers () - "Test tp-set with group containing multiple layers preserves tp-layers." + "Test tp-set with group containing multiple layers. +When using tp-set (direct property setting), tp-name and tp-layers are NOT added. +Only the first layer's properties are applied." (tp-test-with-temp-buffer (insert "Hello World") (tp-define-layer-group 'my-group '("first" . (face bold)) '("second" . (face italic))) - ;; Use group name - should include tp-layers for multiple layers + ;; Use group name - only first layer is applied (no layer stacking for direct setting) (tp-set 1 6 'my-group) - ;; First layer is on top + ;; First layer's properties are applied (should (eq (tp-at 1 'face) 'bold)) - (should (eq (tp-at 1 'tp-name) 'my-group-first)) - ;; tp-layers should contain the second layer - (let ((layers (tp-at 1 'tp-layers))) - (should layers) - (should (= (length layers) 1)) - (should (eq (plist-get (car layers) 'tp-name) 'my-group-second))))) + ;; tp-name should NOT be set for direct property setting + (should-not (tp-at 1 'tp-name)) + ;; tp-layers should NOT be set for direct property setting + (should-not (tp-at 1 'tp-layers)))) (ert-deftest tp-test-match-set-with-group-name () - "Test tp-match-set accepts a group name." + "Test tp-match-set accepts a group name. +When using tp-match-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "Hello World Hello") (tp-define-layer-group 'my-group @@ -2224,8 +2238,8 @@ incorrectly generate an anonymous tp-name instead of using the layer name." (tp-match-set "Hello" 'my-group) (should (eq (tp-at 1 'face) 'italic)) (should (eq (tp-at 13 'face) 'italic)) - ;; tp-name should be preserved - (should (eq (tp-at 1 'tp-name) 'my-group-style)))) + ;; tp-name should NOT be set for direct property setting + (should-not (tp-at 1 'tp-name)))) (ert-deftest tp-test-resolve-props-returns-nil-for-unknown () "Test tp--resolve-props returns nil for unknown layer name." @@ -2285,15 +2299,16 @@ preserving the native text property behavior." (makunbound 'tp-test-anon-color)))) (ert-deftest tp-test-set-anonymous-layer-preserves-existing-tp-name () - "Test that tp-set with anonymous plist preserves existing tp-name." + "Test that tp-set with anonymous plist preserves existing tp-name property." (tp-test-with-temp-buffer (insert "Hello World") - ;; First set with a layer name + ;; First set with a layer name - this does NOT set tp-name (tp-define-layer 'my-existing-layer '(face bold)) (tp-set 1 6 'my-existing-layer) - (should (eq (tp-at 1 'tp-name) 'my-existing-layer)) - ;; Now set with anonymous plist that already has tp-name + (should-not (tp-at 1 'tp-name)) ; no tp-name for direct setting + ;; Now set with anonymous plist that has explicit tp-name (tp-set 1 6 '(face italic tp-name my-custom-name)) + ;; Explicit tp-name in plist should be preserved (should (eq (tp-at 1 'tp-name) 'my-custom-name)))) (ert-deftest tp-test-match-set-anonymous-reactive-layer () @@ -3134,24 +3149,26 @@ the inserted text should be that string, not the source text." (should (equal (eval (cadr entry)) '(face bold)))))) (ert-deftest tp-test-define-tp-non-parameterized-usage-string () - "Test non-parameterized layer usage with string: (tp-set string 'layer-name t)." + "Test non-parameterized layer usage with string: (tp-set string 'layer-name t). +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-bold () '(face bold)) (let ((result (tp-set "emacs" 'tp-bold t))) ;; Result should have the correct properties - (should (eq (get-text-property 0 'tp-name result) 'tp-bold)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting (should (eq (get-text-property 0 'face result) 'bold))))) (ert-deftest tp-test-define-tp-non-parameterized-usage-region () - "Test non-parameterized layer usage with region: (tp-set start end '(layer-name t))." + "Test non-parameterized layer usage with region: (tp-set start end '(layer-name t)). +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "emacs") (define-tp tp-bold () '(face bold)) (tp-set 1 6 '(tp-bold t)) ;; Check properties in buffer - (should (eq (tp-at 1 'tp-name) 'tp-bold)) + (should-not (tp-at 1 'tp-name)) ; no tp-name for direct setting (should (eq (tp-at 1 'face) 'bold)))) (ert-deftest tp-test-define-tp-parameterized () @@ -3168,33 +3185,36 @@ the inserted text should be that string, not the source text." (should (equal (car entry) '(pixel)))))) (ert-deftest tp-test-define-tp-parameterized-usage-string () - "Test parameterized layer usage with string: (tp-set string 'layer-name arg)." + "Test parameterized layer usage with string: (tp-set string 'layer-name arg). +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-space (pixel) (list 'display (list 'space :width (list pixel)))) (let ((result (tp-set "emacs" 'tp-space 2))) ;; Result should have the correct properties - (should (eq (get-text-property 0 'tp-name result) 'tp-space)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting (should (equal (get-text-property 0 'display result) '(space :width (2))))))) (ert-deftest tp-test-define-tp-parameterized-usage-region () - "Test parameterized layer usage with region: (tp-set start end '(layer-name arg))." + "Test parameterized layer usage with region: (tp-set start end '(layer-name arg)). +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (insert "emacs") (define-tp tp-space (pixel) (list 'display (list 'space :width (list pixel)))) (tp-set 1 6 '(tp-space 5)) ;; Check properties in buffer - (should (eq (tp-at 1 'tp-name) 'tp-space)) + (should-not (tp-at 1 'tp-name)) ; no tp-name for direct setting (should (equal (tp-at 1 'display) '(space :width (5)))))) (ert-deftest tp-test-define-tp-parameterized-backquote () - "Test parameterized layer with backquote syntax." + "Test parameterized layer with backquote syntax. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-test-space (pixel) `(display (space :width (,pixel)))) (let ((result (tp-set "emacs" 'tp-test-space 10))) - (should (eq (get-text-property 0 'tp-name result) 'tp-test-space)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting (should (equal (get-text-property 0 'display result) '(space :width (10))))))) (ert-deftest tp-test-define-tp-parameterized-undefine () @@ -3217,55 +3237,58 @@ the inserted text should be that string, not the source text." (should-not tp-layer-alist))) (ert-deftest tp-test-layer-with-extra-props-string () - "Test layer with extra native properties on string." + "Test layer with extra native properties on string. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-bold () '(face bold)) ;; Non-parameterized layer with extra props (let ((result (tp-set "emacs" 'tp-bold t 'face '(:foreground "green")))) - (should (eq (get-text-property 0 'tp-name result) 'tp-bold)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting ;; Should have both face values in the plist (let ((props (text-properties-at 0 result))) - (should (member 'face props)) - (should (eq (plist-get props 'tp-name) 'tp-bold)))))) + (should (member 'face props)))))) (ert-deftest tp-test-parameterized-layer-with-extra-props-string () - "Test parameterized layer with extra native properties on string." + "Test parameterized layer with extra native properties on string. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-space (pixel) `(display (space :width (,pixel)))) ;; Parameterized layer with extra props (let ((result (tp-set "emacs" 'tp-space 6 'face '(:foreground "green")))) - (should (eq (get-text-property 0 'tp-name result) 'tp-space)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting (should (equal (get-text-property 0 'display result) '(space :width (6)))) (should (equal (get-text-property 0 'face result) '(:foreground "green")))))) (ert-deftest tp-test-layer-with-extra-props-region () - "Test layer with extra native properties on region." + "Test layer with extra native properties on region. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-bold () '(face bold)) ;; Region form with extra props (let ((result (tp-set 0 5 '(tp-bold t face (:foreground "green")) "emacs"))) - (should (eq (get-text-property 0 'tp-name result) 'tp-bold)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting ;; Should have both face values in the plist (let ((props (text-properties-at 0 result))) - (should (member 'face props)) - (should (eq (plist-get props 'tp-name) 'tp-bold)))))) + (should (member 'face props)))))) (ert-deftest tp-test-parameterized-layer-with-extra-props-region () - "Test parameterized layer with extra native properties on region." + "Test parameterized layer with extra native properties on region. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-space (pixel) `(display (space :width (,pixel)))) ;; Region form with extra props (let ((result (tp-set 0 5 '(tp-space 6 face (:foreground "green")) "emacs"))) - (should (eq (get-text-property 0 'tp-name result) 'tp-space)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting (should (equal (get-text-property 0 'display result) '(space :width (6)))) (should (equal (get-text-property 0 'face result) '(:foreground "green")))))) (ert-deftest tp-test-layer-at-any-position-string () - "Test layer properties can be at any position in string form." + "Test layer properties can be at any position in string form. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-space (pixel) `(display (space :width (,pixel)))) @@ -3274,25 +3297,27 @@ the inserted text should be that string, not the source text." 'face '(:foreground "green") 'tp-space 6 'test "test"))) - (should (eq (get-text-property 0 'tp-name result) 'tp-space)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting (should (equal (get-text-property 0 'display result) '(space :width (6)))) (should (equal (get-text-property 0 'face result) '(:foreground "green"))) (should (equal (get-text-property 0 'test result) "test"))))) (ert-deftest tp-test-layer-at-any-position-region () - "Test layer properties can be at any position in region form." + "Test layer properties can be at any position in region form. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-space (pixel) `(display (space :width (,pixel)))) ;; Layer in the middle of the plist (let ((result (tp-set 0 5 '(face (:foreground "green") tp-space 6 test "test") "emacs"))) - (should (eq (get-text-property 0 'tp-name result) 'tp-space)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting (should (equal (get-text-property 0 'display result) '(space :width (6)))) (should (equal (get-text-property 0 'face result) '(:foreground "green"))) (should (equal (get-text-property 0 'test result) "test"))))) (ert-deftest tp-test-non-param-layer-at-any-position () - "Test non-parameterized layer at any position." + "Test non-parameterized layer at any position. +When using tp-set (direct property setting), tp-name is NOT added." (tp-test-with-temp-buffer (define-tp tp-bold () '(face bold)) @@ -3301,7 +3326,7 @@ the inserted text should be that string, not the source text." 'test1 "value1" 'tp-bold t 'test2 "value2"))) - (should (eq (get-text-property 0 'tp-name result) 'tp-bold)) + (should-not (get-text-property 0 'tp-name result)) ; no tp-name for direct setting (should (equal (get-text-property 0 'test1 result) "value1")) (should (equal (get-text-property 0 'test2 result) "value2"))))) diff --git a/tp.el b/tp.el index bfda294..7afeba4 100644 --- a/tp.el +++ b/tp.el @@ -319,6 +319,13 @@ Only the reactive portions of the properties are stored for each variable." (tp--unregister-layer-computed layer-name) (tp--unregister-layer-data layer-name)) +(defun tp--layer-has-reactive-deps-p (layer-name) + "Return non-nil if LAYER-NAME has reactive dependencies registered. +Layers with reactive deps need tp-name for reactive tracking." + (cl-some (lambda (dep) + (assoc layer-name (cdr dep))) + tp-reactive-deps)) + (defun tp--reactive-variable-watcher (symbol newval operation where) "Watcher function called when a reactive variable changes. SYMBOL is the variable that changed. @@ -539,7 +546,7 @@ Re-applies the layer properties using tp-search-map and tp-add. WHERE specifies which buffers to update: - If WHERE is a buffer, only update that buffer (setq-local case). - If WHERE is nil, update all buffers that have the text property (setq case)." - (let ((props (tp-layer-props layer-name))) + (let ((props (tp-layer-props layer-name t))) ; include tp-name for reactive tracking (when props ;; Callback for tp-search-map: applies props to matched region. ;; _TEXT is unused (the matched text), START and END are buffer positions. @@ -603,7 +610,7 @@ This is called when a reactive variable bound to tp-text changes. WHERE specifies which buffers to update: - If WHERE is a buffer, only update that buffer (setq-local case). - If WHERE is nil, update all buffers that have the text property (setq case)." - (let ((props (tp-layer-props layer-name))) + (let ((props (tp-layer-props layer-name t))) ; include tp-name for reactive tracking (when props (let ((new-text (plist-get props 'tp-text))) (when (and new-text (stringp new-text)) @@ -2418,33 +2425,41 @@ This is an internal function used by group definition macros." (setf (cdr (assoc group-name tp-layer-groups)) layer-names) (push (cons group-name layer-names) tp-layer-groups))) -(defun tp-layer-props (layer-name) +(defun tp-layer-props (layer-name &optional include-tp-name) "Return properties for layer LAYER-NAME from `tp-layer-alist'. -Appends 'tp-name property to identify the layer. +If INCLUDE-TP-NAME is non-nil, appends 'tp-name property to identify the layer. +Also includes tp-name automatically if the layer has reactive dependencies registered. Handles two storage formats: 1. Old format (from tp--set-layer-props): (LAYER-NAME . PLIST) - flat plist 2. Unified format (from define-tp): (LAYER-NAME ARGLIST BODY-FORM) For parameterized layers (ARGLIST non-nil), returns nil - use `tp-layer-props-with-arg'." (when-let ((entry (cdr (assoc layer-name tp-layer-alist)))) - (cond - ;; Unified format: entry is (ARGLIST BODY-FORM) where first elem is nil or a list - ;; Check: exactly 2 elements and first is nil or a list of symbols - ((and (= (length entry) 2) - (or (null (car entry)) - (and (listp (car entry)) - (cl-every #'symbolp (car entry))))) - (let ((arglist (car entry)) - (body (cadr entry))) - (if arglist - ;; Parameterized - needs argument, return nil - nil - ;; Non-parameterized - evaluate body and return props - (let ((plist (eval body))) - (when plist - (append plist (list 'tp-name layer-name))))))) - ;; Old format: entry is just a flat plist - (t - (append entry (list 'tp-name layer-name)))))) + ;; Auto-include tp-name for layers with reactive deps + (let ((needs-tp-name (or include-tp-name + (tp--layer-has-reactive-deps-p layer-name)))) + (cond + ;; Unified format: entry is (ARGLIST BODY-FORM) where first elem is nil or a list + ;; Check: exactly 2 elements and first is nil or a list of symbols + ((and (= (length entry) 2) + (or (null (car entry)) + (and (listp (car entry)) + (cl-every #'symbolp (car entry))))) + (let ((arglist (car entry)) + (body (cadr entry))) + (if arglist + ;; Parameterized - needs argument, return nil + nil + ;; Non-parameterized - evaluate body and return props + (let ((plist (eval body))) + (when plist + (if needs-tp-name + (append plist (list 'tp-name layer-name)) + plist)))))) + ;; Old format: entry is just a flat plist + (t + (if needs-tp-name + (append entry (list 'tp-name layer-name)) + entry)))))) (defun tp-layer-parameterized-p (layer-name) "Return non-nil if LAYER-NAME is a parameterized layer. @@ -2458,10 +2473,10 @@ where ARGLIST is a non-nil list of argument symbols." (not (null (car entry))) (cl-every #'symbolp (car entry))))) -(defun tp-layer-props-with-arg (layer-name arg) +(defun tp-layer-props-with-arg (layer-name arg &optional include-tp-name) "Return properties for parameterized layer LAYER-NAME with ARG. Evaluates the body form with the argument bound to the parameter. -Appends 'tp-name property to identify the layer." +If INCLUDE-TP-NAME is non-nil, appends 'tp-name property to identify the layer." (when-let ((entry (cdr (assoc layer-name tp-layer-alist)))) ;; entry is (ARGLIST BODY-FORM) (let ((arglist (car entry)) @@ -2471,13 +2486,16 @@ Appends 'tp-name property to identify the layer." ;; Evaluate the body with the argument bound (plist (eval `(let ((,arg-sym ',arg)) ,body)))) (when plist - (append plist (list 'tp-name layer-name)))))))) + (if include-tp-name + (append plist (list 'tp-name layer-name)) + plist))))))) -(defun tp-group-props (group-name) - "Return list of properties for all layers in GROUP-NAME." +(defun tp-group-props (group-name &optional include-tp-name) + "Return list of properties for all layers in GROUP-NAME. +If INCLUDE-TP-NAME is non-nil, each layer's props will include tp-name." (when-let ((layers (cdr (assoc group-name tp-layer-groups)))) (mapcar (lambda (layer) - (tp-layer-props layer)) + (tp-layer-props layer include-tp-name)) layers))) (defun tp--is-layer-name-p (sym) @@ -2490,6 +2508,7 @@ Appends 'tp-name property to identify the layer." "Expand any layer names found in PROPS plist. Scans through PROPS treating it as a plist (key value pairs). When a key is a layer/group name, expands it with its properties. +Does NOT add tp-name - this is for direct property setting (tp-set/add/reset). Returns the expanded plist." (let ((result nil) (remaining props)) @@ -2503,14 +2522,17 @@ Returns the expanded plist." (cond ;; Parameterized layer - evaluate with the argument (val) ((tp-layer-parameterized-p key) - (tp-layer-props-with-arg key val)) + (tp-layer-props-with-arg key val nil)) ; no tp-name ;; Non-parameterized layer - val should be t ((assoc key tp-layer-alist) - (tp-layer-props key)) - ;; Layer group + (tp-layer-props key nil)) ; no tp-name + ;; Layer group - merge all layers' properties for direct setting ((assoc key tp-layer-groups) (when-let ((layer-props-list (tp-group-props key))) - (tp--build-layer-props layer-props-list)))))) + ;; For direct property setting, merge all layers' properties + ;; without the tp-layers structure. + ;; Reverse so first layer's properties are applied last (take precedence) + (apply #'append (reverse layer-props-list))))))) (when layer-props (setq result (append result layer-props))))) ;; Regular property - keep as-is @@ -2569,15 +2591,18 @@ For group names, includes `tp-layers' property with the full layer stack." (cond ;; Parameterized layer - evaluate with the argument ((tp-layer-parameterized-p first-elem) - (tp-layer-props-with-arg first-elem second-elem)) + (tp-layer-props-with-arg first-elem second-elem nil)) ; no tp-name ;; Non-parameterized layer - arg should be t, return the layer props ;; (silently ignore non-t values for flexibility) ((assoc first-elem tp-layer-alist) - (tp-layer-props first-elem)) - ;; Layer group + (tp-layer-props first-elem nil)) ; no tp-name + ;; Layer group - merge all layers' properties for direct setting ((assoc first-elem tp-layer-groups) (when-let ((layer-props-list (tp-group-props first-elem))) - (tp--build-layer-props layer-props-list)))))) + ;; For direct property setting, merge all layers' properties + ;; without the tp-layers structure. + ;; 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))) @@ -2614,7 +2639,7 @@ For group names, includes `tp-layers' property with the full layer stack." (tp--set-layer-props layer-name resolved-props) ;; Register reactive dependencies with the original props (tp--register-reactive-deps layer-name reactive-syms props) - ;; Return resolved props with tp-name + ;; Return resolved props with tp-name for reactive tracking (append resolved-props (list 'tp-name layer-name))) ;; No reactive symbols - return props as-is (no tp-name needed) ;; This preserves the native text property behavior for non-reactive plists @@ -2625,14 +2650,16 @@ For group names, includes `tp-layers' property with the full layer stack." ;; Parameterized layer without argument - cannot resolve, return nil ((tp-layer-parameterized-p props) nil) - ;; Check layer - use tp-layer-props which adds tp-name + ;; Check layer - get props without tp-name for direct property setting ((assoc props tp-layer-alist) - (tp-layer-props props)) - ;; Check group - build layer stack with tp-layers + (tp-layer-props props nil)) ; no tp-name + ;; Check group - merge all layers' properties for direct setting ((assoc props tp-layer-groups) (when-let ((layer-props-list (tp-group-props props))) - ;; Build the layer stack: first layer on top, rest in tp-layers - (tp--build-layer-props layer-props-list))) + ;; For direct property setting, merge all layers' properties + ;; without the tp-layers structure. + ;; Reverse so first layer's properties are applied last (take precedence) + (apply #'append (reverse layer-props-list)))) ;; Not found - return nil (let caller decide how to handle) (t nil))) (t nil))) @@ -2705,11 +2732,12 @@ Returns a list of (START END PROPERTIES) for matching intervals." ;;; --- Layer Stack Utilities --- (defun tp--normalize-layer-spec (layer-spec) - "Normalize LAYER-SPEC to a plist with tp-name." + "Normalize LAYER-SPEC to a plist with tp-name. +Used by layer stack functions that need tp-name for identification." (cond ;; Symbol - look up in tp-layer-alist ((symbolp layer-spec) - (or (tp-layer-props layer-spec) + (or (tp-layer-props layer-spec t) ; include tp-name for layer stack (error "Layer %S not found in tp-layer-alist" layer-spec))) ;; List starting with symbol followed by plist - named inline layer (name &rest plist) ((and (listp layer-spec) @@ -2827,7 +2855,7 @@ OBJECT defaults to current buffer for region form." ;; Check if it's a group name ((and (symbolp layer-spec) (assoc layer-spec tp-layer-groups)) - (tp-group-props layer-spec)) + (tp-group-props layer-spec t)) ; include tp-name for layer stack ;; Single layer spec ((or (symbolp layer-spec) (and (listp layer-spec)