Adopt Step 7 language features across SX codebase
112 conversions across 19 .sx files using match, let-match, and pipe operators: match (17): type/value dispatch replacing cond/if chains - lib/vm.sx: HO form dispatch (for-each/map/filter/reduce/some/every?) - lib/tree-tools.sx: node-display, node-matches?, rename, count, replace, free-symbols - lib/types.sx: narrow-type, substitute-in-type, infer-type, resolve-type - web/engine.sx: default-trigger, resolve-target, classify-trigger - web/deps.sx: scan-refs-walk, scan-io-refs-walk let-match (89): dict destructuring replacing (get d "key") patterns - shared/page-functions.sx (20), blog/admin.sx (17), pub-api.sx (13) - events/ layouts/page/tickets/entries/forms (27 total) - specs-explorer.sx (7), federation/social.sx (3), lib/ small files (3) -> pipes (6): replacing triple-chained gets in lib/vm.sx - frame-closure → closure-code → code-bytecode chains Also: lib/vm.sx accessor upgrades (get vm "sp" → vm-sp vm throughout) 2650/2650 tests pass, zero regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
654
web/deps.sx
654
web/deps.sx
@@ -1,6 +1,7 @@
|
||||
|
||||
|
||||
(define-library (web deps)
|
||||
(define-library
|
||||
(web deps)
|
||||
(export
|
||||
scan-refs
|
||||
scan-refs-walk
|
||||
@@ -22,347 +23,340 @@
|
||||
page-render-plan
|
||||
env-components)
|
||||
(begin
|
||||
|
||||
(define
|
||||
scan-refs
|
||||
:effects ()
|
||||
(fn (node) (let ((refs (list))) (scan-refs-walk node refs) refs)))
|
||||
|
||||
(define
|
||||
scan-refs-walk
|
||||
:effects ()
|
||||
(fn
|
||||
(node (refs :as list))
|
||||
(cond
|
||||
(= (type-of node) "symbol")
|
||||
(let
|
||||
((name (symbol-name node)))
|
||||
(when
|
||||
(starts-with? name "~")
|
||||
(when (not (contains? refs name)) (append! refs name))))
|
||||
(= (type-of node) "list")
|
||||
(for-each (fn (item) (scan-refs-walk item refs)) node)
|
||||
(= (type-of node) "dict")
|
||||
(for-each
|
||||
(fn (key) (scan-refs-walk (dict-get node key) refs))
|
||||
(keys node))
|
||||
:else nil)))
|
||||
|
||||
(define
|
||||
transitive-deps-walk
|
||||
:effects ()
|
||||
(fn
|
||||
((n :as string) (seen :as list) (env :as dict))
|
||||
(when
|
||||
(not (contains? seen n))
|
||||
(append! seen n)
|
||||
(let
|
||||
((val (env-get env n)))
|
||||
(cond
|
||||
(or (= (type-of val) "component") (= (type-of val) "island"))
|
||||
(for-each
|
||||
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
|
||||
(scan-refs (component-body val)))
|
||||
(= (type-of val) "macro")
|
||||
(for-each
|
||||
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
|
||||
(scan-refs (macro-body val)))
|
||||
:else nil)))))
|
||||
|
||||
(define
|
||||
transitive-deps
|
||||
:effects ()
|
||||
(fn
|
||||
((name :as string) (env :as dict))
|
||||
(let
|
||||
((seen (list))
|
||||
(key (if (starts-with? name "~") name (str "~" name))))
|
||||
(transitive-deps-walk key seen env)
|
||||
(filter (fn ((x :as string)) (not (= x key))) seen))))
|
||||
|
||||
(define
|
||||
compute-all-deps
|
||||
:effects (mutation)
|
||||
(fn
|
||||
((env :as dict))
|
||||
(for-each
|
||||
(define
|
||||
scan-refs
|
||||
:effects ()
|
||||
(fn (node) (let ((refs (list))) (scan-refs-walk node refs) refs)))
|
||||
(define
|
||||
scan-refs-walk
|
||||
:effects ()
|
||||
(fn
|
||||
((name :as string))
|
||||
(let
|
||||
((val (env-get env name)))
|
||||
(when
|
||||
(or (= (type-of val) "component") (= (type-of val) "island"))
|
||||
(component-set-deps! val (transitive-deps name env)))))
|
||||
(env-components env))))
|
||||
|
||||
(define
|
||||
scan-components-from-source
|
||||
:effects ()
|
||||
(fn
|
||||
((source :as string))
|
||||
(let
|
||||
((matches (regex-find-all "\\(~([a-zA-Z_][a-zA-Z0-9_\\-:/]*)" source)))
|
||||
(map (fn ((m :as string)) (str "~" m)) matches))))
|
||||
|
||||
(define
|
||||
components-needed
|
||||
:effects ()
|
||||
(fn
|
||||
((page-source :as string) (env :as dict))
|
||||
(let
|
||||
((direct (scan-components-from-source page-source))
|
||||
(all-needed (list)))
|
||||
(for-each
|
||||
(fn
|
||||
((name :as string))
|
||||
(when (not (contains? all-needed name)) (append! all-needed name))
|
||||
(let
|
||||
((val (env-get env name)))
|
||||
(node (refs :as list))
|
||||
(match
|
||||
(type-of node)
|
||||
("symbol"
|
||||
(let
|
||||
((deps (if (and (= (type-of val) "component") (not (empty? (component-deps val)))) (component-deps val) (transitive-deps name env))))
|
||||
(for-each
|
||||
(fn
|
||||
((dep :as string))
|
||||
(when
|
||||
(not (contains? all-needed dep))
|
||||
(append! all-needed dep)))
|
||||
deps))))
|
||||
direct)
|
||||
all-needed)))
|
||||
|
||||
(define
|
||||
page-component-bundle
|
||||
:effects ()
|
||||
(fn
|
||||
((page-source :as string) (env :as dict))
|
||||
(components-needed page-source env)))
|
||||
|
||||
(define
|
||||
page-css-classes
|
||||
:effects ()
|
||||
(fn
|
||||
((page-source :as string) (env :as dict))
|
||||
(let
|
||||
((needed (components-needed page-source env)) (classes (list)))
|
||||
(for-each
|
||||
(fn
|
||||
((name :as string))
|
||||
(let
|
||||
((val (env-get env name)))
|
||||
(when
|
||||
(= (type-of val) "component")
|
||||
(for-each
|
||||
(fn
|
||||
((cls :as string))
|
||||
(when (not (contains? classes cls)) (append! classes cls)))
|
||||
(component-css-classes val)))))
|
||||
needed)
|
||||
(for-each
|
||||
(fn
|
||||
((cls :as string))
|
||||
(when (not (contains? classes cls)) (append! classes cls)))
|
||||
(scan-css-classes page-source))
|
||||
classes)))
|
||||
|
||||
(define
|
||||
scan-io-refs-walk
|
||||
:effects ()
|
||||
(fn
|
||||
(node (io-names :as list) (refs :as list))
|
||||
(cond
|
||||
(= (type-of node) "symbol")
|
||||
(let
|
||||
((name (symbol-name node)))
|
||||
(when
|
||||
(contains? io-names name)
|
||||
(when (not (contains? refs name)) (append! refs name))))
|
||||
(= (type-of node) "list")
|
||||
(for-each (fn (item) (scan-io-refs-walk item io-names refs)) node)
|
||||
(= (type-of node) "dict")
|
||||
(for-each
|
||||
(fn (key) (scan-io-refs-walk (dict-get node key) io-names refs))
|
||||
(keys node))
|
||||
:else nil)))
|
||||
|
||||
(define
|
||||
scan-io-refs
|
||||
:effects ()
|
||||
(fn
|
||||
(node (io-names :as list))
|
||||
(let ((refs (list))) (scan-io-refs-walk node io-names refs) refs)))
|
||||
|
||||
(define
|
||||
transitive-io-refs-walk
|
||||
:effects ()
|
||||
(fn
|
||||
((n :as string)
|
||||
(seen :as list)
|
||||
(all-refs :as list)
|
||||
(env :as dict)
|
||||
(io-names :as list))
|
||||
(when
|
||||
(not (contains? seen n))
|
||||
(append! seen n)
|
||||
(let
|
||||
((val (env-get env n)))
|
||||
(cond
|
||||
(= (type-of val) "component")
|
||||
(do
|
||||
((name (symbol-name node)))
|
||||
(when
|
||||
(starts-with? name "~")
|
||||
(when (not (contains? refs name)) (append! refs name)))))
|
||||
("list" (for-each (fn (child) (scan-refs-walk child refs)) node))
|
||||
("dict"
|
||||
(for-each
|
||||
(fn
|
||||
((ref :as string))
|
||||
(when (not (contains? all-refs ref)) (append! all-refs ref)))
|
||||
(scan-io-refs (component-body val) io-names))
|
||||
(for-each
|
||||
(fn
|
||||
((dep :as string))
|
||||
(transitive-io-refs-walk dep seen all-refs env io-names))
|
||||
(scan-refs (component-body val))))
|
||||
(= (type-of val) "macro")
|
||||
(do
|
||||
(for-each
|
||||
(fn
|
||||
((ref :as string))
|
||||
(when (not (contains? all-refs ref)) (append! all-refs ref)))
|
||||
(scan-io-refs (macro-body val) io-names))
|
||||
(for-each
|
||||
(fn
|
||||
((dep :as string))
|
||||
(transitive-io-refs-walk dep seen all-refs env io-names))
|
||||
(scan-refs (macro-body val))))
|
||||
:else nil)))))
|
||||
|
||||
(define
|
||||
transitive-io-refs
|
||||
:effects ()
|
||||
(fn
|
||||
((name :as string) (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((all-refs (list))
|
||||
(seen (list))
|
||||
(key (if (starts-with? name "~") name (str "~" name))))
|
||||
(transitive-io-refs-walk key seen all-refs env io-names)
|
||||
all-refs)))
|
||||
|
||||
(define
|
||||
compute-all-io-refs
|
||||
:effects (mutation)
|
||||
(fn
|
||||
((env :as dict) (io-names :as list))
|
||||
(for-each
|
||||
(fn (key) (scan-refs-walk (dict-get node key) refs))
|
||||
(keys node)))
|
||||
(_ nil))))
|
||||
(define
|
||||
transitive-deps-walk
|
||||
:effects ()
|
||||
(fn
|
||||
((name :as string))
|
||||
(let
|
||||
((val (env-get env name)))
|
||||
(when
|
||||
(= (type-of val) "component")
|
||||
(component-set-io-refs!
|
||||
val
|
||||
(transitive-io-refs name env io-names)))))
|
||||
(env-components env))))
|
||||
|
||||
(define
|
||||
component-io-refs-cached
|
||||
:effects ()
|
||||
(fn
|
||||
((name :as string) (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((key (if (starts-with? name "~") name (str "~" name))))
|
||||
(let
|
||||
((val (env-get env key)))
|
||||
(if
|
||||
(and
|
||||
(= (type-of val) "component")
|
||||
(not (nil? (component-io-refs val)))
|
||||
(not (empty? (component-io-refs val))))
|
||||
(component-io-refs val)
|
||||
(transitive-io-refs name env io-names))))))
|
||||
|
||||
(define
|
||||
component-pure?
|
||||
:effects ()
|
||||
(fn
|
||||
(name (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((key (if (starts-with? name "~") name (str "~" name))))
|
||||
(let
|
||||
((val (if (env-has? env key) (env-get env key) nil)))
|
||||
(if
|
||||
(and
|
||||
(= (type-of val) "component")
|
||||
(not (nil? (component-io-refs val)))
|
||||
(not (empty? (component-io-refs val))))
|
||||
false
|
||||
(empty? (transitive-io-refs name env io-names)))))))
|
||||
|
||||
(define
|
||||
render-target
|
||||
:effects ()
|
||||
(fn
|
||||
(name (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((key (if (starts-with? name "~") name (str "~" name))))
|
||||
(let
|
||||
((val (if (env-has? env key) (env-get env key) nil)))
|
||||
(if
|
||||
(not (= (type-of val) "component"))
|
||||
"server"
|
||||
((n :as string) (seen :as list) (env :as dict))
|
||||
(when
|
||||
(not (contains? seen n))
|
||||
(append! seen n)
|
||||
(let
|
||||
((affinity (component-affinity val)))
|
||||
((val (env-get env n)))
|
||||
(cond
|
||||
(= affinity "server")
|
||||
"server"
|
||||
(= affinity "client")
|
||||
"client"
|
||||
(not (component-pure? name env io-names))
|
||||
"server"
|
||||
:else "client")))))))
|
||||
|
||||
(define
|
||||
page-render-plan
|
||||
:effects ()
|
||||
(fn
|
||||
((page-source :as string) (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((needed (components-needed page-source env))
|
||||
(comp-targets (dict))
|
||||
(server-list (list))
|
||||
(client-list (list))
|
||||
(io-deps (list)))
|
||||
(for-each
|
||||
(fn
|
||||
((name :as string))
|
||||
(or (= (type-of val) "component") (= (type-of val) "island"))
|
||||
(for-each
|
||||
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
|
||||
(scan-refs (component-body val)))
|
||||
(= (type-of val) "macro")
|
||||
(for-each
|
||||
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
|
||||
(scan-refs (macro-body val)))
|
||||
:else nil)))))
|
||||
(define
|
||||
transitive-deps
|
||||
:effects ()
|
||||
(fn
|
||||
((name :as string) (env :as dict))
|
||||
(let
|
||||
((seen (list))
|
||||
(key (if (starts-with? name "~") name (str "~" name))))
|
||||
(transitive-deps-walk key seen env)
|
||||
(filter (fn ((x :as string)) (not (= x key))) seen))))
|
||||
(define
|
||||
compute-all-deps
|
||||
:effects (mutation)
|
||||
(fn
|
||||
((env :as dict))
|
||||
(for-each
|
||||
(fn
|
||||
((name :as string))
|
||||
(let
|
||||
((val (env-get env name)))
|
||||
(when
|
||||
(or
|
||||
(= (type-of val) "component")
|
||||
(= (type-of val) "island"))
|
||||
(component-set-deps! val (transitive-deps name env)))))
|
||||
(env-components env))))
|
||||
(define
|
||||
scan-components-from-source
|
||||
:effects ()
|
||||
(fn
|
||||
((source :as string))
|
||||
(let
|
||||
((matches (regex-find-all "\\(~([a-zA-Z_][a-zA-Z0-9_\\-:/]*)" source)))
|
||||
(map (fn ((m :as string)) (str "~" m)) matches))))
|
||||
(define
|
||||
components-needed
|
||||
:effects ()
|
||||
(fn
|
||||
((page-source :as string) (env :as dict))
|
||||
(let
|
||||
((direct (scan-components-from-source page-source))
|
||||
(all-needed (list)))
|
||||
(for-each
|
||||
(fn
|
||||
((name :as string))
|
||||
(when
|
||||
(not (contains? all-needed name))
|
||||
(append! all-needed name))
|
||||
(let
|
||||
((val (env-get env name)))
|
||||
(let
|
||||
((deps (if (and (= (type-of val) "component") (not (empty? (component-deps val)))) (component-deps val) (transitive-deps name env))))
|
||||
(for-each
|
||||
(fn
|
||||
((dep :as string))
|
||||
(when
|
||||
(not (contains? all-needed dep))
|
||||
(append! all-needed dep)))
|
||||
deps))))
|
||||
direct)
|
||||
all-needed)))
|
||||
(define
|
||||
page-component-bundle
|
||||
:effects ()
|
||||
(fn
|
||||
((page-source :as string) (env :as dict))
|
||||
(components-needed page-source env)))
|
||||
(define
|
||||
page-css-classes
|
||||
:effects ()
|
||||
(fn
|
||||
((page-source :as string) (env :as dict))
|
||||
(let
|
||||
((needed (components-needed page-source env)) (classes (list)))
|
||||
(for-each
|
||||
(fn
|
||||
((name :as string))
|
||||
(let
|
||||
((val (env-get env name)))
|
||||
(when
|
||||
(= (type-of val) "component")
|
||||
(for-each
|
||||
(fn
|
||||
((cls :as string))
|
||||
(when
|
||||
(not (contains? classes cls))
|
||||
(append! classes cls)))
|
||||
(component-css-classes val)))))
|
||||
needed)
|
||||
(for-each
|
||||
(fn
|
||||
((cls :as string))
|
||||
(when (not (contains? classes cls)) (append! classes cls)))
|
||||
(scan-css-classes page-source))
|
||||
classes)))
|
||||
(define
|
||||
scan-io-refs-walk
|
||||
:effects ()
|
||||
(fn
|
||||
(node (io-names :as list) (refs :as list))
|
||||
(match
|
||||
(type-of node)
|
||||
("symbol"
|
||||
(let
|
||||
((name (symbol-name node)))
|
||||
(when
|
||||
(contains? io-names name)
|
||||
(when (not (contains? refs name)) (append! refs name)))))
|
||||
("list"
|
||||
(for-each
|
||||
(fn (item) (scan-io-refs-walk item io-names refs))
|
||||
node))
|
||||
("dict"
|
||||
(for-each
|
||||
(fn
|
||||
(key)
|
||||
(scan-io-refs-walk (dict-get node key) io-names refs))
|
||||
(keys node)))
|
||||
(_ nil))))
|
||||
(define
|
||||
scan-io-refs
|
||||
:effects ()
|
||||
(fn
|
||||
(node (io-names :as list))
|
||||
(let ((refs (list))) (scan-io-refs-walk node io-names refs) refs)))
|
||||
(define
|
||||
transitive-io-refs-walk
|
||||
:effects ()
|
||||
(fn
|
||||
((n :as string)
|
||||
(seen :as list)
|
||||
(all-refs :as list)
|
||||
(env :as dict)
|
||||
(io-names :as list))
|
||||
(when
|
||||
(not (contains? seen n))
|
||||
(append! seen n)
|
||||
(let
|
||||
((target (render-target name env io-names)))
|
||||
(dict-set! comp-targets name target)
|
||||
(if
|
||||
(= target "server")
|
||||
((val (env-get env n)))
|
||||
(cond
|
||||
(= (type-of val) "component")
|
||||
(do
|
||||
(append! server-list name)
|
||||
(for-each
|
||||
(fn
|
||||
((io-ref :as string))
|
||||
((ref :as string))
|
||||
(when
|
||||
(not (contains? io-deps io-ref))
|
||||
(append! io-deps io-ref)))
|
||||
(component-io-refs-cached name env io-names)))
|
||||
(append! client-list name))))
|
||||
needed)
|
||||
{:io-deps io-deps :server server-list :components comp-targets :client client-list})))
|
||||
|
||||
(define
|
||||
env-components
|
||||
:effects ()
|
||||
(fn
|
||||
((env :as dict))
|
||||
(filter
|
||||
(not (contains? all-refs ref))
|
||||
(append! all-refs ref)))
|
||||
(scan-io-refs (component-body val) io-names))
|
||||
(for-each
|
||||
(fn
|
||||
((dep :as string))
|
||||
(transitive-io-refs-walk dep seen all-refs env io-names))
|
||||
(scan-refs (component-body val))))
|
||||
(= (type-of val) "macro")
|
||||
(do
|
||||
(for-each
|
||||
(fn
|
||||
((ref :as string))
|
||||
(when
|
||||
(not (contains? all-refs ref))
|
||||
(append! all-refs ref)))
|
||||
(scan-io-refs (macro-body val) io-names))
|
||||
(for-each
|
||||
(fn
|
||||
((dep :as string))
|
||||
(transitive-io-refs-walk dep seen all-refs env io-names))
|
||||
(scan-refs (macro-body val))))
|
||||
:else nil)))))
|
||||
(define
|
||||
transitive-io-refs
|
||||
:effects ()
|
||||
(fn
|
||||
((k :as string))
|
||||
(let ((v (env-get env k))) (or (component? v) (macro? v))))
|
||||
(keys env))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
((name :as string) (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((all-refs (list))
|
||||
(seen (list))
|
||||
(key (if (starts-with? name "~") name (str "~" name))))
|
||||
(transitive-io-refs-walk key seen all-refs env io-names)
|
||||
all-refs)))
|
||||
(define
|
||||
compute-all-io-refs
|
||||
:effects (mutation)
|
||||
(fn
|
||||
((env :as dict) (io-names :as list))
|
||||
(for-each
|
||||
(fn
|
||||
((name :as string))
|
||||
(let
|
||||
((val (env-get env name)))
|
||||
(when
|
||||
(= (type-of val) "component")
|
||||
(component-set-io-refs!
|
||||
val
|
||||
(transitive-io-refs name env io-names)))))
|
||||
(env-components env))))
|
||||
(define
|
||||
component-io-refs-cached
|
||||
:effects ()
|
||||
(fn
|
||||
((name :as string) (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((key (if (starts-with? name "~") name (str "~" name))))
|
||||
(let
|
||||
((val (env-get env key)))
|
||||
(if
|
||||
(and
|
||||
(= (type-of val) "component")
|
||||
(not (nil? (component-io-refs val)))
|
||||
(not (empty? (component-io-refs val))))
|
||||
(component-io-refs val)
|
||||
(transitive-io-refs name env io-names))))))
|
||||
(define
|
||||
component-pure?
|
||||
:effects ()
|
||||
(fn
|
||||
(name (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((key (if (starts-with? name "~") name (str "~" name))))
|
||||
(let
|
||||
((val (if (env-has? env key) (env-get env key) nil)))
|
||||
(if
|
||||
(and
|
||||
(= (type-of val) "component")
|
||||
(not (nil? (component-io-refs val)))
|
||||
(not (empty? (component-io-refs val))))
|
||||
false
|
||||
(empty? (transitive-io-refs name env io-names)))))))
|
||||
(define
|
||||
render-target
|
||||
:effects ()
|
||||
(fn
|
||||
(name (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((key (if (starts-with? name "~") name (str "~" name))))
|
||||
(let
|
||||
((val (if (env-has? env key) (env-get env key) nil)))
|
||||
(if
|
||||
(not (= (type-of val) "component"))
|
||||
"server"
|
||||
(let
|
||||
((affinity (component-affinity val)))
|
||||
(cond
|
||||
(= affinity "server")
|
||||
"server"
|
||||
(= affinity "client")
|
||||
"client"
|
||||
(not (component-pure? name env io-names))
|
||||
"server"
|
||||
:else "client")))))))
|
||||
(define
|
||||
page-render-plan
|
||||
:effects ()
|
||||
(fn
|
||||
((page-source :as string) (env :as dict) (io-names :as list))
|
||||
(let
|
||||
((needed (components-needed page-source env))
|
||||
(comp-targets (dict))
|
||||
(server-list (list))
|
||||
(client-list (list))
|
||||
(io-deps (list)))
|
||||
(for-each
|
||||
(fn
|
||||
((name :as string))
|
||||
(let
|
||||
((target (render-target name env io-names)))
|
||||
(dict-set! comp-targets name target)
|
||||
(if
|
||||
(= target "server")
|
||||
(do
|
||||
(append! server-list name)
|
||||
(for-each
|
||||
(fn
|
||||
((io-ref :as string))
|
||||
(when
|
||||
(not (contains? io-deps io-ref))
|
||||
(append! io-deps io-ref)))
|
||||
(component-io-refs-cached name env io-names)))
|
||||
(append! client-list name))))
|
||||
needed)
|
||||
{:io-deps io-deps :server server-list :components comp-targets :client client-list})))
|
||||
(define
|
||||
env-components
|
||||
:effects ()
|
||||
(fn
|
||||
((env :as dict))
|
||||
(filter
|
||||
(fn
|
||||
((k :as string))
|
||||
(let ((v (env-get env k))) (or (component? v) (macro? v))))
|
||||
(keys env)))))) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (web deps))
|
||||
|
||||
1565
web/engine.sx
1565
web/engine.sx
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user