Collapse reactive islands into scopes: replace TrackingContext and *island-scope* with scope-push!/scope-pop!/context
Reactive tracking (deref/computed/effect dep discovery) and island lifecycle now use the general scoped effects system instead of parallel infrastructure. Two scope names: "sx-reactive" for tracking context, "sx-island-scope" for island disposable collection. Eliminates ~98 net lines: _TrackingContext class, 7 tracking context platform functions (Python + JS), *island-scope* global, and corresponding RENAME_MAP entries. All 20 signal tests pass (17 original + 3 new scope integration tests), plus CEK/continuation/type tests clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,7 @@
|
||||
;; Signal → reactive text in island scope, deref outside
|
||||
:else
|
||||
(if (signal? expr)
|
||||
(if *island-scope*
|
||||
(if (context "sx-island-scope" nil)
|
||||
(reactive-text expr)
|
||||
(create-text-node (str (deref expr))))
|
||||
(create-text-node (str expr))))))
|
||||
@@ -143,7 +143,7 @@
|
||||
(render-dom-element name args env ns)
|
||||
|
||||
;; deref in island scope → reactive text node
|
||||
(and (= name "deref") *island-scope*)
|
||||
(and (= name "deref") (context "sx-island-scope" nil))
|
||||
(let ((sig-or-val (trampoline (eval-expr (first args) env))))
|
||||
(if (signal? sig-or-val)
|
||||
(reactive-text sig-or-val)
|
||||
@@ -215,7 +215,7 @@
|
||||
;; Inside island scope: reactive attribute binding.
|
||||
;; The effect tracks signal deps automatically — if none
|
||||
;; are deref'd, it fires once and never again (safe).
|
||||
*island-scope*
|
||||
(context "sx-island-scope" nil)
|
||||
(reactive-attr el attr-name
|
||||
(fn () (trampoline (eval-expr attr-expr env))))
|
||||
;; Static attribute (outside islands)
|
||||
@@ -237,7 +237,7 @@
|
||||
(let ((child (render-to-dom arg env new-ns)))
|
||||
(cond
|
||||
;; Reactive spread: track signal deps, update attrs on change
|
||||
(and (spread? child) *island-scope*)
|
||||
(and (spread? child) (context "sx-island-scope" nil))
|
||||
(reactive-spread el (fn () (render-to-dom arg env new-ns)))
|
||||
;; Static spread: already emitted via provide, skip
|
||||
(spread? child) nil
|
||||
@@ -392,7 +392,7 @@
|
||||
(cond
|
||||
;; if — reactive inside islands (re-renders when signal deps change)
|
||||
(= name "if")
|
||||
(if *island-scope*
|
||||
(if (context "sx-island-scope" nil)
|
||||
(let ((marker (create-comment "r-if"))
|
||||
(current-nodes (list))
|
||||
(initial-result nil))
|
||||
@@ -440,7 +440,7 @@
|
||||
|
||||
;; when — reactive inside islands
|
||||
(= name "when")
|
||||
(if *island-scope*
|
||||
(if (context "sx-island-scope" nil)
|
||||
(let ((marker (create-comment "r-when"))
|
||||
(current-nodes (list))
|
||||
(initial-result nil))
|
||||
@@ -486,7 +486,7 @@
|
||||
|
||||
;; cond — reactive inside islands
|
||||
(= name "cond")
|
||||
(if *island-scope*
|
||||
(if (context "sx-island-scope" nil)
|
||||
(let ((marker (create-comment "r-cond"))
|
||||
(current-nodes (list))
|
||||
(initial-result nil))
|
||||
@@ -563,7 +563,7 @@
|
||||
;; map — reactive-list when mapping over a signal inside an island
|
||||
(= name "map")
|
||||
(let ((coll-expr (nth expr 2)))
|
||||
(if (and *island-scope*
|
||||
(if (and (context "sx-island-scope" nil)
|
||||
(= (type-of coll-expr) "list")
|
||||
(> (len coll-expr) 1)
|
||||
(= (type-of (first coll-expr)) "symbol")
|
||||
@@ -1168,7 +1168,7 @@
|
||||
(dom-set-attr container "data-sx-boundary" "true")
|
||||
|
||||
;; The entire body is rendered inside ONE effect + try-catch.
|
||||
;; Body renders WITHOUT *island-scope* so that if/when/cond use static
|
||||
;; Body renders WITHOUT island scope so that if/when/cond use static
|
||||
;; paths — their signal reads become direct deref calls tracked by THIS
|
||||
;; effect. Errors from signal changes throw synchronously within try-catch.
|
||||
;; The error boundary's own effect handles all reactivity for its subtree.
|
||||
@@ -1179,31 +1179,30 @@
|
||||
;; Clear container
|
||||
(dom-set-prop container "innerHTML" "")
|
||||
|
||||
;; Save and clear island scope BEFORE try-catch so it can be
|
||||
;; restored in both success and error paths.
|
||||
(let ((saved-scope *island-scope*))
|
||||
(set! *island-scope* nil)
|
||||
(try-catch
|
||||
(fn ()
|
||||
;; Body renders statically — signal reads tracked by THIS effect,
|
||||
;; throws propagate to our try-catch.
|
||||
(let ((frag (create-fragment)))
|
||||
(for-each
|
||||
(fn (child)
|
||||
(dom-append frag (render-to-dom child env ns)))
|
||||
body-exprs)
|
||||
(dom-append container frag))
|
||||
(set! *island-scope* saved-scope))
|
||||
(fn (err)
|
||||
;; Restore scope first, then render fallback
|
||||
(set! *island-scope* saved-scope)
|
||||
;; Push nil island scope to suppress reactive rendering in body.
|
||||
;; Pop in both success and error paths.
|
||||
(scope-push! "sx-island-scope" nil)
|
||||
(try-catch
|
||||
(fn ()
|
||||
;; Body renders statically — signal reads tracked by THIS effect,
|
||||
;; throws propagate to our try-catch.
|
||||
(let ((frag (create-fragment)))
|
||||
(for-each
|
||||
(fn (child)
|
||||
(dom-append frag (render-to-dom child env ns)))
|
||||
body-exprs)
|
||||
(dom-append container frag))
|
||||
(scope-pop! "sx-island-scope"))
|
||||
(fn (err)
|
||||
;; Pop scope first, then render fallback
|
||||
(scope-pop! "sx-island-scope")
|
||||
(let ((fallback-fn (trampoline (eval-expr fallback-expr env)))
|
||||
(retry-fn (fn () (swap! retry-version (fn (n) (+ n 1))))))
|
||||
(let ((fallback-dom
|
||||
(if (lambda? fallback-fn)
|
||||
(render-lambda-dom fallback-fn (list err retry-fn) env ns)
|
||||
(render-to-dom (apply fallback-fn (list err retry-fn)) env ns))))
|
||||
(dom-append container fallback-dom))))))))
|
||||
(dom-append container fallback-dom)))))))
|
||||
|
||||
container)))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user