tp/README_CN.md
copilot-swe-agent[bot] 240e590f03 Replace put-text-property with tp-set in README examples
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
2025-12-15 14:16:56 +00:00

1822 lines
48 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# tp.el - Emacs 文本属性操作库
<p align="center">
<strong>一个功能强大的文本属性操作库,具有创新的属性层系统</strong>
</p>
<p align="center">
<a href="#功能特性">功能特性</a>
<a href="#安装">安装</a>
<a href="#快速开始">快速开始</a>
<a href="#api-参考">API 参考</a>
<a href="#属性层系统">属性层系统</a>
</p>
---
## 概述
**tp.el** 是一个全面增强 Emacs 文本属性操作的库。它不仅仅是对原生文本属性 API`put-text-property`、`get-text-property`)的简单封装,更提供了许多**原生函数所不具备的功能拓展**。
灵感来自用于叠加层的 [ov.el](https://github.com/emacsorphanage/ov)tp.el 在以下方面进行了创新:
### 核心创新
1. **统一的 API 参数规范**:所有函数支持多种灵活的调用方式,同时适用于字符串和缓冲区
2. **子属性的精细操作**:支持嵌套属性的路径式访问、修改和深度合并
3. **创新的属性层系统**:在同一文本区域上堆叠、管理多组属性,实现属性的分层控制
4. **模式匹配批量操作**:通过字符串或正则表达式批量应用属性
5. **增强的搜索导航**:丰富的属性搜索和遍历功能
## 功能特性
### 统一的 API 参数规范
原生 Emacs API 针对字符串和缓冲区有不同的函数和参数顺序tp.el 统一了这一切:
-**三种调用约定**:所有核心函数(`tp-set`、`tp-get`、`tp-remove` 等)支持三种灵活的调用方式:
```elisp
;; 1. 当前缓冲区
(tp-set START END '(face bold))
;; 2. 指定缓冲区或字符串
(tp-set START END '(face bold) OBJECT)
;; 3. 整个字符串(平铺属性)
(tp-set STRING 'face 'bold 'help-echo "tip")
```
-**统一对象支持**:同一个函数同时支持字符串和缓冲区,无需记忆不同的 API
### 三种属性操作语义
原生 API 只有简单的设置和获取tp.el 提供了三种清晰的操作语义:
-**`tp-reset`**:完全替换 - 清除所有现有属性,设置新属性
-**`tp-set`**:部分替换 - 只替换指定属性,保留其他属性
-**`tp-add`**:深度合并 - 智能合并嵌套属性,而非简单覆盖
```elisp
;; 深度合并示例
(tp-set 1 10 '(face (:foreground "red")))
(tp-add 1 10 '(face (:background "blue")))
;; 结果: face 是 (:foreground "red" :background "blue")
;; 原生 API 会完全覆盖,而 tp-add 会智能合并
```
### 子属性的精细操作
**这是原生 API 完全不具备的功能**。tp.el 支持对嵌套属性进行精细的读取、修改和删除:
-**路径式访问**:通过路径语法访问深层嵌套的属性值
```elisp
;; 获取嵌套属性
(tp-get str 'face :underline :style) ; => wave
(tp-at 5 '(face :box :color)) ; => "blue"
;; 获取多个嵌套键
(tp-get str 'face :underline '(:color :style))
;; => ((:color "green" :style wave))
```
-**子属性删除**:精确移除嵌套属性中的特定键
```elisp
;; 只删除 :underline 中的 :style保留 :color
(tp-remove 1 10 '(face :underline (:style)))
```
-**深度合并**`tp-add` 递归合并嵌套的 plist 结构
-**Face 智能合并**:符号 face 自动前置到 face 列表plist face 深度合并
### 创新的属性层系统
**这是 tp.el 最具创新性的功能**,原生 Emacs 完全不支持。属性层系统允许在同一文本区域上堆叠多组属性:
-**属性层栈概念**:多个属性层像栈一样堆叠,只有顶层可见,下层被保留
-**属性层定义与复用**:通过 `tp-define-layer` 定义可复用的属性层和属性层组
-**丰富的属性层操作**
- 放置:`tp-put-layer`(指定位置)、`tp-push-layer`(顶部)
- 删除:`tp-delete-layer`(按名称/索引)、`tp-pop-layer`(顶层)
- 移动:`tp-raise-layer`(上下移动)、`tp-rotate-layer`(轮换)、`tp-pin-layer`(置顶)、`tp-switch-layer`(交换)
- 合并:`tp-merge-layers`(合并指定层)、`tp-flatten-layers`(扁平化所有层)
-**属性层查询**`tp-layer-list`、`tp-layer-count`、`tp-layer-exists-p`、`tp-layer-top`
```elisp
;; 属性层使用示例
(tp-define-layer highlight (face (:background "yellow")))
(tp-define-layer error (face (:foreground "red")))
;; 堆叠多个属性层
(tp-push-layer 1 10 'highlight)
(tp-push-layer 1 10 'error) ; error 现在可见
;; 轮换显示
(tp-rotate-layer 1 10) ; highlight 现在可见
```
### 模式匹配与批量操作
原生 API 需要手动搜索和循环tp.el 提供了便捷的模式匹配功能:
-**字符串匹配**`tp-match-set`、`tp-match-reset`、`tp-match-add`
-**正则匹配**`tp-regexp-set`、`tp-regexp-reset`、`tp-regexp-add`
-**三种语义变体**:每种匹配都支持 set/reset/add 三种操作语义
```elisp
;; 高亮所有 TODO
(tp-match-set "TODO" '(face warning))
;; 正则匹配所有数字
(tp-regexp-set "[0-9]+" '(face font-lock-number-face))
;; 深度合并方式添加属性
(tp-match-add "TODO" '(face (:underline t)))
```
### 增强的搜索与导航
-**范围搜索**`tp-search` 返回所有匹配区间的列表
-**N次搜索**`tp-forward`/`tp-backward` 支持向前/向后搜索N次
-**搜索并执行**`tp-forward-do`/`tp-backward-do` 搜索并对匹配文本执行函数
-**批量转换**`tp-search-map` 对所有匹配应用转换函数
```elisp
;; 搜索所有标记
(tp-search my-string 'marker) ; => ((0 5 t) (12 17 t))
;; 将所有标记文本转为大写
(tp-search-map #'upcase my-string 'marker)
```
## 系统要求
- **Emacs 28.1+**(使用 `object-intervals` 函数)
- **dash.el**(列表操作工具库)
## 安装
```elisp
;; 添加到 load-path
(add-to-list 'load-path "/path/to/tp")
(require 'tp)
```
或使用 `use-package`
```elisp
(use-package tp
:load-path "/path/to/tp")
```
---
## API 参考
### API 快速参考
tp.el 所有函数按类别组织的完整概览:
#### 核心属性函数
| 函数 | 描述 |
|------|------|
| [`tp-set`](#tp-set---设置文本属性) | 设置文本属性(仅替换指定属性) |
| [`tp-reset`](#tp-reset---替换所有属性) | 替换所有文本属性 |
| [`tp-add`](#tp-add---添加合并属性) | 添加/合并属性,支持深度合并 |
| [`tp-get`](#tp-get---获取属性值) | 从范围或字符串获取属性值 |
| [`tp-at`](#tp-at---获取位置属性) | 获取单个位置的属性值 |
| [`tp-remove`](#tp-remove---移除属性) | 移除属性或子属性 |
| [`tp-clear`](#tp-clear---清除所有属性) | 清除区域中的所有文本属性 |
#### 模式匹配函数
| 函数 | 描述 |
|------|------|
| [`tp-match-set`](#tp-match-set---匹配字符串) | 在字符串匹配处设置属性 |
| [`tp-match-reset`](#tp-match-reset---匹配并重置) | 在字符串匹配处重置所有属性 |
| [`tp-match-add`](#tp-match-add---匹配并添加) | 在字符串匹配处添加/合并属性 |
| [`tp-regexp-set`](#tp-regexp-set---匹配正则表达式) | 在正则匹配处设置属性 |
| [`tp-regexp-reset`](#tp-regexp-reset---正则匹配并重置) | 在正则匹配处重置所有属性 |
| [`tp-regexp-add`](#tp-regexp-add---正则匹配并添加) | 在正则匹配处添加/合并属性 |
#### 搜索和导航函数
| 函数 | 描述 |
|------|------|
| [`tp-search-forward`](#tp-search-forward--tp-search-backward) | text-property-search-forward 的原始包装 |
| [`tp-search-backward`](#tp-search-forward--tp-search-backward) | text-property-search-backward 的原始包装 |
| [`tp-forward`](#tp-forward--tp-backward) | 向前搜索 N 次具有属性的文本(支持缓冲区和字符串) |
| [`tp-backward`](#tp-forward--tp-backward) | 向后搜索 N 次具有属性的文本(支持缓冲区和字符串) |
| [`tp-forward-do`](#tp-forward-do--tp-backward-do) | 对 N 个向前匹配的文本应用函数(支持起始位置) |
| [`tp-backward-do`](#tp-forward-do--tp-backward-do) | 对 N 个向后匹配的文本应用函数(支持起始位置) |
| [`tp-search`](#tp-search---搜索所有匹配) | 在范围或字符串中搜索所有匹配的属性 |
| [`tp-search-map`](#tp-search-map---对匹配文本应用函数) | 对所有匹配的文本应用函数 |
#### 属性层定义函数
| 函数 | 描述 |
|------|------|
| [`tp-define-layer`](#tp-define-layer---定义属性层) | 定义属性层或属性层组 |
| [`tp-layer-props`](#tp-layer-props--tp-group-props) | 获取属性层的属性 |
| [`tp-group-props`](#tp-layer-props--tp-group-props) | 获取属性层组中所有属性层的属性 |
| [`tp-layer-undefine`](#tp-layer-undefine--tp-group-undefine) | 移除属性层定义 |
| [`tp-group-undefine`](#tp-layer-undefine--tp-group-undefine) | 移除属性层组定义 |
| [`tp-layer-reset`](#tp-layer-reset) | 清除所有属性层/属性层组定义 |
#### 属性层放置函数
| 函数 | 描述 |
|------|------|
| [`tp-put-layer`](#tp-put-layer---在指定位置设置属性层) | 在指定索引位置设置属性层 |
| [`tp-push-layer`](#tp-push-layer---推送属性层到顶部) | 将属性层推到堆栈顶部 |
#### 属性层删除函数
| 函数 | 描述 |
|------|------|
| [`tp-delete-layer`](#tp-delete-layer---按名称索引删除属性层) | 按名称或索引删除属性层 |
| [`tp-pop-layer`](#tp-pop-layer---弹出顶层) | 移除顶层属性层 |
#### 属性层移动函数
| 函数 | 描述 |
|------|------|
| [`tp-raise-layer`](#tp-raise-layer---上移下移属性层) | 将属性层上移/下移 N 个位置 |
| [`tp-rotate-layer`](#tp-rotate-layer---轮换属性层) | 轮换属性层(顶层移到底部) |
| [`tp-pin-layer`](#tp-pin-layer---将属性层置顶) | 将属性层置顶(使其可见) |
| [`tp-switch-layer`](#tp-switch-layer---交换两个属性层) | 交换两个属性层的位置 |
#### 属性层合并函数
| 函数 | 描述 |
|------|------|
| [`tp-merge-layers`](#tp-merge-layers---合并多个属性层) | 将指定属性层合并为新属性层 |
| [`tp-flatten-layers`](#tp-flatten-layers---扁平化所有属性层) | 将所有属性层扁平化为单一属性层 |
#### 属性层查询函数
| 函数 | 描述 |
|------|------|
| [`tp-layer-list`](#tp-layer-list---列出所有属性层) | 列出区域中的所有属性层名称 |
| [`tp-layer-count`](#tp-layer-count) | 计算区域中的属性层数量 |
| [`tp-layer-exists-p`](#tp-layer-exists-p) | 检查区域中是否存在某属性层 |
| [`tp-layer-top`](#tp-layer-top) | 获取顶层(可见)属性层的名称 |
---
### 核心属性函数
#### `tp-set` - 设置文本属性
在字符串或缓冲区区域上设置文本属性。只替换指定的属性,保留其他属性。
```elisp
;; 当前缓冲区(属性作为列表)
(tp-set START END '(PROPERTY VALUE ...))
;; 特定缓冲区或字符串
(tp-set START END '(PROPERTY VALUE ...) OBJECT)
;; 整个字符串(平铺属性)
(tp-set STRING PROPERTY VALUE ...)
```
**示例:**
```elisp
;; 在缓冲区区域设置 face
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face bold)))
;; => (1 . 10)
;; 设置多个属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face bold help-echo "Click me")))
;; => (1 . 10)
;; 在特定缓冲区设置
(let ((my-buffer (generate-new-buffer "*test*")))
(with-current-buffer my-buffer
(insert "Hello World"))
(tp-set 1 10 '(face italic) my-buffer)
(kill-buffer my-buffer))
;; => (1 . 10)
;; 在字符串上设置属性0 索引)
(let ((my-string (tp-set 0 5 '(face italic) "Hello World")))
my-string)
;; => #("Hello World" 0 5 (face italic))
;; 在整个字符串上设置属性
(tp-set "Hello" 'face 'bold 'mouse-face 'highlight)
;; => #("Hello" 0 5 (face bold mouse-face highlight))
```
---
#### `tp-reset` - 替换所有属性
用指定的属性完全替换所有文本属性。
```elisp
(tp-reset START END '(PROPERTY VALUE ...) &optional OBJECT)
(tp-reset STRING PROPERTY VALUE ...)
```
**示例:**
```elisp
;; 替换区域中的所有属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(help-echo "old")) ; 设置已有属性
(tp-reset 1 10 '(face bold)) ; 任何现有属性都会被移除
(tp-at 1))
;; => (face bold) ; help-echo 被移除了
;; 在字符串上
(tp-reset "Hello" 'face 'italic)
;; => #("Hello" 0 5 (face italic))
```
---
#### `tp-add` - 添加/合并属性
添加或更新属性,支持嵌套属性列表的深度合并。
```elisp
(tp-add START END '(PROPERTY VALUE ...) &optional OBJECT)
(tp-add STRING PROPERTY VALUE ...)
```
**示例:**
```elisp
;; 添加属性(保留现有,合并嵌套)
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face bold))
(tp-add 1 10 '(help-echo "tooltip"))
(tp-at 1))
;; => (face bold help-echo "tooltip")
;; 深度合并 face 属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face (:foreground "red")))
(tp-add 1 10 '(face (:background "blue")))
(tp-at 1 'face))
;; => (:foreground "red" :background "blue")
;; Face 前置 - 符号 face 会被添加到 face 列表的开头
(let ((str (tp-set "Hello" 'face 'bold)))
(tp-add str 'face 'shadow)
(tp-at 0 'face str))
;; => (shadow bold)
```
---
#### `tp-get` - 获取属性值
从范围或字符串获取属性值,支持嵌套子属性访问。
返回 `(START END VALUE)` 区间列表,让你可以查看范围内所有的属性值。
对于单个位置的查询,请使用 `tp-at`
```elisp
;; 范围 - 特定属性(返回区间列表)
(tp-get START END PROPERTY)
(tp-get START END PROPERTY OBJECT)
;; 范围 - 属性路径作为列表
(tp-get START END '(PROPERTY) OBJECT)
(tp-get START END '(PROPERTY SUB-KEY ...) OBJECT)
;; 范围 - 深层嵌套属性路径
(tp-get START END '(PROPERTY SUB-KEY SUB-SUB-KEY ...) OBJECT)
;; 范围 - 从嵌套属性中提取多个键
(tp-get START END '(PROPERTY SUB-KEY (KEY1 KEY2 ...)) OBJECT)
;; 范围 - 所有属性(返回区间列表)
(tp-get START END)
(tp-get START END OBJECT)
;; 整个字符串(返回区间列表)
(tp-get STRING)
(tp-get STRING PROPERTY)
(tp-get STRING PROPERTY SUB-KEY ...)
(tp-get STRING PROPERTY SUB-KEY '(KEY1 KEY2 ...))
(tp-get STRING '(PROPERTY SUB-KEY ...))
```
**示例:**
```elisp
;; 从范围获取 - 返回 (START END VALUE) 区间列表
(with-temp-buffer
(insert "Hello World")
(tp-set 1 6 '(face bold))
(tp-get 1 10 'face))
;; => ((1 6 bold))
;; 获取多个区间
(let ((str (copy-sequence "Hello World Hello")))
(tp-set 0 5 '(face bold) str)
(tp-set 12 17 '(face italic) str)
(tp-get 0 17 'face str))
;; => ((0 5 bold) (12 17 italic))
;; 使用列表形式的属性路径
(let ((my-string (copy-sequence "Hello World Hello World")))
(tp-set 5 20 '(face (:underline (:style wave))) my-string)
(tp-get 5 20 '(face :underline :style) my-string))
;; => ((5 20 wave))
;; 从整个字符串获取深层嵌套属性
(let ((str (copy-sequence "Hello World")))
(tp-set 0 5 '(face (:underline (:color "green"))) str)
(tp-set 6 11 '(face (:underline (:color "yellow"))) str)
(tp-get str 'face :underline :color))
;; => ((0 5 "green") (6 11 "yellow"))
;; 从嵌套属性中获取多个键
(let ((str (copy-sequence "Hello World")))
(tp-set 0 5 '(face (:underline (:color "green" :style wave))) str)
(tp-set 6 11 '(face (:underline (:color "yellow" :style line))) str)
(tp-get str 'face :underline '(:color :style)))
;; => ((0 5 (:color "green" :style wave)) (6 11 (:color "yellow" :style line)))
;; 获取范围内的所有属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 6 '(face bold help-echo "test"))
(tp-get 1 10))
;; => ((1 6 (face bold help-echo "test")))
;; 从整个字符串获取 - 返回区间列表
(let ((str (copy-sequence "Hello World Hello")))
(tp-set 0 5 '(face bold) str)
(tp-set 12 17 '(face italic) str)
(list (tp-get str) ; => ((0 5 (face bold)) (12 17 (face italic)))
(tp-get str 'face))) ; => ((0 5 bold) (12 17 italic))
;; => (((0 5 (face bold)) (12 17 (face italic))) ((0 5 bold) (12 17 italic)))
```
---
#### `tp-at` - 获取位置属性
```elisp
;; 获取位置的所有属性
(tp-at POS)
(tp-at POS OBJECT)
;; 获取位置的特定属性
(tp-at POS PROPERTY)
(tp-at POS PROPERTY OBJECT)
;; 获取位置的嵌套子属性
(tp-at POS '(PROPERTY SUB-KEY ...))
(tp-at POS '(PROPERTY SUB-KEY ...) OBJECT)
```
获取 POS 位置的文本属性,可选择按 PROPERTY 过滤。
对于单位置属性查询(以前使用 `tp-get`),现在使用 `tp-at`
**示例:**
```elisp
;; 获取当前缓冲区位置 5 的所有属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face bold help-echo "test"))
(tp-at 5))
;; => (face bold help-echo "test")
;; 获取字符串位置 0 的所有属性
(let ((my-string (tp-set "Hello" 'face 'italic 'help-echo "greeting")))
(tp-at 0 my-string))
;; => (face italic help-echo "greeting")
;; 获取位置的特定属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face bold))
(tp-at 5 'face))
;; => bold
;; 获取字符串位置的特定属性
(let ((my-string (tp-set "Hello" 'face 'italic)))
(tp-at 0 'face my-string))
;; => italic
;; 获取位置的嵌套子属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face (:foreground "red" :box (:color "blue"))))
(list (tp-at 5 '(face :foreground))
(tp-at 5 '(face :box :color))))
;; => ("red" "blue")
;; 从字符串获取嵌套子属性
(let ((str (copy-sequence "Hello")))
(tp-set 0 5 '(face (:foreground "red" :underline t)) str)
(tp-at 0 '(face :foreground) str))
;; => "red"
```
---
#### `tp-remove` - 移除属性
从区域或整个字符串中移除属性或嵌套子属性。
```elisp
;; 移除整个属性(缓冲区)
(tp-remove START END PROPERTY &optional OBJECT)
;; 移除子属性(缓冲区)
(tp-remove START END '(PROPERTY SUB-KEY) &optional OBJECT)
;; 移除嵌套子属性(缓冲区)
(tp-remove START END '(PROPERTY SUB-KEY (NESTED-KEYS...)) &optional OBJECT)
;; 从整个字符串移除
(tp-remove STRING PROP1 PROP2 ...)
(tp-remove STRING PROPERTY SUB-KEY)
(tp-remove STRING PROPERTY SUB-KEY '(NESTED-KEYS...))
```
**示例:**
```elisp
;; 移除整个属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face bold help-echo "test"))
(tp-remove 1 10 'face)
(tp-at 1))
;; => (help-echo "test")
;; 从 face 移除子属性
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face (:foreground "red" :underline t)))
(tp-remove 1 10 '(face :underline))
(tp-at 1 'face))
;; => (:foreground "red")
;; 移除特定嵌套键,保留其他
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face (:underline (:style wave :position t :color "blue"))))
(tp-remove 1 10 '(face :underline (:style :position)))
(tp-at 1 '(face :underline)))
;; => (:color "blue") ; :style 和 :position 被移除, :color 保留
;; 从整个字符串移除 - 移除多个属性
(let ((str (tp-set "Hello World" 'face 'bold 'help-echo "tip")))
(tp-remove str 'face 'help-echo)
(tp-at 0 str))
;; => nil
;; 从字符串移除子属性
(let ((str (copy-sequence "Hello World")))
(tp-set 0 11 '(face (:foreground "red" :underline t)) str)
(tp-remove str 'face :underline)
(tp-at 0 'face str))
;; => (:foreground "red")
;; 从字符串移除嵌套键
(let ((str (copy-sequence "Hello World")))
(tp-set 0 11 '(face (:underline (:style wave :color "blue"))) str)
(tp-remove str 'face :underline '(:style))
(tp-at 0 '(face :underline) str))
;; => (:color "blue")
```
---
#### `tp-clear` - 清除所有属性
```elisp
(tp-clear &optional START END OBJECT)
```
清除区域中的所有文本属性。
**示例:**
```elisp
;; 清除区域
(with-temp-buffer
(insert "Hello World")
(tp-set 1 10 '(face bold))
(tp-clear 1 10)
(tp-at 1))
;; => nil
;; 清除整个缓冲区
(with-temp-buffer
(insert "Hello World")
(tp-set 1 12 '(face bold))
(tp-clear)
(tp-at 5))
;; => nil
```
---
### 模式匹配函数
#### `tp-match-set` - 匹配字符串
```elisp
(tp-match-set PATTERN PLIST &optional OBJECT)
```
在所有字符串模式匹配处设置属性。
PATTERN 可以是字符串(单个模式)或字符串列表(多个模式)。
PLIST 是属性列表,如 `'(face bold help-echo "tip")`
OBJECT 是缓冲区或字符串nil 表示当前缓冲区。
**示例:**
```elisp
;; 在缓冲区中 - 返回 (START . END) 对的列表
(with-temp-buffer
(insert "TODO: fix this. TODO: also this.")
(tp-match-set "TODO" '(face warning)))
;; => ((1 . 5) (17 . 21))
;; 在字符串上 - 返回修改后的字符串
(tp-match-set "o" '(face bold) "Hello World")
;; => #("Hello World" 4 5 (face bold) 7 8 (face bold))
;; 多个模式 - 同时匹配 "world" 和 "Hello"
(with-temp-buffer
(insert "Hello world, Hello again")
(tp-match-set '("world" "Hello") '(face bold)))
;; => ((1 . 6) (7 . 12) (14 . 19)) ; 匹配 "Hello", "world", "Hello"
;; 在字符串上使用多个模式
(tp-match-set '("Hello" "world") '(face bold) "Hello world")
;; => #("Hello world" 0 5 (face bold) 6 11 (face bold))
```
---
#### `tp-match-reset` - 匹配并重置
重置(完全替换)匹配处的所有属性。
PATTERN 可以是字符串或字符串列表(多个模式)。
PLIST 是属性列表,如 `'(face bold help-echo "tip")`
OBJECT 是缓冲区或字符串nil 表示当前缓冲区。
```elisp
(tp-match-reset PATTERN PLIST &optional OBJECT)
```
**示例:**
```elisp
;; 替换匹配文本上的所有属性
(with-temp-buffer
(insert "TODO: fix this")
(tp-set 1 5 '(help-echo "original")) ; 设置已有属性
(tp-match-reset "TODO" '(face warning))
(tp-at 1))
;; => (face warning) ; help-echo 被移除
;; 多个模式
(with-temp-buffer
(insert "TODO: fix. FIXME: also fix.")
(tp-match-reset '("TODO" "FIXME") '(face warning)))
;; => ((1 . 5) (12 . 17))
```
---
#### `tp-match-add` - 匹配并添加
在匹配处添加/合并属性,支持深度合并。
PATTERN 可以是字符串或字符串列表(多个模式)。
PLIST 是属性列表,如 `'(face bold help-echo "tip")`
OBJECT 是缓冲区或字符串nil 表示当前缓冲区。
```elisp
(tp-match-add PATTERN PLIST &optional OBJECT)
```
**示例:**
```elisp
;; 与现有属性合并
(with-temp-buffer
(insert "TODO: fix this")
(tp-set 1 5 '(help-echo "important"))
(tp-match-add "TODO" '(face (:underline t)))
(tp-at 1))
;; => (face (:underline t) help-echo "important")
;; 多个模式
(with-temp-buffer
(insert "TODO: fix. FIXME: also fix.")
(tp-match-add '("TODO" "FIXME") '(face (:underline t))))
;; => ((1 . 5) (12 . 17))
```
---
#### `tp-regexp-set` - 匹配正则表达式
```elisp
(tp-regexp-set PATTERN PLIST &optional OBJECT)
```
在所有正则表达式匹配处设置属性。
PATTERN 可以是字符串(单个正则)或字符串列表(多个正则)。
PLIST 是属性列表,如 `'(face bold help-echo "tip")`
OBJECT 是缓冲区或字符串nil 表示当前缓冲区。
**示例:**
```elisp
;; 高亮缓冲区中的所有数字
(with-temp-buffer
(insert "abc 123 def 456")
(tp-regexp-set "[0-9]+" '(face font-lock-number-face))
(list (tp-at 5 'face) (tp-at 13 'face)))
;; => (font-lock-number-face font-lock-number-face)
;; 在字符串上
(tp-regexp-set "[A-Z]+" '(face bold) "Hello WORLD")
;; => #("Hello WORLD" 6 11 (face bold))
;; 多个正则 - 同时匹配数字和大写字母
(tp-regexp-set '("[0-9]+" "[A-Z]+") '(face bold) "abc 123 XYZ")
;; => #("abc 123 XYZ" 4 7 (face bold) 8 11 (face bold))
```
---
#### `tp-regexp-reset` - 正则匹配并重置
重置(完全替换)正则匹配处的所有属性。
PATTERN 可以是字符串或字符串列表(多个正则)。
PLIST 是属性列表,如 `'(face bold help-echo "tip")`
OBJECT 是缓冲区或字符串nil 表示当前缓冲区。
```elisp
(tp-regexp-reset PATTERN PLIST &optional OBJECT)
```
**示例:**
```elisp
;; 重置正则匹配处的所有属性
(with-temp-buffer
(insert "abc 123 def 456")
(tp-set 5 8 '(help-echo "original"))
(tp-regexp-reset "[0-9]+" '(face bold))
(tp-at 5))
;; => (face bold) ; help-echo 被移除
;; 在字符串上
(let ((str (copy-sequence "abc 123 def")))
(tp-set 4 7 '(help-echo "original") str)
(tp-regexp-reset "[0-9]+" '(face italic) str)
(tp-at 4 str))
;; => (face italic)
```
---
#### `tp-regexp-add` - 正则匹配并添加
在正则匹配处添加/合并属性,支持深度合并。
PATTERN 可以是字符串或字符串列表(多个正则)。
PLIST 是属性列表,如 `'(face bold help-echo "tip")`
OBJECT 是缓冲区或字符串nil 表示当前缓冲区。
```elisp
(tp-regexp-add PATTERN PLIST &optional OBJECT)
```
**示例:**
```elisp
;; 添加属性到正则匹配处(保留现有)
(with-temp-buffer
(insert "abc 123 def 456")
(tp-set 5 8 '(help-echo "number"))
(tp-regexp-add "[0-9]+" '(face bold))
(tp-at 5))
;; => (face bold help-echo "number")
;; 在字符串上
(let ((str (copy-sequence "abc 123 def")))
(tp-set 4 7 '(help-echo "number") str)
(tp-regexp-add "[0-9]+" '(face italic) str)
(tp-at 4 str))
;; => (face italic help-echo "number")
```
---
### 搜索和导航函数
#### `tp-search-forward` / `tp-search-backward`
```elisp
(tp-search-forward PROPERTY &optional VALUE PREDICATE NOT-CURRENT)
(tp-search-backward PROPERTY &optional VALUE PREDICATE NOT-CURRENT)
```
Emacs 的 `text-property-search-forward``text-property-search-backward` 的原始包装。
这些是直接使用 prop-match 对象的底层搜索函数。
---
#### `tp-forward` / `tp-backward`
```elisp
(tp-forward PROPERTY &optional VALUE OBJECT N)
(tp-backward PROPERTY &optional VALUE OBJECT N)
```
向前/向后搜索 N 次具有 PROPERTY 的文本。
- **N** 是搜索次数,默认为 1。
- **VALUE** 是可选的匹配值。
- **OBJECT** 可以是缓冲区或字符串nil 默认为当前缓冲区。
- 对于缓冲区,返回最后一次成功搜索的 prop-match 对象。
- 对于字符串,返回所有匹配的 (START END VALUE) 列表。
**示例:**
```elisp
;; 查找下一个具有 'marker 属性的文本
(with-temp-buffer
(insert "Hello World Test")
(tp-set 7 12 '(marker t))
(goto-char 1)
(let ((match (tp-forward 'marker)))
(when match
(prop-match-beginning match))))
;; => 7
;; 查找下一个 'type 等于 'heading 的文本
(with-temp-buffer
(insert "Hello World")
(tp-set 1 6 '(type heading))
(goto-char 1)
(let ((match (tp-forward 'type 'heading)))
(when match
(prop-match-value match))))
;; => heading
;; 在字符串中搜索
(let ((my-string (copy-sequence "Hello World Hello")))
(tp-set 0 5 '(marker t) my-string)
(tp-set 12 17 '(marker t) my-string)
(tp-forward 'marker nil my-string 2))
;; => ((0 5 t) (12 17 t))
```
---
#### `tp-forward-do` / `tp-backward-do`
```elisp
(tp-forward-do FUNCTION PROPERTY &optional VALUE OBJECT POINT N)
(tp-backward-do FUNCTION PROPERTY &optional VALUE OBJECT POINT N)
```
向前/向后搜索 N 次具有 PROPERTY 的文本,并对匹配的文本应用 FUNCTION。
- **FUNCTION** 接收匹配到的文本作为唯一参数。FUNCTION 的返回值将替换字符串或缓冲区中的匹配文本。
- **N** 是搜索次数,默认为 1。
- **OBJECT** 可以是缓冲区或字符串nil 默认为当前缓冲区。
- **POINT** 是搜索的起始位置;对于缓冲区 nil 表示当前位置,对于字符串 nil 表示 0向前或字符串末尾向后
- 返回成功匹配的数量。
**示例:**
```elisp
;; 将缓冲区中匹配的文本转为大写(从当前位置开始)
(with-temp-buffer
(insert "hello world test")
(tp-set 1 6 '(marker t))
(tp-set 13 17 '(marker t))
(goto-char 1)
(tp-forward-do #'upcase 'marker nil nil nil 2)
(buffer-string))
;; => "HELLO world TEST"
;; 将字符串中匹配的文本转为大写(从位置 0 开始)
(let ((my-string (copy-sequence "hello world hello")))
(tp-set 0 5 '(marker t) my-string)
(tp-set 12 17 '(marker t) my-string)
(tp-forward-do #'upcase 'marker nil my-string nil 2)
my-string)
;; => "HELLO world HELLO"
;; 从特定位置开始搜索
(let ((my-string (copy-sequence "hello world hello")))
(tp-set 0 5 '(marker t) my-string)
(tp-set 12 17 '(marker t) my-string)
(tp-forward-do #'upcase 'marker nil my-string 6 2)
my-string)
;; => "hello world HELLO" ; 只处理位置 6 之后的匹配
;; 自定义转换
(with-temp-buffer
(insert "hello world test")
(tp-set 1 6 '(marker t))
(goto-char 1)
(tp-forward-do
(lambda (text)
(concat "[" text "]"))
'marker nil nil nil 1)
(buffer-string))
;; => "[hello] world test"
```
---
#### `tp-search` - 搜索所有匹配
```elisp
;; 缓冲区/字符串区域
(tp-search START END PROPERTY &optional VALUE OBJECT)
;; 整个字符串
(tp-search STRING PROPERTY &optional VALUE)
```
在缓冲区/字符串范围或整个字符串中搜索所有具有 PROPERTY 的文本。
返回所有匹配区域的 (START END VALUE) 列表。
**示例:**
```elisp
;; 在缓冲区范围内查找所有 'marker 属性
(with-temp-buffer
(insert "Hello World Test Again")
(tp-set 1 6 '(marker t))
(tp-set 13 17 '(marker t))
(tp-search 1 22 'marker))
;; => ((1 6 t) (13 17 t))
;; 在字符串中查找所有值为 'heading 的 'type 属性
(let ((my-string (copy-sequence "Title Here Body Text")))
(tp-set 0 10 '(type heading) my-string)
(tp-search my-string 'type 'heading))
;; => ((0 10 heading))
;; 按值过滤
(with-temp-buffer
(insert "Heading1 Body Heading2")
(tp-set 1 9 '(type heading))
(tp-set 10 14 '(type body))
(tp-set 15 23 '(type heading))
(tp-search 1 23 'type 'heading))
;; => ((1 9 heading) (15 23 heading))
```
---
#### `tp-search-map` - 对匹配文本应用函数
```elisp
;; 缓冲区/字符串区域
(tp-search-map FUNCTION START END PROPERTY &optional VALUE OBJECT)
;; 整个字符串
(tp-search-map FUNCTION STRING PROPERTY &optional VALUE)
```
对所有 PROPERTY 匹配的文本应用 FUNCTION。
- **FUNCTION** 接收匹配到的文本作为唯一参数。FUNCTION 的返回值将替换字符串或缓冲区中的匹配文本。
- 返回处理的匹配数量。
**示例:**
```elisp
;; 将字符串中所有 marker 文本转为大写
(let ((my-string (copy-sequence "hello world hello")))
(tp-set 0 5 '(marker t) my-string)
(tp-set 12 17 '(marker t) my-string)
(tp-search-map #'upcase my-string 'marker)
my-string)
;; => "HELLO world HELLO"
;; 将缓冲区范围内所有 marker 文本转为大写
(with-temp-buffer
(insert "hello world test")
(tp-set 1 6 '(marker t))
(tp-set 13 17 '(marker t))
(tp-search-map #'upcase 1 17 'marker)
(buffer-string))
;; => "HELLO world TEST"
;; 自定义转换
(let ((my-string (copy-sequence "hello world")))
(tp-set 0 5 '(marker t) my-string)
(tp-search-map
(lambda (text)
(concat "[" text "]"))
my-string 'marker)
my-string)
;; => "[hello] world"
```
---
## 属性层系统
属性层系统是 tp.el 的创新功能,允许在同一文本区域堆叠多组属性。只有顶层属性可见,但下层属性会被保留,并可通过轮转或固定操作使其显现。
### 属性层概念
┌─────────────────────────────┐
│ 顶层(可见) │ ← idx=0你看到的
├─────────────────────────────┤
│ 中间层(隐藏) │ ← idx=1被保留
├─────────────────────────────┤
│ 底层(隐藏) │ ← idx=-1被保留
└─────────────────────────────┘
### 属性层定义
#### `tp-define-layer` - 定义属性层
定义单个属性层或多个属性层组。
**单个属性层:**
```elisp
(tp-define-layer layer-name
(face (:background "cyan") line-prefix ">>"))
```
**多个属性层(属性层组):**
```elisp
(tp-define-layer my-group
layer-1 ; 引用已存在的属性层
(face (:background "red") line-prefix ">>") ; 匿名属性层
(face (:background "green" :weight bold))) ; 另一个匿名属性层
```
定义中的第一个属性层是顶层(默认可见)。
**示例:**
```elisp
;; 定义单个属性层
(progn
(setq tp-layer-alist nil) ; 重置以确保干净的示例
(tp-define-layer highlight
(face (:background "yellow" :foreground "black")))
(tp-layer-props 'highlight))
;; => (face (:background "yellow" :foreground "black") tp-name highlight)
(progn
(tp-define-layer error
(face (:background "red" :foreground "white")
help-echo "错误!"))
(tp-layer-props 'error))
;; => (face (:background "red" :foreground "white") help-echo "错误!" tp-name error)
(progn
(tp-define-layer info
(face (:background "blue" :foreground "white")))
(tp-layer-props 'info))
;; => (face (:background "blue" :foreground "white") tp-name info)
;; 定义属性层组
(progn
(tp-define-layer status-colors
highlight
error
info)
(length (tp-group-props 'status-colors)))
;; => 3
```
---
#### `tp-layer-props` / `tp-group-props`
```elisp
(tp-layer-props LAYER-NAME)
(tp-group-props GROUP-NAME)
```
获取属性层或属性层组中所有属性层的属性。
**示例:**
```elisp
;; 获取属性层属性
(progn
(setq tp-layer-alist nil)
(tp-define-layer my-layer (face bold help-echo "tip"))
(tp-layer-props 'my-layer))
;; => (face bold help-echo "tip" tp-name my-layer)
;; 获取属性层组属性
(progn
(setq tp-layer-alist nil)
(setq tp-layer-groups nil)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(tp-define-layer my-group layer1 layer2)
(length (tp-group-props 'my-group)))
;; => 2
```
---
#### `tp-layer-undefine` / `tp-group-undefine`
```elisp
(tp-layer-undefine NAME)
(tp-group-undefine NAME)
```
移除属性层或属性层组定义。
**示例:**
```elisp
;; 取消定义属性层
(progn
(setq tp-layer-alist nil)
(tp-define-layer temp-layer (face bold))
(tp-layer-undefine 'temp-layer)
(tp-layer-props 'temp-layer))
;; => nil
;; 取消定义属性层组
(progn
(setq tp-layer-alist nil)
(setq tp-layer-groups nil)
(tp-define-layer l1 (face bold))
(tp-define-layer my-group l1)
(tp-group-undefine 'my-group)
(assoc 'my-group tp-layer-groups))
;; => nil
```
---
#### `tp-layer-reset`
```elisp
(tp-layer-reset)
```
清除所有属性层和属性层组定义。
**示例:**
```elisp
(progn
(tp-define-layer test-layer (face bold))
(tp-layer-reset)
(list tp-layer-alist tp-layer-groups))
;; => (nil nil)
```
---
### 属性层放置
#### `tp-put-layer` - 在指定位置设置属性层
```elisp
;; 缓冲区/字符串区域
(tp-put-layer START END LAYER IDX OBJECT)
;; 整个字符串
(tp-put-layer STRING LAYER IDX)
```
在属性层堆栈的指定索引位置设置属性层。
- `IDX = 0`:顶部(可见属性层)
- `IDX = -1`:底部
- 其他值在该位置插入
**示例:**
```elisp
;; 将 base 属性层放在顶部
(progn
(tp-layer-reset)
(tp-define-layer base (face default))
(tp-define-layer highlight (face (:background "yellow")))
(with-temp-buffer
(insert "Hello World")
(tp-put-layer 1 10 'base 0)
(tp-at 1 'tp-name)))
;; => base
;; 将 highlight 放在索引 1顶部下面
(progn
(tp-layer-reset)
(tp-define-layer base (face default))
(tp-define-layer highlight (face (:background "yellow")))
(with-temp-buffer
(insert "Hello World")
(tp-put-layer 1 10 'base 0)
(tp-put-layer 1 10 'highlight 1)
(tp-layer-count 1 10)))
;; => 2
;; 将属性层放在底部
(progn
(tp-layer-reset)
(tp-define-layer base (face default))
(tp-define-layer info (face (:foreground "blue")))
(with-temp-buffer
(insert "Hello World")
(tp-put-layer 1 10 'base 0)
(tp-put-layer 1 10 'info -1)
(tp-layer-top 1 10)))
;; => base ; info 在底部base 可见
```
---
#### `tp-push-layer` - 推送属性层到顶部
```elisp
;; 缓冲区/字符串区域
(tp-push-layer START END LAYER OBJECT)
;; 整个字符串
(tp-push-layer STRING LAYER)
```
将属性层推到堆栈顶部(相当于 `tp-put-layer ... 0`)。
**示例:**
```elisp
;; 首先推入 base 属性层
(progn
(tp-layer-reset)
(tp-define-layer base (face default))
(tp-define-layer highlight (face (:background "yellow")))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'base)
(tp-at 1 'tp-name)))
;; => base
;; 将 highlight 推到顶部(现在可见)
(progn
(tp-layer-reset)
(tp-define-layer base (face default))
(tp-define-layer highlight (face (:background "yellow")))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'base)
(tp-push-layer 1 10 'highlight)
(tp-at 1 'tp-name)))
;; => highlight
```
---
### 属性层删除
#### `tp-delete-layer` - 按名称/索引删除属性层
```elisp
;; 缓冲区/字符串区域
(tp-delete-layer START END LAYER-NAME/IDX OBJECT)
;; 整个字符串
(tp-delete-layer STRING LAYER-NAME/IDX)
```
通过名称或索引从堆栈任意位置删除属性层。
**示例:**
```elisp
;; 按名称删除
(progn
(tp-layer-reset)
(tp-define-layer highlight (face (:background "yellow")))
(tp-define-layer base (face default))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'base)
(tp-push-layer 1 10 'highlight)
(tp-delete-layer 1 10 'highlight)
(tp-at 1 'tp-name)))
;; => base
;; 删除顶层idx=0
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-delete-layer 1 10 0)
(tp-at 1 'tp-name)))
;; => layer1
;; 删除底层
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-delete-layer 1 10 -1)
(tp-layer-count 1 10)))
;; => 1
```
---
#### `tp-pop-layer` - 弹出顶层
```elisp
;; 缓冲区/字符串区域
(tp-pop-layer START END OBJECT)
;; 整个字符串
(tp-pop-layer STRING)
```
删除顶层(相当于 `tp-delete-layer ... 0`)。
**示例:**
```elisp
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-pop-layer 1 10)
(tp-at 1 'tp-name)))
;; => layer1
```
---
### 属性层移动
#### `tp-raise-layer` - 上移/下移属性层
```elisp
;; 缓冲区/字符串区域
(tp-raise-layer START END IDX/LAYER-NAME N OBJECT)
;; 整个字符串
(tp-raise-layer STRING IDX/LAYER-NAME N)
```
将属性层上移 N 个位置。正数 N 向顶部移动,负数向底部移动。
**示例:**
```elisp
;; 将 layer1 上移 2 个位置(到顶部)
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(tp-define-layer layer3 (face underline))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-push-layer 1 10 'layer3)
;; 堆栈: layer3 (顶), layer2, layer1 (底)
(tp-raise-layer 1 10 'layer1 2)
(tp-layer-top 1 10)))
;; => layer1
;; 将索引 0 的属性层下移 1 个位置
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
;; 堆栈: layer2 (idx 0), layer1 (idx 1)
(tp-raise-layer 1 10 0 -1)
(tp-layer-top 1 10)))
;; => layer1
```
---
#### `tp-rotate-layer` - 轮换属性层
```elisp
;; 缓冲区/字符串区域
(tp-rotate-layer START END OBJECT)
;; 整个字符串
(tp-rotate-layer STRING)
```
轮换属性层 - 顶层移到底部,下一层变为可见。
**示例:**
```elisp
;; 堆栈: highlight (顶) -> base (底)
(progn
(tp-layer-reset)
(tp-define-layer base (face default))
(tp-define-layer highlight (face (:background "yellow")))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'base)
(tp-push-layer 1 10 'highlight)
;; 堆栈: highlight (顶) -> base (底)
(tp-rotate-layer 1 10)
;; 堆栈: base (顶) -> highlight (底)
(tp-layer-top 1 10)))
;; => base
```
---
#### `tp-pin-layer` - 将属性层置顶
```elisp
;; 缓冲区/字符串区域
(tp-pin-layer START END IDX/LAYER-NAME OBJECT)
;; 整个字符串
(tp-pin-layer STRING IDX/LAYER-NAME)
```
将特定属性层移到顶部(使其可见)。
**示例:**
```elisp
;; 将 'base 设为顶层
(progn
(tp-layer-reset)
(tp-define-layer base (face default))
(tp-define-layer highlight (face (:background "yellow")))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'base)
(tp-push-layer 1 10 'highlight)
;; highlight 在顶部
(tp-pin-layer 1 10 'base)
(tp-layer-top 1 10)))
;; => base
```
---
#### `tp-switch-layer` - 交换两个属性层
```elisp
;; 缓冲区/字符串区域
(tp-switch-layer START END IDX1/NAME1 IDX2/NAME2 OBJECT)
;; 整个字符串
(tp-switch-layer STRING IDX1/NAME1 IDX2/NAME2)
```
交换两个属性层的位置。
**示例:**
```elisp
;; 交换 layer1 和 layer2
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
;; layer2 在顶部
(tp-switch-layer 1 10 'layer1 'layer2)
;; 现在 layer1 在顶部
(tp-layer-top 1 10)))
;; => layer1
```
---
### 属性层合并
#### `tp-merge-layers` - 合并多个属性层
```elisp
;; 缓冲区/字符串区域
(tp-merge-layers START END NEW-LAYER-NAME '(IDX1 LAYER-NAME1 IDX2 ...) OBJECT)
;; 整个字符串
(tp-merge-layers STRING NEW-LAYER-NAME '(IDX1 LAYER-NAME1 IDX2 ...))
```
将指定的属性层合并为一个新属性层。列表中靠前的属性层优先级更高。
**示例:**
```elisp
;; 将 layer1 和 layer2 合并为 merged-layer
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (help-echo "tip"))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-merge-layers 1 10 'merged-layer '(layer1 layer2))
(tp-at 1 'tp-name)))
;; => merged-layer
;; 按索引合并
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (help-echo "tip"))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-merge-layers 1 10 'merged '(0 1))
(tp-layer-count 1 10)))
;; => 1
```
---
#### `tp-flatten-layers` - 扁平化所有属性层
```elisp
;; 缓冲区/字符串区域
(tp-flatten-layers START END NAME OBJECT)
;; 整个字符串
(tp-flatten-layers STRING NAME)
```
将所有属性层扁平化为一个具有给定名称的单一属性层。
**示例:**
```elisp
;; 将所有属性层扁平化为 'flat-layer
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (help-echo "tip"))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-flatten-layers 1 10 'flat-layer)
(tp-at 1 'tp-name)))
;; => flat-layer
;; 使用 nil 名称扁平化(无名属性层)
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-flatten-layers 1 10 nil)
(tp-at 1 'tp-name)))
;; => nil
```
---
### 属性层查询函数
#### `tp-layer-list` - 列出所有属性层
```elisp
(tp-layer-list START END &optional OBJECT)
```
获取区域中所有属性层名称的列表。
**示例:**
```elisp
(progn
(tp-layer-reset)
(tp-define-layer highlight (face (:background "yellow")))
(tp-define-layer base (face default))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'base)
(tp-push-layer 1 10 'highlight)
(tp-layer-list 1 10)))
;; => (highlight base)
```
---
#### `tp-layer-count`
```elisp
(tp-layer-count START END &optional OBJECT)
```
计算区域中的属性层数量。
**示例:**
```elisp
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-layer-count 1 10)))
;; => 2
```
---
#### `tp-layer-exists-p`
```elisp
(tp-layer-exists-p START END NAME &optional OBJECT)
```
检查区域中是否存在某属性层。
**示例:**
```elisp
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(list (tp-layer-exists-p 1 10 'layer1)
(tp-layer-exists-p 1 10 'layer2))))
;; => (t nil)
```
---
#### `tp-layer-top`
```elisp
(tp-layer-top START END &optional OBJECT)
```
获取顶层(可见)属性层的名称。
**示例:**
```elisp
(progn
(tp-layer-reset)
(tp-define-layer layer1 (face bold))
(tp-define-layer layer2 (face italic))
(with-temp-buffer
(insert "Hello World")
(tp-push-layer 1 10 'layer1)
(tp-push-layer 1 10 'layer2)
(tp-layer-top 1 10)))
;; => layer2
```
---
## 实用示例
### 多属性层语法高亮
```elisp
;; 可以在缓冲区中运行的完整示例
(progn
(tp-layer-reset)
;; 为不同高亮目的定义属性层
(tp-define-layer code-base
(face font-lock-keyword-face))
(tp-define-layer code-error
(face (:underline (:color "red" :style wave))
help-echo "语法错误"))
(tp-define-layer code-debug
(face (:background "dark blue")))
(with-temp-buffer
(insert (make-string 100 ?x)) ; 创建 100 字符缓冲区
;; 应用基础高亮
(tp-push-layer 1 100 'code-base)
;; 在有问题的代码上添加错误高亮
(tp-push-layer 50 60 'code-error)
;; 检查位置 55 的顶层
(tp-layer-top 50 60)))
;; => code-error
;; 切换函数(用于实际缓冲区)
(defun toggle-error-view (start end)
"在错误和正常视图之间切换。"
(interactive "r")
(tp-rotate-layer start end))
```
### 状态指示器
```elisp
;; 包含属性层组的完整示例
(progn
(tp-layer-reset)
;; 将状态属性层定义为一个组
(tp-define-layer status-todo (face (:foreground "gray")))
(tp-define-layer status-progress (face (:foreground "yellow")))
(tp-define-layer status-done (face (:foreground "green")))
(tp-define-layer task-status status-todo status-progress status-done)
;; 检查组是否已定义
(length (tp-group-props 'task-status)))
;; => 3
;; 循环切换状态(用于实际缓冲区)
(defun cycle-task-status ()
"循环切换当前行的任务状态属性层。"
(interactive)
(tp-rotate-layer (line-beginning-position) (line-end-position)))
```
### 临时高亮
```elisp
;; 定义临时高亮属性层
(progn
(tp-layer-reset)
(tp-define-layer temp-highlight
(face (:background "yellow")))
(tp-layer-props 'temp-highlight))
;; => (face (:background "yellow") tp-name temp-highlight)
;; 闪烁函数(用于实际缓冲区)
(defun flash-region (start end)
"临时闪烁一个区域。"
(tp-push-layer start end 'temp-highlight)
(run-with-timer 0.5 nil
(lambda (s e)
(tp-delete-layer s e 'temp-highlight))
start end))
```
---
## 别名
为方便使用tp.el 提供以下别名:
| 别名 | 原函数 |
|------|--------|
| `tp-layer-properties` | `tp-layer-props` |
| `tp-layer-group-properties` | `tp-group-props` |
| `tp-layer-group-undefine` | `tp-group-undefine` |
---
## 许可证
GNU 通用公共许可证 v2 或更高版本。
---
## 贡献
欢迎贡献!请随时提交 issues 或 pull requests。
---
<p align="center">
<em>tp.el - 让文本属性变得强大且易用</em>
</p>