perf: coalesce viewport resize delivery
This commit is contained in:
parent
175ae2ed4e
commit
d58ec51650
@ -431,6 +431,12 @@ context. For pure rendering, callers may instead bind `ebox-viewport-width`
|
||||
in pixels and `ebox-viewport-height` in lines around the render. Do not mutate
|
||||
a published node tree in place.
|
||||
|
||||
Visible mounted buffers follow their displaying window automatically. Ebox
|
||||
coalesces continuous size events and publishes only the latest sample after
|
||||
`ebox-viewport-resize-delay` (0.05 seconds by default).
|
||||
`ebox-viewport-window-width` returns the same display-safe pixel width used by
|
||||
that controller, so hosts must not install a second resize hook or width rule.
|
||||
|
||||
## 7. Selectors
|
||||
|
||||
`ebox-selector-parse` delegates to ECSS and returns its structured AST.
|
||||
@ -516,6 +522,7 @@ The main customization variables are:
|
||||
| Lazy scroll | `ebox-scroll-lazy-prefix-lookahead-lines` 8; `ebox-scroll-lazy-idle-prefetch-lines` 128; `ebox-scroll-lazy-idle-prefetch-slice-lines` 16; `ebox-scroll-lazy-idle-prefetch-delay` 0.15 |
|
||||
| Runtime prewarm | `ebox-runtime-idle-prewarm` t; `ebox-runtime-idle-prewarm-delay` 0.1; `ebox-runtime-idle-prewarm-prefix-resume-delay` 2.0; `ebox-runtime-idle-prewarm-slice-size` 32; `ebox-native-buffer-scroll` t (initial/visible-window handoff only; strict root-owner proof) |
|
||||
| Predicted reflow | `ebox-runtime-idle-reflow-cache-prewarm` t; `ebox-runtime-idle-reflow-cache-prewarm-delay` 0.15 |
|
||||
| Viewport delivery | `ebox-viewport-resize-delay` 0.05; continuous window events replace one pending sample per mounted buffer |
|
||||
| Reflow GC | `ebox-reflow-cache-prewarm-gc-cons-threshold` `auto`; `ebox-reflow-cache-prewarm-gc-auto-frame-budget` 0.2; `ebox-reflow-cache-prewarm-gc-auto-min-threshold` 64 MiB; `ebox-reflow-cache-prewarm-gc-auto-max-threshold` 1 GiB; `ebox-reflow-cache-prewarm-gc-auto-initial-threshold` 512 MiB; `ebox-reflow-cache-prewarm-gc-auto-target-layouts` 24; `ebox-reflow-cache-prewarm-gc-cons-percentage` 0.1 |
|
||||
| Visual verification | `ebox-visual-check-output-dir` points to the temporary directory used for optional screenshots and reports. |
|
||||
|
||||
@ -583,7 +590,7 @@ style-rule functions immediately below are public module-level style APIs.
|
||||
| `ebox-flex`, `ebox-flex-item` | Build flex containers and item metadata. |
|
||||
| `ebox-grid`, `ebox-grid-fr`, `ebox-grid-item` | Build Grid layouts, fractional tracks, and placement metadata. |
|
||||
| `ebox-render`, `ebox-render-to-buffer`, `ebox-display-buffer` | Pure materialization, retained mount, and display wrapper. |
|
||||
| `ebox-commit`, `ebox-buffer-update-report`, `ebox-rerender-buffer-with-context` | Atomic root commit, report lookup, and viewport-context rerender. |
|
||||
| `ebox-commit`, `ebox-buffer-update-report`, `ebox-rerender-buffer-with-context`, `ebox-viewport-window-width` | Atomic root commit, report lookup, viewport-context rerender, and the shared display-safe width sampler. |
|
||||
| `ebox-region-ids`, `ebox-region-resolve`, `ebox-region-update` | Region mapping, logical handle lookup, and direct update. |
|
||||
| `ebox-child-range`, `ebox-range-ref-present-p`, `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` | Persistent material child segments, backend-anchor lookup, subtree replacement, and retained Host paint transactions. |
|
||||
| `ebox-host-ref-bounds`, `ebox-host-ref-position` | Live host-reference bounds and first position. |
|
||||
|
||||
@ -392,6 +392,11 @@ report;若后续 phase 失败,rollback 最多一次接收同一个 report,
|
||||
render 外部动态绑定 `ebox-viewport-width`(像素)与 `ebox-viewport-height`
|
||||
(行数)。不要原地修改已经发布的 node tree。
|
||||
|
||||
可见的 mounted buffer 会自动跟随展示它的 window。Ebox 合并连续 size event,
|
||||
并在 `ebox-viewport-resize-delay`(默认 0.05 秒)后只发布最新 sample。
|
||||
`ebox-viewport-window-width` 返回 controller 使用的同一个显示安全像素宽度;
|
||||
宿主不应再安装第二套 resize hook 或宽度算法。
|
||||
|
||||
## 7. Selector
|
||||
|
||||
`ebox-selector-parse` 委托 ECSS 并返回结构化 AST;
|
||||
@ -468,6 +473,7 @@ text scale 和固定 `display` space;它也是布局使用的公共测量原
|
||||
| Lazy scroll | `ebox-scroll-lazy-prefix-lookahead-lines` 8;`ebox-scroll-lazy-idle-prefetch-lines` 128;`ebox-scroll-lazy-idle-prefetch-slice-lines` 16;`ebox-scroll-lazy-idle-prefetch-delay` 0.15 |
|
||||
| Runtime prewarm | `ebox-runtime-idle-prewarm` t;`ebox-runtime-idle-prewarm-delay` 0.1;`ebox-runtime-idle-prewarm-prefix-resume-delay` 2.0;`ebox-runtime-idle-prewarm-slice-size` 32;`ebox-native-buffer-scroll` t(仅 initial/visible-window handoff;严格 root-owner proof) |
|
||||
| 预测 reflow | `ebox-runtime-idle-reflow-cache-prewarm` t;`ebox-runtime-idle-reflow-cache-prewarm-delay` 0.15 |
|
||||
| Viewport delivery | `ebox-viewport-resize-delay` 0.05;连续 window event 对每个 mounted buffer 只保留一个最新 sample |
|
||||
| Reflow GC | `ebox-reflow-cache-prewarm-gc-cons-threshold` `auto`;`ebox-reflow-cache-prewarm-gc-auto-frame-budget` 0.2;`ebox-reflow-cache-prewarm-gc-auto-min-threshold` 64 MiB;`ebox-reflow-cache-prewarm-gc-auto-max-threshold` 1 GiB;`ebox-reflow-cache-prewarm-gc-auto-initial-threshold` 512 MiB;`ebox-reflow-cache-prewarm-gc-auto-target-layouts` 24;`ebox-reflow-cache-prewarm-gc-cons-percentage` 0.1 |
|
||||
| Visual verification | `ebox-visual-check-output-dir` 指向可选 screenshot/report 使用的临时目录。 |
|
||||
|
||||
@ -531,7 +537,7 @@ style API。
|
||||
| `ebox-flex`、`ebox-flex-item` | 构建 Flex 容器与 item metadata。 |
|
||||
| `ebox-grid`、`ebox-grid-fr`、`ebox-grid-item` | 构建 Grid、分数轨道和 placement metadata。 |
|
||||
| `ebox-render`、`ebox-render-to-buffer`、`ebox-display-buffer` | 纯 materialization、retained mount 与展示包装。 |
|
||||
| `ebox-commit`、`ebox-buffer-update-report`、`ebox-rerender-buffer-with-context` | 原子根提交、报告查询和 viewport-context rerender。 |
|
||||
| `ebox-commit`、`ebox-buffer-update-report`、`ebox-rerender-buffer-with-context`、`ebox-viewport-window-width` | 原子根提交、报告查询、viewport-context rerender 与统一的显示安全宽度采样。 |
|
||||
| `ebox-region-ids`、`ebox-region-resolve`、`ebox-region-update` | Region 映射、logical handle 查询与直接更新。 |
|
||||
| `ebox-child-range`、`ebox-range-ref-present-p`、`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` | 持久 material child segment、backend anchor 查询、子树替换与 retained Host paint transaction。 |
|
||||
| `ebox-host-ref-bounds`、`ebox-host-ref-position` | Host-reference 的 live bounds 与首位置。 |
|
||||
|
||||
103
ebox-viewport.el
Normal file
103
ebox-viewport.el
Normal file
@ -0,0 +1,103 @@
|
||||
;;; ebox-viewport.el --- Coalesced Emacs viewport delivery -*- lexical-binding: t; -*-
|
||||
|
||||
;;; Commentary:
|
||||
;; Emacs owns window events; Ebox owns the mounted viewport. This module is
|
||||
;; the single bridge between them. Size hooks only sample the latest visible
|
||||
;; dimensions. One deferred callback per buffer publishes the newest sample,
|
||||
;; so continuous frame dragging cannot enqueue synchronous layout transactions.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'ebox-surface)
|
||||
|
||||
(declare-function ebox--buffer-render-state "ebox-incremental" (buffer))
|
||||
(declare-function ebox-rerender-buffer-with-context
|
||||
"ebox" (buffer viewport-width &optional viewport-height))
|
||||
|
||||
(defcustom ebox-viewport-resize-delay 0.05
|
||||
"Seconds after the latest size event before publishing its viewport.
|
||||
Continuous size events replace one pending sample for each mounted buffer."
|
||||
:type 'number
|
||||
:group 'ebox)
|
||||
|
||||
(defvar ebox--viewport-resize-pending-table (make-hash-table :test #'eq)
|
||||
"Buffer-keyed latest viewport samples awaiting publication.")
|
||||
|
||||
(defvar ebox--viewport-resize-generation 0
|
||||
"Monotonic identity for pending viewport samples.")
|
||||
|
||||
(defun ebox-viewport-window-width (window)
|
||||
"Return Ebox's display-safe viewport width for live WINDOW."
|
||||
(and (window-live-p window)
|
||||
(ebox-surface--window-content-width window)))
|
||||
|
||||
(defun ebox--cancel-buffer-viewport-resize (buffer)
|
||||
"Cancel BUFFER's pending viewport publication."
|
||||
(when-let* ((pending (gethash buffer ebox--viewport-resize-pending-table)))
|
||||
(let ((timer (aref pending 4)))
|
||||
(when (timerp timer) (cancel-timer timer)))
|
||||
(remhash buffer ebox--viewport-resize-pending-table))
|
||||
nil)
|
||||
|
||||
(defun ebox--viewport-resize-apply (buffer generation)
|
||||
"Publish BUFFER's pending viewport identified by GENERATION."
|
||||
(when-let* ((pending (gethash buffer ebox--viewport-resize-pending-table)))
|
||||
(when (= generation (aref pending 0))
|
||||
(remhash buffer ebox--viewport-resize-pending-table)
|
||||
(let ((window (aref pending 1))
|
||||
(width (aref pending 2))
|
||||
(height (aref pending 3)))
|
||||
(when (and (buffer-live-p buffer)
|
||||
(window-live-p window)
|
||||
(eq (window-buffer window) buffer)
|
||||
(ebox-surface-buffer-mounted-p buffer))
|
||||
(let ((state (ebox--buffer-render-state buffer)))
|
||||
(unless (and (= width (or (plist-get state :viewport-width) -1))
|
||||
(= height (or (plist-get state :viewport-height) -1)))
|
||||
(ebox-rerender-buffer-with-context buffer width height))))))))
|
||||
|
||||
(defun ebox--schedule-buffer-viewport-resize
|
||||
(buffer window width height)
|
||||
"Replace BUFFER's pending viewport with WINDOW, WIDTH, and HEIGHT."
|
||||
(ebox--cancel-buffer-viewport-resize buffer)
|
||||
(let* ((generation (cl-incf ebox--viewport-resize-generation))
|
||||
(pending (vector generation window width height nil)))
|
||||
(puthash buffer pending ebox--viewport-resize-pending-table)
|
||||
(aset pending 4
|
||||
(run-at-time
|
||||
(max 0 ebox-viewport-resize-delay) nil
|
||||
#'ebox--viewport-resize-apply buffer generation)))
|
||||
buffer)
|
||||
|
||||
(defun ebox--window-size-change (frame)
|
||||
"Sample latest viewports for mounted Ebox buffers visible in FRAME."
|
||||
(when (and (frame-live-p frame) (not noninteractive))
|
||||
(let ((seen (make-hash-table :test #'eq)))
|
||||
(dolist (window (window-list frame 'no-minibuf))
|
||||
(when (window-live-p window)
|
||||
(let ((buffer (window-buffer window)))
|
||||
(unless (gethash buffer seen)
|
||||
(puthash buffer t seen)
|
||||
(when (and (buffer-live-p buffer)
|
||||
(ebox-surface-buffer-mounted-p buffer))
|
||||
(let* ((state (ebox--buffer-render-state buffer))
|
||||
(width (ebox-viewport-window-width window))
|
||||
(height (window-body-height window)))
|
||||
(when (and width
|
||||
state
|
||||
(not (and
|
||||
(= width
|
||||
(or (plist-get state :viewport-width) -1))
|
||||
(= height
|
||||
(or (plist-get state :viewport-height)
|
||||
-1)))))
|
||||
(ebox--schedule-buffer-viewport-resize
|
||||
buffer window width height)))))))))))
|
||||
|
||||
(when (boundp 'window-size-change-functions)
|
||||
(add-hook 'window-size-change-functions #'ebox--window-size-change))
|
||||
|
||||
(provide 'ebox-viewport)
|
||||
|
||||
;;; ebox-viewport.el ends here
|
||||
42
ebox.el
42
ebox.el
@ -26,7 +26,8 @@
|
||||
'("ebox-cache.el" "ebox-style.el" "ebox-child-range.el" "ebox-tree.el" "ebox-measure.el"
|
||||
"ebox-fragment.el" "ebox-render-context.el" "ebox-layout.el"
|
||||
"ebox-flex.el" "ebox-grid.el" "ebox-buffer-backend.el" "ebox-incremental.el"
|
||||
"ebox-native-commit.el" "ebox-surface.el" "ebox-dsl.el" "ebox-selector.el" "ebox.el"
|
||||
"ebox-native-commit.el" "ebox-surface.el" "ebox-viewport.el"
|
||||
"ebox-dsl.el" "ebox-selector.el" "ebox.el"
|
||||
"ebox-native-reflow.el")
|
||||
"Active Ebox Lisp sources compiled by `ebox-byte-compile'.")
|
||||
|
||||
@ -49,6 +50,7 @@
|
||||
(require 'ebox-buffer-backend)
|
||||
(require 'ebox-incremental)
|
||||
(require 'ebox-surface)
|
||||
(require 'ebox-viewport)
|
||||
(require 'ebox-dsl)
|
||||
(require 'ebox-selector)
|
||||
|
||||
@ -2742,42 +2744,6 @@ that was rendered off-window cannot miss the native-scroll handoff."
|
||||
(when (boundp 'window-state-change-functions)
|
||||
(add-hook 'window-state-change-functions #'ebox--window-state-change))
|
||||
|
||||
(defvar ebox--window-size-change-in-progress nil
|
||||
"Non-nil while visible Ebox buffers are synchronizing their viewport.")
|
||||
|
||||
(defun ebox--window-size-change (frame)
|
||||
"Synchronize visible Ebox buffers after FRAME changes size.
|
||||
|
||||
Ebox stores the containing-block viewport in its mounted surface. A frame
|
||||
resize must publish the new window content width before the next render;
|
||||
otherwise responsive Flex/Grid trees keep using the width from mount time.
|
||||
One mounted surface represents one viewport, so when a buffer is visible in
|
||||
multiple windows the first window in FRAME supplies the shared dimensions."
|
||||
(when (and (frame-live-p frame)
|
||||
(not noninteractive)
|
||||
(not ebox--window-size-change-in-progress))
|
||||
(let ((ebox--window-size-change-in-progress t)
|
||||
(seen (make-hash-table :test #'eq)))
|
||||
(dolist (window (window-list frame 'no-minibuf))
|
||||
(when (and (window-live-p window)
|
||||
(not (gethash (window-buffer window) seen)))
|
||||
(let ((buffer (window-buffer window)))
|
||||
(puthash buffer t seen)
|
||||
(when (and (buffer-live-p buffer)
|
||||
(ebox--buffer-render-state buffer)
|
||||
(ebox-surface-buffer-mounted-p buffer))
|
||||
(let* ((state (ebox--buffer-render-state buffer))
|
||||
(width (ebox-surface--window-content-width window))
|
||||
(height (window-body-height window)))
|
||||
(unless (and (= width (or (plist-get state :viewport-width)
|
||||
-1))
|
||||
(= height (or (plist-get state :viewport-height)
|
||||
-1)))
|
||||
(ebox-rerender-buffer-with-context buffer width height))))))))))
|
||||
|
||||
(when (boundp 'window-size-change-functions)
|
||||
(add-hook 'window-size-change-functions #'ebox--window-size-change))
|
||||
|
||||
(defun ebox--runtime-prewarm-record-viewport-node
|
||||
(job node width-allowed)
|
||||
"Record NODE's direct viewport dependencies in JOB.
|
||||
@ -3882,6 +3848,7 @@ With prefix ARG, scroll by that many content lines."
|
||||
"Remove all runtime caches owned by BUFFER.
|
||||
Defaults to the current buffer."
|
||||
(let ((buffer (or buffer (current-buffer))))
|
||||
(ebox--cancel-buffer-viewport-resize buffer)
|
||||
(ebox--cancel-buffer-runtime-prewarm buffer)
|
||||
(when-let ((root (ebox--buffer-root-node buffer)))
|
||||
(ebox--clear-region-runtime-caches
|
||||
@ -4716,6 +4683,7 @@ through dirty-set and patch-set execution before falling back to root rerender."
|
||||
ebox-stack
|
||||
ebox-string-pixel-width
|
||||
ebox-update-selector
|
||||
ebox-viewport-window-width
|
||||
ebox-wheel-scroll-down
|
||||
ebox-wheel-scroll-up)
|
||||
"Stable core Ebox entry points available to applications and tooling.
|
||||
|
||||
@ -4,6 +4,11 @@
|
||||
(require 'ebox-child-range)
|
||||
(require 'ebox)
|
||||
|
||||
;; This suite locks child-range and Elisp projection semantics. Native frame
|
||||
;; execution is verified separately and must not replace the named fallback
|
||||
;; strategies merely because a developer has built the optional module.
|
||||
(setq ebox-native-reflow-module-path nil)
|
||||
|
||||
(defconst ebox-child-range-test--root
|
||||
(expand-file-name ".." (file-name-directory (or load-file-name buffer-file-name)))
|
||||
"Ebox repository root for source audits.")
|
||||
|
||||
@ -5,6 +5,12 @@
|
||||
(require 'ebox)
|
||||
(require 'ebox-native-commit)
|
||||
|
||||
;; These tests lock the named Elisp projection proofs. Native commit has its
|
||||
;; own focused contract tests below; disable runtime module discovery here so
|
||||
;; a locally built optional module cannot silently replace paint/span/mixed
|
||||
;; plans with `native-frame' and make this suite environment-dependent.
|
||||
(setq ebox-native-reflow-module-path nil)
|
||||
|
||||
(defun ebox-commit-test--buffer-string (buffer)
|
||||
"Return BUFFER's complete propertized contents."
|
||||
(with-current-buffer buffer
|
||||
|
||||
@ -10,6 +10,11 @@
|
||||
|
||||
(load-file (expand-file-name "../ebox.el" (file-name-directory load-file-name)))
|
||||
|
||||
;; Core tests assert named Elisp render/patch strategies. Keep optional local
|
||||
;; native artifacts from replacing those strategies; native execution has
|
||||
;; separate module and integration gates.
|
||||
(setq ebox-native-reflow-module-path nil)
|
||||
|
||||
(defvar ebox--scroll-window-initial-lookahead-lines-override nil)
|
||||
|
||||
(defun ebox-test--reset-runtime-state ()
|
||||
|
||||
@ -51,7 +51,8 @@
|
||||
(dolist (feature '(ebox-cache ebox-style ebox-tree ebox-measure
|
||||
ebox-fragment ebox-layout ebox-flex ebox-grid
|
||||
ebox-buffer-backend ebox-incremental
|
||||
ebox-surface ebox-dsl ebox-selector))
|
||||
ebox-surface ebox-viewport ebox-dsl
|
||||
ebox-selector))
|
||||
(should (featurep feature))))
|
||||
|
||||
(ert-deftest ebox-facade-exposes-native-workflow-without-loading-it ()
|
||||
@ -70,8 +71,8 @@
|
||||
"ebox-tree.el" "ebox-measure.el"
|
||||
"ebox-fragment.el" "ebox-render-context.el" "ebox-layout.el"
|
||||
"ebox-flex.el" "ebox-grid.el" "ebox-buffer-backend.el" "ebox-incremental.el"
|
||||
"ebox-native-commit.el" "ebox-surface.el" "ebox-dsl.el"
|
||||
"ebox-selector.el" "ebox.el"
|
||||
"ebox-native-commit.el" "ebox-surface.el" "ebox-viewport.el"
|
||||
"ebox-dsl.el" "ebox-selector.el" "ebox.el"
|
||||
"ebox-native-reflow.el"))))
|
||||
|
||||
(ert-deftest ebox-surface-depends-only-on-public-tp-api ()
|
||||
|
||||
@ -171,11 +171,15 @@
|
||||
(when (get-buffer " *other-window*")
|
||||
(kill-buffer " *other-window*")))))
|
||||
|
||||
(ert-deftest ebox-window-size-change-publishes-visible-viewport ()
|
||||
"A live frame resize updates the mounted surface's containing block."
|
||||
(ert-deftest ebox-window-size-change-coalesces-to-latest-visible-viewport ()
|
||||
"Continuous frame changes publish only the latest sampled viewport."
|
||||
(let* ((buffer (generate-new-buffer " *ebox-window-size-change*"))
|
||||
(window (selected-window))
|
||||
(old-buffer (window-buffer window))
|
||||
(sampled-height (window-body-height window))
|
||||
(sampled-widths (number-sequence 240 430 10))
|
||||
scheduled
|
||||
cancelled
|
||||
calls)
|
||||
(unwind-protect
|
||||
(progn
|
||||
@ -186,18 +190,58 @@
|
||||
((symbol-function 'ebox-surface-buffer-mounted-p)
|
||||
(lambda (_buffer) t))
|
||||
((symbol-function 'ebox-surface--window-content-width)
|
||||
(lambda (_window) 240))
|
||||
((symbol-function 'window-body-height)
|
||||
(lambda (_window &optional _pixelwise) 20))
|
||||
(lambda (_window) (pop sampled-widths)))
|
||||
((symbol-function 'ebox-rerender-buffer-with-context)
|
||||
(lambda (target width height)
|
||||
(setq calls (list target width height))))
|
||||
(push (list target width height) calls)))
|
||||
((symbol-function 'run-at-time)
|
||||
(lambda (delay _repeat function &rest arguments)
|
||||
(let ((timer (intern (format "ebox-resize-timer-%d"
|
||||
(1+ (length scheduled))))))
|
||||
(push (list timer delay function arguments) scheduled)
|
||||
timer)))
|
||||
((symbol-function 'timerp)
|
||||
(lambda (value)
|
||||
(string-prefix-p "ebox-resize-timer-"
|
||||
(symbol-name value))))
|
||||
((symbol-function 'cancel-timer)
|
||||
(lambda (timer) (push timer cancelled)))
|
||||
(noninteractive nil))
|
||||
(ebox--window-size-change (selected-frame))
|
||||
(should (equal calls (list buffer 240 20)))))
|
||||
(dotimes (_index 20)
|
||||
(ebox--window-size-change (selected-frame)))
|
||||
(should-not calls)
|
||||
(should (= (length scheduled) 20))
|
||||
(should (= (length cancelled) 19))
|
||||
(pcase-let ((`(,_timer ,delay ,function ,arguments)
|
||||
(car scheduled)))
|
||||
(should (= delay ebox-viewport-resize-delay))
|
||||
(apply function arguments))
|
||||
(should (equal calls
|
||||
(list (list buffer 430 sampled-height))))))
|
||||
(set-window-buffer window old-buffer)
|
||||
(when (boundp 'ebox--viewport-resize-pending-table)
|
||||
(clrhash ebox--viewport-resize-pending-table))
|
||||
(kill-buffer buffer))))
|
||||
|
||||
(ert-deftest ebox-buffer-cleanup-cancels-pending-viewport-resize ()
|
||||
"A dead mounted buffer cannot retain a viewport timer or pending sample."
|
||||
(let ((buffer (generate-new-buffer " *ebox-viewport-cleanup*"))
|
||||
(timer 'ebox-test-viewport-cleanup-timer)
|
||||
cancelled)
|
||||
(unwind-protect
|
||||
(progn
|
||||
(puthash buffer (vector 1 (selected-window) 400 20 timer)
|
||||
ebox--viewport-resize-pending-table)
|
||||
(cl-letf (((symbol-function 'timerp)
|
||||
(lambda (value) (eq value timer)))
|
||||
((symbol-function 'cancel-timer)
|
||||
(lambda (value) (setq cancelled value))))
|
||||
(with-current-buffer buffer (ebox--cleanup-current-buffer)))
|
||||
(should (eq cancelled timer))
|
||||
(should-not (gethash buffer ebox--viewport-resize-pending-table)))
|
||||
(remhash buffer ebox--viewport-resize-pending-table)
|
||||
(when (buffer-live-p buffer) (kill-buffer buffer)))))
|
||||
|
||||
(defun ebox-surface-test--hash-fingerprint (table)
|
||||
"Return a stable content fingerprint for hash TABLE.
|
||||
The fingerprint checks entries rather than only table identity, so a failed
|
||||
@ -732,10 +776,13 @@ candidate cannot hide mutations by restoring the old hash-table pointer."
|
||||
|
||||
(ert-deftest ebox-surface-plan-stays-free-of-runtime-state ()
|
||||
"The surface plan should not contain markers or runtime handles."
|
||||
(require 'ebox-native-commit)
|
||||
(let ((original (symbol-function 'tp-surface-result-create))
|
||||
(original-owned (symbol-function 'tp-surface-result-create-owned))
|
||||
captured client-state)
|
||||
(cl-letf (((symbol-function 'tp-surface-result-create)
|
||||
(cl-letf (((symbol-function 'ebox-native-commit-render)
|
||||
(lambda (&rest _) nil))
|
||||
((symbol-function 'tp-surface-result-create)
|
||||
(lambda (plan &optional state)
|
||||
(setq captured plan
|
||||
client-state state)
|
||||
@ -753,7 +800,8 @@ candidate cannot hide mutations by restoring the old hash-table pointer."
|
||||
(let* ((fragments-plan (car (tp-surface-plan-children captured)))
|
||||
(text-plan (car (tp-surface-plan-children fragments-plan)))
|
||||
(plan-text (tp-surface-plan-text text-plan))
|
||||
(state-fragments (plist-get client-state :surface-fragments))
|
||||
(state-fragments
|
||||
(ebox-surface--materialized-fragment-ledger client-state))
|
||||
(owner-position
|
||||
(cl-loop for position from 0 below (length plan-text)
|
||||
when (get-text-property position
|
||||
@ -1323,7 +1371,9 @@ candidate cannot hide mutations by restoring the old hash-table pointer."
|
||||
(plist-put node :key 'growing-source)
|
||||
node))
|
||||
(let ((original-render (symbol-function 'ebox-render)))
|
||||
(cl-letf (((symbol-function 'ebox-render)
|
||||
(cl-letf (((symbol-function 'ebox-native-commit-render)
|
||||
(lambda (&rest _) nil))
|
||||
((symbol-function 'ebox-render)
|
||||
(lambda (node)
|
||||
(cond
|
||||
((equal (plist-get node :key) 'stable-source)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user