mirror of
https://github.com/littensy/charm.git
synced 2026-10-23 05:46:27 +00:00
fix: allow nested captures
This commit is contained in:
parent
c5d4ef470b
commit
b9d576c876
4 changed files with 38 additions and 19 deletions
|
|
@ -28,4 +28,21 @@ return function()
|
|||
expect(deps[a]).to.be.ok()
|
||||
expect(result).to.equal(1)
|
||||
end)
|
||||
|
||||
it("allows nested captures", function()
|
||||
local a = atom(1)
|
||||
local b = atom(1)
|
||||
local inner
|
||||
local outer = store.capture(function()
|
||||
inner = store.capture(function()
|
||||
return b()
|
||||
end)
|
||||
return a()
|
||||
end)
|
||||
|
||||
assert(outer[a], "outer did not capture a")
|
||||
assert(outer[b], "outer did not capture b")
|
||||
assert(inner[a] == nil, "inner captured a")
|
||||
assert(inner[b], "inner did not capture b")
|
||||
end)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ local function atom<T>(state: T, options: AtomOptions<T>?): Atom<T>
|
|||
|
||||
local function atom(...)
|
||||
if select("#", ...) == 0 then
|
||||
if store.capturing.current then
|
||||
store.capturing.current[atom] = true
|
||||
for set in store.capturing do
|
||||
set[atom] = true
|
||||
end
|
||||
|
||||
return state
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
local types = require(script.Parent.types)
|
||||
type Atom<T> = types.Atom<T>
|
||||
type Set<T> = types.Set<T>
|
||||
type WeakMap<K, V> = types.WeakMap<K, V>
|
||||
|
||||
local listeners: WeakMap<Atom<any>, WeakMap<() -> (), unknown>> = setmetatable({}, { __mode = "k" })
|
||||
|
||||
local capturing: { current: { [Atom<any>]: true }? } = {}
|
||||
local batched: { [() -> ()]: true } = {}
|
||||
local capturing: Set<Set<Atom<any>>> = {}
|
||||
local batched: Set<() -> ()> = {}
|
||||
local batching = false
|
||||
|
||||
--[=[
|
||||
|
|
@ -81,18 +81,16 @@ end
|
|||
@param molecule The function to run.
|
||||
@return A tuple containing the captured atoms and the result of the function.
|
||||
]=]
|
||||
local function capture<T>(molecule: () -> T): ({ [Atom<any>]: true }, T)
|
||||
local function capture<T>(molecule: () -> T): (Set<Atom<any>>, T)
|
||||
if listeners[molecule] then
|
||||
return { [molecule] = true }, molecule()
|
||||
end
|
||||
|
||||
local dependencies = {}
|
||||
local previous = capturing.current
|
||||
|
||||
capturing.current = dependencies
|
||||
local dependencies: Set<Atom<any>> = {}
|
||||
capturing[dependencies] = true
|
||||
|
||||
local result = try(molecule, function()
|
||||
capturing.current = previous
|
||||
capturing[dependencies] = nil
|
||||
end)
|
||||
|
||||
return dependencies, result
|
||||
|
|
@ -135,16 +133,18 @@ local function peek<T, U...>(callback: ((U...) -> T) | T, ...: U...): T
|
|||
return callback
|
||||
end
|
||||
|
||||
if not capturing.current then
|
||||
if not next(capturing) then
|
||||
return callback(...)
|
||||
end
|
||||
|
||||
local previous = capturing.current
|
||||
local snapshot = table.clone(capturing)
|
||||
|
||||
capturing.current = nil
|
||||
table.clear(capturing)
|
||||
|
||||
local result = try(callback, function()
|
||||
capturing.current = previous
|
||||
for set in snapshot do
|
||||
capturing[set] = true
|
||||
end
|
||||
end, ...)
|
||||
|
||||
return result
|
||||
|
|
@ -157,7 +157,7 @@ end
|
|||
@param listener The function to call when the atoms change.
|
||||
@param ref Optionally bind the lifetime of the listener to a value.
|
||||
]=]
|
||||
local function connect(dependencies: { [Atom<any>]: true }, listener: () -> (), ref: unknown?)
|
||||
local function connect(dependencies: Set<Atom<any>>, listener: () -> (), ref: unknown?)
|
||||
for atom in dependencies do
|
||||
listeners[atom][listener] = ref or true
|
||||
end
|
||||
|
|
@ -169,7 +169,7 @@ end
|
|||
@param dependencies The atoms to stop listening to.
|
||||
@param listener The function to stop calling when the atoms change.
|
||||
]=]
|
||||
local function disconnect(dependencies: { [Atom<any>]: true }, listener: () -> ())
|
||||
local function disconnect(dependencies: Set<Atom<any>>, listener: () -> ())
|
||||
for atom in dependencies do
|
||||
listeners[atom][listener] = nil
|
||||
end
|
||||
|
|
@ -187,10 +187,10 @@ end
|
|||
]=]
|
||||
local function reconnect<T>(
|
||||
molecule: Atom<T>,
|
||||
dependencies: { [Atom<any>]: true },
|
||||
dependencies: Set<Atom<any>>,
|
||||
listener: () -> (),
|
||||
ref: unknown?
|
||||
): ({ [Atom<any>]: true }, T)
|
||||
): (Set<Atom<any>>, T)
|
||||
disconnect(dependencies, listener)
|
||||
local nextDependencies, state = capture(molecule)
|
||||
connect(nextDependencies, listener, ref)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
export type Set<T> = { [T]: true }
|
||||
|
||||
export type WeakMap<K, V> = typeof(setmetatable({} :: { [K]: V }, { __mode = "k" }))
|
||||
|
||||
--[=[
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue