mirror of
https://github.com/centau/vide.git
synced 2026-08-20 23:01:37 +00:00
Minor refactors
Also fixed potential bug with `create()` being called recursively if a property binding passed as a property to `create()` also calls `create()`
This commit is contained in:
parent
8eb5f96c5b
commit
c288cb92c4
16 changed files with 169 additions and 130 deletions
148
src/apply.luau
148
src/apply.luau
|
|
@ -13,41 +13,58 @@ 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 = {} :: Map<string, () -> ()>
|
||||
local free_caches: {
|
||||
-- event listeners to connect after properties are set
|
||||
events: Map<
|
||||
string, -- event name
|
||||
() -> () -- listener
|
||||
>,
|
||||
|
||||
-- buffer of priority -> callback to run after events are connected
|
||||
local action_buffers = {} :: Map<number, Array<(Instance) -> ()>>
|
||||
-- actions to run after events are connected
|
||||
actions: Map<
|
||||
number, -- priority
|
||||
Array<(Instance) -> ()> -- action callbacks
|
||||
>,
|
||||
|
||||
-- lazily create buffers on nil index
|
||||
setmetatable(action_buffers :: any, {
|
||||
__index = function(_, i: number)
|
||||
action_buffers[i] = {}
|
||||
return action_buffers[i]
|
||||
-- cache to detect duplicate property setting at same nesting depth
|
||||
nested_debug: Map<
|
||||
number, -- depth
|
||||
Map<string, true> -- set of property names
|
||||
>,
|
||||
|
||||
-- use stack instead of recursive function to process nesting 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 }
|
||||
nested_stack: { {} | number }
|
||||
}?
|
||||
|
||||
local function borrow_caches(): typeof(assert(free_caches))
|
||||
if free_caches then
|
||||
local caches = free_caches :: typeof(assert(free_caches))
|
||||
free_caches = nil
|
||||
return caches
|
||||
else
|
||||
return {
|
||||
events = {},
|
||||
actions = setmetatable({} :: any, { -- lazy init
|
||||
__index = function(self, i) self[i] = {}; return self[i] end
|
||||
}),
|
||||
nested_debug = setmetatable({} :: any, {
|
||||
__index = function(self, i: number) self[i] = {}; return self[i] end
|
||||
}),
|
||||
nested_stack = {}
|
||||
}
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- cache in strict mode to detect duplicate property set at same nesting level
|
||||
local nested_debug_cache = {} :: Map<number, Map<string, true>>
|
||||
local function return_caches(caches: typeof(free_caches) )
|
||||
free_caches = caches
|
||||
end
|
||||
|
||||
setmetatable(nested_debug_cache :: any, {
|
||||
__index = function(_, i: number)
|
||||
nested_debug_cache[i] = {}
|
||||
return nested_debug_cache[i]
|
||||
end
|
||||
})
|
||||
|
||||
-- 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 init
|
||||
local aggregates = {}
|
||||
|
||||
for i, v in next, {
|
||||
for name, class in {
|
||||
CFrame = CFrame,
|
||||
Color3 = Color3,
|
||||
UDim = UDim,
|
||||
|
|
@ -55,27 +72,39 @@ for i, v in next, {
|
|||
Vector2 = Vector2,
|
||||
Vector3 = Vector3,
|
||||
Rect = Rect
|
||||
} do
|
||||
aggregates[i] = v.new
|
||||
} :: Map<string, { [string]: any }> do
|
||||
aggregates[name] = class.new
|
||||
end
|
||||
|
||||
-- processes a potentially nested table of values to assign to an instance
|
||||
local function process_props(instance: Instance, properties: Map<unknown, unknown>)
|
||||
-- applies table of nested properties to an instance using full vide semantics
|
||||
local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown }): T
|
||||
if not properties then
|
||||
throw("attempt to call a constructor returned by create() with no properties")
|
||||
end
|
||||
|
||||
local strict = flags.strict
|
||||
|
||||
table.clear(nested_stack)
|
||||
if strict then table.clear(nested_debug_cache) end
|
||||
-- queue parent assignment if any for last
|
||||
local parent: unknown = properties.Parent
|
||||
|
||||
local caches = borrow_caches()
|
||||
local events = caches.events
|
||||
local actions = caches.actions
|
||||
local nested_debug = caches.nested_debug
|
||||
local nested_stack = caches.nested_stack
|
||||
|
||||
-- process all properties
|
||||
local depth = 1
|
||||
|
||||
repeat
|
||||
for property, value in properties do
|
||||
if property == "Parent" then continue end
|
||||
|
||||
if type(property) == "string" then
|
||||
if strict then -- check for duplicate prop assignment at nesting layer
|
||||
if nested_debug_cache[depth][property] then
|
||||
if strict then -- check for duplicate prop assignment at nesting depth
|
||||
if nested_debug[depth][property] then
|
||||
throw(`duplicate property {property} at depth {depth}`)
|
||||
end
|
||||
nested_debug_cache[depth][property] = true
|
||||
nested_debug[depth][property] = true
|
||||
end
|
||||
|
||||
if type(value) == "table" then -- attempt aggregate init
|
||||
|
|
@ -86,7 +115,7 @@ local function process_props(instance: Instance, properties: Map<unknown, unknow
|
|||
(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
|
||||
events[property] = value :: () -> () -- add event to buffer
|
||||
else
|
||||
bind.property(instance, property, value :: () -> ()) -- bind property
|
||||
end
|
||||
|
|
@ -98,7 +127,7 @@ local function process_props(instance: Instance, properties: Map<unknown, unknow
|
|||
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
|
||||
table.insert(actions[(value :: any).priority], (value :: any).callback :: () -> ()) -- add action to buffer
|
||||
else
|
||||
table.insert(nested_stack, value :: {})
|
||||
table.insert(nested_stack, depth + 1) -- push table to stack for later processing
|
||||
|
|
@ -109,40 +138,17 @@ local function process_props(instance: Instance, properties: Map<unknown, unknow
|
|||
end
|
||||
end
|
||||
|
||||
-- pop next nested table off stack
|
||||
depth = table.remove(nested_stack) :: number
|
||||
properties = table.remove(nested_stack) :: {}
|
||||
|
||||
until not properties
|
||||
end
|
||||
|
||||
-- applies table of nested properties to an instance using full vide semantics
|
||||
local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown }): T
|
||||
if not properties then
|
||||
throw("no properties given, did you forget to call the constructor returned by create()?")
|
||||
for event, listener in next, events do
|
||||
(instance :: any)[event]:Connect(listener)
|
||||
end
|
||||
|
||||
-- queue parent assignment if any for last
|
||||
local parent: unknown = properties.Parent
|
||||
if parent then properties.Parent = nil end
|
||||
|
||||
-- reset buffers
|
||||
table.clear(event_buffer)
|
||||
for _, buffer in next, action_buffers do
|
||||
table.clear(buffer)
|
||||
end
|
||||
|
||||
-- process all properties for immediate setting or buffering
|
||||
process_props(instance, properties)
|
||||
|
||||
-- connect buffered events
|
||||
for event, fn in next, event_buffer do
|
||||
(instance :: any)[event]:Connect(fn)
|
||||
end
|
||||
|
||||
-- run buffered actions
|
||||
for _, buffer in next, action_buffers do
|
||||
for _, callback in next, buffer do
|
||||
for _, queued in next, actions do
|
||||
for _, callback in next, queued do
|
||||
callback(instance)
|
||||
end
|
||||
end
|
||||
|
|
@ -156,6 +162,14 @@ local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown
|
|||
end
|
||||
end
|
||||
|
||||
-- clear caches
|
||||
table.clear(events)
|
||||
for _, queued in next, actions do table.clear(queued) end
|
||||
if strict then table.clear(nested_debug) end
|
||||
table.clear(nested_stack)
|
||||
|
||||
return_caches(caches)
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue