tp/docs/API-SEMANTICS.md
Kinneyzhang 0d35358e05 refactor(tp)!: implement retained reactive runtime
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.
2026-08-07 00:39:50 +08:00

228 lines
13 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 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 严格区分:
- absentproperty plist 中没有该 key
- present nilproperty 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 自动 disposeglobal 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 metadataTP 不解释其业务含义 |
| `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 reconcileunkeyed 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 fragmentsattachment 保存在 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 不产生 publicationsurface revision 和 buffer modified state 保持不变。
### 8.3 Unmount
`tp-surface-unmount` 释放 surface、objects、bindings、mounts、markers、subscriptions、indexes 和 opaque client state并返回 generic report。content surface 删除自己拥有的 spanproperties 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 的防御性 reportequal/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 的清晰职责。