fix(playground): lazy-load runtime for inert sources
This commit is contained in:
parent
d237b9bc67
commit
64ca9b5931
@ -19,6 +19,9 @@ into the right-hand preview. Saving a source file also refreshes by default.
|
||||
`etaf-playground-register-example` is available when a companion needs a
|
||||
non-conventional root or feature name.
|
||||
|
||||
ETAF Playground 0.2.1 declares ETAF 0.1.1, ETAF UI 0.1.0, and ETAF SQLite
|
||||
0.1.0 so the bundled Research Shelf companion has a complete install closure.
|
||||
|
||||
Preview placement uses the standard Emacs `display-buffer` action stored in
|
||||
`etaf-playground-display-action`. The default is a right side window using
|
||||
half the frame:
|
||||
@ -56,6 +59,11 @@ examples/research-shelf.ecss # inert style source
|
||||
examples/research-shelf.el # DATA / THEME / STATE / VIEW / ROOT sections
|
||||
```
|
||||
|
||||
Opening an inert `.etaf` or `.ecss` source loads only its lightweight editing
|
||||
mode. The ETAF/Ebox/TP runtime is loaded when a preview is refreshed or
|
||||
mounted. Runtime use requires TP 1.0.1 or newer so `tp-transaction.el` and the
|
||||
Host final-accept contract are present.
|
||||
|
||||
The companion registers `:reload-on-refresh t`, so saving the `.el`, `.etaf`, or
|
||||
`.ecss` source and refreshing reloads the complete consumer before the next
|
||||
mount.
|
||||
|
||||
@ -18,6 +18,9 @@ ETAF Playground 是通用的应用构建工作区:左侧编辑同一个应用
|
||||
也会刷新。若 root 或 feature 不遵循命名约定,可在 companion 中调用
|
||||
`etaf-playground-register-example` 注册覆盖。
|
||||
|
||||
ETAF Playground 0.2.1 会声明 ETAF 0.1.1、ETAF UI 0.1.0 与 ETAF SQLite
|
||||
0.1.0,保证内置 Research Shelf companion 的安装依赖闭包完整。
|
||||
|
||||
预览位置使用标准 Emacs `display-buffer` action,由
|
||||
`etaf-playground-display-action` 配置。默认在右侧使用一半 frame:
|
||||
|
||||
@ -55,6 +58,10 @@ examples/research-shelf.ecss # inert 样式 source
|
||||
examples/research-shelf.el # DATA / THEME / STATE / VIEW / ROOT 分区
|
||||
```
|
||||
|
||||
直接打开 inert `.etaf` 或 `.ecss` source 时只加载轻量编辑 mode;刷新或 mount
|
||||
preview 时才加载 ETAF/Ebox/TP runtime。运行 preview 需要 TP 1.0.1 或更高版本,
|
||||
以保证 `tp-transaction.el` 与 Host final-accept contract 已安装。
|
||||
|
||||
该 companion 注册了 `:reload-on-refresh t`;保存 `.el`、`.etaf` 或 `.ecss` 后刷新
|
||||
source,会在下一次 mount 前重新加载完整 consumer。
|
||||
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
;; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
;; Author: ETAF contributors
|
||||
;; Version: 0.2.0
|
||||
;; Package-Requires: ((emacs "29.1") (etaf "0.1.0"))
|
||||
;; Version: 0.2.1
|
||||
;; Package-Requires: ((emacs "29.1") (etaf "0.1.1") (etaf-sqlite "0.1.0") (etaf-ui "0.1.0"))
|
||||
;; Keywords: tools, convenience, ui
|
||||
|
||||
;;; Commentary:
|
||||
@ -32,7 +32,21 @@
|
||||
(require 'cl-lib)
|
||||
(require 'subr-x)
|
||||
(require 'button)
|
||||
(require 'etaf)
|
||||
|
||||
(define-error 'etaf-playground-dependency-error
|
||||
"ETAF Playground requires TP 1.0.1 or newer with tp-transaction")
|
||||
|
||||
(defun etaf-playground--ensure-runtime ()
|
||||
"Load ETAF runtime dependencies or report an actionable TP mismatch."
|
||||
(condition-case condition
|
||||
(require 'etaf)
|
||||
(file-missing
|
||||
(if (member "tp-transaction" condition)
|
||||
(signal 'etaf-playground-dependency-error
|
||||
(list :required-package '(tp "1.0.1")
|
||||
:missing-feature 'tp-transaction
|
||||
:original-condition condition))
|
||||
(signal (car condition) (cdr condition))))))
|
||||
|
||||
(defgroup etaf-playground nil
|
||||
"Authoring workspaces for ETAF applications."
|
||||
@ -86,6 +100,11 @@ frame."
|
||||
|
||||
(declare-function ebox-call-with-render-burst
|
||||
"ebox-buffer-backend" (function &rest arguments))
|
||||
(declare-function etaf-action-redefine-run "etaf-actions" (function))
|
||||
(declare-function etaf-component-redefine-run "etaf-component" (function))
|
||||
(declare-function etaf-mount "etaf-runtime" (buffer view &optional options))
|
||||
(declare-function etaf-runtime-for-buffer "etaf-runtime" (buffer))
|
||||
(declare-function etaf-unmount "etaf-runtime" (runtime))
|
||||
|
||||
(cl-defstruct (etaf-playground-session
|
||||
(:constructor etaf-playground--session-create))
|
||||
@ -510,6 +529,7 @@ source editors and side-by-side workspace around it. MOUNT-OPTIONS is
|
||||
forwarded to `etaf-mount', including an optional initial viewport. Source
|
||||
loading, root construction, ETAF publication, and Ebox rendering share one
|
||||
public framework render burst so GC cannot split an interactive mount."
|
||||
(etaf-playground--ensure-runtime)
|
||||
(ebox-call-with-render-burst
|
||||
#'etaf-playground--mount-example-now
|
||||
buffer-name name session mount-options))
|
||||
@ -534,6 +554,7 @@ public framework render burst so GC cannot split an interactive mount."
|
||||
(let ((preview (etaf-playground-session-preview-buffer session)))
|
||||
(condition-case err
|
||||
(progn
|
||||
(etaf-playground--ensure-runtime)
|
||||
(when-let* ((runtime (etaf-runtime-for-buffer preview)))
|
||||
(with-current-buffer preview
|
||||
(etaf-unmount runtime)))
|
||||
@ -620,10 +641,12 @@ the preview instead of destroying the source workspace."
|
||||
(remove-hook 'after-change-functions
|
||||
#'etaf-playground--after-source-change t)))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode etaf-playground-etaf-mode emacs-lisp-mode "ETAF"
|
||||
"Major mode for inert ETAF structural source files."
|
||||
(etaf-playground-source-mode 1))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode etaf-playground-ecss-mode emacs-lisp-mode "ECSS"
|
||||
"Major mode for inert ETAF ECSS style source files."
|
||||
(etaf-playground-source-mode 1))
|
||||
@ -634,7 +657,9 @@ the preview instead of destroying the source workspace."
|
||||
(setq-local buffer-read-only nil)
|
||||
(setq-local mode-line-process nil))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.etaf\\'" . etaf-playground-etaf-mode))
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.ecss\\'" . etaf-playground-ecss-mode))
|
||||
|
||||
(defun etaf-playground--source-header ()
|
||||
@ -984,8 +1009,10 @@ default example there."
|
||||
(window-frame preview-window)))
|
||||
(configuration
|
||||
(etaf-playground-session-previous-window-configuration session)))
|
||||
(when-let* ((runtime (and (buffer-live-p preview)
|
||||
(etaf-runtime-for-buffer preview))))
|
||||
(when-let* ((runtime
|
||||
(and (fboundp 'etaf-runtime-for-buffer)
|
||||
(buffer-live-p preview)
|
||||
(etaf-runtime-for-buffer preview))))
|
||||
(with-current-buffer preview
|
||||
(etaf-unmount runtime)))
|
||||
(remhash (etaf-playground-session-name session)
|
||||
@ -1013,7 +1040,8 @@ default example there."
|
||||
((and (stringp target) (get-buffer target))
|
||||
(get-buffer target))
|
||||
((null target) (current-buffer))))
|
||||
(runtime (and (buffer-live-p buffer)
|
||||
(runtime (and (fboundp 'etaf-runtime-for-buffer)
|
||||
(buffer-live-p buffer)
|
||||
(etaf-runtime-for-buffer buffer))))
|
||||
(when runtime
|
||||
(with-current-buffer buffer
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
;;; Code:
|
||||
(require 'cl-lib)
|
||||
(require 'seq)
|
||||
(require 'etaf)
|
||||
(require 'etaf-playground)
|
||||
(require 'etaf-performance)
|
||||
(require 'ebox-native-reflow)
|
||||
|
||||
@ -8,15 +8,100 @@
|
||||
;;; Code:
|
||||
(require 'cl-lib)
|
||||
(require 'ert)
|
||||
(require 'package)
|
||||
(require 'etaf-playground)
|
||||
(require 'etaf-performance)
|
||||
|
||||
(defconst etaf-playground-test--root
|
||||
(file-name-directory
|
||||
(directory-file-name
|
||||
(file-name-directory (or load-file-name buffer-file-name))))
|
||||
"Absolute ETAF Playground repository path used by subprocess gates.")
|
||||
|
||||
(defun etaf-playground-test--package-description (file)
|
||||
"Return the package description parsed from FILE."
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(package-buffer-info)))
|
||||
|
||||
(defun etaf-playground-test--ensure-app-loaded ()
|
||||
"Load the same-basename Research Shelf companion for test setup."
|
||||
(unless (featurep 'etaf-research-shelf)
|
||||
(load-file (expand-file-name "examples/research-shelf.el"
|
||||
default-directory))))
|
||||
|
||||
(ert-deftest etaf-playground-etaf-source-mode-clean-loads-without-runtime ()
|
||||
"Opening research-shelf.etaf must not eagerly load ETAF or TP."
|
||||
(let ((output (generate-new-buffer " *etaf-source-mode-clean-load*"))
|
||||
(program (expand-file-name invocation-name invocation-directory))
|
||||
(package-file
|
||||
(expand-file-name "etaf-playground.el" etaf-playground-test--root))
|
||||
(source-file
|
||||
(expand-file-name "examples/research-shelf.etaf"
|
||||
etaf-playground-test--root)))
|
||||
(unwind-protect
|
||||
(let ((status
|
||||
(call-process
|
||||
program nil output nil "-Q" "--batch"
|
||||
"-L" etaf-playground-test--root
|
||||
"-l" package-file
|
||||
"--eval"
|
||||
(format
|
||||
"(progn (find-file %S) (unless (eq major-mode 'etaf-playground-etaf-mode) (kill-emacs 11)) (when (featurep 'etaf) (kill-emacs 12)) (princ \"ETAF source mode clean-load OK\\n\"))"
|
||||
source-file))))
|
||||
(unless (zerop status)
|
||||
(ert-fail
|
||||
(with-current-buffer output (buffer-string))))
|
||||
(with-current-buffer output
|
||||
(should (string-match-p "ETAF source mode clean-load OK"
|
||||
(buffer-string)))))
|
||||
(when (buffer-live-p output) (kill-buffer output)))))
|
||||
|
||||
(ert-deftest etaf-playground-package-closure-requires-transaction-tp ()
|
||||
"Package metadata must reject TP snapshots without tp-transaction.el."
|
||||
(let* ((workspace (file-name-directory
|
||||
(directory-file-name etaf-playground-test--root)))
|
||||
(tp (etaf-playground-test--package-description
|
||||
(expand-file-name "tp/tp.el" workspace)))
|
||||
(ebox (etaf-playground-test--package-description
|
||||
(expand-file-name "ebox/ebox.el" workspace)))
|
||||
(etaf (etaf-playground-test--package-description
|
||||
(expand-file-name "etaf/etaf.el" workspace)))
|
||||
(playground (etaf-playground-test--package-description
|
||||
(expand-file-name
|
||||
"etaf-playground/etaf-playground.el" workspace))))
|
||||
(should (equal (package-desc-version tp) '(1 0 1)))
|
||||
(should (equal (package-desc-version ebox) '(2 0 1)))
|
||||
(should (member '(tp (1 0 1)) (package-desc-reqs ebox)))
|
||||
(should (equal (package-desc-version etaf) '(0 1 1)))
|
||||
(should (member '(ebox (2 0 1)) (package-desc-reqs etaf)))
|
||||
(should (member '(tp (1 0 1)) (package-desc-reqs etaf)))
|
||||
(should (equal (package-desc-version playground) '(0 2 1)))
|
||||
(should (member '(etaf (0 1 1)) (package-desc-reqs playground)))
|
||||
(should (member '(etaf-ui (0 1 0))
|
||||
(package-desc-reqs playground)))
|
||||
(should (member '(etaf-sqlite (0 1 0))
|
||||
(package-desc-reqs playground)))))
|
||||
|
||||
(ert-deftest etaf-playground-stale-tp-error-is-actionable ()
|
||||
"A stale TP install reports its required package instead of file-missing."
|
||||
(let ((original-require (symbol-function 'require)))
|
||||
(cl-letf (((symbol-function 'require)
|
||||
(lambda (feature &optional filename noerror)
|
||||
(if (eq feature 'etaf)
|
||||
(signal 'file-missing
|
||||
'("Cannot open load file"
|
||||
"No such file or directory"
|
||||
"tp-transaction"))
|
||||
(funcall original-require feature filename noerror)))))
|
||||
(let ((condition
|
||||
(should-error (etaf-playground--ensure-runtime)
|
||||
:type 'etaf-playground-dependency-error)))
|
||||
(should (equal (plist-get (cdr condition) :required-package)
|
||||
'(tp "1.0.1")))
|
||||
(should (eq (plist-get (cdr condition) :missing-feature)
|
||||
'tp-transaction))))))
|
||||
|
||||
(defun etaf-playground-test--text (buffer)
|
||||
"Return BUFFER's plain rendered text."
|
||||
(with-current-buffer buffer
|
||||
|
||||
Loading…
Reference in New Issue
Block a user