Merge reactive scope refactor

This commit is contained in:
aaron 2023-09-15 12:54:42 +01:00
parent 0e439f084f
commit efc4798ddb
48 changed files with 2750 additions and 1949 deletions

View file

@ -10,11 +10,16 @@ local _, is_action = require(script.Parent.action)()
local graph = require(script.Parent.graph)
type Node<T> = graph.Node<T>
type Array<V> = { V }
type Map<K, V> = { [K]: V }
-- buffer of event -> callback to connect after properties are set
local event_buffer: { [string]: () -> () } = {}
local event_buffer = {} :: Map<string, () -> ()>
-- buffer of priority -> callback to run after events are connected
local action_buffers = {} :: { { (Instance) -> () } }
local action_buffers = {} :: Map<number, Array<(Instance) -> ()>>
-- lazily create buffers on nil index
setmetatable(action_buffers :: any, {
__index = function(_, i: number)
action_buffers[i] = {}
@ -22,8 +27,9 @@ setmetatable(action_buffers :: any, {
end
})
-- cache used in strict mode to detect duplicate property sets at same nesting levels
local nested_debug_cache: { [number]: { [string]: true } } = {}
-- cache in strict mode to detect duplicate property set at same nesting level
local nested_debug_cache = {} :: Map<number, Map<string, true>>
setmetatable(nested_debug_cache :: any, {
__index = function(_, i: number)
nested_debug_cache[i] = {}
@ -31,28 +37,30 @@ setmetatable(nested_debug_cache :: any, {
end
})
-- a stack used in place of a recursive function to process nesting layers one at a time
-- enforces the behavior of deeper-nested properties taking precedence of lesser-nested ones
-- each nested table occupies two indexes, reference to table itself and the depth number
-- e.g. props = { t1 = { t3 = {} }, t2 = {} } -> { t1, 1, t2, 1, t3, 2 }
-- use stack instead of recursive function to process nested layers one at time
-- deeper-nested properties take precedence over shallower-nested ones
-- each nested layer occupies two indexes: 1. table ref 2. nested depth
-- e.g. { t1 = { t3 = {} }, t2 = {} } -> { t1, 1, t2, 1, t3, 2 }
local nested_stack = {} :: { {} | number }
-- todo: solution without manual updating of this table
-- map of datatype names to class default constructor for aggregate initialization
-- map of datatype names to class default constructor for aggregate init
local aggregates = {}
for i, v in next, {
Vector2 = Vector2,
UDim2 = UDim2,
CFrame = CFrame,
Color3 = Color3,
UDim = UDim,
Rect = Rect,
Color3 = Color3
UDim2 = UDim2,
Vector2 = Vector2,
Vector3 = Vector3,
Rect = Rect
} do
aggregates[i] = v.new
end
-- processes a potentially nested table of values to assign to an instance
local function process_nested(instance: Instance, properties: { [unknown]: unknown })
local function process_props(instance: Instance, properties: Map<unknown, unknown>)
local strict = flags.strict
table.clear(nested_stack)
@ -73,27 +81,27 @@ local function process_nested(instance: Instance, properties: { [unknown]: unkno
if type(value) == "table" then -- attempt aggregate init
local ctor = aggregates[typeof((instance :: any)[property])]
if ctor == nil then
throw(`cannot aggregate construct type {typeof(value)} for property {property}`)
throw(`cannot aggregate type {typeof(value)} for property {property}`)
end
(instance :: any)[property] = ctor(unpack(value :: {}))
elseif type(value) == "function" then
if typeof((instance :: any)[property]) == "RBXScriptSignal" then
event_buffer[property] = value :: () -> () -- add event to buffer
else
bind.property(instance, property, value :: () -> ()) -- bind source
bind.property(instance, property, value :: () -> ()) -- bind property
end
else
(instance :: any)[property] = value -- set property
end
elseif type(property) == "number" then
if type(value) == "function" then
bind.children(instance, value :: () -> { Instance }) -- bind children
bind.children(instance, value :: () -> Instance | Array<Instance>) -- bind children
elseif type(value) == "table" then
if is_action(value) then
table.insert(action_buffers[(value :: any).priority], (value :: any).callback :: () -> ()) -- add action to buffer
else
table.insert(nested_stack, depth + 1) -- push table to stack for later processing
table.insert(nested_stack, value :: {})
table.insert(nested_stack, depth + 1) -- push table to stack for later processing
end
else
(value :: Instance).Parent = instance -- parent child
@ -102,8 +110,8 @@ local function process_nested(instance: Instance, properties: { [unknown]: unkno
end
-- pop next nested table off stack
properties = table.remove(nested_stack) :: {}
depth = table.remove(nested_stack) :: number
properties = table.remove(nested_stack) :: {}
until not properties
end
@ -121,14 +129,14 @@ local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown
end
-- process all properties for immediate setting or buffering
process_nested(instance, properties)
process_props(instance, properties)
-- connect buffered events
for event, fn in next, event_buffer do
(instance :: any)[event]:Connect(fn)
end
-- run buffered actions respecting their priorities
-- run buffered actions
for _, buffer in next, action_buffers do
for _, callback in next, buffer do
callback(instance)
@ -138,7 +146,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 :: () -> Instance)
else
instance.Parent = parent :: Instance
end