From a24bd264c1be1e3af06903001e92d0e00b0afc7d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Dec 2025 16:42:25 +0000 Subject: [PATCH] Update define-tp format: use empty arglist () for non-parameterized layers Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com> --- README.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README_CN.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tp-tests.el | 46 +++++++++++++++++++------------------- tp.el | 43 +++++++++++++++++++---------------- 4 files changed, 173 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 822d705..28bb6d8 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/README_CN.md b/README_CN.md index 85c95bf..0ef879c 100644 --- a/README_CN.md +++ b/README_CN.md @@ -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 diff --git a/tp-tests.el b/tp-tests.el index 7dd6560..68016c6 100644 --- a/tp-tests.el +++ b/tp-tests.el @@ -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 diff --git a/tp.el b/tp.el index d5b8ed5..09fc717 100644 --- a/tp.el +++ b/tp.el @@ -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.