mirror of
https://github.com/centau/vide.git
synced 2026-08-20 23:01:37 +00:00
This commit is contained in:
parent
866135b152
commit
e776183452
10 changed files with 480 additions and 367 deletions
|
|
@ -138,7 +138,7 @@ local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown
|
|||
-- finally set parent if any
|
||||
if parent then
|
||||
if type(parent) == "function" then
|
||||
error("cannot set parent to state")
|
||||
bind.parent(instance, parent :: () -> ())
|
||||
else
|
||||
instance.Parent = parent :: Instance
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ local flags = require(script.Parent.flags)
|
|||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local create_node = graph.create_node
|
||||
local get_scope = graph.get_scope
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
local track = graph.track
|
||||
local add_child = graph.add_child
|
||||
local capture_parents = graph.capture_parents
|
||||
|
||||
--[[
|
||||
|
||||
|
|
@ -70,9 +71,13 @@ function bind(instance: Instance, property: string, setter: (Instance) -> ())
|
|||
local binding = create_node(instance)
|
||||
binding.effect = setter
|
||||
|
||||
open_scope(binding.scope)
|
||||
local owner = get_scope()
|
||||
assert(owner)
|
||||
|
||||
capture_parents(binding, setter :: () -> any, instance)
|
||||
open_scope(binding)
|
||||
track(owner)
|
||||
|
||||
setter(instance)
|
||||
|
||||
close_scope()
|
||||
end
|
||||
|
|
@ -84,12 +89,7 @@ local function bind_property(instance: Instance, property: string, fn: () -> unk
|
|||
end
|
||||
|
||||
local function bind_parent(instance: Instance, fn: () -> Instance?)
|
||||
instance.Destroying:Connect(function()
|
||||
instance = nil :: any -- allow gc when destroyed
|
||||
end)
|
||||
|
||||
bind(instance, "Parent", function(instance)
|
||||
local _ = instance -- state will strongly reference instance when parent is bound
|
||||
instance.Parent = fn()
|
||||
end)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ if not game then script = require "test/relative-string" end
|
|||
|
||||
local graph = require(script.Parent.graph)
|
||||
local create_node = graph.create_node
|
||||
local capture_parents = graph.capture_parents
|
||||
local add_child = graph.add_child
|
||||
local update = graph.update
|
||||
local track = graph.track
|
||||
|
|
@ -11,17 +10,19 @@ local open_scope = graph.open_scope
|
|||
local close_scope = graph.close_scope
|
||||
|
||||
local function derive<T>(fn: () -> T): () -> T
|
||||
assert(get_scope())
|
||||
local owner = get_scope()
|
||||
assert(owner)
|
||||
|
||||
local node = create_node((false :: any) :: T)
|
||||
node.effect = function()
|
||||
node.cache = fn()
|
||||
update(node)
|
||||
end
|
||||
|
||||
open_scope(node.scope)
|
||||
add_child(owner, node)
|
||||
|
||||
node.cache = capture_parents(node, fn)
|
||||
open_scope(node)
|
||||
|
||||
node.cache = fn()
|
||||
|
||||
close_scope()
|
||||
|
||||
|
|
|
|||
|
|
@ -11,13 +11,16 @@ export type StartNode<T> = {
|
|||
export type Node<T> = {
|
||||
cache: T,
|
||||
children: { [Node<T>]: true } | false,
|
||||
effect: (T) -> (),
|
||||
effect: (T) -> () | false,
|
||||
cleanups: { () -> () } | false,
|
||||
}
|
||||
|
||||
local active = {} :: { [Node<any>]: true }
|
||||
|
||||
local scopes = { n = 0 } :: { [number]: Node<any>, n: number }
|
||||
|
||||
local WEAK_VALUES = { __mode = "v" }
|
||||
local WEAK_KEYS = { __mode = "k" }
|
||||
local EVALUATION_ERR = "error while evaluating source:\n\n"
|
||||
|
||||
-- runs a given callback in a context that Luau does not allow yielding in
|
||||
|
|
@ -46,34 +49,27 @@ local check_for_yield: <T...>(fn: (T...) -> unknown, T...) -> () do
|
|||
end
|
||||
end
|
||||
|
||||
local function get_scope(): Node<unknown>
|
||||
local function get_stack_scope(offset: number): Node<unknown>?
|
||||
return scopes[scopes.n - offset]
|
||||
end
|
||||
|
||||
local function get_scope(): Node<unknown>?
|
||||
return scopes[scopes.n]
|
||||
end
|
||||
|
||||
local function add_child<T>(parent: Node<any>, child: Node<any>)
|
||||
local function add_child<T>(parent: StartNode<any>, child: Node<any>)
|
||||
if parent.children then
|
||||
parent.children[child] = true
|
||||
else
|
||||
parent.children = { [child] = true :: true }
|
||||
setmetatable(parent.children :: any, WEAK_KEYS) -- todo:
|
||||
end
|
||||
end
|
||||
|
||||
-- local function open_root_scope<T>(node: Node<T>)
|
||||
-- assert(not scopes[1])
|
||||
|
||||
-- local n = scopes.n + 1
|
||||
-- scopes.n = n
|
||||
-- scopes[n] = node
|
||||
-- end
|
||||
|
||||
local function open_scope<T>(node: Node<T>)
|
||||
local n = scopes.n + 1
|
||||
scopes.n = n
|
||||
scopes[n] = node
|
||||
|
||||
-- local parent = scopes[n-1]
|
||||
-- assert(parent)
|
||||
-- add_child(parent, node)
|
||||
end
|
||||
|
||||
local function close_scope()
|
||||
|
|
@ -100,49 +96,71 @@ local function run_cleanups<T>(node: Node<T>)
|
|||
end
|
||||
|
||||
local function run_effect<T>(node: Node<T>)
|
||||
node.effect(node.cache)
|
||||
if node.effect then
|
||||
node.effect(node.cache)
|
||||
end
|
||||
end
|
||||
|
||||
local function destroy<T>(node: Node<T>)
|
||||
run_cleanups(node)
|
||||
for _, child in ipairs(node) do
|
||||
destroy(child)
|
||||
active[node] = nil
|
||||
if node.children then
|
||||
for child in node.children do
|
||||
destroy(child)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- runs node effects, recalculates descendants and runs descendant effects
|
||||
local function update<T>(node: StartNode<T>)
|
||||
local function rec<T>(node: StartNode<T>, update_queue: { Node<any> })
|
||||
if not node.children then return end
|
||||
|
||||
local cache = {}
|
||||
for child in node.children do
|
||||
table.insert(cache, child)
|
||||
for child in next, node.children do
|
||||
table.insert(update_queue, child)
|
||||
rec(child, update_queue)
|
||||
end
|
||||
|
||||
for _, child in next, cache do
|
||||
open_scope(child)
|
||||
run_cleanups(child)
|
||||
run_effect(child)
|
||||
close_scope()
|
||||
update(child)
|
||||
end
|
||||
table.clear(node.children)
|
||||
end
|
||||
|
||||
local function track<T>(node: Node<T>)
|
||||
add_child(node, get_scope())
|
||||
local function update<T>(node: StartNode<T>)
|
||||
local update_queue = {} :: { Node<any> }
|
||||
--assert(#update_queue == 0, "update already in progress")
|
||||
rec(node, update_queue)
|
||||
|
||||
for _, n in next, update_queue do
|
||||
open_scope(n) -- todo
|
||||
run_cleanups(n)
|
||||
run_effect(n)
|
||||
close_scope()
|
||||
end
|
||||
|
||||
table.clear(update_queue)
|
||||
end
|
||||
|
||||
local function track<T>(node: StartNode<T>)
|
||||
local scope = get_scope()
|
||||
assert(scope)
|
||||
if scope.effect then -- todo
|
||||
add_child(node, scope)
|
||||
end
|
||||
end
|
||||
|
||||
local function create_node<T>(value: T): Node<T>
|
||||
return {
|
||||
local node: Node<T> = {
|
||||
cache = value,
|
||||
effect = function() end,
|
||||
cleanups = false,
|
||||
children = false
|
||||
effect = false,
|
||||
cleanups = false :: false,
|
||||
children = false :: false
|
||||
}
|
||||
|
||||
active[node] = true
|
||||
|
||||
return node
|
||||
end
|
||||
|
||||
local function get_children(node: Node<unknown>): { Node<unknown> }
|
||||
local function get_children<T>(node: Node<T>): { Node<unknown> }
|
||||
if not node.children then return {} end
|
||||
|
||||
local children = {}
|
||||
|
|
@ -151,7 +169,7 @@ local function get_children(node: Node<unknown>): { Node<unknown> }
|
|||
table.insert(children, child)
|
||||
end
|
||||
|
||||
return children
|
||||
return children :: { Node<any> }
|
||||
end
|
||||
|
||||
local function create_start_node<T>(value: T): StartNode<T>
|
||||
|
|
@ -159,10 +177,10 @@ local function create_start_node<T>(value: T): StartNode<T>
|
|||
end
|
||||
|
||||
return table.freeze {
|
||||
open_root_scope = open_root_scope,
|
||||
open_scope = open_scope,
|
||||
close_scope = close_scope,
|
||||
get_scope = get_scope,
|
||||
get_stack_scope = get_stack_scope,
|
||||
add_cleanup = add_cleanup,
|
||||
destroy = destroy,
|
||||
run_cleanups = run_cleanups,
|
||||
|
|
|
|||
|
|
@ -9,12 +9,8 @@ type Scope = graph.Scope
|
|||
type StartNode<T> = graph.StartNode<T>
|
||||
local create_node = graph.create_node
|
||||
local create_start_node = graph.create_start_node
|
||||
local create_scope = graph.create_scope
|
||||
local track = graph.track
|
||||
local update = graph.update
|
||||
local capture = graph.capture
|
||||
local capture_parents = graph.capture_parents
|
||||
local add_child = graph.add_child
|
||||
local get_scope = graph.get_scope
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
|
|
@ -33,9 +29,8 @@ end
|
|||
|
||||
-- todo: optimize output array
|
||||
local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI, K) -> VO): () -> { VO }
|
||||
assert(get_scope())
|
||||
|
||||
local root = create_scope()
|
||||
local owner = get_scope()
|
||||
assert(owner)
|
||||
|
||||
local input_cache = {} :: Map<K, VI>
|
||||
local output_cache = {} :: Map<K, VO>
|
||||
|
|
@ -65,7 +60,7 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
|
||||
table.clear(remove_queue)
|
||||
|
||||
open_scope(root)
|
||||
open_scope(owner) -- todo: needed?
|
||||
|
||||
-- process new or changed values
|
||||
for i, v in next, data do
|
||||
|
|
@ -73,10 +68,11 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
|
||||
if cv ~= v then
|
||||
if cv == nil then
|
||||
local scope = create_scope()
|
||||
local scope = create_node(false)
|
||||
scopes[i] = scope
|
||||
|
||||
open_scope(scope)
|
||||
track(owner)
|
||||
|
||||
local node = create_start_node(v)
|
||||
input_nodes[i] = node
|
||||
|
|
@ -112,9 +108,11 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
update_children(input())
|
||||
end
|
||||
|
||||
local value = capture_parents(output, input)
|
||||
open_scope(output)
|
||||
|
||||
output.cache = update_children(value)
|
||||
output.cache = update_children(input())
|
||||
|
||||
close_scope()
|
||||
|
||||
return function()
|
||||
track(output)
|
||||
|
|
@ -124,9 +122,8 @@ end
|
|||
|
||||
-- todo: optimize output array
|
||||
local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () -> K) -> VO): () -> { VO }
|
||||
assert(get_scope())
|
||||
|
||||
local root = create_scope()
|
||||
local owner = get_scope()
|
||||
assert(owner)
|
||||
|
||||
local cur_input_cache_up = {} :: Map<VI, K>
|
||||
local new_input_cache_up = {} :: Map<VI, K>
|
||||
|
|
@ -159,7 +156,7 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
local cv = cur_input_cache[v]
|
||||
|
||||
if cv == nil then
|
||||
local scope = create_scope()
|
||||
local scope = create_node(false)
|
||||
scopes[v] = scope
|
||||
|
||||
open_scope(scope)
|
||||
|
|
@ -213,9 +210,11 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
update_children(input())
|
||||
end
|
||||
|
||||
local value = capture_parents(output, input)
|
||||
open_scope(output)
|
||||
|
||||
output.cache = update_children(value)
|
||||
output.cache = update_children(input())
|
||||
|
||||
close_scope()
|
||||
|
||||
return function()
|
||||
track(output)
|
||||
|
|
|
|||
|
|
@ -5,24 +5,24 @@ local throw = require(script.Parent.throw)
|
|||
local on_gc = require(script.Parent.on_gc)()
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local create_scope = graph.create_scope
|
||||
local create_node = graph.create_node
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
local get_scope = graph.get_scope
|
||||
local destroy = graph.destroy
|
||||
|
||||
local function root<T>(fn: () -> T): (T, () -> ())
|
||||
assert(not get_scope())
|
||||
local scope = create_scope()
|
||||
--assert(not get_scope())
|
||||
local node = create_node(false)
|
||||
|
||||
open_scope(scope)
|
||||
open_scope(node)
|
||||
|
||||
local v = fn()
|
||||
|
||||
close_scope()
|
||||
|
||||
return v, function()
|
||||
destroy(scope)
|
||||
destroy(node)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@ type Node<T> = graph.Node<T>
|
|||
type StartNode<T> = graph.StartNode<T>
|
||||
local create_node = graph.create_node
|
||||
local create_start_node = graph.create_start_node
|
||||
local get_scope = graph.get_scope
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
local update = graph.update
|
||||
local capture = graph.capture
|
||||
local add_child = graph.add_child
|
||||
local track = graph.track
|
||||
|
||||
|
|
@ -148,7 +150,18 @@ local springs: { [SpringData<any>]: StartNode<any> } = {}
|
|||
setmetatable(springs, { __mode = "v" })
|
||||
|
||||
local function spring<T>(source: () -> T, period: number?, damping_ratio: number?): () -> T
|
||||
local inputs, initial_value = capture(source)
|
||||
local owner = get_scope()
|
||||
assert(owner)
|
||||
|
||||
local updater = create_node(false)
|
||||
updater.effect = true :: any -- todo
|
||||
|
||||
add_child(owner, updater)
|
||||
open_scope(updater)
|
||||
|
||||
local initial_value = source()
|
||||
|
||||
close_scope()
|
||||
|
||||
local vtype = typeof(initial_value)
|
||||
|
||||
|
|
@ -178,10 +191,9 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
|||
|
||||
source_value = initial_value,
|
||||
}
|
||||
|
||||
|
||||
local output = create_start_node(initial_value)
|
||||
|
||||
local updater = create_node(false)
|
||||
updater.effect = function()
|
||||
local v = source()
|
||||
data.x1_123, data.x1_456 = type_to_vec6[typeof(v)](v)
|
||||
|
|
@ -189,10 +201,6 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
|||
springs[data] = output -- todo: investigate why insertion is not O(1) at ~20k springs
|
||||
end
|
||||
|
||||
for _, input in next, inputs do
|
||||
add_child(input, updater)
|
||||
end
|
||||
|
||||
return function()
|
||||
track(output)
|
||||
return output.cache
|
||||
|
|
@ -258,6 +266,7 @@ local function update_spring_sources()
|
|||
else
|
||||
output.cache = vec6_to_type[typeof(data.source_value)](x0_123, x0_456)
|
||||
end
|
||||
|
||||
update(output)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,25 @@
|
|||
if not game then script = require "test/relative-string" end
|
||||
|
||||
local create = require(script.Parent.create)
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local refs = graph.refs
|
||||
local create_node = graph.create_node
|
||||
local get_stack_scope = graph.get_stack_scope
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
|
||||
local non_tracking_scope = create_node(false)
|
||||
|
||||
local function untrack<T>(source: () -> T): T
|
||||
local initial = #refs
|
||||
local scope = get_stack_scope(1)
|
||||
|
||||
local value = source()
|
||||
open_scope(scope or non_tracking_scope)
|
||||
|
||||
-- remove any references made since `untrack()` was called
|
||||
for i = initial, #refs do
|
||||
refs[i] = nil
|
||||
end
|
||||
local v = source()
|
||||
|
||||
return value
|
||||
close_scope()
|
||||
|
||||
return v
|
||||
end
|
||||
|
||||
return untrack
|
||||
|
|
|
|||
|
|
@ -2,20 +2,24 @@ if not game then script = require "test/relative-string" end
|
|||
|
||||
local graph = require(script.Parent.graph)
|
||||
local create_node = graph.create_node
|
||||
local capture_parents = graph.capture_parents
|
||||
local get_scope = graph.get_scope
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
local add_child = graph.add_child
|
||||
local track = graph.track
|
||||
|
||||
local function watch(effect: () -> ())
|
||||
assert(get_scope())
|
||||
local owner = get_scope()
|
||||
assert(owner)
|
||||
|
||||
local node = create_node(false)
|
||||
node.effect = effect
|
||||
|
||||
open_scope(node.scope)
|
||||
add_child(owner, node)
|
||||
|
||||
capture_parents(node, effect :: () -> any)
|
||||
open_scope(node)
|
||||
|
||||
effect()
|
||||
|
||||
close_scope()
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue