feat: support framework Host teardown
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
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
This commit is contained in:
parent
17d67c063a
commit
958132f604
@ -90,7 +90,7 @@ Caller-owned Source Tree
|
||||
- `ebox-layout.el` has no load or call edge to `ebox-surface.el` or TP. Candidate isolation, inherited-cascade detection, and one-shot materialization enter layout only through the validated port owned by `ebox-render-context.el`; `ebox.el` wires the surface implementation after both sides load.
|
||||
- `ebox-patch-plan.el` consumes only tentative operation plists plus the immutable generation parent table and returns a deterministic plan artifact. The incremental adapter owns all live-fact preparation. E3 defaults to the pure route while retaining explicit `legacy` and equality-checking `shadow` routes; none of the three routes publishes.
|
||||
- `ebox-style--property-definitions` is also the single used-value projection truth. Non-default engine lowering lives in each property's `:engine-projection`; ECSS metadata exposes the derived `:engine-targets`, and only non-public engine fields may use the small internal exception table.
|
||||
- `ebox-framework-spi-capabilities` returns a fresh immutable v2 provider record declaring paired initial/update stage+rollback, combined participant ordering, same-object legacy reports, and `tp-transaction-protocol-v1+v2`. Ebox provides no selected port or consumer bootstrap; the existing v1 callback APIs remain available.
|
||||
- `ebox-framework-spi-capabilities` returns a fresh immutable v2 provider record declaring paired initial/update stage+rollback, combined participant ordering, same-object legacy reports, initial observation replay, and `tp-transaction-protocol-v1+v2`. Replay derives defensive TP/Ebox provider reports from a completed initial report and carries no mutation authority. Ebox provides no selected port or consumer bootstrap; the existing v1 callback APIs remain available.
|
||||
- `owner-rerender` is broader than `span-patch`, which is broader than `paint-patch`.
|
||||
- Buffer coordinates belong to the generation that produced them and must be refreshed after mutation.
|
||||
- Grid uses the normal measurement and rendering pipeline. Native reflow may reject an ineligible tree and must fall back to Elisp without changing correctness.
|
||||
|
||||
@ -90,7 +90,7 @@ active 合同还覆盖 `Makefile`、`.github/workflows/ci.yml`、`tests/ebox-cor
|
||||
- `ebox-layout.el` 不再 load 或调用 `ebox-surface.el`/TP。candidate isolation、继承 cascade 判定和一次性 materialization 只能通过 `ebox-render-context.el` 拥有的已校验 port 进入 layout;`ebox.el` 在两侧加载后接线 surface 实现。
|
||||
- `ebox-patch-plan.el` 只消费 tentative operation plist 与不可变 generation parent table,并返回 deterministic plan artifact;所有 live fact 准备仍由 incremental adapter 拥有。E3 默认使用 pure route,同时保留显式 `legacy` 与执行等价检查的 `shadow` route;三条 route 都不能发布。
|
||||
- `ebox-style--property-definitions` 同时也是 used-value projection 的唯一真相源。非默认 engine lowering 写在每个 property 的 `:engine-projection` 中;ECSS metadata 暴露派生的 `:engine-targets`,只有非公共 engine 字段可以进入小型 internal exception table。
|
||||
- `ebox-framework-spi-capabilities` 返回 fresh immutable v2 provider record,声明 paired initial/update stage+rollback、combined participant order、same-object legacy report 以及 `tp-transaction-protocol-v1+v2`。Ebox 不创建 selected port 或 consumer bootstrap;现有 v1 callback API 保留。
|
||||
- `ebox-framework-spi-capabilities` 返回 fresh immutable v2 provider record,声明 paired initial/update stage+rollback、combined participant order、same-object legacy report、initial observation replay 以及 `tp-transaction-protocol-v1+v2`。replay 只从 completed initial report 构造 defensive TP/Ebox provider reports,不带 mutate authority。Ebox 不创建 selected port 或 consumer bootstrap;现有 v1 callback API 保留。
|
||||
- `owner-rerender` 范围大于 `span-patch`,`span-patch` 大于 `paint-patch`。
|
||||
- Buffer 坐标属于生成它的 generation,变更后必须重新获取。
|
||||
- Grid 使用普通测量与渲染流水线;native reflow 可以拒绝不适合的树并回退到 Elisp,正确性不变。
|
||||
|
||||
@ -175,6 +175,7 @@ though the facade inventory below focuses on application entry points.
|
||||
| --- | --- |
|
||||
| `ebox-render` | Accept a single-root `CanonicalEboxInput` and return a propertized string without publishing a live buffer. |
|
||||
| `ebox-render-to-buffer` | Accept a single-root `CanonicalEboxInput`, mount a retained TP surface, enable `ebox-buffer-mode`, and return the buffer. Its optional plist accepts only `:observer FUNCTION`. |
|
||||
| `ebox-unmount-buffer` | Release a mounted Ebox/TP surface, observers, retained indexes, and runtime authority from a live buffer. |
|
||||
| `ebox-display-buffer` | Accept a single-root `CanonicalEboxInput`, render through the retained path, delete other windows, and switch to the result. |
|
||||
| `ebox-commit` | Atomically publish a single-root `CanonicalEboxInput` or a one-shot logical candidate returned by `ebox-candidate-begin`. |
|
||||
| `ebox-buffer-set-observer` | Set or remove one function-valued observer on an already mounted buffer. |
|
||||
@ -288,7 +289,8 @@ names every entry:
|
||||
`ebox-column-layout-create`, `ebox-flex-layout-create`,
|
||||
`ebox-grid-layout-create`, `ebox-box-create`.
|
||||
- Render/publication: `ebox-render`, `ebox-render-to-buffer`,
|
||||
`ebox-display-buffer`, `ebox-commit`, `ebox-buffer-set-observer`,
|
||||
`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`,
|
||||
|
||||
@ -163,6 +163,7 @@ typed 模块还公开 `ebox-node-kind`、`ebox-text-node-p`、`ebox-box-node-p`
|
||||
| --- | --- |
|
||||
| `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。 |
|
||||
@ -264,8 +265,8 @@ native 模块只是加速器,不是正确性依赖。加载包不会构建它
|
||||
- 构造:`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-display-buffer`、
|
||||
`ebox-commit`、`ebox-buffer-set-observer`、`ebox-buffer-update-report`、
|
||||
- 渲染/发布:`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`;
|
||||
|
||||
34
ebox-spi.el
34
ebox-spi.el
@ -37,7 +37,8 @@
|
||||
'(initial-paired-stage-rollback
|
||||
update-paired-stage-rollback
|
||||
combined-participant-ordering
|
||||
same-object-legacy-report)
|
||||
same-object-legacy-report
|
||||
initial-observation-replay)
|
||||
"Capabilities guaranteed by every Ebox framework SPI v2 provider record.")
|
||||
|
||||
(defconst ebox-framework-spi--stage-order
|
||||
@ -171,6 +172,37 @@ Return the same completed legacy report object observed by FRAMEWORK-STAGE."
|
||||
framework-stage framework-rollback)
|
||||
(ebox-commit buffer-or-name input framework-stage framework-rollback))
|
||||
|
||||
(defun ebox-framework-spi-initial-observation-reports (report)
|
||||
"Return TP/Ebox provider reports reconstructed from completed initial REPORT.
|
||||
The reports are defensive observational snapshots; they expose no publication
|
||||
or promotion capability."
|
||||
(unless (and (proper-list-p report)
|
||||
(eq (plist-get report :framework-participant-state) 'completed)
|
||||
(integerp (plist-get report :tp-transaction-id)))
|
||||
(signal 'ebox-framework-spi-provider-error
|
||||
(list :invalid-initial-report report)))
|
||||
(let* ((transaction-id (plist-get report :tp-transaction-id))
|
||||
(tp-report
|
||||
(list :provider 'tp
|
||||
:stage 'publication
|
||||
:status 'success
|
||||
:duration-ms 0.0
|
||||
:correlation-id transaction-id
|
||||
:transaction-id transaction-id
|
||||
:surface-revision (plist-get report :surface-revision)
|
||||
:text-operations (plist-get report :tp-text-operations)
|
||||
:property-operations
|
||||
(plist-get report :tp-property-operations)
|
||||
:operation-count (plist-get report :tp-operation-count)))
|
||||
(ebox-report (copy-tree report)))
|
||||
(dolist (entry `((:provider . ebox)
|
||||
(:stage . mount)
|
||||
(:status . success)
|
||||
(:duration-ms . 0.0)
|
||||
(:correlation-id . ,transaction-id)))
|
||||
(setq ebox-report (plist-put ebox-report (car entry) (cdr entry))))
|
||||
(list tp-report ebox-report)))
|
||||
|
||||
(defun ebox-framework-spi--operation
|
||||
(kind function argument-schema)
|
||||
"Return a fresh KIND operation descriptor for FUNCTION and ARGUMENT-SCHEMA."
|
||||
|
||||
22
ebox.el
22
ebox.el
@ -4445,6 +4445,27 @@ TP/Ebox reports after accepted initial publication and later commits."
|
||||
(declare (indent 1))
|
||||
(ebox--render-to-buffer-internal buffer-or-name input options))
|
||||
|
||||
;;;###autoload
|
||||
(defun ebox-unmount-buffer (buffer-or-name)
|
||||
"Unmount Ebox's retained surface from BUFFER-OR-NAME and return the buffer.
|
||||
The operation releases TP surface authority, observers, retained indexes,
|
||||
scroll/native runtime state, and the buffer-local Ebox surface handle."
|
||||
(let ((buffer (get-buffer buffer-or-name)))
|
||||
(unless (buffer-live-p buffer)
|
||||
(user-error "Ebox unmount target is not a live buffer: %S"
|
||||
buffer-or-name))
|
||||
(let ((surface (ebox-surface--live-buffer-surface buffer)))
|
||||
(unless surface
|
||||
(user-error "Ebox buffer has no mounted TP surface: %S" buffer-or-name))
|
||||
(tp-surface-unmount surface)
|
||||
(ebox-surface-cleanup-buffer-observer buffer)
|
||||
(ebox--clear-buffer-runtime-state buffer)
|
||||
(with-current-buffer buffer
|
||||
(setq-local ebox-surface--buffer-surface nil)
|
||||
(remove-hook 'kill-buffer-hook #'ebox--cleanup-current-buffer t)
|
||||
(ebox-buffer-mode -1))
|
||||
buffer)))
|
||||
|
||||
(defun ebox--host-ref-buffer (buffer-or-name)
|
||||
"Return the live buffer named by BUFFER-OR-NAME, or nil."
|
||||
(let ((buffer (and buffer-or-name (get-buffer buffer-or-name))))
|
||||
@ -4829,6 +4850,7 @@ through dirty-set and patch-set execution before falling back to root rerender."
|
||||
ebox-selector-update-buffer
|
||||
ebox-string-pixel-width
|
||||
ebox-update-selector
|
||||
ebox-unmount-buffer
|
||||
ebox-viewport-window-width
|
||||
ebox-wheel-scroll-down
|
||||
ebox-wheel-scroll-up)
|
||||
|
||||
@ -47,7 +47,8 @@
|
||||
'(initial-paired-stage-rollback
|
||||
update-paired-stage-rollback
|
||||
combined-participant-ordering
|
||||
same-object-legacy-report)))
|
||||
same-object-legacy-report
|
||||
initial-observation-replay)))
|
||||
(should
|
||||
(equal (ebox-framework-spi-provider-stage-order first)
|
||||
'(ebox-mirror/native framework-stage)))
|
||||
@ -181,6 +182,58 @@
|
||||
(point-min) (point-max)))))))
|
||||
(when (buffer-live-p buffer) (kill-buffer buffer)))))
|
||||
|
||||
(ert-deftest ebox-spi-initial-replays-observational-provider-reports ()
|
||||
"Completed initial reports replay TP then Ebox without mutable authority."
|
||||
(ebox-spi-test--reset-runtime-state)
|
||||
(let ((buffer (generate-new-buffer " *ebox-m2a-e5-observation*")))
|
||||
(unwind-protect
|
||||
(let* ((report
|
||||
(ebox-framework-spi-initial
|
||||
buffer
|
||||
(ebox-test-box :key 'root (ebox-test-text "observed"))
|
||||
#'ignore #'ignore))
|
||||
(reports
|
||||
(ebox-framework-spi-initial-observation-reports report)))
|
||||
(should (equal (mapcar (lambda (item) (plist-get item :provider))
|
||||
reports)
|
||||
'(tp ebox)))
|
||||
(should (equal (mapcar (lambda (item) (plist-get item :stage))
|
||||
reports)
|
||||
'(publication mount)))
|
||||
(should
|
||||
(apply #'=
|
||||
(mapcar (lambda (item)
|
||||
(plist-get item :correlation-id))
|
||||
reports)))
|
||||
(dolist (item reports)
|
||||
(should (numberp (plist-get item :duration-ms))))
|
||||
(plist-put (car reports) :provider 'mutated)
|
||||
(should-not (plist-member report :provider)))
|
||||
(when (buffer-live-p buffer) (kill-buffer buffer)))))
|
||||
|
||||
(ert-deftest ebox-spi-public-unmount-allows-a-fresh-initial-generation ()
|
||||
"Public Ebox teardown removes retained authority before a later initial."
|
||||
(ebox-spi-test--reset-runtime-state)
|
||||
(let ((buffer (generate-new-buffer " *ebox-m2a-e5-remount*")))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(ebox-framework-spi-initial
|
||||
buffer (ebox-test-box :key 'root (ebox-test-text "first"))
|
||||
#'ignore #'ignore)
|
||||
(should (ebox-surface-buffer-mounted-p buffer))
|
||||
(should (eq (ebox-unmount-buffer buffer) buffer))
|
||||
(should-not (ebox-surface-buffer-mounted-p buffer))
|
||||
(ebox-framework-spi-initial
|
||||
buffer (ebox-test-box :key 'root (ebox-test-text "second"))
|
||||
#'ignore #'ignore)
|
||||
(should (ebox-surface-buffer-mounted-p buffer))
|
||||
(should (string-match-p
|
||||
"second"
|
||||
(with-current-buffer buffer
|
||||
(buffer-substring-no-properties
|
||||
(point-min) (point-max))))))
|
||||
(when (buffer-live-p buffer) (kill-buffer buffer)))))
|
||||
|
||||
(ert-deftest ebox-spi-initial-rolls-the-same-report-back ()
|
||||
"A later TP failure rolls initial framework and Ebox state back in order."
|
||||
(ebox-spi-test--reset-runtime-state)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user