HS: when :count changes — scoped watch + parse-cmd feature boundary fix
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 47s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 47s
Three-part fix for element-scoped reactive expressions: 1. Parser: add when/bind to parse-cmd's feature-keyword nil set so `... then when X changes ...` is parsed as a new feature, not absorbed into the preceding on-handler body as a (ref "when") expression. 2. Parser: parse-when-feat now recognises local (:var) token type so `when :count changes ...` dispatches to the when-changes branch. 3. Runtime + compiler: hs-scoped-set! now fires hs-scoped-fire-watchers! on change; new hs-scoped-watch! / hs-scoped-fire-watchers! registry; compiler emits (hs-scoped-watch! me name (fn (it) body)) for local expressions in when-changes AST nodes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2356,14 +2356,26 @@
|
||||
((= head (quote when-changes))
|
||||
(let
|
||||
((expr (nth ast 1)) (body (nth ast 2)))
|
||||
(if
|
||||
(and (list? expr) (= (first expr) (quote dom-ref)))
|
||||
(list
|
||||
(quote hs-dom-watch!)
|
||||
(hs-to-sx (nth expr 2))
|
||||
(nth expr 1)
|
||||
(list (quote fn) (list (quote it)) (hs-to-sx body)))
|
||||
nil)))
|
||||
(cond
|
||||
((and (list? expr) (= (first expr) (quote dom-ref)))
|
||||
(list
|
||||
(quote hs-dom-watch!)
|
||||
(hs-to-sx (nth expr 2))
|
||||
(nth expr 1)
|
||||
(list
|
||||
(quote fn)
|
||||
(list (quote it))
|
||||
(hs-to-sx body))))
|
||||
((and (list? expr) (= (first expr) (quote local)))
|
||||
(list
|
||||
(quote hs-scoped-watch!)
|
||||
(quote me)
|
||||
(nth expr 1)
|
||||
(list
|
||||
(quote fn)
|
||||
(list (quote it))
|
||||
(hs-to-sx body))))
|
||||
(true nil))))
|
||||
((= head (quote init))
|
||||
(list
|
||||
(quote hs-init)
|
||||
|
||||
@@ -2840,7 +2840,7 @@
|
||||
((body (parse-cmd-list)))
|
||||
(match-kw "end")
|
||||
(list (quote view-transition!) using body)))))
|
||||
((and (= typ "keyword") (or (= val "on") (= val "init") (= val "def") (= val "behavior") (= val "live")))
|
||||
((and (= typ "keyword") (or (= val "on") (= val "init") (= val "def") (= val "behavior") (= val "live") (= val "when") (= val "bind")))
|
||||
nil)
|
||||
(true (parse-expr))))))
|
||||
(define
|
||||
@@ -3060,13 +3060,17 @@
|
||||
(define
|
||||
plf-skip
|
||||
(fn
|
||||
()
|
||||
(depth)
|
||||
(cond
|
||||
((at-end?) nil)
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "end") (= (tp-val) "on") (= (tp-val) "init") (= (tp-val) "def") (= (tp-val) "behavior") (= (tp-val) "live") (= (tp-val) "when")))
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "on") (= (tp-val) "init") (= (tp-val) "def") (= (tp-val) "behavior") (= (tp-val) "live") (= (tp-val) "when")))
|
||||
nil)
|
||||
(true (do (adv!) (plf-skip))))))
|
||||
(plf-skip)
|
||||
((and (= (tp-type) "keyword") (= (tp-val) "end"))
|
||||
(if (> depth 0) (do (adv!) (plf-skip (- depth 1))) nil))
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "if") (= (tp-val) "repeat")))
|
||||
(do (adv!) (plf-skip (+ depth 1))))
|
||||
(true (do (adv!) (plf-skip depth))))))
|
||||
(plf-skip 0)
|
||||
(match-kw "end")
|
||||
(list (quote live-no-op))))
|
||||
(define
|
||||
@@ -3076,15 +3080,20 @@
|
||||
(define
|
||||
pwf-skip
|
||||
(fn
|
||||
()
|
||||
(depth)
|
||||
(cond
|
||||
((at-end?) nil)
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "end") (= (tp-val) "on") (= (tp-val) "init") (= (tp-val) "def") (= (tp-val) "behavior") (= (tp-val) "live") (= (tp-val) "when")))
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "on") (= (tp-val) "init") (= (tp-val) "def") (= (tp-val) "behavior") (= (tp-val) "live") (= (tp-val) "when")))
|
||||
nil)
|
||||
(true (do (adv!) (pwf-skip))))))
|
||||
((and (= (tp-type) "keyword") (= (tp-val) "end"))
|
||||
(if (> depth 0) (do (adv!) (pwf-skip (- depth 1))) nil))
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "if") (= (tp-val) "repeat")))
|
||||
(do (adv!) (pwf-skip (+ depth 1))))
|
||||
(true (do (adv!) (pwf-skip depth))))))
|
||||
(if
|
||||
(or
|
||||
(= (tp-type) "hat")
|
||||
(= (tp-type) "local")
|
||||
(and (= (tp-type) "keyword") (= (tp-val) "dom")))
|
||||
(let
|
||||
((expr (parse-expr)))
|
||||
@@ -3096,10 +3105,13 @@
|
||||
(match-kw "end")
|
||||
(list (quote when-changes) expr body)))
|
||||
(do
|
||||
(pwf-skip)
|
||||
(pwf-skip 0)
|
||||
(match-kw "end")
|
||||
(list (quote when-feat-no-op)))))
|
||||
(do (pwf-skip) (match-kw "end") (list (quote when-feat-no-op))))))
|
||||
(do
|
||||
(pwf-skip 0)
|
||||
(match-kw "end")
|
||||
(list (quote when-feat-no-op))))))
|
||||
(define
|
||||
parse-bind-feat
|
||||
(fn
|
||||
|
||||
@@ -1686,7 +1686,34 @@
|
||||
|
||||
(define
|
||||
hs-scoped-set!
|
||||
(fn (el name val) (dom-set-data el (str "hs-local-" name) val)))
|
||||
(fn
|
||||
(el name val)
|
||||
(let
|
||||
((changed (not (= (hs-scoped-get el name) val))))
|
||||
(do
|
||||
(dom-set-data el (str "hs-local-" name) val)
|
||||
(when changed (hs-scoped-fire-watchers! el name val))))))
|
||||
|
||||
(begin
|
||||
(define _hs-scoped-watchers (list))
|
||||
(define
|
||||
hs-scoped-watch!
|
||||
(fn
|
||||
(el name handler)
|
||||
(set!
|
||||
_hs-scoped-watchers
|
||||
(cons (list el name handler) _hs-scoped-watchers))))
|
||||
(define
|
||||
hs-scoped-fire-watchers!
|
||||
(fn
|
||||
(el name val)
|
||||
(for-each
|
||||
(fn
|
||||
(entry)
|
||||
(when
|
||||
(and (= (nth entry 0) el) (= (nth entry 1) name))
|
||||
((nth entry 2) val)))
|
||||
_hs-scoped-watchers))))
|
||||
|
||||
(define
|
||||
hs-scoped-get
|
||||
@@ -2701,6 +2728,8 @@
|
||||
(if match (dom-parent match) nil)))
|
||||
(true el))))))
|
||||
|
||||
;; ── SourceInfo API ────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
hs-dom-walk
|
||||
(fn
|
||||
@@ -2711,8 +2740,6 @@
|
||||
((= (dom-get-attr el "dom-scope") "isolated") nil)
|
||||
(true (hs-dom-walk (dom-parent el) name)))))
|
||||
|
||||
;; ── SourceInfo API ────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
hs-dom-find-owner
|
||||
(fn
|
||||
|
||||
@@ -2356,14 +2356,26 @@
|
||||
((= head (quote when-changes))
|
||||
(let
|
||||
((expr (nth ast 1)) (body (nth ast 2)))
|
||||
(if
|
||||
(and (list? expr) (= (first expr) (quote dom-ref)))
|
||||
(list
|
||||
(quote hs-dom-watch!)
|
||||
(hs-to-sx (nth expr 2))
|
||||
(nth expr 1)
|
||||
(list (quote fn) (list (quote it)) (hs-to-sx body)))
|
||||
nil)))
|
||||
(cond
|
||||
((and (list? expr) (= (first expr) (quote dom-ref)))
|
||||
(list
|
||||
(quote hs-dom-watch!)
|
||||
(hs-to-sx (nth expr 2))
|
||||
(nth expr 1)
|
||||
(list
|
||||
(quote fn)
|
||||
(list (quote it))
|
||||
(hs-to-sx body))))
|
||||
((and (list? expr) (= (first expr) (quote local)))
|
||||
(list
|
||||
(quote hs-scoped-watch!)
|
||||
(quote me)
|
||||
(nth expr 1)
|
||||
(list
|
||||
(quote fn)
|
||||
(list (quote it))
|
||||
(hs-to-sx body))))
|
||||
(true nil))))
|
||||
((= head (quote init))
|
||||
(list
|
||||
(quote hs-init)
|
||||
|
||||
@@ -2840,7 +2840,7 @@
|
||||
((body (parse-cmd-list)))
|
||||
(match-kw "end")
|
||||
(list (quote view-transition!) using body)))))
|
||||
((and (= typ "keyword") (or (= val "on") (= val "init") (= val "def") (= val "behavior") (= val "live")))
|
||||
((and (= typ "keyword") (or (= val "on") (= val "init") (= val "def") (= val "behavior") (= val "live") (= val "when") (= val "bind")))
|
||||
nil)
|
||||
(true (parse-expr))))))
|
||||
(define
|
||||
@@ -3060,13 +3060,17 @@
|
||||
(define
|
||||
plf-skip
|
||||
(fn
|
||||
()
|
||||
(depth)
|
||||
(cond
|
||||
((at-end?) nil)
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "end") (= (tp-val) "on") (= (tp-val) "init") (= (tp-val) "def") (= (tp-val) "behavior") (= (tp-val) "live") (= (tp-val) "when")))
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "on") (= (tp-val) "init") (= (tp-val) "def") (= (tp-val) "behavior") (= (tp-val) "live") (= (tp-val) "when")))
|
||||
nil)
|
||||
(true (do (adv!) (plf-skip))))))
|
||||
(plf-skip)
|
||||
((and (= (tp-type) "keyword") (= (tp-val) "end"))
|
||||
(if (> depth 0) (do (adv!) (plf-skip (- depth 1))) nil))
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "if") (= (tp-val) "repeat")))
|
||||
(do (adv!) (plf-skip (+ depth 1))))
|
||||
(true (do (adv!) (plf-skip depth))))))
|
||||
(plf-skip 0)
|
||||
(match-kw "end")
|
||||
(list (quote live-no-op))))
|
||||
(define
|
||||
@@ -3076,15 +3080,20 @@
|
||||
(define
|
||||
pwf-skip
|
||||
(fn
|
||||
()
|
||||
(depth)
|
||||
(cond
|
||||
((at-end?) nil)
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "end") (= (tp-val) "on") (= (tp-val) "init") (= (tp-val) "def") (= (tp-val) "behavior") (= (tp-val) "live") (= (tp-val) "when")))
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "on") (= (tp-val) "init") (= (tp-val) "def") (= (tp-val) "behavior") (= (tp-val) "live") (= (tp-val) "when")))
|
||||
nil)
|
||||
(true (do (adv!) (pwf-skip))))))
|
||||
((and (= (tp-type) "keyword") (= (tp-val) "end"))
|
||||
(if (> depth 0) (do (adv!) (pwf-skip (- depth 1))) nil))
|
||||
((and (= (tp-type) "keyword") (or (= (tp-val) "if") (= (tp-val) "repeat")))
|
||||
(do (adv!) (pwf-skip (+ depth 1))))
|
||||
(true (do (adv!) (pwf-skip depth))))))
|
||||
(if
|
||||
(or
|
||||
(= (tp-type) "hat")
|
||||
(= (tp-type) "local")
|
||||
(and (= (tp-type) "keyword") (= (tp-val) "dom")))
|
||||
(let
|
||||
((expr (parse-expr)))
|
||||
@@ -3096,10 +3105,13 @@
|
||||
(match-kw "end")
|
||||
(list (quote when-changes) expr body)))
|
||||
(do
|
||||
(pwf-skip)
|
||||
(pwf-skip 0)
|
||||
(match-kw "end")
|
||||
(list (quote when-feat-no-op)))))
|
||||
(do (pwf-skip) (match-kw "end") (list (quote when-feat-no-op))))))
|
||||
(do
|
||||
(pwf-skip 0)
|
||||
(match-kw "end")
|
||||
(list (quote when-feat-no-op))))))
|
||||
(define
|
||||
parse-bind-feat
|
||||
(fn
|
||||
|
||||
@@ -1686,7 +1686,34 @@
|
||||
|
||||
(define
|
||||
hs-scoped-set!
|
||||
(fn (el name val) (dom-set-data el (str "hs-local-" name) val)))
|
||||
(fn
|
||||
(el name val)
|
||||
(let
|
||||
((changed (not (= (hs-scoped-get el name) val))))
|
||||
(do
|
||||
(dom-set-data el (str "hs-local-" name) val)
|
||||
(when changed (hs-scoped-fire-watchers! el name val))))))
|
||||
|
||||
(begin
|
||||
(define _hs-scoped-watchers (list))
|
||||
(define
|
||||
hs-scoped-watch!
|
||||
(fn
|
||||
(el name handler)
|
||||
(set!
|
||||
_hs-scoped-watchers
|
||||
(cons (list el name handler) _hs-scoped-watchers))))
|
||||
(define
|
||||
hs-scoped-fire-watchers!
|
||||
(fn
|
||||
(el name val)
|
||||
(for-each
|
||||
(fn
|
||||
(entry)
|
||||
(when
|
||||
(and (= (nth entry 0) el) (= (nth entry 1) name))
|
||||
((nth entry 2) val)))
|
||||
_hs-scoped-watchers))))
|
||||
|
||||
(define
|
||||
hs-scoped-get
|
||||
@@ -2701,6 +2728,8 @@
|
||||
(if match (dom-parent match) nil)))
|
||||
(true el))))))
|
||||
|
||||
;; ── SourceInfo API ────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
hs-dom-walk
|
||||
(fn
|
||||
@@ -2711,8 +2740,6 @@
|
||||
((= (dom-get-attr el "dom-scope") "isolated") nil)
|
||||
(true (hs-dom-walk (dom-parent el) name)))))
|
||||
|
||||
;; ── SourceInfo API ────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
hs-dom-find-owner
|
||||
(fn
|
||||
|
||||
Reference in New Issue
Block a user