Update define-tp format: use empty arglist () for non-parameterized layers

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-28 16:42:25 +00:00
parent 0b44d1f57b
commit a24bd264c1
4 changed files with 173 additions and 42 deletions

View File

@ -58,6 +58,7 @@
- [Property Layer Definition](#property-layer-definition)
- [tp-define-layer](#tp-define-layer---define-single-layer)
- [tp-define-layer-group](#tp-define-layer-group---define-layer-group)
- [define-tp / define-tp-group](#define-tp--define-tp-group---convenience-macros)
- [tp-layer-props / tp-group-props](#tp-layer-props--tp-group-props)
- [tp-undefine-layer / tp-undefine-group](#tp-undefine-layer--tp-undefine-group)
- [tp-layer-reset](#tp-layer-reset)
@ -338,6 +339,8 @@ A complete overview of all tp.el functions organized by category:
|----------|-------------|
| [`tp-define-layer`](#tp-define-layer---define-single-layer) | Define a single layer with optional reactive features (:props, :data, :watch, :compute) |
| [`tp-define-layer-group`](#tp-define-layer-group---define-layer-group) | Define a group of layers with optional reactive features |
| [`define-tp`](#define-tp--define-tp-group---convenience-macros) | Convenience macro for defining layers (supports parameterized layers) |
| [`define-tp-group`](#define-tp--define-tp-group---convenience-macros) | Convenience macro for defining layer groups |
| [`tp-layer-props`](#tp-layer-props--tp-group-props) | Get properties for a layer |
| [`tp-group-props`](#tp-layer-props--tp-group-props) | Get properties for all layers in a group |
| [`tp-undefine-layer`](#tp-undefine-layer--tp-undefine-group) | Remove layer definition |
@ -1479,6 +1482,66 @@ The first layer in the definition is the top layer (visible by default).
---
#### `define-tp` / `define-tp-group` - Convenience Macros
`define-tp` and `define-tp-group` are convenience macros that provide a more concise syntax for `tp-define-layer` and `tp-define-layer-group`.
**`define-tp` - Define Single Layer**
Supports two formats:
**Format 1 - Non-parameterized (empty arglist):**
```elisp
(define-tp tp-bold ()
'(face bold))
```
**Format 2 - Parameterized (with a single parameter):**
```elisp
(define-tp tp-space (pixel)
`(display (space :width (,pixel))))
```
**Usage with `tp-set`:**
For non-parameterized layers, use `t` as the value:
```elisp
;; Entire string
(tp-set "emacs" 'tp-bold t)
;; => #("emacs" 0 5 (tp-name tp-bold face bold))
;; Region (with plist-like format)
(tp-set 0 5 '(tp-bold t) "emacs")
;; => #("emacs" 0 5 (tp-name tp-bold face bold))
```
For parameterized layers, pass the argument value:
```elisp
;; Entire string
(tp-set "emacs" 'tp-space 2)
;; => #("emacs" 0 5 (tp-name tp-space display (space :width (2))))
;; Region (with plist-like format)
(tp-set 0 5 '(tp-space 2) "emacs")
;; => #("emacs" 0 5 (tp-name tp-space display (space :width (2))))
```
**`define-tp-group` - Define Layer Group**
Convenience macro wrapping `tp-define-layer-group`:
```elisp
(define-tp-group tp-moon-phases
'(display "🌑")
'(display "🌕"))
```
---
#### `tp-layer-props` / `tp-group-props`
```elisp

View File

@ -57,6 +57,7 @@
- [属性层定义](#属性层定义)
- [tp-define-layer](#tp-define-layer---定义单个属性层)
- [tp-define-layer-group](#tp-define-layer-group---定义属性层组)
- [define-tp / define-tp-group](#define-tp--define-tp-group---便捷宏)
- [tp-layer-props / tp-group-props](#tp-layer-props--tp-group-props)
- [tp-undefine-layer / tp-undefine-group](#tp-undefine-layer--tp-undefine-group)
- [tp-layer-reset](#tp-layer-reset)
@ -337,6 +338,8 @@ tp.el 所有函数按类别组织的完整概览:
|------|------|
| [`tp-define-layer`](#tp-define-layer---定义单个属性层) | 定义单个属性层,支持响应式特性(:props、:data、:watch、:compute |
| [`tp-define-layer-group`](#tp-define-layer-group---定义属性层组) | 定义属性层组,支持响应式特性 |
| [`define-tp`](#define-tp--define-tp-group---便捷宏) | 定义属性层的便捷宏(支持参数化属性层) |
| [`define-tp-group`](#define-tp--define-tp-group---便捷宏) | 定义属性层组的便捷宏 |
| [`tp-layer-props`](#tp-layer-props--tp-group-props) | 获取属性层的属性 |
| [`tp-group-props`](#tp-layer-props--tp-group-props) | 获取属性层组中所有属性层的属性 |
| [`tp-undefine-layer`](#tp-undefine-layer--tp-undefine-group) | 移除属性层定义 |
@ -1468,6 +1471,66 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
---
#### `define-tp` / `define-tp-group` - 便捷宏
`define-tp``define-tp-group` 是便捷宏,为 `tp-define-layer``tp-define-layer-group` 提供更简洁的语法。
**`define-tp` - 定义单个属性层**
支持两种格式:
**格式一 - 无参数(空参数列表):**
```elisp
(define-tp tp-bold ()
'(face bold))
```
**格式二 - 有参数(带单个参数):**
```elisp
(define-tp tp-space (pixel)
`(display (space :width (,pixel))))
```
**`tp-set` 用法:**
对于无参数属性层,使用 `t` 作为值:
```elisp
;; 整个字符串
(tp-set "emacs" 'tp-bold t)
;; => #("emacs" 0 5 (tp-name tp-bold face bold))
;; 区域(使用类似 plist 的格式)
(tp-set 0 5 '(tp-bold t) "emacs")
;; => #("emacs" 0 5 (tp-name tp-bold face bold))
```
对于有参数属性层,传递参数值:
```elisp
;; 整个字符串
(tp-set "emacs" 'tp-space 2)
;; => #("emacs" 0 5 (tp-name tp-space display (space :width (2))))
;; 区域(使用类似 plist 的格式)
(tp-set 0 5 '(tp-space 2) "emacs")
;; => #("emacs" 0 5 (tp-name tp-space display (space :width (2))))
```
**`define-tp-group` - 定义属性层组**
包装 `tp-define-layer-group` 的便捷宏:
```elisp
(define-tp-group tp-moon-phases
'(display "🌑")
'(display "🌕"))
```
---
#### `tp-layer-props` / `tp-group-props`
```elisp

View File

@ -3123,13 +3123,34 @@ the inserted text should be that string, not the source text."
;;; ============================================================
(ert-deftest tp-test-define-tp-non-parameterized ()
"Test define-tp with non-parameterized format."
"Test define-tp with non-parameterized format (empty arglist)."
(tp-test-with-temp-buffer
(define-tp tp-bold
(define-tp tp-bold ()
'(face bold))
(should (assoc 'tp-bold tp-layer-alist))
(should (equal (cdr (assoc 'tp-bold tp-layer-alist)) '(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)."
(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 (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))."
(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 (eq (tp-at 1 'face) 'bold))))
(ert-deftest tp-test-define-tp-parameterized ()
"Test define-tp with parameterized format."
(tp-test-with-temp-buffer
@ -3162,27 +3183,6 @@ the inserted text should be that string, not the source text."
(should (eq (tp-at 1 'tp-name) 'tp-space))
(should (equal (tp-at 1 'display) '(space :width (5))))))
(ert-deftest tp-test-define-tp-non-parameterized-usage-string ()
"Test non-parameterized layer usage with string: (tp-set string 'layer-name t)."
(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 (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))."
(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 (eq (tp-at 1 'face) 'bold))))
(ert-deftest tp-test-define-tp-parameterized-backquote ()
"Test parameterized layer with backquote syntax."
(tp-test-with-temp-buffer

43
tp.el
View File

@ -2130,14 +2130,13 @@ The layer is stored in `tp-layer-alist'."
(tp--update-layer-regions name)
(assoc name tp-layer-alist)))))
(defmacro define-tp (name &rest args)
"Define a single text property layer named NAME.
(defmacro define-tp (name arglist body)
"Define a text property layer named NAME.
This macro provides a convenient syntax for `tp-define-layer'.
Supports two formats: with parameters and without parameters.
This macro supports two formats:
Format 1 - Without parameters (static properties):
(define-tp tp-bold
Format 1 - Non-parameterized (empty arglist):
(define-tp tp-bold ()
\\='(face bold))
Usage:
@ -2145,7 +2144,7 @@ Usage:
(tp-set 0 5 \\='(tp-bold t) \"emacs\")
;; => #(\"emacs\" 0 5 (tp-name tp-bold face bold))
Format 2 - With a single parameter:
Format 2 - Parameterized (single argument):
(define-tp tp-space (pixel)
\\=`(display (space :width (,pixel))))
@ -2154,19 +2153,25 @@ Usage:
(tp-set 0 5 \\='(tp-space 2) \"emacs\")
;; => #(\"emacs\" 0 5 (tp-name tp-space display (space :width (2))))
Note: The parameterized form stores the body as a function that takes
the parameter and returns the resolved property list."
ARGLIST must be either:
- An empty list () for non-parameterized layers
- A list containing exactly one symbol for parameterized layers
BODY is the property list expression. For parameterized layers,
it will be evaluated with the argument bound."
(declare (indent defun))
(if (and (car args) (listp (car args)) (not (eq (caar args) 'quote)))
;; Parameterized form: (define-tp name (arg) body)
(let ((arglist (car args))
(body (cadr args)))
(unless (and (= (length arglist) 1)
(symbolp (car arglist)))
(error "define-tp with parameters only supports exactly one argument"))
`(tp--define-parameterized-layer ',name ',arglist ',body))
;; Non-parameterized form: (define-tp name 'props) or (define-tp name :props 'props ...)
`(tp-define-layer ',name ,@args)))
(unless (listp arglist)
(error "define-tp ARGLIST must be a list"))
(cond
;; Non-parameterized: empty arglist
((null arglist)
`(tp-define-layer ',name ,body))
;; Parameterized: single argument
((and (= (length arglist) 1)
(symbolp (car arglist)))
`(tp--define-parameterized-layer ',name ',arglist ',body))
(t
(error "define-tp ARGLIST must be empty or contain exactly one symbol"))))
(defun tp--define-parameterized-layer (name arglist body)
"Define a parameterized layer NAME with ARGLIST and BODY.