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.
228 lines
13 KiB
Markdown
228 lines
13 KiB
Markdown
# TP 1.0 API Semantics
|
||
|
||
本文记录 TP 1.0 当前公共 API 的 ownership、presence、响应式、retained surface、事务与失败合同。它描述已经实现的行为;目标背景与设计理由见 [retained runtime architecture](retained-runtime-target-architecture.md)。
|
||
|
||
## 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 必须显式使用:
|
||
|
||
```elisp
|
||
(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`
|
||
|
||
```elisp
|
||
(tp-propertize STRING DECLARATIONS)
|
||
```
|
||
|
||
返回新的 propertized string,不修改输入 STRING,不创建 object、binding、anchor、mount 或 surface。
|
||
|
||
### 5.2 `tp-apply`
|
||
|
||
```elisp
|
||
(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 前调用:
|
||
|
||
```elisp
|
||
(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
|
||
|
||
```elisp
|
||
(tp-surface-materialize-string PLAN-OR-PRODUCER)
|
||
```
|
||
|
||
以 ephemeral prepare context 生成 propertized string,不建立 live surface。函数返回前释放 candidate objects、bindings、subscriptions 和 anchors。
|
||
|
||
### 8.2 Mount/update
|
||
|
||
```elisp
|
||
(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`
|
||
|
||
```elisp
|
||
(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` 的顺序是:
|
||
|
||
1. 保存 candidate signal writes 并去重 dirty bindings;
|
||
2. 为所有实际受影响 surfaces 建立 prepare contexts;
|
||
3. 运行 binding graph 与 producers;
|
||
4. 校验 object、plan、capability、range、conflict 与 lifecycle;
|
||
5. 为所有 surfaces 准备 text/property operations 与 inverse journals;
|
||
6. 按稳定 surface id publish;
|
||
7. 原子切换 signals、bindings、plans、mount/index、client state 和 revisions;
|
||
8. 全部成功后运行 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` 双向内容替换;
|
||
- `$variable` declaration 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 的清晰职责。
|