-
Notifications
You must be signed in to change notification settings - Fork 0
/
cell.luau
92 lines (86 loc) · 2.17 KB
/
cell.luau
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
local FENV_KEY = "74709086-5669-43dc-a7d1-195d71ae0464"
local function topologicallyUpdate(rootDependents: any)
local visited = {}
local stack = {}
local function visit(d)
if visited[d] then
return
end
visited[d] = true
for d in d.dependents do
visit(d)
end
table.insert(stack, d)
end
for d in rootDependents do
visit(d)
end
for i = #stack, 1, -1 do
local d = stack[i]
d:recompute()
end
end
local function metaIndex(self, key)
if key == "value" then
local ourData = getmetatable(self)._data
local d = getfenv(0)[FENV_KEY]
if d and d.seen and not d.seen[self] then
d.seen[self] = true
if d.roots then
d.roots[ourData] = true
end
ourData.dependents[d] = true
table.insert(d.unsubscribes, function()
ourData.dependents[d] = nil
end)
end
return ourData.value
else
error(string.format('Cannot get key %q from a cell, only valid field is "value"', key), 2)
end
end
local function metaNewIndex(self, key, value)
if key == "value" then
local ourData = getmetatable(self)._data
local fenv = getfenv(0)[FENV_KEY]
if fenv == nil then
if ourData.value ~= value then
ourData.value = value
local dependents = ourData.dependents
ourData.dependents = setmetatable({}, { __mode = "k" })
topologicallyUpdate(dependents)
end
elseif fenv.batch then
if ourData.value ~= value then
ourData.value = value
fenv.batch[ourData] = true
end
elseif fenv.seen then
error("Cannot set cell value from within a cell computation", 2)
end
else
error(string.format('Cannot set key %q in a cell, only valid field is "value"', key), 2)
end
end
local function metaToString(self)
local ourData = getmetatable(self)._data
return tostring(ourData.value)
end
local function cell<T>(initial: T): { value: T }
local cell = newproxy(true)
local mt = getmetatable(cell)
mt.__index = metaIndex
mt.__newindex = metaNewIndex
mt.__tostring = metaToString
local data = {
proxy = cell, -- Store a reference to the proxy so it doesn't get garbage collected.
seen = {},
unsubscribes = {},
dependents = setmetatable({}, { __mode = "k" }),
value = initial,
subscriptions = {},
}
mt._data = data
return cell
end
return cell