tp/README_CN.md

236 lines
13 KiB
Markdown
Raw Permalink 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
TP 2.0 是一个可独立使用的 Emacs retained/reactive text runtime。它把声明式属性、响应式数据和稳定文本对象投影到 string 与 buffer并拥有文本属性 contribution 合成、精确依赖追踪、保留式身份、marker-backed mount、diff、事务、回滚和最终 Buffer publication。
TP 不依赖 Ebox 或 ECSS也不实现 CSS selector、stylesheet、specificity、cascade winner、Box、Flex、Grid、测量或布局。需要 CSS 的调用者可以先用 ECSS 算出最终 declarations再交给 TP 发布TP 自身只理解 Emacs 文本属性和通用 retained text surface。
英文文档:[README.md](README.md)。
完整公共 API 参考:[API-REFERENCE.md](docs/API-REFERENCE.md)。本文负责概念
和快速开始API 参考按当前 TP 2.0 源码列出入口、参数语义、返回值和用法。
## 运行要求
- Emacs 28.1 或更高版本。
- 没有第三方运行时依赖。
```elisp
(add-to-list 'load-path "/path/to/tp")
(require 'tp)
```
## 选择最小的公共入口
| 需求 | API | 是否建立 live runtime |
| --- | --- | --- |
| 返回带属性的字符串 | `tp-propertize` | 否 |
| 一次性装饰已有 buffer 范围 | `tp-apply` | 否 |
| 响应式装饰 host-owned 文本 | `tp-watch` | 是,`properties` capability |
| 管理 retained text content | `tp-surface-mount` / `tp-surface-update` | 是,`content` capability |
| 检查或卸载 retained publication | `tp-surface-report` / `tp-surface-inspect` / `tp-surface-unmount` | 是 |
一次性 API 和 retained API 使用相同的 property policy 与 projection 语义。一次性调用不会创建 object、binding、marker、subscription 或 surface state。
## 静态文本属性
```elisp
(let* ((callback (lambda (_window _object _position) "Open"))
(text
(tp-propertize
"Hello"
(list 'face '(:foreground "white" :background "navy")
'help-echo callback
'keymap nil))))
text)
```
显式 `nil` 和属性不存在是两种状态。上例中的 `keymap` 存在且值为 `nil`。函数对象始终是 literal data因此 TP 会保留 `callback`,不会调用它。
一次性修改已有范围而不改变文字:
```elisp
(with-current-buffer (get-buffer-create "*tp-demo*")
(erase-buffer)
(insert "abcdef")
(tp-apply (current-buffer) 2 5 '(face italic)))
```
原有的 `tp-set`、`tp-reset`、`tp-add`、`tp-remove`、`tp-clear`、`tp-get`、`tp-at`、`tp-member`、match、regexp、search、navigation、interval 和 native query API 继续作为普通文本属性操作的直接 façade。
## 可复用声明配方
`define-tp``define-tps` 定义可以展开为直接 Emacs 属性的复用配方。它们只是 definition-time convenience不是 live mounted layer也不会把 identity 或 provenance 写入显示文本。
```elisp
(define-tp link-style (foreground)
`(face (:foreground ,foreground :weight bold)
mouse-face highlight
help-echo "Open item"))
(tp-set "Project" '(link-style "#58a6ff"))
```
属性值需要求值时使用 `tp-computed`;任何普通函数对象仍然按 literal 保存。
```elisp
(defvar my-height 1.2)
(define-tp sized-label ()
`(face ,(tp-computed (lambda () (list :height my-height)))))
```
computed function 在当前 binding/prepare context 中执行,因此其中的 `tp-signal-read``tp-binding-read` 会建立精确依赖。返回值只 normalize/project 一次,然后作为 literal data 使用。
## 响应式已有文本
`tp-watch` 装饰 marker-backed host range但不拥有或替换其中的文字
```elisp
(let ((online (tp-signal-create nil)))
(with-current-buffer (get-buffer-create "*tp-status*")
(erase-buffer)
(insert "offline")
(tp-watch
(current-buffer) 1 8
(lambda ()
(list 'face
(list :foreground
(if (tp-signal-read online) "green" "red")))))))
```
signal 在被读取时记录真正依赖它的 binding。修改一个 signal 只会使它的订阅者失效;条件分支改变后,不再读取的 source 会自动取消订阅。相等写入是 no-op`tp-with-transaction` 内的重复写入会让每个受影响 binding 最多重算一次。
`tp-watch` 返回底层 surface handle可以交给 `tp-surface-inspect`、`tp-surface-report` 或 `tp-surface-unmount`
## Retained content
retained producer 接收 prepare context在生成输出前取得稳定 object、安装 object-local binding并返回纯 surface plan
```elisp
(let* ((status (tp-signal-create "ready"))
(producer
(lambda (context)
(let* ((object (tp-object-ensure context nil 'status 'text))
(binding
(tp-bind object '(demo . status)
(lambda () (tp-signal-read status)))))
(tp-surface-plan-create
:key 'status
:kind 'text
:text (tp-binding-read binding)
:props '(face bold)
:capability 'content))))
(buffer (get-buffer-create "*tp-retained*"))
(surface
(tp-surface-mount buffer producer '(:capability content))))
(tp-signal-set status "done")
(tp-surface-report surface))
```
plan 字段只有 `key`、`kind`、`text`、`props`、`children`、`tags` 和 `capability`,不包含 marker、buffer position、patch operation、producer closure 或 client continuation。普通 constructor 会防御性复制调用者持有的 string 和 property datacandidate-local producer 只有在转移所有嵌套值且不再暴露它们时,才能使用显式的 `-owned` plan/result constructor。Owned result 必须绑定当前 active prepare context并且只消费一次。
稳定身份只在一个 surface 内有效。`tp-object-ensure` 按 parent、sibling key 和 kind reconcile`tp-object-resolve` 按 key path 返回 live opaque handle。`tp-surface-update-scoped` 接收完整 candidate但只授权一个或多个 retained object 当前 mount 范围内的输出变化;除非调用者显式选择 root fallback否则越界变化会直接失败。
`tp-surface-materialize-string` 使用相同 producer/plan 语义生成一次性 string但不建立 live surface。返回前会释放 ephemeral object、binding、subscription 和 anchor。
## Host range 与属性所有权
`properties` capability 使用 opaque range anchor。producer 通过 `tp-range-anchor-create` 创建 anchor再用 `tp-object-attach-range` 挂到 object 上。marker 跟随 host edit而 plan 仍然不携带位置。
TP 为每个 property interval 保存 host baseline 和各个 TP contribution。重叠 contribution 通过已注册的 property policy 合成。如果外部代码在 TP publication 后修改同名属性,下一次更新会报告 `tp-property-conflict`,不会覆盖外部值。`tp-range-rebase` 显式接受当前 host 值作为新 baseline。unmount 只恢复仍由 TP 拥有的值,并保留冲突的 host edit。
## 事务与失败语义
`tp-with-transaction` 批量处理 signal writes 和所有实际受影响的 surfaces。TP 先 prepare 所有 candidate再按稳定顺序 publish。compute、validation、buffer write、marker/index publication 或 transaction participant 任一步失败signal values、binding values/dependencies、文本、属性、marker、index、plan、client state、revision 和 previous report 都会一起恢复。
observer 只在成功提交之后运行observer failure 只记录,不回滚已完成的 transaction。publication 过程中被 kill 的 buffer 保持死亡rollback 不会把它重新创建。
TP 2.0 从 transaction-scoped batch 的 exact entries、同一个冻结 participant
vector 和 candidate-bound final accept 驱动唯一 live publicationjournal、
surface snapshot 与 change group 仍只保留一份。transaction participant 统一通过
`tp-transaction-participate-v2` 注册,不再提供备用 transaction writer 或 runtime
route switch。generic opaque authority marker 在 final accept 前
完成固定上界与 whitelist 校验partial apply 或 accept failure 时先逆序恢复
marker再执行普通 rollback。`tp-with-transaction` 仍返回 body result内部
outcome 仅作为只读 side-channel evidence。
ETAF 集成复用同一事务边界,只注册一个 opaque transaction participant。ETAF
先准备 immutable generation 和 Ebox candidate再由 TP 按固定顺序完成
participant publish、TP final accept 和 post-accept cleanup。participant failure
会恢复旧 generation 与 client stateaccept 之后的 observer failure 只保留为
diagnostic。ETAF 的 Runtime fixed-point guard 会在一次 flush 中记录每个
effect 的 input/version tuple重复或超过图规模上限时停止循环不会静默自旋。
## Property policy
`tp-define-property-policy` 为一个最终 Emacs text property 注册通用语义:
- normalization 与 validation
- no-op 判断使用的 equality
- contribution merge
- 到最终 property value 的 projection
- 包括 present `nil` 在内的显式 presence。
`tp-register-text-property` 为原生 property 建立默认 policy。`face` contribution 使用 merge 语义,其他属性使用各自注册的 policy。`tp-merge-declarations`、`tp-define-style` 和 `tp-style-declarations` 只处理 direct declarations不实现 CSS cascade。
## 公共 runtime API 家族
| 家族 | 主要公共 API |
| --- | --- |
| Core inspection 与 debug | `tp-debug-*`、`tp-intervals`、`tp-intervals-map`、`tp-plist`、`tp-text-snapshot`、`tp-empty-p` |
| Property policy 与 declaration | `tp-define-property-policy`、`tp-register-text-property`、`tp-text-declarations`、`tp-computed`、`tp-resolve-value`、`tp-merge-declarations`、`tp-define-style`、`tp-style-declarations` |
| 静态 recipe | `define-tp`/`tp-define-layer`、`define-tps`/`define-tp-group`/`tp-define-group`、layer/group 查询、undefine/reset/describe |
| Signal 与 binding | signal create/read/peek/set/dispose、binding install/read/dispose、`tp-variable-signal`、`tp-with-transaction`、`tp-transaction-participate-v2`、只读 `tp-transaction-active-p`、`tp-runtime-manifest`、counter/reset |
| Object 与 plan | plan/result constructor、`tp-object-ensure`、retain/reuse、fragment/content-range attach、resolve、mounted/mounts |
| Host range | `tp-range-anchor-create`、`tp-range-anchor-live-p`、`tp-object-attach-range`、`tp-range-rebase` |
| Surface | mount/update/scoped update、materialize、live/revision/client-state、at-point、report/report-summary/inspect、unmount |
| Direct façade | `tp-propertize`、`tp-apply`、`tp-watch`、`tp-set`、`tp-reset`、`tp-add`、`tp-remove`、`tp-clear`、`tp-get`、`tp-at`、`tp-member` |
| Search 与 navigation | `tp-match-*`、`tp-regexp-*`、`tp-search`、`tp-search-map`、`tp-forward*`、`tp-backward*` |
| Native query 与 mutation | `tp-lookup`、`tp-property-change`、`tp-property-any`、`tp-property-not-all`、`tp-with-mutation-policy` |
| Palette 与 display helper | palette definition/lookup、built-in recipe、`tp-palette-show`、`tp-pop-to-buffer`、`tp-switch-to-buffer`、`tp-display-buffer-mode` |
完整签名、返回值、示例和模块索引见 [API 参考](docs/API-REFERENCE.md)
ownership、生命周期、错误和返回值合同见 [API semantics](docs/API-SEMANTICS.md)
模块边界和完整事务流程见 [architecture](docs/ARCHITECTURE.md)。
## TP 1.0 迁移
TP 1.0 直接删除 0.3 的 managed stack/renderer runtime不使用隐藏兼容分支。删除的行为包括 `tp-render.el`、`tp-stack.el`、stack mutation APIs、`tp-text`、`$variable` declarations、layer-to-buffer registry、scan-driven refresh、managed attach/detach/diagnostics以及以 `tp-name`/`tp-layers`/`tp-meta` 作为权威 runtime storage 的机制。
## TP 2.0 transaction 迁移
TP 2.0 删除 v1 transaction participant facade 和 legacy execution route。把
`(tp-transaction-participate KEY PUBLISH ROLLBACK)` 精确替换为
`(tp-transaction-participate-v2 :key KEY :stage PUBLISH :rollback ROLLBACK)`
检查 `tp-runtime-manifest` 的调用方必须要求 `tp-transaction-protocol-v2`manifest
不再发布 route-selection 和 v1-adapter 字段。
可复用静态声明使用 direct recipe已有文本的响应式属性使用 `tp-watch`;响应式文字或结构化 UI 使用 retained content surface。TP 不会自动扫描历史 propertized text 来重建 runtime identity。
## 示例
- [静态属性](examples/static-properties.el)
- [响应式状态](examples/reactive-status.el)
- [Retained dashboard](examples/retained-dashboard.el)
- [诊断装饰](examples/diagnostic-decoration.el)
这些示例只调用 TP public API并在没有 Ebox 或 ECSS load-path 的环境中测试。
## 验证
```sh
make test
make test-shuffled SHUFFLE_SEED=20260806
make doctest
make compile-all WERROR=t
make checkdoc
make package-lint
make diff-check
```
结构测试还会证明:正常 signal/surface update 不扫描 `buffer-list`,不从显示文本搜索 identity相等结果不会发布新 revisionunmount/kill cleanup 会释放 subscriptions 和 marker-backed runtime state。
## 许可证
GPL-3.0-or-later详见 [LICENSE](LICENSE)。