Optimize function naming and add object parameter support
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
parent
8e7c9ef989
commit
73f65e294b
793
README.md
Normal file
793
README.md
Normal file
@ -0,0 +1,793 @@
|
||||
# tp.el - Text Properties Library for Emacs
|
||||
|
||||
<p align="center">
|
||||
<strong>A powerful text properties manipulation library with an innovative layer system</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#features">Features</a> •
|
||||
<a href="#installation">Installation</a> •
|
||||
<a href="#quick-start">Quick Start</a> •
|
||||
<a href="#api-reference">API Reference</a> •
|
||||
<a href="#the-layer-system">Layer System</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<em>tp.el - Making text properties powerful and easy to use</em>
|
||||
</p>
|
||||
259
readme.md
259
readme.md
@ -1,259 +0,0 @@
|
||||
# tp.el - Text Properties Library for Emacs
|
||||
|
||||
A convenient wrapper around Emacs text properties with an innovative **layer system** that allows setting multiple layers of text properties on the same text region.
|
||||
|
||||
Inspired by [ov.el](https://github.com/emacsorphanage/ov) for overlays.
|
||||
|
||||
## Features
|
||||
|
||||
- **Simple API** for text property manipulation (similar to ov.el for overlays)
|
||||
- **Innovative tp-layer system** for multi-layer text properties
|
||||
- **Layer groups** for defining reusable property sets
|
||||
- **Search and navigation** functions for text properties
|
||||
- **Match and regexp** functions for applying properties to text patterns
|
||||
|
||||
## Installation
|
||||
|
||||
```elisp
|
||||
(require 'tp)
|
||||
```
|
||||
|
||||
**Requirements:**
|
||||
- Emacs 28.1+ (uses `object-intervals` function)
|
||||
- `dash` (for list manipulation utilities)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Text Properties
|
||||
|
||||
```elisp
|
||||
;; Set properties on a region
|
||||
(tp-put 10 20 'face 'warning 'help-echo "Hello!")
|
||||
|
||||
;; Get property at point
|
||||
(tp-get 15 'face) ; => warning
|
||||
|
||||
;; Remove a property
|
||||
(tp-remove 10 20 'face)
|
||||
|
||||
;; Clear all properties in region
|
||||
(tp-clear 10 20)
|
||||
|
||||
;; Get all properties at point
|
||||
(tp-at 15) ; => (face warning help-echo "Hello!")
|
||||
```
|
||||
|
||||
### Match and Regexp
|
||||
|
||||
```elisp
|
||||
;; Apply properties to all matches of a string
|
||||
(tp-match "TODO" 'face 'warning)
|
||||
|
||||
;; Apply properties to all matches of a regexp
|
||||
(tp-regexp "\\bfunction\\b" 'face 'font-lock-function-name-face)
|
||||
```
|
||||
|
||||
### The Layer System
|
||||
|
||||
The layer system allows you to stack multiple sets of properties on the same text. Only the top layer is visible, but you can rotate, pin, or delete layers to reveal hidden ones.
|
||||
|
||||
```elisp
|
||||
;; Define reusable layers
|
||||
(tp-layer-define highlight
|
||||
'(face (:background "yellow" :foreground "black")))
|
||||
|
||||
(tp-layer-define urgent
|
||||
'(face (:background "red" :foreground "white")))
|
||||
|
||||
(tp-layer-define info
|
||||
'(face (:background "blue" :foreground "white")))
|
||||
|
||||
;; Push layers onto text (first pushed is bottom, last is top)
|
||||
(tp-layer-push 1 10 'highlight)
|
||||
(tp-layer-push 1 10 'urgent) ; urgent is now visible
|
||||
|
||||
;; Rotate layers (urgent moves to bottom, highlight becomes visible)
|
||||
(tp-layer-rotate 1 10)
|
||||
|
||||
;; Pin a specific layer to top
|
||||
(tp-layer-pin 1 10 'urgent)
|
||||
|
||||
;; Delete a layer
|
||||
(tp-layer-delete 1 10 'highlight)
|
||||
```
|
||||
|
||||
### Layer Groups
|
||||
|
||||
Define groups of layers that work together:
|
||||
|
||||
```elisp
|
||||
(tp-group-define my-status-group
|
||||
status-normal '(face (:background "green" :foreground "black"))
|
||||
status-warning '(face (:background "yellow" :foreground "black"))
|
||||
status-error '(face (:background "red" :foreground "white")))
|
||||
|
||||
;; Apply all layers from a group to a string
|
||||
(tp-group-propertize "Status" 'my-status-group)
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Basic Text Property Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-put (start end &rest properties)` | Set text properties on region |
|
||||
| `tp-get (position property &optional object)` | Get property value at position |
|
||||
| `tp-remove (start end property &optional object)` | Remove a property from region |
|
||||
| `tp-remove-list (start end properties &optional object)` | Remove multiple properties |
|
||||
| `tp-clear (&optional start end object)` | Clear all properties in region |
|
||||
| `tp-at (&optional point object)` | Get all properties at point |
|
||||
| `tp-plist (start end &optional object)` | Get merged plist for region |
|
||||
|
||||
### Match and Regexp Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-match (string &rest properties)` | Set properties on string matches |
|
||||
| `tp-regexp (regexp &rest properties)` | Set properties on regexp matches |
|
||||
|
||||
### Layer Definition Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-layer-define (name properties)` | Define a named layer |
|
||||
| `tp-group-define (name &rest layers)` | Define a layer group |
|
||||
| `tp-layer-props (layer-name)` | Get properties for a layer |
|
||||
| `tp-group-props (group-name)` | Get properties for all layers in group |
|
||||
| `tp-layer-undefine (name)` | Remove a layer definition |
|
||||
| `tp-group-undefine (name)` | Remove a layer group definition |
|
||||
| `tp-layer-reset ()` | Clear all layer definitions |
|
||||
|
||||
### Layer Manipulation Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-layer-push (start end name &optional object)` | Push layer to top of stack |
|
||||
| `tp-layer-delete (start end name &optional object)` | Delete layer from stack |
|
||||
| `tp-layer-rotate (start end &optional object)` | Rotate layers (top to bottom) |
|
||||
| `tp-layer-pin (start end name &optional object)` | Pin layer to top |
|
||||
| `tp-layer-set (start end name &optional object)` | Name the current top layer |
|
||||
| `tp-layer-hide (start end name &optional object)` | Move layer to bottom |
|
||||
| `tp-layer-show (start end name &optional object)` | Move layer to top (alias for pin) |
|
||||
| `tp-layer-merge (start end layer1 layer2 new-name &optional object)` | Merge two layers |
|
||||
|
||||
### Layer Query Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-layer-list (start end &optional object)` | List all layer names in region |
|
||||
| `tp-layer-count (start end &optional object)` | Count layers in region |
|
||||
| `tp-layer-exists-p (start end name &optional object)` | Check if layer exists |
|
||||
| `tp-layer-top (start end &optional object)` | Get name of top layer |
|
||||
| `tp-region-layer-props (start end layer-name &optional object)` | Get layer properties in region |
|
||||
|
||||
### Propertize String Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-propertize (string &rest properties)` | Propertize string |
|
||||
| `tp-layer-propertize (string layer)` | Apply layer to string |
|
||||
| `tp-group-propertize (string layer-group)` | Apply layer group to string |
|
||||
|
||||
### Search Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-forward (property &optional value predicate not-current)` | Search forward for property |
|
||||
| `tp-backward (property &optional value predicate not-current)` | Search backward for property |
|
||||
| `tp-forward-do (function property &optional ...)` | Search forward and apply function |
|
||||
| `tp-backward-do (function property &optional ...)` | Search backward and apply function |
|
||||
| `tp-regions-map (function property &optional ...)` | Apply function to all matching regions |
|
||||
| `tp-strings-map (function property &optional ...)` | Apply function to all matching strings |
|
||||
|
||||
### Navigation Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-next (&optional point property value)` | Get next position with property |
|
||||
| `tp-prev (&optional point property value)` | Get previous position with property |
|
||||
| `tp-goto-next (&optional property value)` | Move to next property |
|
||||
| `tp-goto-prev (&optional property value)` | Move to previous property |
|
||||
|
||||
### Query Functions
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `tp-in (property &optional value start end)` | Get regions with property |
|
||||
| `tp-all (&optional start end)` | Get all regions with properties |
|
||||
| `tp-intervals (start end &optional object)` | Get all property intervals |
|
||||
| `tp-empty-p (object)` | Check if object has no properties |
|
||||
|
||||
## The Layer Concept
|
||||
|
||||
The layer system stores multiple sets of properties in a stack structure:
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ TOP LAYER (visible) │ <- Properties you see
|
||||
├─────────────────────────┤
|
||||
│ Middle Layer │ <- Hidden, but preserved
|
||||
├─────────────────────────┤
|
||||
│ Bottom Layer │ <- Hidden, but preserved
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
Each layer is identified by a name (symbol) and contains:
|
||||
- Standard text properties (face, display, help-echo, etc.)
|
||||
- A special `tp-name` property for identification
|
||||
- A `tp-layers` property containing the list of layers below
|
||||
|
||||
### Layer Operations
|
||||
|
||||
- **Push**: Add a new layer on top
|
||||
- **Delete**: Remove a layer from anywhere in the stack
|
||||
- **Rotate**: Move top layer to bottom (cycles visibility)
|
||||
- **Pin**: Move any layer to the top
|
||||
- **Hide**: Move a layer to the bottom
|
||||
- **Merge**: Combine two layers into one
|
||||
|
||||
## Examples
|
||||
|
||||
### Syntax Highlighting with Layers
|
||||
|
||||
```elisp
|
||||
;; Define layers for different highlighting purposes
|
||||
(tp-layer-define code-syntax
|
||||
'(face font-lock-keyword-face))
|
||||
|
||||
(tp-layer-define code-error
|
||||
'(face (:underline (:color "red" :style wave))))
|
||||
|
||||
(tp-layer-define code-selection
|
||||
'(face (:background "light blue")))
|
||||
|
||||
;; Apply base syntax highlighting
|
||||
(tp-layer-push 1 100 'code-syntax)
|
||||
|
||||
;; Add error highlighting on top (doesn't remove syntax highlighting)
|
||||
(tp-layer-push 1 100 'code-error)
|
||||
|
||||
;; Toggle between showing error and syntax
|
||||
(tp-layer-rotate 1 100)
|
||||
```
|
||||
|
||||
### Interactive Layer Switching
|
||||
|
||||
```elisp
|
||||
(defun my-toggle-layers ()
|
||||
"Toggle between different property layers on current line."
|
||||
(interactive)
|
||||
(tp-layer-rotate (line-beginning-position)
|
||||
(line-end-position)))
|
||||
|
||||
(global-set-key (kbd "C-c t") 'my-toggle-layers)
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v2 or later.
|
||||
52
tp-tests.el
52
tp-tests.el
@ -693,5 +693,57 @@
|
||||
(set-text-properties 0 5 nil str)
|
||||
(should (tp-empty-p str))))
|
||||
|
||||
;;; ============================================================
|
||||
;;; Object Parameter Support Tests
|
||||
;;; ============================================================
|
||||
|
||||
(ert-deftest tp-test-put-on-string ()
|
||||
"Test tp-put works on string objects."
|
||||
(let ((str (copy-sequence "Hello World")))
|
||||
(tp-put str 0 5 'face 'bold)
|
||||
(should (eq (get-text-property 0 'face str) 'bold))
|
||||
(should (null (get-text-property 6 'face str)))))
|
||||
|
||||
(ert-deftest tp-test-put-on-string-returns-string ()
|
||||
"Test tp-put returns the modified string."
|
||||
(let* ((str (copy-sequence "Hello"))
|
||||
(result (tp-put str 0 5 'face 'bold)))
|
||||
(should (stringp result))
|
||||
(should (eq (get-text-property 0 'face result) 'bold))))
|
||||
|
||||
(ert-deftest tp-test-match-on-string ()
|
||||
"Test tp-match works on string objects."
|
||||
(let* ((str (copy-sequence "Hello World Hello"))
|
||||
(result (tp-match "Hello" str 'face 'bold)))
|
||||
(should (stringp result))
|
||||
(should (eq (get-text-property 0 'face result) 'bold))
|
||||
(should (eq (get-text-property 12 'face result) 'bold))
|
||||
(should (null (get-text-property 6 'face result)))))
|
||||
|
||||
(ert-deftest tp-test-regexp-on-string ()
|
||||
"Test tp-regexp works on string objects."
|
||||
(let* ((str (copy-sequence "abc 123 def 456"))
|
||||
(result (tp-regexp "[0-9]+" str 'face 'bold)))
|
||||
(should (stringp result))
|
||||
(should (eq (get-text-property 4 'face result) 'bold))
|
||||
(should (eq (get-text-property 12 'face result) 'bold))
|
||||
(should (null (get-text-property 0 'face result)))))
|
||||
|
||||
(ert-deftest tp-test-propertize-with-region ()
|
||||
"Test tp-propertize with object and region."
|
||||
(let* ((str (copy-sequence "Hello World"))
|
||||
(result (tp-propertize str 0 5 'face 'bold)))
|
||||
(should (stringp result))
|
||||
(should (eq (get-text-property 0 'face result) 'bold))))
|
||||
|
||||
(ert-deftest tp-test-layer-propertize-with-range ()
|
||||
"Test tp-layer-propertize with start/end range."
|
||||
(tp-test-with-temp-buffer
|
||||
(tp-layer-define range-layer '(face bold))
|
||||
(let* ((str (copy-sequence "Hello World"))
|
||||
(result (tp-layer-propertize str 'range-layer 0 5)))
|
||||
(should (stringp result))
|
||||
(should (eq (get-text-property 0 'face result) 'bold)))))
|
||||
|
||||
(provide 'tp-ert-tests)
|
||||
;;; tp-ert-tests.el ends here
|
||||
|
||||
356
tp.el
356
tp.el
@ -100,20 +100,61 @@ Appends 'tp-name property to identify the layer."
|
||||
|
||||
;;; Basic text property functions (similar to ov.el)
|
||||
|
||||
(defun tp-put (start end &rest properties)
|
||||
"Set text PROPERTIES from START to END in current buffer.
|
||||
(defun tp-put (object-or-start &optional start-or-end end-or-prop &rest properties)
|
||||
"Set text PROPERTIES on OBJECT (string or buffer region).
|
||||
|
||||
This function supports two calling conventions:
|
||||
|
||||
1. With OBJECT (string or buffer):
|
||||
(tp-put OBJECT START END PROPERTY VALUE ...)
|
||||
(tp-put OBJECT START END \\='(PROPERTY VALUE ...))
|
||||
|
||||
2. Without OBJECT (current buffer):
|
||||
(tp-put START END PROPERTY VALUE ...)
|
||||
(tp-put START END \\='(PROPERTY VALUE ...))
|
||||
|
||||
PROPERTIES is a plist of property-value pairs.
|
||||
Return the modified region as (START . END)."
|
||||
(when (listp (car-safe properties))
|
||||
(setq properties (car properties)))
|
||||
(let ((len (length properties))
|
||||
(i 0))
|
||||
(while (< i len)
|
||||
(put-text-property start end
|
||||
(nth i properties)
|
||||
(nth (1+ i) properties))
|
||||
(setq i (+ i 2))))
|
||||
(cons start end))
|
||||
Return the modified object (string) or region (START . END) for buffer."
|
||||
(let (object start end props)
|
||||
;; Determine calling convention based on first argument type
|
||||
(cond
|
||||
;; First arg is a string - use object convention
|
||||
((stringp object-or-start)
|
||||
(setq object object-or-start
|
||||
start start-or-end
|
||||
end end-or-prop
|
||||
props properties))
|
||||
;; First arg is a buffer - use object convention
|
||||
((bufferp object-or-start)
|
||||
(setq object object-or-start
|
||||
start start-or-end
|
||||
end end-or-prop
|
||||
props properties))
|
||||
;; First arg is a number - use buffer region convention
|
||||
((numberp object-or-start)
|
||||
(setq object nil
|
||||
start object-or-start
|
||||
end start-or-end
|
||||
props (if end-or-prop
|
||||
(cons end-or-prop properties)
|
||||
properties)))
|
||||
(t (error "Invalid first argument: %S" object-or-start)))
|
||||
;; Handle properties as a list
|
||||
(when (listp (car-safe props))
|
||||
(setq props (car props)))
|
||||
;; Apply properties
|
||||
(let ((len (length props))
|
||||
(i 0))
|
||||
(while (< i len)
|
||||
(put-text-property start end
|
||||
(nth i props)
|
||||
(nth (1+ i) props)
|
||||
object)
|
||||
(setq i (+ i 2))))
|
||||
;; Return result
|
||||
(if (stringp object)
|
||||
object
|
||||
(cons start end))))
|
||||
|
||||
(defalias 'tp-set 'tp-put
|
||||
"Alias for `tp-put'.")
|
||||
@ -334,41 +375,136 @@ Signals an error if layer NAME does not exist in the region."
|
||||
start end object)
|
||||
nil)
|
||||
|
||||
;;; Propertize string functions
|
||||
;;; Propertize functions
|
||||
|
||||
(defun tp-propertize (object-or-string &rest args)
|
||||
"Apply text properties to OBJECT.
|
||||
|
||||
This function supports multiple calling conventions:
|
||||
|
||||
1. String only (create propertized string):
|
||||
(tp-propertize STRING PROPERTY VALUE ...)
|
||||
(tp-propertize STRING \\='(PROPERTY VALUE ...))
|
||||
|
||||
2. With region (apply to object):
|
||||
(tp-propertize OBJECT START END PROPERTY VALUE ...)
|
||||
(tp-propertize OBJECT START END \\='(PROPERTY VALUE ...))
|
||||
|
||||
When called with just a string and properties, returns a new
|
||||
propertized string. When called with an object, start, and end,
|
||||
applies properties to the region and returns the object.
|
||||
|
||||
(defun tp-propertize (string &rest properties)
|
||||
"Return a copy of STRING with PROPERTIES applied.
|
||||
PROPERTIES should be a plist of property-value pairs."
|
||||
(declare (indent defun))
|
||||
(when (listp (car-safe properties))
|
||||
(setq properties (car properties)))
|
||||
(apply #'propertize string properties))
|
||||
(cond
|
||||
;; Called with just string and properties (no start/end)
|
||||
((and (stringp object-or-string)
|
||||
(or (null args)
|
||||
(symbolp (car args))
|
||||
(and (listp (car args)) (symbolp (caar args)))))
|
||||
(let ((properties args))
|
||||
(when (listp (car-safe properties))
|
||||
(setq properties (car properties)))
|
||||
(if properties
|
||||
(apply #'propertize object-or-string properties)
|
||||
(copy-sequence object-or-string))))
|
||||
;; Called with object, start, end, properties
|
||||
((and (or (stringp object-or-string) (bufferp object-or-string))
|
||||
(>= (length args) 2)
|
||||
(numberp (car args))
|
||||
(numberp (cadr args)))
|
||||
(let ((object object-or-string)
|
||||
(start (car args))
|
||||
(end (cadr args))
|
||||
(properties (cddr args)))
|
||||
(when (listp (car-safe properties))
|
||||
(setq properties (car properties)))
|
||||
(tp-put object start end properties)))
|
||||
(t (error "Invalid arguments to tp-propertize"))))
|
||||
|
||||
(defun tp-layer-propertize (string layer)
|
||||
"Return STRING with properties from LAYER applied.
|
||||
LAYER must be defined in `tp-layer-alist'."
|
||||
(defun tp-layer-propertize (object layer &optional start end)
|
||||
"Apply LAYER properties to OBJECT.
|
||||
|
||||
OBJECT can be a string or buffer.
|
||||
LAYER must be defined in `tp-layer-alist'.
|
||||
|
||||
Calling conventions:
|
||||
1. String (full string):
|
||||
(tp-layer-propertize STRING LAYER)
|
||||
|
||||
2. String with range:
|
||||
(tp-layer-propertize STRING LAYER START END)
|
||||
|
||||
3. Buffer with range:
|
||||
(tp-layer-propertize BUFFER LAYER START END)
|
||||
|
||||
Returns the modified object."
|
||||
(if-let ((layer-info (assoc layer tp-layer-alist)))
|
||||
(apply #'propertize string (cdr layer-info))
|
||||
(let ((props (cdr layer-info)))
|
||||
(cond
|
||||
;; String without range - apply to whole string
|
||||
((and (stringp object) (null start))
|
||||
(apply #'propertize object props))
|
||||
;; String or buffer with range
|
||||
((or (stringp object) (bufferp object))
|
||||
(let ((beg (or start 0))
|
||||
(fin (or end (if (stringp object)
|
||||
(length object)
|
||||
(with-current-buffer object (point-max))))))
|
||||
(tp-put object beg fin props)))
|
||||
(t (error "Invalid object type: %S" (type-of object)))))
|
||||
(error "Layer %S doesn't exist!" layer)))
|
||||
|
||||
(defun tp-group-propertize (string layer-group)
|
||||
"Return STRING with all layers from LAYER-GROUP applied.
|
||||
(defun tp-group-propertize (object layer-group &optional start end)
|
||||
"Apply all layers from LAYER-GROUP to OBJECT.
|
||||
|
||||
OBJECT can be a string or buffer.
|
||||
LAYER-GROUP must be defined in `tp-layer-groups'.
|
||||
Layers are applied in order, with later layers on top."
|
||||
Layers are applied in order, with later layers on top.
|
||||
|
||||
Calling conventions:
|
||||
1. String (full string):
|
||||
(tp-group-propertize STRING LAYER-GROUP)
|
||||
|
||||
2. String with range:
|
||||
(tp-group-propertize STRING LAYER-GROUP START END)
|
||||
|
||||
3. Buffer with range:
|
||||
(tp-group-propertize BUFFER LAYER-GROUP START END)
|
||||
|
||||
Returns the modified object."
|
||||
(if-let* ((group-info (assoc layer-group tp-layer-groups))
|
||||
(layers (cdr group-info)))
|
||||
(let ((result string))
|
||||
(let* ((beg (or start 0))
|
||||
(fin (or end (if (stringp object)
|
||||
(length object)
|
||||
(with-current-buffer object (point-max)))))
|
||||
(result (if (stringp object)
|
||||
(copy-sequence object)
|
||||
object)))
|
||||
;; Apply base layer first
|
||||
(when-let ((first-layer (car layers)))
|
||||
(setq result (tp-layer-propertize result first-layer)))
|
||||
(if (stringp result)
|
||||
(setq result (tp-layer-propertize result first-layer beg fin))
|
||||
(tp-layer-propertize result first-layer beg fin)))
|
||||
;; Apply additional layers using the layer system
|
||||
(dolist (layer (cdr layers))
|
||||
(when-let ((props (tp-layer-props layer)))
|
||||
(set-text-properties 0 (length result)
|
||||
(append props
|
||||
(list 'tp-layers
|
||||
(list (tp-at 0 result))))
|
||||
result)))
|
||||
(if (stringp result)
|
||||
(set-text-properties beg fin
|
||||
(append props
|
||||
(list 'tp-layers
|
||||
(list (tp-at beg result))))
|
||||
result)
|
||||
(with-current-buffer result
|
||||
(tp-intervals-map
|
||||
(lambda (i-start i-end top belows)
|
||||
(set-text-properties
|
||||
(+ beg i-start) (+ beg i-end)
|
||||
(append props
|
||||
(list 'tp-layers (append (list top) belows)))
|
||||
result))
|
||||
beg fin result)))))
|
||||
result)
|
||||
(error "Layer group %S doesn't exist!" layer-group)))
|
||||
|
||||
@ -430,37 +566,131 @@ FUNCTION receives two arguments: STRING and INDEX."
|
||||
|
||||
;;; Match and regexp functions (similar to ov-match and ov-regexp)
|
||||
|
||||
(defun tp-match (string &rest properties)
|
||||
"Set PROPERTIES on all occurrences of STRING in current buffer.
|
||||
Returns list of (START . END) pairs for all matches."
|
||||
(when (listp (car-safe properties))
|
||||
(setq properties (car properties)))
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let (regions)
|
||||
(while (search-forward string nil t)
|
||||
(let ((beg (match-beginning 0))
|
||||
(end (match-end 0)))
|
||||
(when properties
|
||||
(tp-put beg end properties))
|
||||
(push (cons beg end) regions)))
|
||||
(nreverse regions))))
|
||||
(defun tp-match (pattern &rest args)
|
||||
"Set properties on all occurrences of PATTERN.
|
||||
|
||||
(defun tp-regexp (regexp &rest properties)
|
||||
"Set PROPERTIES on all matches of REGEXP in current buffer.
|
||||
Returns list of (START . END) pairs for all matches."
|
||||
(when (listp (car-safe properties))
|
||||
(setq properties (car properties)))
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let (regions)
|
||||
(while (re-search-forward regexp nil t)
|
||||
(let ((beg (match-beginning 0))
|
||||
(end (match-end 0)))
|
||||
(when properties
|
||||
(tp-put beg end properties))
|
||||
(push (cons beg end) regions)))
|
||||
(nreverse regions))))
|
||||
This function supports two calling conventions:
|
||||
|
||||
1. With OBJECT (string or buffer):
|
||||
(tp-match PATTERN OBJECT PROPERTY VALUE ...)
|
||||
(tp-match PATTERN OBJECT \\='(PROPERTY VALUE ...))
|
||||
|
||||
2. Without OBJECT (current buffer):
|
||||
(tp-match PATTERN PROPERTY VALUE ...)
|
||||
(tp-match PATTERN \\='(PROPERTY VALUE ...))
|
||||
|
||||
PATTERN is the string to search for.
|
||||
PROPERTIES is a plist of property-value pairs.
|
||||
Returns:
|
||||
- For strings: the modified string
|
||||
- For buffers: list of (START . END) pairs for all matches."
|
||||
(let (object properties)
|
||||
;; Determine calling convention based on second argument type
|
||||
(cond
|
||||
;; Second arg is a string - it's the object
|
||||
((and args (stringp (car args)))
|
||||
(setq object (car args)
|
||||
properties (cdr args)))
|
||||
;; Second arg is a buffer - it's the object
|
||||
((and args (bufferp (car args)))
|
||||
(setq object (car args)
|
||||
properties (cdr args)))
|
||||
;; No object specified, use current buffer
|
||||
(t
|
||||
(setq object nil
|
||||
properties args)))
|
||||
;; Handle properties as a list
|
||||
(when (listp (car-safe properties))
|
||||
(setq properties (car properties)))
|
||||
;; Dispatch based on object type
|
||||
(cond
|
||||
;; String object
|
||||
((stringp object)
|
||||
(let ((pos 0))
|
||||
(while (string-match (regexp-quote pattern) object pos)
|
||||
(let ((beg (match-beginning 0))
|
||||
(end (match-end 0)))
|
||||
(when properties
|
||||
(tp-put object beg end properties))
|
||||
(setq pos end)))
|
||||
object))
|
||||
;; Buffer or nil (current buffer)
|
||||
(t
|
||||
(let ((buf (or object (current-buffer))))
|
||||
(with-current-buffer buf
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let (regions)
|
||||
(while (search-forward pattern nil t)
|
||||
(let ((beg (match-beginning 0))
|
||||
(end (match-end 0)))
|
||||
(when properties
|
||||
(tp-put beg end properties))
|
||||
(push (cons beg end) regions)))
|
||||
(nreverse regions)))))))))
|
||||
|
||||
(defun tp-regexp (pattern &rest args)
|
||||
"Set properties on all matches of PATTERN (regexp).
|
||||
|
||||
This function supports two calling conventions:
|
||||
|
||||
1. With OBJECT (string or buffer):
|
||||
(tp-regexp PATTERN OBJECT PROPERTY VALUE ...)
|
||||
(tp-regexp PATTERN OBJECT \\='(PROPERTY VALUE ...))
|
||||
|
||||
2. Without OBJECT (current buffer):
|
||||
(tp-regexp PATTERN PROPERTY VALUE ...)
|
||||
(tp-regexp PATTERN \\='(PROPERTY VALUE ...))
|
||||
|
||||
PATTERN is the regexp to search for.
|
||||
PROPERTIES is a plist of property-value pairs.
|
||||
Returns:
|
||||
- For strings: the modified string
|
||||
- For buffers: list of (START . END) pairs for all matches."
|
||||
(let (object properties)
|
||||
;; Determine calling convention based on second argument type
|
||||
(cond
|
||||
;; Second arg is a string - it's the object
|
||||
((and args (stringp (car args)))
|
||||
(setq object (car args)
|
||||
properties (cdr args)))
|
||||
;; Second arg is a buffer - it's the object
|
||||
((and args (bufferp (car args)))
|
||||
(setq object (car args)
|
||||
properties (cdr args)))
|
||||
;; No object specified, use current buffer
|
||||
(t
|
||||
(setq object nil
|
||||
properties args)))
|
||||
;; Handle properties as a list
|
||||
(when (listp (car-safe properties))
|
||||
(setq properties (car properties)))
|
||||
;; Dispatch based on object type
|
||||
(cond
|
||||
;; String object
|
||||
((stringp object)
|
||||
(let ((pos 0))
|
||||
(while (string-match pattern object pos)
|
||||
(let ((beg (match-beginning 0))
|
||||
(end (match-end 0)))
|
||||
(when properties
|
||||
(tp-put object beg end properties))
|
||||
(setq pos end)))
|
||||
object))
|
||||
;; Buffer or nil (current buffer)
|
||||
(t
|
||||
(let ((buf (or object (current-buffer))))
|
||||
(with-current-buffer buf
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let (regions)
|
||||
(while (re-search-forward pattern nil t)
|
||||
(let ((beg (match-beginning 0))
|
||||
(end (match-end 0)))
|
||||
(when properties
|
||||
(tp-put beg end properties))
|
||||
(push (cons beg end) regions)))
|
||||
(nreverse regions)))))))))
|
||||
|
||||
;;; Layer list and query functions
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user