24 KiB
TP 1.0 公共 API 与用法参考
本文是 TP 当前实现的完整公共入口索引。它以 tp.el 加载的模块为准;带 tp-- 前缀的函数、变量和结构体是内部实现,不属于本文的稳定 API。
- 快速开始与能力边界:README、中文 README
- 语义合同:API-SEMANTICS.md
- 当前架构:ARCHITECTURE.md
- 可运行示例:examples/
1. 运行边界
TP 只负责 Emacs text properties、响应式 signal/binding、retained object、 marker-backed mount、diff、事务和 Buffer publication。它不实现 CSS selector、 stylesheet、specificity、cascade、Box、Flex、Grid、测量或布局,也不依赖 Ebox/ECSS。
所有坐标都遵循 Emacs 原生对象的坐标规则:
- string 使用从 0 开始的 [start, end);
- buffer 使用从 1 开始的 [start, end);
- tp-surface-plan 中的 content leaf range 是相对于该 leaf 文本的 0-based offset,不是 Buffer position。
普通函数值、callback、keymap command 和 list 都是 literal data。只有显式的 tp-computed source 会被执行。显式存在且值为 nil 与属性不存在始终不同。
2. 先选入口
| 目标 | 入口 | 是否建立 live state |
|---|---|---|
| 创建带属性的 string | tp-propertize | 否 |
| 一次性改已有范围 | tp-apply、tp-set、tp-reset、tp-add、tp-remove、tp-clear | 否 |
| 查询属性和区间 | tp-get、tp-at、tp-member、tp-lookup | 否 |
| 按文字或属性搜索 | tp-match-、tp-regexp-、tp-search*、tp-forward/backward | 否 |
| 让已有 host text 响应变化 | tp-watch | 是,properties |
| 管理 TP 自己拥有的文字 | tp-surface-mount / tp-surface-update | 是,content |
| 检查或卸载 publication | tp-surface-inspect / tp-surface-report / tp-surface-unmount | 是 |
| 定义可复用声明 | define-tp、define-tps | 只建立 recipe,不建立 live state |
| 定义主题颜色 | define-tp-palette | 只更新 palette registry |
安装只需要把仓库加入 load-path:
(add-to-list 'load-path "/path/to/tp")
(require 'tp)
tp.el 会加载所有运行时模块。单独加载模块只适合包开发或测试。
3. 一次性文本属性操作
3.1 创建和应用
(tp-propertize
"Hello"
'(face bold help-echo "greeting" keymap nil))
(with-current-buffer (get-buffer-create "*tp-demo*")
(erase-buffer)
(insert "abcdef")
(tp-apply (current-buffer) 2 5 '(face italic)))
tp-propertize 返回原 string 的副本;tp-apply 保留范围内未出现在 declarations 中的其他属性,并返回 (START . END)。
declarations 可以是原生 property plist、define-tp recipe、recipe call, 或包含 recipe key 的 plist。tp-apply 和下面的直接操作不会创建 object、 binding、marker、subscription 或 surface。
3.2 set/reset/add/remove/clear
对完整 string 的形式会返回新 string;对 Buffer 范围的形式会原地修改,并 返回 (START . END)(tp-remove 和 tp-clear 的 Buffer 形式返回 nil)。
;; string:property/value 形式,原 string 不变
(tp-set "item" 'face 'bold)
(tp-reset "item" 'face 'italic)
(tp-add "item" 'face 'underline)
(tp-remove (tp-set "item" 'face 'bold) 'face)
;; buffer 或 string 范围:一个 direct property plist,可选 OBJECT
(tp-set start end '(face bold help-echo "tip") buffer)
(tp-reset start end '(face italic) buffer)
(tp-add start end '(face (:weight bold)) buffer)
(tp-remove start end 'help-echo buffer)
(tp-clear start end buffer)
- tp-set 替换所指定的顶层属性;
- tp-reset 用 declarations 完全替换范围内所有属性;
- tp-add 合并属性;face、font-lock-face、mouse-face 支持 contribution 合成和嵌套 plist 合并;
- tp-remove 支持移除顶层属性,也支持 (property sub-key) 或 (property sub-key nested-key...);
- tp-clear 移除范围内全部 text properties。
4. 声明、property policy 和 recipe
4.1 Property policy
(tp-define-property-policy
'text/my-property
:normalizer #'identity
:validator (lambda (_value) t)
:equality #'equal
:merge (lambda (_old new) new)
:projector #'identity)
(tp-property-policy 'text/my-property)
(tp-register-text-property 'help-echo)
(tp-text-property-id 'help-echo)
(tp-text-declarations '(face bold help-echo "tip"))
tp-define-property-policy 的 id 必须是包含 / 的 canonical symbol。可选策略 为 :normalizer、:validator、:equality、:merge、:projector。注册是原子的; 无效策略不会替换旧定义。
- tp-register-text-property 为原生 Emacs property 安装默认 policy;
- tp-text-property-id 把原生 property 映射为 text/PROPERTY;
- tp-text-declarations 把原生 plist 转成 canonical declarations;
- tp-merge-declarations 按参数顺序合并 direct declaration groups,后者 覆盖前者,并保留显式 nil;
- tp-computed 把一个函数标记为唯一允许执行的 value source;
- tp-resolve-value 解析 literal 或显式 computed value;
- tp-define-style、tp-style-declarations、tp-undefine-style 管理已归一化的 命名 direct style。
4.2 静态 recipe
(define-tp link-style (color)
(list 'face (list :foreground color :weight 'bold)
'mouse-face 'highlight))
(define-tps status-group ()
link-style
'(help-echo "status"))
(tp-set 1 5 '(link-style "#58a6ff") buffer)
(tp-layer-props-with-arg 'link-style "#58a6ff")
(tp-group-props 'status-group)
入口及别名:
- define-tp(别名 tp-define-layer)定义一个返回原生 property plist 的 recipe;
- define-tps(别名 define-tp-group、tp-define-group)定义由多个 recipe、 recipe call、原生 plist 或命名元素组成的 declaration group;
- tp-layer-parameterized-p、tp-layer-arglist、tp-layer-props、 tp-layer-props-with-args、tp-layer-props-with-arg 查询或展开 layer;
- tp-group-parameterized-p、tp-group-props、tp-group-props-with-args、 tp-group-props-with-arg 查询或展开 group;
- tp-undefine-layer、tp-undefine-group、tp-layer-reset 删除 recipe;
- tp-describe-layer 返回 recipe 的声明式诊断数据。
recipe 是 definition-time convenience,不是 mounted layer,也不会向文本 写入 identity、provenance、tp-name 或 tp-layers。旧的 $variable reactive syntax 会被拒绝。
tp-layer-alist 和 tp-layer-groups 是 recipe registry;通常通过上面的 query、 undefine 和 reset API 访问,不要直接修改 registry。
5. Reactive signal 和 binding
5.1 Signal
(let ((online (tp-signal-create nil)))
(tp-signal-read online) ; 在 binding/producer 中收集依赖
(tp-signal-peek online) ; 读取但不收集依赖
(tp-signal-set online t)
(tp-signal-subscriber-count online)
(tp-signal-live-p online)
(tp-signal-dispose online))
tp-signal-create 的关键字选项:
- :equality,默认 equal;相等写入是 no-op;
- :scope,默认 global,也可以是一个 live buffer。Buffer kill 会释放该范围 内 signal。
tp-signal-read 只在当前 computation 中登记 dependency;tp-signal-peek 不会 登记。disposed signal 的读取会报错。
5.2 Binding
(let* ((source (tp-signal-create 0))
(owner (list 'owner))
(binding
(tp-bind owner 'value
(lambda () (tp-signal-read source))
:lifecycle 'delete)))
(tp-binding-read binding)
(tp-binding-live-p binding)
(tp-binding-dependency-count binding)
(tp-binding-subscriber-count binding)
(tp-binding-owner-bindings owner)
(tp-binding-dispose binding)
(tp-signal-dispose source))
tp-bind 按 OWNER + KEY 幂等安装 computation。KEY 应使用调用者自己的命名空间; :equality 默认 equal;:lifecycle 为 delete 或 retain。binding 依赖集合按每次 实际读取重建,因此条件分支会自动断开不再使用的 source。tp-binding-read 会在需要时计算并把 binding-to-binding 依赖登记到当前 computation。
tp-binding-dispose-owner 释放 owner 的全部 binding,返回释放数量; tp-binding-dispose 释放单个 binding。
5.3 Transaction 和变量适配器
(let ((left (tp-signal-create 0))
(right (tp-signal-create 0)))
(tp-with-transaction
(tp-signal-set left 1)
(tp-signal-set right 2))
(tp-signal-dispose left)
(tp-signal-dispose right))
(tp-transaction-participate
'my-external-state
(lambda () (my-publish))
(lambda () (my-rollback)))
(tp-variable-signal 'my-variable)
(tp-variable-signal 'my-buffer-variable some-buffer)
(tp-reactive-counters)
(tp-reactive-reset-counters)
(tp-reactive-reset)
tp-with-transaction 将 signal、binding、surface 和注册的 transaction participant 一起原子处理。participant 必须在 active transaction 内注册; publish 在 surface publication 后、source commit 前运行,失败时按逆序 rollback。tp-variable-signal 用 Emacs variable watcher 适配全局或指定 Buffer 的变量,不是旧的 $variable API。
ETAF registers one opaque participant for its immutable generation and Ebox client state. Its publish is paired with rollback across TP final accept; the ETAF runtime separately records effect input/version tuples and reports a bounded non-converging flush instead of spinning.
6. Retained surface
6.1 Plan、result 和 object
(tp-surface-plan-create
:key 'label :kind 'text :text "Ready"
:props '(face bold) :tags '(:role status)
:capability 'content)
Plan 字段为 key、kind、text、props、children、tags、capability。leaf 使用 text,容器使用 children;同一层的 key 必须唯一。capability 是 content 或 properties。
- tp-surface-plan-create 防御性复制 caller-owned 数据;
- tp-surface-plan-create-owned 直接接收 candidate-local 数据;调用者必须 转移所有权,之后不得修改或向其他 owner 暴露;
- tp-surface-result-create 返回携带 plan 和 opaque client-state 的 producer result;
- tp-surface-result-create-owned 只可对当前 active prepare context 使用, 结果只消费一次。
- tp-surface-retained-content-result-create 用于对象拓扑不变的 content
candidate;最后一个可选
property-contributions参数接受按顺序排列的(:start N :end N :props PLIST)相对范围。TP 使用已注册 property merge policy 在 prepare 内组合这些贡献,再通过同一个 content surface 原子 diff、 发布和回滚;调用者不需要创建重叠的 properties surface,也不应预先压平 Theme/状态/inline face。
producer 接收一个 prepare context,并应返回 plan 或 result:
(lambda (context)
(let ((object (tp-object-ensure context nil 'row 'text)))
(tp-surface-plan-create
:key 'row :kind 'text :text "row"
:props '(face italic)
:capability 'content)))
producer 不得在 prepare 阶段直接修改 live surface buffer;plan 不携带 position、marker、patch operation、producer closure 或 continuation。
对象入口:
- tp-object-ensure 按 parent、sibling key、kind 返回或创建当前 candidate 的 稳定 opaque object;
- tp-object-live-p、tp-object-mounted-p 检查 object 状态;
- tp-object-resolve 用 key path 从 surface side index 取得 object,不扫描文本;
- tp-object-mounts 返回 :start、:end、:tags 的数值快照;
- tp-object-retain 保留没有输出 fragment 的 object;
- tp-object-retain-subtree 保留已证明未删除的 object 子树;
- tp-object-reuse-subtree 复用已证明完全不变的 object 子树;
- tp-object-attach-fragment 把一个 object 绑定到 content fragment;
- tp-object-attach-content-range 把 object 绑定到 leaf 的相对字符范围;
- tp-object-attach-content-ranges 批量绑定 range,并复制 tags;
- tp-object-attach-content-ranges-owned 批量绑定 candidate-owned tags。
retain-subtree/reuse-subtree 不会替调用者证明 candidate 完整或输出未变化; producer 仍须提交完整 plan,并承担“不变”证明。
6.2 Mount、update、scope 和 report
(let* ((surface
(tp-surface-mount
buffer producer
'(:capability content
:start 1 :end 1
:client-state initial-state
:inhibit-read-only t
:observers (my-after-commit)))))
(tp-surface-update surface producer)
(tp-surface-update-scoped
surface
(list object)
producer
'(:on-mismatch error :return-report t))
(tp-surface-report surface)
(tp-surface-report-summary surface)
(tp-surface-inspect surface)
(tp-surface-unmount surface))
tp-surface-mount 的 plan-or-producer 可以是 plan、result 或 producer; producer 的调用上下文由 TP 管理。properties surface 只能写属性,content surface 才能拥有和更新文字。可用 mount options: :capability、:start、:end、:inhibit-read-only、:coordinate-mounts、 :client-state、:observers。
tp-surface-update 发布下一份完整 candidate。tp-surface-update-scoped 仍要求 producer 生成完整 candidate,但只授权指定 object 当前 mount 范围; :on-mismatch 默认报 tp-scope-mismatch,设为 root 才允许显式 root fallback; :return-report 控制是否返回 report。
其他 surface API:
- tp-surface-live-p、tp-surface-revision、tp-surface-client-state;
- tp-surface-materialize-string 使用相同 producer/plan 语义生成一次性 string, 但不保留 surface、object、binding、subscription 或 anchor;
- tp-surface-at-point 通过 side index 返回当前位置的 live objects;
- tp-surface-report 返回完整 defensive report;
- tp-surface-report-summary 只返回 scalar commit metrics;
- tp-surface-inspect 返回 surface、capability、revision、object/mount 数量、 client state 和 report;
- tp-surface-unmount 卸载并返回 report。Content 会删除自己的 span; properties 只撤销仍由 TP 拥有的 contribution。
Report 的常用字段包括: :transaction-id、:surface-id、:old-revision、:new-revision、 :candidate-source-writes、:invalidated-bindings、:recomputed-bindings、 :skipped-bindings、:reconciled-objects、:created-objects、:removed-objects、 :moved-objects、:text-operations、:property-operations、 :touched-characters、:full-root、:scope-count、:scope-range-count、 :scope-fallback、:property-conflicts、:rolled-back、:failure、 :observer-errors、:timing。
6.3 Host range 和 tp-watch
(let ((anchor
(tp-range-anchor-create
buffer start end
:start-insertion-type nil
:end-insertion-type t
:boundary-policy 'shorten)))
;; 在 producer 的 context 中:
(tp-object-attach-range context object anchor)
(tp-range-anchor-live-p anchor)
(tp-range-rebase anchor))
boundary-policy 可为 stale、shorten、remove。anchor 是 marker-backed opaque range;host text 编辑由 marker 跟随,TP 不扫描附近文字猜测位置。 tp-range-rebase 明确接受当前 host properties 作为新 baseline。
tp-watch 是 properties-only surface 的便捷入口:
(let ((surface
(tp-watch
buffer start end
(lambda ()
(list 'face
(if enabled 'success 'shadow))))))
(tp-surface-report surface))
compute 是零参数函数,返回原生 declarations。文字由 host 所有;返回值是 底层 properties surface,可交给 update/report/inspect/unmount。
7. 属性查询、区间和 mutation policy
7.1 Direct query
- tp-get 返回范围内的 (START END PROPS) property intervals;指定 property 或 nested path 时只返回对应区间;
- tp-at 返回某一位置的完整 plist,或指定 property/nested path 的值;
- tp-member 只在 property 存在时返回 (PROPERTY VALUE),因此可区分 present nil 和 absent;
- tp-intervals 返回对象范围的 property intervals,可用 absolute 请求 Buffer 原生坐标;
- tp-intervals-map 对每个 interval 调用函数;
- tp-plist 返回位置或 string range 的合并 property plist;
- tp-empty-p 判断对象是否为空;
- tp-text-snapshot 返回 string 和 text properties 的防御性快照。
7.2 Native query
(tp-lookup position 'face
:object buffer
:mode :text-effective)
(tp-property-change position :property 'face :direction :next)
(tp-property-any start end 'face 'bold buffer)
(tp-property-not-all start end 'face 'bold buffer)
(tp-with-mutation-policy '(:modified :silent :read-only :inhibit)
(tp-apply buffer start end '(face bold)))
tp-lookup 返回 tp-lookup-result,mode 可为:
- :text-direct:直接 text property;
- :text-effective:考虑 category/default 等 text-property 继承;
- :text-source:返回实际 text source;
- :char:使用 get-char-property-and-overlay 的字符属性;
- :char-source:字符属性的实际来源,overlay 会记录为 :overlay。
result 的 present-p 区分显式 nil;tp-property-change 的 direction 为 :next 或 :previous,property 为 nil 时观察任意属性变化。
tp-with-mutation-policy 的 :modified 可为 :ordinary 或 :silent, :read-only 可为 :respect 或 :inhibit。支持 ordinary/respect、 ordinary/inhibit、silent/inhibit;silent/respect 会报错。
8. Match、regexp、search 和 navigation
所有 search 都是 presence-aware:省略 VALUE 或显式传入 tp-any-value 匹配 所有存在的值;显式 nil 只匹配“存在且值为 nil”。
8.1 按文字匹配并修改
- tp-match-set:literal pattern 的所有匹配上设置属性;
- tp-match-reset:literal pattern 的所有匹配上重置属性;
- tp-match-add:literal pattern 的所有匹配上合并属性;
- tp-regexp-set、tp-regexp-reset、tp-regexp-add:regexp 匹配,可用 subexp 只操作 capture group。
Pattern 可以是一个 string 或 string 列表。String object 返回新 string; Buffer object 原地修改并返回匹配范围列表。start > end 时边界会交换, 匹配不会跨出指定范围。
8.2 查找和批量处理
(tp-search string 'status)
(tp-search start end 'status 'ready buffer)
(tp-forward 'status tp-any-value buffer 2)
(tp-backward 'status nil buffer)
(tp-search-map
(lambda (text _start _end _index) (upcase text))
'status tp-any-value buffer)
- tp-search 返回所有匹配的 (START END VALUE);
- tp-forward / tp-backward 在 Buffer 中从 point 搜索并返回 prop-match, 在 string 中返回 0-based 区间列表;
- tp-forward-do / tp-backward-do 找到第 N 个匹配后只对最后一个执行 function,不是 for-each;
- tp-search-map 对每个匹配执行 function。function 可返回替换 string、 修改 text 后的 string,或 nil 跳过。String 替换必须保持长度不变; Buffer 替换可以改变长度;
- tp-search-forward / tp-search-backward 只是 Emacs text-property-search-* 的 obsolete raw wrapper,新增代码使用 tp-forward / tp-backward 或直接使用 Emacs primitive。
9. Palette、内置 recipe 和显示辅助
(define-tp-palette status
:fg ("#22863a" . "#3fb950")
:bg ("#f0fff4" . "#1a2e1f"))
(tp-palette-color 'status :fg)
(tp-palette-fg-color 'status)
(tp-palette-bg-color 'status)
(tp-palette-border-color 'status)
(tp-palette-has-p 'status :fg)
(tp-palette-pure 'status-fg)
(tp-set "Ready" 'tp-palette 'status-fg)
define-tp-palette(别名 tp-define-palette)注册 :fg、:bg、:border。颜色 可以是 string、(LIGHT . DARK) 或 (:light LIGHT :dark DARK)。查询和主题工具:
tp-palette-alist 是 palette lookup 的单一来源;使用 define-tp-palette/tp-define-palette 更新它。
- tp-theme-dark-p、tp-theme-light-p、tp-parse-color;
- tp-palette-color、tp-palette-fg-color、tp-palette-bg-color、 tp-palette-border-color;
- tp-palette-has-p 判断 palette 或 key 是否存在;
- tp-palette-p 判断 palette 名;
- tp-palette-fg-p、tp-palette-bg-p、tp-palette-fbg-p、tp-palette-border-p 判断 NAME-* variant;
- tp-palette-pure 去掉 variant suffix;
- tp-palette-show 打开 tp-palette-gallery,按 q 退出。
TP 自带的 recipe 包括:
tp-palette、tp-fg、tp-bg、tp-button、tp-underline、tp-delete、tp-link、 tp-space、tp-headline、tp-action。
显示辅助:
- tp-pop-to-buffer:清空并填充 buffer 后 pop-to-buffer,最后设为只读;
- tp-switch-to-buffer:同样的填充和只读行为,但使用 switch-to-buffer;
- tp-display-buffer-mode:为 TP 显示 buffer 提供 buffer-local 的 q 退出键, 不修改共享 major-mode map。
tp-display-buffer-mode-map 是该 minor mode 的 keymap;默认只绑定 q 到 quit-window。
10. Debug 和公共 record
Debug 入口:
- tp-debug-mode、tp-debug-echo 控制日志行为;
- tp-debug-log 记录格式化消息;
- tp-debug-clear 清空日志;
- tp-debug-show 显示日志;
- tp-with-current-buffer 在指定 Buffer 中执行 body;
- tp-face-properties 列出 face family properties。
以下公共结果类型由 cl-defstruct 提供 predicate 和字段 accessor。调用者不应 直接调用内部 constructor;优先使用前文的 create/query API:
- tp-property-policy:id、normalizer、validator、equality、merge、projector;
- tp-signal:id、committed-value、equality、scope、revision 等运行时字段;
- tp-binding:id、owner、key、compute、last-value、dependencies、lifecycle 等运行时字段;
- tp-surface-plan:key、kind、text、props、children、tags、capability;
- tp-surface-result:plan、client-state 及 owned/consumed 状态;
- tp-lookup-result:property、value、present-p、source、mode、object、 position、overlay。
11. 错误、生命周期和已删除 API
重要错误类型:
- core:tp-unsupported-buffer;
- property:tp-property-error、tp-invalid-property-policy、 tp-invalid-declaration;
- recipe:tp-invalid-layer-definition、tp-unresolved-layer;
- reactive:tp-reactive-error、tp-invalid-signal-scope、tp-disposed-signal、 tp-disposed-binding、tp-binding-cycle;
- surface:tp-surface-error、tp-invalid-surface-plan、tp-duplicate-object-key、 tp-invalid-prepare-context、tp-stale-object、tp-cross-surface-object、 tp-orphan-object、tp-capability-error、tp-stale-mount、 tp-property-conflict、tp-dead-surface、tp-invalid-range-anchor、 tp-invalid-content-range、tp-producer-buffer-mutation、 tp-publication-mismatch、tp-owned-result-error、tp-scope-mismatch。
Live object、binding、surface、anchor 或 signal 被 dispose、unmount 或 Buffer kill 后,继续使用会报相应 stale/disposed 错误。Observer 只在成功 commit 后运行;observer error 会记录在 report,不会回滚已提交 publication。
TP 1.0 已删除并且不应在新代码中使用:
- tp-render.el、tp-stack.el、managed stack mutation;
- tp-text、$variable、inline tp-name/tp-layers/tp-meta runtime database;
- layer-to-buffer registry、scan-driven refresh、managed attach/detach;
- tp-with-batch-updates 和旧 CSS/ECSS runtime integration。
因此,当前正确的组合是:静态声明用 recipe/direct API,已有文字的响应式 属性用 tp-watch,TP 自己拥有的响应式文字用 content surface。
12. 完整模块索引
| 模块 | 当前公共入口 |
|---|---|
| tp-core.el | tp-debug-*、tp-with-current-buffer、tp-intervals、tp-intervals-map、tp-empty-p、tp-plist、tp-text-snapshot |
| tp-style.el | tp-define-property-policy、tp-property-policy、tp-text-property-id、tp-register-text-property、tp-text-declarations、tp-computed、tp-resolve-value、tp-merge-declarations、tp-define-style、tp-style-declarations、tp-undefine-style |
| tp-layer.el | define-tp/tp-define-layer、define-tps/define-tp-group/tp-define-group、layer/group query、tp-layer-reset、tp-undefine-*、tp-describe-layer |
| tp-reactive.el | signal、binding、transaction、variable adapter、counter 和 reset API |
| tp-surface.el | plan/result、object、range anchor、surface lifecycle、scoped update、report、tp-watch |
| tp-ops.el | tp-propertize、tp-apply、tp-set、tp-reset、tp-add、tp-remove、tp-clear、tp-get、tp-at、tp-member |
| tp-search.el | tp-match-、tp-regexp-、tp-search、tp-search-map、tp-forward*、tp-backward*、tp-any-value |
| tp-query.el | tp-lookup、tp-property-change、tp-property-any、tp-property-not-all、tp-with-mutation-policy |
| tp-palette.el | palette definition、theme/color lookup、palette predicates |
| tp-builtins.el | built-in recipes、tp-palette-show、display-buffer macros/mode |
Makefile 提供 make test、make test-shuffled、make doctest、make compile-all WERROR=t、 make checkdoc、make package-lint、make diff-check 和 make benchmark。实现细节 和测试合同以 source、tests/、API semantics 和 architecture 为准。