Introduce the pure TP 1.0 property schema, selector, cascade, custom-property, computed-value, provenance, and Emacs projection kernel. Compile static legacy layer definitions into canonical named styles without freezing parameterized or reactive definitions.\n\nVerified with 667 ERT tests, 92 doctests, shuffled seed 20260806, and compile-all with warnings as errors.
tp.el (4866 lines) is now an umbrella over nine modules with an
enforceable dependency order: tp-core -> tp-reactive -> tp-layer ->
tp-ops -> tp-search -> tp-render -> tp-stack -> tp-palette ->
tp-builtins. Upward dependencies are inverted through four hook
variables installed by tp-render.el.
Also: require text-property-search (fixes tp-backward void-function),
clip tp-intervals to the requested range, add the shared clipping
interval walker tp--map-intervals and tp-face-properties, remove
synced-conflict junk files, byte-compilation now succeeds (define-tp
macroexpansion previously failed at compile time).
All 280 legacy tests pass plus 8 new tp-core tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When (frame-parameter nil 'background-mode) returns nil (e.g., in
batch mode or certain configurations), both tp-theme-light-p and
tp-theme-dark-p return nil, causing color parsing to fail for
cons cell ("light" . "dark") and plist (:light "l" :dark "d") formats.
Added default fallback to light color when background-mode is unknown.
This is the root cause of tp-palette settings not taking effect.
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
- Rename second tp--remove-nested-keys to tp--remove-nested-sub-keys
- Fix logic in tp--remove-props-from-string to properly handle nil face subtraction result
- Fix tests to properly use return values from tp-set and tp-remove
- Add face-was-modified tracking variable
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
- Add tp--remove-sub-from-face-value to handle complex face structures
- Add tp--subtract-face-from-face-value to remove layer face contributions
- Add tp--get-layer-face-contribution helper function
- Update tp--remove-props-from-string to handle layer face subtraction
- Update tp--remove-sub-from-string to use new helper
- Update tp--expand-layer-to-props-list to detect layer properties
- Fix missing closing paren in tp--remove-property function
- Update tests to match expected behavior
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
When tp-remove is called with a layer name (defined with define-tp),
it now correctly identifies and removes all properties that the layer
adds, plus the tp-name property.
Example:
(define-tp tp-delete (color)
`(face (:strike-through ,color)))
(let ((str "emacs"))
(setq str (tp-set str 'face 'bold 'tp-delete t))
(insert (tp-remove str 'tp-delete)))
Changes:
- Added tp--expand-layer-to-props-list helper function
- Added tp--expand-props-to-remove helper function
- Modified tp--remove-props-from-string to expand layer names
- Modified tp--remove-property for buffer regions to handle layer names
- Added tests for custom layer removal
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
- Collect all match positions before applying transformations to avoid
string-match position invalidation issues
- Use list* instead of nested cons for better readability
- Improve plist building efficiency by iterating instead of copy-sequence
- Add detailed comment explaining :reset mode usage with tp-text
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
Updated the implementation to clearly distinguish:
- (tp-set/add/reset START END PROPS STRING): Modifies original string in-place
- (tp-set/add/reset "string" PROP VAL ...): Creates NEW string using propertize
Also fixed code review issues:
- Removed unused 'offset' variable
- Made tp--remove-nested-keys non-destructive
Updated documentation with detailed behavior table and examples.
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This change makes tp-set, tp-add, tp-reset, and tp-remove return a NEW
propertized string when operating on strings, rather than modifying the
original string in-place. This is more idiomatic for Emacs Lisp and
prevents unintended side effects.
For buffer operations, the existing in-place modification behavior is
preserved.
Key changes:
- Added tp--apply-props-to-string helper function that uses propertize
- Updated tp-set, tp-add, tp-reset to use the new helper for strings
- Updated tp-remove with new helper functions for non-destructive removal
- Updated tp--match-apply and tp--regexp-apply to accumulate changes
- Added tests verifying original strings are not modified
- Updated documentation to reflect the new behavior
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
When face values contain mixed lists (symbols and plists like
(bold (:foreground "green"))), the plist parts are now correctly
merged with earlier plists. This ensures that later values for
the same sub-property (like :foreground) properly override earlier
values without creating duplicates.
Example that now works correctly:
(tp-add
(tp-add
(tp-set "emacs" 'face 'bold)
'face '(:foreground "red"))
'face '(bold (:foreground "green")))
=> face is (bold (:foreground "green")) instead of
(bold (:foreground "green") (:foreground "red"))
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
- Fix tp--equal-including-string-properties to only use
equal-including-properties when BOTH args are strings
- Improve test to use (point-min) and (point-max) instead of
hardcoded positions
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
Changed from using tp-add to set-text-properties when text content is
same but properties differ. This prevents duplicate property merging
since merged-props already contains the complete set of properties.
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
- Added `tp--equal-including-string-properties` helper function that uses
`equal-including-properties` for strings to properly detect changes in
text properties when the text content is the same
- Updated `tp--reactive-variable-watcher` to use this new comparison function
- Updated `tp--replace-reactive-text-in-buffer` to use `tp-add` when text
content is the same but properties may differ
- Added test `tp-test-tp-text-same-text-different-properties` to verify
the fix
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
Added tp--parse-face-list helper function to parse mixed face lists
that contain both symbols and inline plist keys/values:
- (bold :foreground "green") -> symbols: (bold), plist: (:foreground "green")
- (bold (:foreground "green")) -> symbols: (bold), plist: (:foreground "green")
- (:foreground "green") -> symbols: nil, plist: (:foreground "green")
Updated tp--merge-face-values to use this helper when merging face lists.
Added test case for mixed format to tp-test-tp-add-face-override-subprops.
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
When tp-add merges face properties, new props values should now
override embedded values for the same sub-properties (e.g., if
new face has :foreground "green" and embedded has :foreground "red",
result should be :foreground "green").
Updated tp--merge-string-props-into-plist to pass props as the
override value when merging faces.
Updated tp--merge-face-values to properly handle mixed face lists
(symbols + plists) when the base face is a plist.
Added test tp-test-tp-add-face-override-subprops to verify this behavior.
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
- tp-set: props override embedded text properties in tp-text value
- tp-add: props are merged with embedded text properties
- tp-reset: embedded properties are ignored, only props used
Added merge-mode parameter to tp--handle-tp-text-property:
- :override - props take precedence (tp-set)
- :merge - embedded props merged with props (tp-add)
- :reset - embedded props ignored (tp-reset)
Updated tests to reflect correct semantics.
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
- Add tp--merge-string-props-into-plist to merge embedded properties
from tp-text value into props plist with proper face merging
- Simplify tp--handle-tp-text-property to use the new merge function
and return merged props directly
- Simplify tp-set, tp-reset, tp-add by removing marker-based handling
- Fix tp-add to avoid duplicate face merging for strings with tp-text
- Remove unused tp--apply-string-props-to-region and
tp--remove-internal-markers functions
- Update tests to match the simplified implementation
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
Use tp--string-has-properties-p helper function instead of checking
only position 0. This properly detects text properties that start at
non-zero positions in the string.
Added test tp-test-tp-text-with-properties-starting-at-nonzero to
verify this fix.
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
- Modify tp--handle-tp-text-property to detect and preserve embedded text
properties in tp-text values for both strings and buffers
- Add tp--apply-string-props-to-region helper to apply string properties
- Update tp-set, tp-reset, tp-add to preserve embedded tp-text properties
by using put-text-property instead of set-text-properties when needed
- Update tp--replace-reactive-text-in-buffer to preserve embedded properties
- Add tests for tp-text with embedded text properties
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
The bug occurred because tp--parse-args was incorrectly omitting nil values
from the property list, producing (face) instead of (face nil).
Fixed the same bug pattern in:
- tp--parse-args (used by tp-set, tp-reset, tp-add)
- tp-add-to-layers
- tp-add-to-all-layers
Added 6 new regression tests for nil property values.
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>