# tp.el - Text Properties Library for Emacs

A powerful text properties manipulation library with an innovative layer system

FeaturesInstallationQuick StartAPI ReferenceLayer System中文文档

--- **tp.el** provides a convenient and unified API for manipulating Emacs text properties. Inspired by [ov.el](https://github.com/emacsorphanage/ov) for overlays, tp.el offers: - **Unified API**: All property-setting functions work on both **strings** and **buffers** - **Layer System**: Stack multiple property sets on the same text region - **Pattern Matching**: Apply properties to text matching strings or regexps ## Features - ✅ **Unified Object Support**: Functions like `tp-put`, `tp-match`, `tp-regexp` work on both strings and buffers - ✅ **Innovative Layer System**: Stack, rotate, and manage multiple layers of properties - ✅ **Layer Groups**: Define reusable sets of related layers - ✅ **Search & Navigation**: Find and navigate through propertized text - ✅ **Pattern Matching**: Apply properties to string/regexp matches - ✅ **Clean API**: Consistent naming and calling conventions ## Requirements - **Emacs 28.1+** (uses `object-intervals` function) - **dash.el** (list manipulation utilities) ## Installation ```elisp ;; Add to your load-path (add-to-list 'load-path "/path/to/tp") (require 'tp) ``` Or with `use-package`: ```elisp (use-package tp :load-path "/path/to/tp") ``` --- ## Quick Start ### Setting Properties ```elisp ;; On current buffer (tp-put 1 10 'face 'bold 'help-echo "Hello!") ;; On a string (tp-put "Hello World" 0 5 'face 'bold) ;; => #("Hello World" 0 5 (face bold)) ;; Using a property list (tp-put 1 10 '(face bold help-echo "test")) ``` ### Getting Properties ```elisp ;; Get specific property (tp-get 5 'face) ; => bold ;; Get all properties at point (tp-at 5) ; => (face bold help-echo "Hello!") ``` ### Pattern Matching ```elisp ;; Apply properties to all occurrences of "TODO" in buffer (tp-match "TODO" 'face 'warning) ;; Apply to string (tp-match "world" "Hello world world" 'face 'bold) ;; => #("Hello world world" 6 11 (face bold) 12 17 (face bold)) ;; Using regexp (tp-regexp "\\b[0-9]+\\b" 'face 'font-lock-number-face) ``` --- ## API Reference ### Core Property Functions #### `tp-put` - Set Text Properties Set text properties on a string or buffer region. ```elisp ;; Buffer (current buffer) (tp-put START END PROPERTY VALUE ...) (tp-put START END '(PROPERTY VALUE ...)) ;; String or Buffer object (tp-put OBJECT START END PROPERTY VALUE ...) (tp-put OBJECT START END '(PROPERTY VALUE ...)) ``` **Examples:** ```elisp ;; Set face on buffer region (tp-put 1 10 'face 'bold) ; => (1 . 10) ;; Set multiple properties (tp-put 1 10 'face 'bold 'help-echo "Click me") ;; Set properties on a string (setq my-string (tp-put "Hello World" 0 5 'face 'italic)) ;; => #("Hello World" 0 5 (face italic)) ;; Properties as a list (tp-put 1 10 '(face bold mouse-face highlight)) ``` --- #### `tp-get` - Get Property Value ```elisp (tp-get POSITION PROPERTY &optional OBJECT) ``` Get the value of PROPERTY at POSITION. **Examples:** ```elisp (tp-get 5 'face) ; Get from current buffer (tp-get 0 'face my-string) ; Get from string ``` --- #### `tp-at` - Get All Properties ```elisp (tp-at &optional POINT OBJECT) ``` Get all text properties at POINT as a plist. **Examples:** ```elisp (tp-at 5) ; => (face bold help-echo "test") (tp-at 0 my-string) ; Get from string ``` --- #### `tp-remove` - Remove Property ```elisp (tp-remove START END PROPERTY &optional OBJECT) ``` Remove a specific property from a region. **Examples:** ```elisp (tp-remove 1 10 'face) ; Remove face property ``` --- #### `tp-remove-list` - Remove Multiple Properties ```elisp (tp-remove-list START END PROPERTIES &optional OBJECT) ``` Remove multiple properties at once. **Examples:** ```elisp (tp-remove-list 1 10 '(face help-echo mouse-face)) ``` --- #### `tp-clear` - Clear All Properties ```elisp (tp-clear &optional START END OBJECT) ``` Clear all text properties from a region. **Examples:** ```elisp (tp-clear 1 10) ; Clear region (tp-clear) ; Clear entire buffer ``` --- ### Propertize Functions #### `tp-propertize` - Create Propertized String ```elisp ;; Create propertized string (tp-propertize STRING PROPERTY VALUE ...) (tp-propertize STRING '(PROPERTY VALUE ...)) ;; Apply to region of object (tp-propertize OBJECT START END PROPERTY VALUE ...) ``` **Examples:** ```elisp ;; Simple usage - returns propertized string (tp-propertize "Hello" 'face 'bold) ;; => #("Hello" 0 5 (face bold)) ;; With property list (tp-propertize "World" '(face italic help-echo "greeting")) ;; Apply to substring (tp-propertize "Hello World" 6 11 'face 'underline) ``` --- #### `tp-layer-propertize` - Apply Layer to Object ```elisp (tp-layer-propertize OBJECT LAYER &optional START END) ``` Apply a predefined layer's properties to an object. **Examples:** ```elisp ;; Define a layer first (tp-layer-define highlight '(face (:background "yellow"))) ;; Apply to string (tp-layer-propertize "Important" 'highlight) ;; Apply to substring (tp-layer-propertize "Hello World" 'highlight 0 5) ;; Apply to buffer region (tp-layer-propertize (current-buffer) 'highlight 1 10) ``` --- #### `tp-group-propertize` - Apply Layer Group ```elisp (tp-group-propertize OBJECT LAYER-GROUP &optional START END) ``` Apply all layers from a layer group to an object. --- ### Pattern Matching Functions #### `tp-match` - Match String ```elisp ;; Buffer (tp-match PATTERN PROPERTY VALUE ...) ;; String or Buffer object (tp-match PATTERN OBJECT PROPERTY VALUE ...) ``` Set properties on all occurrences of a string pattern. **Examples:** ```elisp ;; In buffer - returns list of (START . END) pairs (tp-match "TODO" 'face 'warning) ;; => ((10 . 14) (50 . 54) ...) ;; On string - returns modified string (tp-match "o" "Hello World" 'face 'bold) ;; => #("Hello World" 4 5 (face bold) 7 8 (face bold)) ``` --- #### `tp-regexp` - Match Regexp ```elisp ;; Buffer (tp-regexp PATTERN PROPERTY VALUE ...) ;; String or Buffer object (tp-regexp PATTERN OBJECT PROPERTY VALUE ...) ``` Set properties on all matches of a regular expression. **Examples:** ```elisp ;; Highlight all numbers in buffer (tp-regexp "[0-9]+" 'face 'font-lock-number-face) ;; On string (tp-regexp "[A-Z]+" "Hello WORLD" 'face 'bold) ;; => #("Hello WORLD" 6 11 (face bold)) ``` --- ### Search & Navigation Functions #### `tp-forward` / `tp-backward` ```elisp (tp-forward PROPERTY &optional VALUE PREDICATE NOT-CURRENT) (tp-backward PROPERTY &optional VALUE PREDICATE NOT-CURRENT) ``` Search forward/backward for text with PROPERTY. **Examples:** ```elisp ;; Find next text with 'marker property (tp-forward 'marker) ;; Find next text where 'type equals 'heading (tp-forward 'type 'heading) ``` --- #### `tp-next` / `tp-prev` ```elisp (tp-next &optional POINT PROPERTY VALUE) (tp-prev &optional POINT PROPERTY VALUE) ``` Get the next/previous position with text properties. --- #### `tp-goto-next` / `tp-goto-prev` ```elisp (tp-goto-next &optional PROPERTY VALUE) (tp-goto-prev &optional PROPERTY VALUE) ``` Move point to next/previous text with PROPERTY. --- #### `tp-regions-map` / `tp-strings-map` ```elisp (tp-regions-map FUNCTION PROPERTY &optional VALUE PREDICATE COLLECT) (tp-strings-map FUNCTION PROPERTY &optional VALUE PREDICATE COLLECT) ``` Apply a function to all regions/strings with PROPERTY. **Examples:** ```elisp ;; Upcase all marked text (tp-strings-map (lambda (str idx) (message "Found: %s at index %d" str idx)) 'marker) ``` --- ### Query Functions #### `tp-in` - Find Regions with Property ```elisp (tp-in PROPERTY &optional VALUE START END) ``` Get all regions with PROPERTY in current buffer. **Examples:** ```elisp ;; Get all regions with 'marker property (tp-in 'marker) ;; => ((1 5 (marker t ...)) (10 15 (marker t ...))) ;; Filter by value (tp-in 'type 'heading) ``` --- #### `tp-all` - Get All Propertized Regions ```elisp (tp-all &optional START END) ``` Get all regions with any text properties. --- #### `tp-intervals` - Get Property Intervals ```elisp (tp-intervals START END &optional OBJECT) ``` Get all text property intervals in a region. --- #### `tp-empty-p` - Check for Properties ```elisp (tp-empty-p OBJECT) ``` Return t if OBJECT has no text properties. --- #### `tp-plist` - Get Merged Properties ```elisp (tp-plist START END &optional OBJECT) ``` Get a merged plist of all properties in a region. --- ## The Layer System The **layer system** is tp.el's innovative feature that allows stacking multiple sets of properties on the same text region. Only the **top layer** is visible, but lower layers are preserved and can be revealed through rotation or pinning. ### Layer Concept ``` ┌─────────────────────────────┐ │ TOP LAYER (visible) │ ← What you see ├─────────────────────────────┤ │ Middle Layer (hidden) │ ← Preserved ├─────────────────────────────┤ │ Bottom Layer (hidden) │ ← Preserved └─────────────────────────────┘ ``` ### Layer Definition Functions #### `tp-layer-define` - Define a Layer ```elisp (tp-layer-define NAME PROPERTIES) ``` Define a named layer with properties. **Examples:** ```elisp (tp-layer-define highlight '(face (:background "yellow" :foreground "black"))) (tp-layer-define error '(face (:background "red" :foreground "white") help-echo "Error!")) (tp-layer-define info '(face (:background "blue" :foreground "white"))) ``` --- #### `tp-group-define` - Define Layer Group ```elisp (tp-group-define NAME LAYER1 PROPERTIES1 LAYER2 PROPERTIES2 ...) ``` Define a group of related layers. **Examples:** ```elisp (tp-group-define status-colors status-ok '(face (:foreground "green")) status-warning '(face (:foreground "orange")) status-error '(face (:foreground "red"))) ``` --- #### `tp-layer-props` / `tp-group-props` ```elisp (tp-layer-props LAYER-NAME) (tp-group-props GROUP-NAME) ``` Get properties for a layer or all layers in a group. --- #### `tp-layer-undefine` / `tp-group-undefine` ```elisp (tp-layer-undefine NAME) (tp-group-undefine NAME) ``` Remove layer or group definition. --- #### `tp-layer-reset` ```elisp (tp-layer-reset) ``` Clear all layer and group definitions. --- ### Layer Manipulation Functions #### `tp-layer-push` - Add Layer ```elisp (tp-layer-push START END NAME &optional OBJECT) ``` Push a layer to the top of the stack. **Examples:** ```elisp (tp-layer-define base '(face default)) (tp-layer-define highlight '(face (:background "yellow"))) ;; Push base layer first (tp-layer-push 1 10 'base) ;; Push highlight on top (now visible) (tp-layer-push 1 10 'highlight) ``` --- #### `tp-layer-delete` - Remove Layer ```elisp (tp-layer-delete START END NAME &optional OBJECT) ``` Delete a layer from anywhere in the stack. **Examples:** ```elisp ;; Remove the highlight layer (tp-layer-delete 1 10 'highlight) ;; base layer is now visible ``` --- #### `tp-layer-rotate` - Cycle Layers ```elisp (tp-layer-rotate START END &optional OBJECT) ``` Rotate layers - top goes to bottom, next becomes visible. **Examples:** ```elisp ;; Stack: highlight (top) -> base (bottom) (tp-layer-rotate 1 10) ;; Stack: base (top) -> highlight (bottom) ``` --- #### `tp-layer-pin` - Bring Layer to Top ```elisp (tp-layer-pin START END NAME &optional OBJECT) ``` Move a specific layer to the top. **Examples:** ```elisp ;; Make 'base the top layer (tp-layer-pin 1 10 'base) ``` --- #### `tp-layer-hide` / `tp-layer-show` ```elisp (tp-layer-hide START END NAME &optional OBJECT) (tp-layer-show START END NAME &optional OBJECT) ``` Hide layer (move to bottom) or show layer (move to top). --- #### `tp-layer-merge` ```elisp (tp-layer-merge START END LAYER1 LAYER2 NEW-NAME &optional OBJECT) ``` Merge two layers into one new layer. --- ### Layer Query Functions #### `tp-layer-list` - List All Layers ```elisp (tp-layer-list START END &optional OBJECT) ``` Get list of all layer names in region. **Examples:** ```elisp (tp-layer-list 1 10) ; => (highlight base) ``` --- #### `tp-layer-count` ```elisp (tp-layer-count START END &optional OBJECT) ``` Count layers in region. --- #### `tp-layer-exists-p` ```elisp (tp-layer-exists-p START END NAME &optional OBJECT) ``` Check if layer exists in region. --- #### `tp-layer-top` ```elisp (tp-layer-top START END &optional OBJECT) ``` Get name of the top (visible) layer. --- ## Practical Examples ### Syntax Highlighting with Multiple Layers ```elisp ;; Define layers for different highlighting purposes (tp-layer-define code-base '(face font-lock-keyword-face)) (tp-layer-define code-error '(face (:underline (:color "red" :style wave)) help-echo "Syntax error")) (tp-layer-define code-debug '(face (:background "dark blue"))) ;; Apply base highlighting (tp-layer-push 1 100 'code-base) ;; Add error highlight on problematic code (tp-layer-push 50 60 'code-error) ;; Toggle between error and normal view (defun toggle-error-view () (interactive) (tp-layer-rotate 50 60)) ``` ### Status Indicator ```elisp (tp-group-define task-status status-todo '(face (:foreground "gray")) status-progress '(face (:foreground "yellow")) status-done '(face (:foreground "green"))) ;; Cycle through statuses (defun cycle-task-status () (interactive) (tp-layer-rotate (line-beginning-position) (line-end-position))) ``` ### Temporary Highlights ```elisp (tp-layer-define temp-highlight '(face (:background "yellow"))) (defun flash-region (start end) "Flash a region temporarily." (tp-layer-push start end 'temp-highlight) (run-with-timer 0.5 nil (lambda () (tp-layer-delete start end 'temp-highlight)))) ``` --- ## Aliases For convenience, tp.el provides these aliases: | Alias | Original Function | |-------|-------------------| | `tp-set` | `tp-put` | | `tp-layer-properties` | `tp-layer-props` | | `tp-layer-group-define` | `tp-group-define` | | `tp-layer-group-properties` | `tp-group-props` | | `tp-layer-group-propertize` | `tp-group-propertize` | | `tp-layer-group-undefine` | `tp-group-undefine` | --- ## License GNU General Public License v2 or later. --- ## Contributing Contributions are welcome! Please feel free to submit issues or pull requests. ---

tp.el - Making text properties powerful and easy to use