Some checks are pending
CI / test (push) Waiting to run
CI / native-build (macos-latest) (push) Waiting to run
CI / native-build (ubuntu-latest) (push) Waiting to run
CI / native-build (windows-latest) (push) Waiting to run
CI / native-msrv (macos-latest) (push) Waiting to run
CI / native-msrv (ubuntu-latest) (push) Waiting to run
CI / native-msrv (windows-latest) (push) Waiting to run
298 lines
16 KiB
Markdown
298 lines
16 KiB
Markdown
# Ebox 公共 API 参考
|
||
|
||
[English](ebox-api-reference.en.md)
|
||
|
||
本文描述独立 Ebox 包当前的公共边界,并把普通 author 路径与框架集成使用的
|
||
evaluated typed API 分开。以 `ebox--` 开头的名称都是私有实现。
|
||
|
||
Ebox 需要 Emacs 29.1 或更高版本、ECSS 和 TP:
|
||
|
||
```elisp
|
||
(require 'ebox)
|
||
```
|
||
|
||
## 1. Author 语法
|
||
|
||
`ebox-build` 只接受以下 author 入口:
|
||
|
||
```text
|
||
STRING
|
||
(text PROPERTY VALUE ... STRING)
|
||
(box PROPERTY VALUE ... CHILD ...)
|
||
(row PROPERTY VALUE ... CHILD ...)
|
||
(column PROPERTY VALUE ... CHILD ...)
|
||
(flex PROPERTY VALUE ... CHILD ...)
|
||
(grid PROPERTY VALUE ... CHILD ...)
|
||
```
|
||
|
||
`STRING` 是 `(text STRING)` 的简写。Text form 必须且只能有一个字符串 payload。
|
||
每种 Box form 都接受零个或多个直接嵌套的 Text/Box 子节点。form 名称选择 Normal、
|
||
Row、Column、Flex 或 Grid 子布局;author 不通过 property 传入布局选择器。
|
||
|
||
`ebox-build` 返回一个不透明的 `CanonicalEboxInput`,以原子整体携带有序 canonical
|
||
forest 和拥有其 author facts 的 source generation。普通 author 代码只把这个值原样
|
||
传给 Ebox 渲染/发布函数,不取出 node,也不构造 source index。
|
||
|
||
```elisp
|
||
(ebox-build
|
||
'(column :padding (1 2)
|
||
(box :id "status" :color "#166534" "Ready")
|
||
(row :item-gap 1
|
||
(box "Left")
|
||
(box "Right"))))
|
||
```
|
||
|
||
author source field 是 `:key`、`:class` 与 `:id`。`:outer` 可取 `inline` 或
|
||
`block`,控制 Box 如何参与父布局。
|
||
|
||
### 布局拥有的 property
|
||
|
||
| Form 或关系 | Property |
|
||
| --- | --- |
|
||
| `row`、`column` | `:item-gap`、`:cross-align` |
|
||
| `flex` | `:flex-direction`、`:flex-wrap`、`:flex-flow`、`:justify-content`、`:align-items`、`:align-content`、`:gap`、`:row-gap`、`:column-gap` |
|
||
| `flex` 的直接子 Box | `:order`、`:flex`、`:flex-grow`、`:flex-shrink`、`:flex-basis`、`:align-self` |
|
||
| `grid` | `:grid-template-columns`、`:grid-template-rows`、`:grid-auto-columns`、`:grid-auto-rows`、`:grid-auto-flow`、`:justify-items`、`:justify-content`、`:align-items`、`:align-content`、`:gap`、`:row-gap`、`:column-gap` |
|
||
| `grid` 的直接子 Box | `:order`、`:grid-column`、`:grid-row`、`:grid-column-span`、`:grid-row-span`、`:align-self` |
|
||
|
||
participation property 就是普通子 Box property,不会创建另一种公共节点或 wrapper。
|
||
|
||
### 当前常用 property
|
||
|
||
| 范围 | 当前输入 |
|
||
| --- | --- |
|
||
| 尺寸 | `:box-sizing`、`:width`、`:min-width`、`:max-width`、`:height`、`:min-height`、`:max-height` |
|
||
| 边缘 | `:padding`、`:margin`、`:border`、各 side shorthand 与 longhand |
|
||
| 绘制 | `:color`、`:background-color`、精确 alias `:bgcolor`、border color、`:visibility` |
|
||
| 文本布局 | `:text-align`、`:wrap-mode`(`word`、`char`、`kp`、`none`) |
|
||
| Overflow | `:overflow`(`visible`、`hidden`、`scroll`) |
|
||
|
||
普通横向数字表示字符列,单元素横向 list 表示像素;纵向数字表示行数。Border
|
||
width 是例外,始终使用非负整数像素,因此写 `(1 solid "#687386")`,不写
|
||
`((1) solid "#687386")`。`(viewport)` 与 `(viewport-height)` 使用当前 render context。property 验证属于
|
||
Ebox;context/value 组合无效时会直接报错。
|
||
|
||
## 2. Evaluated typed construction
|
||
|
||
已经拥有 author parsing 的框架可以使用 evaluated 集成 API;它不是第二套 author
|
||
语法。一个 `ebox-source-builder` 拥有完整 source generation;每个 typed node 都携带
|
||
该 builder 返回的 handle,以及由同一份 normalized declarations 投影出的 node-owned
|
||
facts。最后用一个 `CanonicalEboxInput` 同时传递 forest 与封存的 source index:
|
||
|
||
```elisp
|
||
(let* ((builder (ebox-source-builder-create))
|
||
(root-declarations nil)
|
||
(left-declarations nil)
|
||
(right-declarations nil)
|
||
(root-handle
|
||
(ebox-source-builder-bind
|
||
builder :declarations root-declarations))
|
||
(left-handle
|
||
(ebox-source-builder-bind
|
||
builder :declarations left-declarations))
|
||
(right-handle
|
||
(ebox-source-builder-bind
|
||
builder :declarations right-declarations))
|
||
(left
|
||
(ebox-text-create
|
||
:value "Left"
|
||
:source-handle left-handle
|
||
:owned-facts
|
||
(ebox-canonical-facts-from-declarations
|
||
'text left-declarations)))
|
||
(right
|
||
(ebox-text-create
|
||
:value "Right"
|
||
:source-handle right-handle
|
||
:owned-facts
|
||
(ebox-canonical-facts-from-declarations
|
||
'text right-declarations)))
|
||
(root
|
||
(ebox-box-create
|
||
:layout (ebox-row-layout-create
|
||
:item-gap 1 :cross-align 'center)
|
||
:children (list left right)
|
||
:source-handle root-handle
|
||
:owned-facts
|
||
(ebox-canonical-facts-from-declarations
|
||
'row root-declarations))))
|
||
(ebox-canonical-input-create
|
||
(list root)
|
||
(ebox-source-builder-finish builder)))
|
||
```
|
||
|
||
- `ebox-source-builder-create` 开始且只开始一次 mutable source assembly;
|
||
- `ebox-source-builder-bind` 保存 normalized source facts,并返回只含 identity 的
|
||
opaque handle;
|
||
- `ebox-source-builder-finish` 分离并封存 builder 的 immutable fact index;封存后
|
||
builder 拒绝继续修改;
|
||
- `ebox-canonical-facts-from-declarations` 从传给 source builder 的同一份 normalized
|
||
declarations 投影 node-owned facts;
|
||
- `ebox-text-create` 要求 `:value`、`:source-handle` 与 `:owned-facts`;
|
||
- `ebox-normal-layout-create` 构造 Normal LayoutConfig;
|
||
- `ebox-row-layout-create` 与 `ebox-column-layout-create` 接受
|
||
`:item-gap` 和 `:cross-align`;
|
||
- `ebox-flex-layout-create` 构造经过验证的 Flex LayoutConfig;
|
||
- `ebox-grid-layout-create` 构造经过验证的 Grid LayoutConfig;
|
||
- `ebox-box-create` 要求 `:layout`、`:source-handle` 与 `:owned-facts`,并接受 typed
|
||
node 的 `:children` list;
|
||
- `ebox-canonical-input-create` 把有序 node forest 与拥有其中全部 handle 的唯一
|
||
source index 组合为一个值;这个边界验证精确 handle 集合,并从 forest 的最终
|
||
preorder 封存 source order,不依赖 bottom-up 构造顺序。
|
||
- `ebox-canonical-input-root-host-ref` 返回经过验证的 single-root input 的分离发布
|
||
地址,不暴露其中的 source handle;
|
||
- `ebox-canonical-input-roots` 返回 exact top-level roots 的 shallow、只读副本,
|
||
不会把 source facts 导入另一个 candidate;
|
||
- `ebox-canonical-input-import-roots` 只接受该 input 中 exact、唯一的 top-level
|
||
roots 与一个 open source builder;它只导入选中 subtree 的 facts,保留 node、
|
||
source-handle 与 immutable record identity,绝不接管未选 root 的 facts;
|
||
- `ebox-canonical-input-equal-p` 比较完整 canonical forest、Range anchor、source
|
||
identity 和 immutable source facts,用于 no-op 证明。
|
||
|
||
这些 source builder、fact/input constructor 与 typed node/layout constructor 只属于
|
||
evaluated 集成契约;其中的 field 不是 author property,也不是另一套 DSL。
|
||
|
||
typed 模块还公开 `ebox-node-kind`、`ebox-text-node-p`、`ebox-box-node-p`、
|
||
`ebox-node-source-handle`、`ebox-text-node-value`、`ebox-box-node-layout`、
|
||
`ebox-box-node-children` 和 `ebox-box-node-range-anchors`。下面的 facade 清单重点列出
|
||
应用入口,因此不重复这些 inspection function。
|
||
|
||
## 3. 渲染与 retained 发布
|
||
|
||
| 函数 | 契约 |
|
||
| --- | --- |
|
||
| `ebox-render` | 接收 single-root `CanonicalEboxInput`,返回带属性字符串,不发布 live buffer。 |
|
||
| `ebox-render-to-buffer` | 接收 single-root `CanonicalEboxInput`,挂载 retained TP surface,启用 `ebox-buffer-mode` 并返回 buffer;可选 plist 只接受 `:observer FUNCTION`。 |
|
||
| `ebox-unmount-buffer` | 从 live buffer 释放 mounted Ebox/TP surface、observer、retained indexes 与 runtime authority。 |
|
||
| `ebox-display-buffer` | 接收 single-root `CanonicalEboxInput`,走 retained 路径渲染,删除其他 window 后切换到结果。 |
|
||
| `ebox-commit` | 原子发布 single-root `CanonicalEboxInput` 或 `ebox-candidate-begin` 返回的 one-shot logical candidate。 |
|
||
| `ebox-buffer-set-observer` | 给已经 mounted 的 buffer 设置或移除一个 function observer。 |
|
||
| `ebox-buffer-update-report` | 返回最近一次成功更新报告的防御性副本。 |
|
||
| `ebox-rerender-buffer-with-context` | 应用显式 viewport width 与可选 height,同时保留 identity。 |
|
||
| `ebox-viewport-window-width` | 返回 Ebox 对 window 使用的 display-safe 像素宽度。 |
|
||
|
||
`ebox-commit` 接受可选 framework publish/rollback callback。它们加入现有原子发布;
|
||
失败会恢复旧 buffer、surface、runtime state 与最近一次成功报告。
|
||
|
||
每个公共 Ebox publication 都拥有自己的 TP transaction。在已经 active 的外层 TP
|
||
transaction 中调用 mount、commit、viewport、region、selector、batch 或 TP-backed
|
||
scroll publication,会在 Ebox mutation 前被拒绝。
|
||
|
||
观察是可选只读边界,不拥有 transaction 权限。observer 签名是
|
||
`(OBSERVER BUFFER REPORT)`。一次 TP publication 被接受且 Ebox finalization 全部完成后,
|
||
observer 按顺序收到两份防御性 flat report:先 TP,后 Ebox。两者共享从 TP transaction
|
||
id 得到的 `:correlation-id`。Ebox stage 是 `mount`、`commit`、`viewport`、
|
||
`region`、`selector`、`batch` 和 `scroll`。嵌套公共调用复用最外层 operation,
|
||
因此多匹配 selector 或显式 batch 仍只发送一对 report。没有 TP surface publication
|
||
的 no-op 与 native-window scroll 不发送 report。observer error 在 accept 后被包含,
|
||
不能回滚渲染。
|
||
|
||
需要观察首次 mount 时,把 `:observer` 传给 `ebox-render-to-buffer`。mount report 只
|
||
瞬时发送;有无 observer 时 `ebox-buffer-update-report` 都仍为 nil。后续通过
|
||
`ebox-buffer-set-observer` 替换 observer,传 nil 时移除并从 TP surface 解绑 bridge。
|
||
observer 为 nil 时,Ebox 不创建 observation context,不取 timestamp/GC snapshot,
|
||
也不装饰 report。
|
||
|
||
`ebox-call-with-render-burst` 是框架 callback 的 exception-safe allocation/GC 边界。
|
||
`ebox-render-burst-begin` 与 `ebox-render-burst-end` 提供较底层的 token 形式,必须用
|
||
`unwind-protect` 配对。它们本身不发布内容。
|
||
|
||
## 4. Identity、直接更新与 logical candidate
|
||
|
||
| 函数 | 契约 |
|
||
| --- | --- |
|
||
| `ebox-region-ids` | 按文档顺序返回当前底层 region id。 |
|
||
| `ebox-region-resolve` | 把 mounted buffer 中一个逻辑 `:id` 解析为不透明、surface-scoped 的 handle。 |
|
||
| `ebox-region-update` | 通过一次 retained transaction 应用支持的可变样式或滚动 property。 |
|
||
| `ebox-child-range` | 仅供框架集成:在组装 typed canonical input 时构造非视觉、可寻址的 child Range descriptor。 |
|
||
| `ebox-candidate-begin` | 在 one-shot candidate 中捕获当前准确 mounted generation。 |
|
||
| `ebox-candidate-replace` | 用 single-root `CanonicalEboxInput` 替换一个 candidate node address。 |
|
||
| `ebox-candidate-replace-range-ref` | 用 forest-valued `CanonicalEboxInput` 替换一个透明 child Range payload。 |
|
||
| `ebox-candidate-replace-root` | 用 single-root `CanonicalEboxInput` 替换 candidate 的私有 root address。 |
|
||
| `ebox-candidate-replace-host-ref` | 用 single-root `CanonicalEboxInput` 替换一个 framework-owned opaque host reference。 |
|
||
| `ebox-candidate-patch-host-paint` | 比较 previous/next single-root canonical input;记录经过证明的 paint-only host patch,需要 replacement 时返回 nil。 |
|
||
| `ebox-host-ref-bounds` | 返回 opaque host reference 当前不含 margin 的 bounds。 |
|
||
| `ebox-host-ref-position` | 返回其当前第一个位置。 |
|
||
|
||
handle 和位置只属于一个 live publication generation;对象被删除后要重新解析。
|
||
candidate 被 commit 封存后不能复用。
|
||
|
||
author DSL 没有 Range form。普通 author 只组合直接嵌套的 Text/Box child,不调用
|
||
`ebox-child-range`;Range address 只服务已经拥有 semantic child range 与 candidate
|
||
update 的框架。
|
||
|
||
## 5. Selector
|
||
|
||
ECSS 是唯一 selector parser 与 matcher;Ebox 只提供 logical tree relation 和
|
||
mounted index。
|
||
|
||
| 函数 | 契约 |
|
||
| --- | --- |
|
||
| `ebox-selector-parse` | 把 CSS-like selector 编译为 ECSS AST。 |
|
||
| `ebox-selector-match-node-p` | 用 selector 或 AST 匹配一个 built node。 |
|
||
| `ebox-selector-query-all` | 按文档顺序查询 unmounted built tree。 |
|
||
| `ebox-selector-query-buffer` | 查询一个 mounted runtime。 |
|
||
| `ebox-selector-update-buffer` | 更新所有可编辑 mounted match 并返回结构化报告。 |
|
||
| `ebox-select-all` | `ebox-selector-query-buffer` 的 alias。 |
|
||
| `ebox-update-selector` | `ebox-selector-update-buffer` 的 alias。 |
|
||
|
||
## 6. 滚动
|
||
|
||
`ebox-buffer-mode` 安装 `ebox-scroll-map`。map 会把行、页和滚轮意图先交给最内层
|
||
semantic scroll owner,再回退到普通 Emacs 滚动。
|
||
|
||
公共命令是 `ebox-scroll-down`、`ebox-scroll-up`、`ebox-scroll-page-down`、
|
||
`ebox-scroll-page-up`、`ebox-wheel-scroll-down` 与 `ebox-wheel-scroll-up`。
|
||
`ebox-scroll-state` 根据数字 region id 返回只读快照。
|
||
|
||
## 7. 测量、缓存与构建工具
|
||
|
||
| 函数 | 契约 |
|
||
| --- | --- |
|
||
| `ebox-string-pixel-width` | 测量字符串第一行的 display width。 |
|
||
| `ebox-display-signature` | 返回 measurement cache invalidation 使用的 display 输入。 |
|
||
| `ebox-clear-cache` | 清理 Ebox measurement/render cache。 |
|
||
| `ebox-byte-compile` | 编译 Ebox 活跃 Elisp 源码;重启 Emacs 后加载新 bytecode。 |
|
||
| `ebox-native-status` | 报告 native toolchain、module、ABI 与加载状态。 |
|
||
| `ebox-native-build` | 异步构建并安装可选 native 模块。 |
|
||
|
||
native 模块只是加速器,不是正确性依赖。加载包不会构建它。
|
||
|
||
## 8. Facade 清单
|
||
|
||
`ebox-public-api` 保存稳定 facade 入口。下面的分组列出其中每一个名称:
|
||
|
||
- 构造:`ebox-build`、`ebox-text-create`、`ebox-normal-layout-create`、
|
||
`ebox-row-layout-create`、`ebox-column-layout-create`、
|
||
`ebox-flex-layout-create`、`ebox-grid-layout-create`、`ebox-box-create`;
|
||
- 渲染/发布:`ebox-render`、`ebox-render-to-buffer`、`ebox-unmount-buffer`、
|
||
`ebox-display-buffer`、`ebox-commit`、`ebox-buffer-set-observer`、`ebox-buffer-update-report`、
|
||
`ebox-rerender-buffer-with-context`、`ebox-viewport-window-width`、
|
||
`ebox-call-with-render-burst`、`ebox-render-burst-begin`、
|
||
`ebox-render-burst-end`;
|
||
- Candidate/identity:`ebox-candidate-begin`、`ebox-candidate-replace`、
|
||
`ebox-candidate-replace-range-ref`、`ebox-candidate-replace-root`、
|
||
`ebox-candidate-replace-host-ref`、`ebox-candidate-patch-host-paint`、
|
||
`ebox-child-range`、
|
||
`ebox-region-ids`、`ebox-region-resolve`、`ebox-region-update`、
|
||
`ebox-host-ref-bounds`、`ebox-host-ref-position`;
|
||
- Selector:`ebox-selector-parse`、`ebox-selector-match-node-p`、
|
||
`ebox-selector-query-all`、`ebox-selector-query-buffer`、
|
||
`ebox-selector-update-buffer`、`ebox-select-all`、`ebox-update-selector`;
|
||
- 滚动:`ebox-buffer-mode`、`ebox-scroll-map`、`ebox-scroll-down`、
|
||
`ebox-scroll-up`、`ebox-scroll-page-down`、`ebox-scroll-page-up`、
|
||
`ebox-wheel-scroll-down`、`ebox-wheel-scroll-up`、`ebox-scroll-state`;
|
||
- 测量/构建:`ebox-string-pixel-width`、`ebox-display-signature`、
|
||
`ebox-clear-cache`、`ebox-byte-compile`、`ebox-native-status`、
|
||
`ebox-native-build`。
|
||
|
||
## 9. 验证
|
||
|
||
```sh
|
||
make load EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
|
||
make compile EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
|
||
make docs-contract-tests EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
|
||
make dsl-tests EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
|
||
make check EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
|
||
```
|