mirror of
https://github.com/littensy/charm.git
synced 2026-10-22 21:01:38 +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(deps[a]).to.be.ok()
|
||||||
expect(result).to.equal(1)
|
expect(result).to.equal(1)
|
||||||
end)
|
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
|
end
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ local function atom<T>(state: T, options: AtomOptions<T>?): Atom<T>
|
||||||
|
|
||||||
local function atom(...)
|
local function atom(...)
|
||||||
if select("#", ...) == 0 then
|
if select("#", ...) == 0 then
|
||||||
if store.capturing.current then
|
for set in store.capturing do
|
||||||
store.capturing.current[atom] = true
|
set[atom] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
local types = require(script.Parent.types)
|
local types = require(script.Parent.types)
|
||||||
type Atom<T> = types.Atom<T>
|
type Atom<T> = types.Atom<T>
|
||||||
|
type Set<T> = types.Set<T>
|
||||||
type WeakMap<K, V> = types.WeakMap<K, V>
|
type WeakMap<K, V> = types.WeakMap<K, V>
|
||||||
|
|
||||||
local listeners: WeakMap<Atom<any>, WeakMap<() -> (), unknown>> = setmetatable({}, { __mode = "k" })
|
local listeners: WeakMap<Atom<any>, WeakMap<() -> (), unknown>> = setmetatable({}, { __mode = "k" })
|
||||||
|
local capturing: Set<Set<Atom<any>>> = {}
|
||||||
local capturing: { current: { [Atom<any>]: true }? } = {}
|
local batched: Set<() -> ()> = {}
|
||||||
local batched: { [() -> ()]: true } = {}
|
|
||||||
local batching = false
|
local batching = false
|
||||||
|
|
||||||
--[=[
|
--[=[
|
||||||
|
|
@ -81,18 +81,16 @@ end
|
||||||
@param molecule The function to run.
|
@param molecule The function to run.
|
||||||
@return A tuple containing the captured atoms and the result of the function.
|
@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
|
if listeners[molecule] then
|
||||||
return { [molecule] = true }, molecule()
|
return { [molecule] = true }, molecule()
|
||||||
end
|
end
|
||||||
|
|
||||||
local dependencies = {}
|
local dependencies: Set<Atom<any>> = {}
|
||||||
local previous = capturing.current
|
capturing[dependencies] = true
|
||||||
|
|
||||||
capturing.current = dependencies
|
|
||||||
|
|
||||||
local result = try(molecule, function()
|
local result = try(molecule, function()
|
||||||
capturing.current = previous
|
capturing[dependencies] = nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return dependencies, result
|
return dependencies, result
|
||||||
|
|
@ -135,16 +133,18 @@ local function peek<T, U...>(callback: ((U...) -> T) | T, ...: U...): T
|
||||||
return callback
|
return callback
|
||||||
end
|
end
|
||||||
|
|
||||||
if not capturing.current then
|
if not next(capturing) then
|
||||||
return callback(...)
|
return callback(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
local previous = capturing.current
|
local snapshot = table.clone(capturing)
|
||||||
|
|
||||||
capturing.current = nil
|
table.clear(capturing)
|
||||||
|
|
||||||
local result = try(callback, function()
|
local result = try(callback, function()
|
||||||
capturing.current = previous
|
for set in snapshot do
|
||||||
|
capturing[set] = true
|
||||||
|
end
|
||||||
end, ...)
|
end, ...)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
@ -157,7 +157,7 @@ end
|
||||||
@param listener The function to call when the atoms change.
|
@param listener The function to call when the atoms change.
|
||||||
@param ref Optionally bind the lifetime of the listener to a value.
|
@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
|
for atom in dependencies do
|
||||||
listeners[atom][listener] = ref or true
|
listeners[atom][listener] = ref or true
|
||||||
end
|
end
|
||||||
|
|
@ -169,7 +169,7 @@ end
|
||||||
@param dependencies The atoms to stop listening to.
|
@param dependencies The atoms to stop listening to.
|
||||||
@param listener The function to stop calling when the atoms change.
|
@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
|
for atom in dependencies do
|
||||||
listeners[atom][listener] = nil
|
listeners[atom][listener] = nil
|
||||||
end
|
end
|
||||||
|
|
@ -187,10 +187,10 @@ end
|
||||||
]=]
|
]=]
|
||||||
local function reconnect<T>(
|
local function reconnect<T>(
|
||||||
molecule: Atom<T>,
|
molecule: Atom<T>,
|
||||||
dependencies: { [Atom<any>]: true },
|
dependencies: Set<Atom<any>>,
|
||||||
listener: () -> (),
|
listener: () -> (),
|
||||||
ref: unknown?
|
ref: unknown?
|
||||||
): ({ [Atom<any>]: true }, T)
|
): (Set<Atom<any>>, T)
|
||||||
disconnect(dependencies, listener)
|
disconnect(dependencies, listener)
|
||||||
local nextDependencies, state = capture(molecule)
|
local nextDependencies, state = capture(molecule)
|
||||||
connect(nextDependencies, listener, ref)
|
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" }))
|
export type WeakMap<K, V> = typeof(setmetatable({} :: { [K]: V }, { __mode = "k" }))
|
||||||
|
|
||||||
--[=[
|
--[=[
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue