Replace the legacy managed layer renderer with one independent retained/reactive text runtime. TP now owns exact dependencies, stable objects, marker-backed mounts, property contribution composition, atomic publication, rollback, and direct text-property facades without ECSS or Ebox dependencies.\n\nBREAKING CHANGE: remove tp-render, tp-stack, scan-driven managed layers, inline runtime metadata, TP-owned CSS cascade APIs, and dollar-variable declarations.\n\nVerified: 290/290 ERT, shuffled 290/290 (seed 20260806), 8/8 doctests, WERROR compile-all, checkdoc, package-lint, diff-check, and isolated TP-only load.
13 KiB
TP 1.0 API Semantics
本文记录 TP 1.0 当前公共 API 的 ownership、presence、响应式、retained surface、事务与失败合同。它描述已经实现的行为;目标背景与设计理由见 retained runtime architecture。
1. 产品边界
TP 负责最终 Emacs text property policy/contribution、signals/bindings、stable objects、marker-backed mounts、surface diff、transaction、rollback 和 Buffer publication。
TP 不实现 selector、stylesheet、specificity、origin/importance、CSS cascade layers、CSS-wide values、custom properties 或 Box/Flex/Grid。需要 CSS cascade 的调用者先通过独立 ECSS 获得 final declarations,再交给 TP。
2. 普通值、显式 nil 与 computed source
普通 Elisp value 始终是 literal,包括 function object、keymap command、help-echo callback 和 list。TP 不会因为一个值可调用就执行它。
需要求值的 declaration 必须显式使用:
(tp-computed (lambda () ...))
tp-resolve-value 只执行这种 tagged source。compute result 只 normalize/project 一次,不会被隐式二次调用。compute error 直接终止 candidate transaction。
TP 严格区分:
- absent:property plist 中没有该 key;
- present nil:property key 存在,value 为
nil。
tp-member 和底层 contribution ledger 使用 presence-aware 语义。properties mount 的显式 nil contribution 可以覆盖 baseline,但 unmount 仍只撤销 TP 自己拥有的 contribution。
3. Property policy 与 direct declarations
tp-define-property-policy 为一个 canonical native property id 注册:
- normalizer;
- validator;
- equality;
- merge;
- projector。
注册是原子的:无效 options 或 function 会报 tp-invalid-property-policy,旧 definition 保持不变。
tp-register-text-property 为原生 Emacs property 建立默认 policy,并返回 policy record。tp-text-property-id 把原生 property 映射到 canonical text/PROPERTY id;tp-text-declarations 把普通 property plist 转换为 canonical declarations。
tp-merge-declarations 按输入顺序合并 direct declaration groups,并防御性复制 caller-owned value。它只做 TP contribution composition,不实现 CSS winner selection。
tp-define-style、tp-style-declarations 和 tp-undefine-style 管理 named direct declarations。registry getter 返回防御性副本。
4. Declaration recipes
define-tp 定义一个返回 native property plist 的 recipe;define-tps 定义一组有序 recipe elements。tp-define-layer、define-tp-group 与 tp-define-group 是同一静态 declaration workflow 的命名入口。
Recipe 可以是静态或参数化的,可以组合其他 recipes。展开结果经过同一 direct property policy/projector。Recipe application 不建立 live identity,不写 tp-name、tp-layers 或 tp-meta。
旧 $variable syntax 会报 tp-invalid-layer-definition。响应式值必须使用 tp-computed 加 signal/binding,不能建立第二套 watcher runtime。
Recipe/group definition 与 redefinition 是原子的:definition body、generated named elements 或 compiled style 任一步失败时,不留下半个新 definition,已有 definition 保持可用。
5. 一次性 façade
5.1 tp-propertize
(tp-propertize STRING DECLARATIONS)
返回新的 propertized string,不修改输入 STRING,不创建 object、binding、anchor、mount 或 surface。
5.2 tp-apply
(tp-apply BUFFER START END DECLARATIONS)
只修改 BUFFER 的 [START, END) 文本属性,不替换文字,成功返回 (START . END)。无效 buffer 不会退回 current buffer;无效 range 直接报错。
5.3 Direct operations
tp-set、tp-reset、tp-add、tp-remove、tp-clear、tp-get、tp-at 与 tp-member 保留既有 string/buffer 调用形状,但统一经过 direct property resolution。
- whole-string
tp-set/tp-reset/tp-add返回新 string; - 带 range 的 string 操作按各函数 docstring 的 mutation contract 执行;
- buffer 坐标使用 Emacs 原生 1-based position;
- string 坐标使用 0-based index;
- direct buffer write 遵循 Emacs read-only、undo 和 modified semantics。
Search/match/regexp/navigation/query API 继续委托 Emacs 原生 text-property interval 语义,不创建 retained runtime。
6. Signals 与 bindings
tp-signal-create 返回 global 或 buffer-scoped signal。tp-signal-read 在当前 binding computation 中登记依赖;tp-signal-peek 只读值而不登记依赖;tp-signal-set 在 transaction 中设置 candidate value。
相等写入按 signal equality 返回 no-op,不 invalidates subscribers。buffer-scoped signal 随 buffer kill 自动 dispose;global signal 使用 tp-signal-dispose 显式释放。
tp-bind 的 identity 是 owner 加 caller-namespaced key。相同 owner/key 幂等复用 binding。binding 保存 last successful value、dynamic dependencies、dirty/revision state 和 lifecycle policy。
tp-binding-read 读取 memoized binding value并登记 binding-to-binding dependency。每次 recompute 成功后,本次实际读取集合替换旧 dependencies;条件分支因此自动断开旧 source。cycle 报 tp-binding-cycle 并包含 dependency path。
删除 owner 会释放 bindings、subscriptions 和下游 edges。tp-reactive-counters 只读报告 invalidated、recomputed、skipped、subscription-added 和 subscription-removed,用于结构性性能验收,不暴露内部 hash tables。
7. Surface plan 与 object identity
tp-surface-plan-create 的公共字段是:
| Field | Contract |
|---|---|
key |
sibling-local stable key;同一 parent 下不可重复 |
kind |
opaque comparable discriminator |
text |
optional plain/propertized string leaf |
props |
final direct Emacs properties |
children |
ordered child plans |
tags |
opaque indexed metadata,TP 不解释其业务含义 |
capability |
content 或 properties |
Plan 不允许 marker、buffer position、patch op、producer closure 或 binding closure。Constructor 防御性复制 string、props、children 与 tags,使 caller 后续 mutation 不改变 committed plan。
producer 在 prepare 阶段接收 context,并在产生 plan 前调用:
(tp-object-ensure CONTEXT PARENT KEY KIND)
Object identity 只在所属 surface 中有效。显式 key 按 parent/key/kind reconcile;unkeyed object 按 position/kind reconcile。duplicate sibling key、stale parent、orphan object 或 cross-surface handle 在 prepare 中失败。
Candidate object 只有成功 publication 后才变为 live。失败 candidate handle 必须不可解析。tp-object-resolve 按 surface/key path 查询 live handle,不创建 identity。
无可见字符但需要保留的 logical object 使用 tp-object-retain。一个 logical object 可以通过 tp-object-attach-fragment 关联多个离散 plan fragments;attachment 保存在 prepare side state,不进入 plan。
8. Surface lifecycle
8.1 Materialize
(tp-surface-materialize-string PLAN-OR-PRODUCER)
以 ephemeral prepare context 生成 propertized string,不建立 live surface。函数返回前释放 candidate objects、bindings、subscriptions 和 anchors。
8.2 Mount/update
(tp-surface-mount BUFFER PLAN-OR-PRODUCER OPTIONS)
(tp-surface-update SURFACE PLAN-OR-PRODUCER)
(tp-surface-update-scoped SURFACE OBJECTS PLAN-OR-PRODUCER OPTIONS)
Mount options 支持 :capability、:start、:end、:inhibit-read-only、:client-state 和 :observers。
content surface 拥有其 span 的 text 和 properties,可以插入、删除、替换或移动输出。properties surface 只能贡献声明的 properties,不能替换 host text。
tp-surface-update-scoped 仍接收完整 candidate。TP 从 object-to-mount index 得到授权范围,验证 candidate 没有改变范围外输出,再在同一 transaction 发布。默认 mismatch 报 tp-scope-mismatch;(:on-mismatch root) 显式允许 full-root fallback。
相等 candidate 不产生 publication,surface revision 和 buffer modified state 保持不变。
8.3 Unmount
tp-surface-unmount 释放 surface、objects、bindings、mounts、markers、subscriptions、indexes 和 opaque client state,并返回 generic report。content surface 删除自己拥有的 span;properties surface 只撤销仍由 TP 拥有的 contributions。
kill-buffer cleanup 以 buffer 死亡为权威结果,释放 runtime state,不尝试复活 buffer。
9. Range anchors 与 property conflicts
tp-range-anchor-create 接受 buffer、start/end、marker insertion policy 与 stale/shorten/remove boundary policy,返回 opaque handle。raw marker 和 position 不进入 surface plan。
producer 使用 tp-object-attach-range 把 object 绑定到 anchor。一个 properties surface 的重叠 mounts 通过 property policy 合成 contributions。
TP 为每个 interval 保存:
- host baseline presence/value;
- ordered TP contributions;
- last published presence/value。
如果当前 host value 与 TP last published value 不同,update 报 tp-property-conflict,不会覆盖外部值。tp-range-rebase 显式把当前 host state 接受为新 baseline。Unmount 只在当前值仍等于 last published value 时恢复 baseline,否则保留 host value并在 report 中列出 conflict。
10. tp-watch
(tp-watch BUFFER START END COMPUTE)
COMPUTE 返回 native direct declarations。tp-watch 组合 range anchor、properties surface、stable object 和 binding,返回 underlying surface handle。COMPUTE 中读取的 signals/bindings进入正常 dependency graph;更新与 unmount 使用同一 conflict和rollback合同。
11. Transactions
tp-with-transaction 的顺序是:
- 保存 candidate signal writes 并去重 dirty bindings;
- 为所有实际受影响 surfaces 建立 prepare contexts;
- 运行 binding graph 与 producers;
- 校验 object、plan、capability、range、conflict 与 lifecycle;
- 为所有 surfaces 准备 text/property operations 与 inverse journals;
- 按稳定 surface id publish;
- 原子切换 signals、bindings、plans、mount/index、client state 和 revisions;
- 全部成功后运行 observers。
嵌套 transaction 加入最外层。一个 global signal 可以原子触达多个 buffers;任一 surface 失败时,已发布 surfaces 和 source/binding state 全部回滚。
tp-transaction-participate 允许 client side state 在 surfaces 发布后、source commit 前加入同一 rollback boundary。participant key 在一个 outer transaction 中必须唯一。它不是 observer;失败会回滚 transaction。Observer failure 只记录,不回滚已提交结果。
12. Diagnostics 与 reports
tp-surface-report 返回最近一次成功 publication 的防御性 report;equal/no-op update 不替换 report。字段包括 transaction/surface/revision、candidate source writes、binding counters、object reconcile counts、text/property operation counts、touched characters、scope/full-root 和 failure-related slots。
tp-surface-inspect 返回 surface id、buffer、capability、revision、object/mount count、opaque client state 与 report。tp-surface-at-point 从 side index 返回 mounted objects;它不扫描显示文本寻找 identity。tp-object-mounts 返回 defensive numeric range/tag snapshots,不暴露 live markers。
13. Error ownership
主要错误类型:
- property/declaration:
tp-property-error、tp-invalid-property-policy、tp-invalid-declaration、tp-invalid-layer-definition、tp-unresolved-layer; - reactive:
tp-reactive-error、tp-invalid-signal-scope、tp-disposed-signal、tp-disposed-binding、tp-binding-cycle; - retained surface:
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-scope-mismatch。
内部 computation 不吞错或返回貌似合理的 fallback。只有用户入口和 batch test runner等外层边界负责把错误转换为展示信息。
14. 1.0 删除项
TP 1.0 删除了不能诚实映射到统一 retained runtime 的 0.3 managed behavior:
tp-render.el和tp-stack.el;- stack push/pop/move/hide/show/merge/flatten workflow;
tp-text双向内容替换;$variabledeclaration syntax;- layer-to-buffer registry、buffer-list/identity scan refresh;
- managed attach/detach/diagnostics/transaction;
- 以
tp-name、tp-layers、tp-meta作为字符上权威 runtime database 的机制。
TP 不提供 hidden compatibility engine,也不根据文本是否含旧 metadata 自动切换执行语义。静态 recipe、tp-watch 和 retained surface 分别承担复用声明、已有文本响应式属性与 retained content 的清晰职责。