@rbxts/centurion-ui 1.0.8 → 2.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -15
- package/centurion.luau +10 -0
- package/package.json +13 -23
- package/src/analyse/cursor.luau +25 -0
- package/src/analyse/highlight.luau +91 -0
- package/src/analyse/init.luau +318 -0
- package/src/analyse/leventine.luau +298 -0
- package/src/analyse/match.luau +39 -0
- package/src/analyse/suggest.luau +41 -0
- package/src/app/header.luau +128 -0
- package/src/app/history.luau +258 -0
- package/src/app/init.luau +699 -0
- package/src/app/suggestions.luau +419 -0
- package/src/button.luau +13 -0
- package/src/components/background.luau +37 -0
- package/src/components/container.luau +33 -0
- package/src/components/corner.luau +11 -0
- package/src/components/divider.luau +28 -0
- package/src/components/flex.luau +108 -0
- package/src/components/gap.luau +25 -0
- package/src/components/image.luau +33 -0
- package/src/components/padding.luau +59 -0
- package/src/components/stroke.luau +20 -0
- package/src/components/text.luau +57 -0
- package/src/components/textbox.luau +186 -0
- package/src/fade.luau +23 -0
- package/src/init.luau +142 -0
- package/src/input.luau +16 -0
- package/src/px.luau +68 -0
- package/src/rich.luau +14 -0
- package/src/state.luau +33 -0
- package/src/theme.luau +70 -0
- package/src/ts/index.d.ts +60 -0
- package/src/ts/init.luau +5 -0
- package/src/types.luau +85 -0
- package/vide/init.luau +8 -0
- package/vide/src/action.luau +25 -0
- package/vide/src/apply.luau +162 -0
- package/vide/src/batch.luau +23 -0
- package/vide/src/branch.luau +40 -0
- package/vide/src/changed.luau +18 -0
- package/vide/src/cleanup.luau +43 -0
- package/vide/src/context.luau +74 -0
- package/vide/src/create.luau +107 -0
- package/vide/src/defaults.luau +113 -0
- package/vide/src/derive.luau +18 -0
- package/vide/src/effect.luau +12 -0
- package/vide/src/flags.luau +12 -0
- package/vide/src/graph.luau +321 -0
- package/vide/src/implicit_effect.luau +125 -0
- package/vide/src/indexes.luau +121 -0
- package/vide/src/init.luau +10 -0
- package/vide/src/lib.luau +120 -0
- package/vide/src/mount.luau +11 -0
- package/vide/src/read.luau +5 -0
- package/vide/src/root.luau +35 -0
- package/vide/src/show.luau +36 -0
- package/vide/src/source.luau +31 -0
- package/vide/src/spring.luau +328 -0
- package/vide/src/switch.luau +114 -0
- package/vide/src/timeout.luau +27 -0
- package/vide/src/untrack.luau +25 -0
- package/vide/src/values.luau +131 -0
- package/out/app/centurion-app.d.ts +0 -3
- package/out/app/centurion-app.luau +0 -86
- package/out/app/index.d.ts +0 -32
- package/out/app/init.luau +0 -105
- package/out/components/history/history-line.d.ts +0 -9
- package/out/components/history/history-line.luau +0 -88
- package/out/components/history/history-list.d.ts +0 -11
- package/out/components/history/history-list.luau +0 -67
- package/out/components/history/index.d.ts +0 -1
- package/out/components/history/init.luau +0 -7
- package/out/components/index.d.ts +0 -2
- package/out/components/init.luau +0 -10
- package/out/components/suggestions/badge.d.ts +0 -16
- package/out/components/suggestions/badge.luau +0 -40
- package/out/components/suggestions/index.d.ts +0 -1
- package/out/components/suggestions/init.luau +0 -7
- package/out/components/suggestions/main-suggestion.d.ts +0 -9
- package/out/components/suggestions/main-suggestion.luau +0 -232
- package/out/components/suggestions/suggestion-list.d.ts +0 -8
- package/out/components/suggestions/suggestion-list.luau +0 -96
- package/out/components/suggestions/suggestions.d.ts +0 -2
- package/out/components/suggestions/suggestions.luau +0 -64
- package/out/components/suggestions/types.d.ts +0 -6
- package/out/components/suggestions/types.luau +0 -2
- package/out/components/suggestions/util.d.ts +0 -1
- package/out/components/suggestions/util.luau +0 -22
- package/out/components/terminal/command.d.ts +0 -6
- package/out/components/terminal/command.luau +0 -74
- package/out/components/terminal/index.d.ts +0 -1
- package/out/components/terminal/init.luau +0 -7
- package/out/components/terminal/suggestion.d.ts +0 -26
- package/out/components/terminal/suggestion.luau +0 -205
- package/out/components/terminal/terminal-text-field/index.d.ts +0 -1
- package/out/components/terminal/terminal-text-field/init.luau +0 -7
- package/out/components/terminal/terminal-text-field/suggestion.d.ts +0 -5
- package/out/components/terminal/terminal-text-field/suggestion.luau +0 -124
- package/out/components/terminal/terminal-text-field/terminal-text-field.d.ts +0 -13
- package/out/components/terminal/terminal-text-field/terminal-text-field.luau +0 -278
- package/out/components/terminal/terminal.d.ts +0 -2
- package/out/components/terminal/terminal.luau +0 -304
- package/out/components/ui/frame.d.ts +0 -17
- package/out/components/ui/frame.luau +0 -32
- package/out/components/ui/group.d.ts +0 -15
- package/out/components/ui/group.luau +0 -29
- package/out/components/ui/layer.d.ts +0 -8
- package/out/components/ui/layer.luau +0 -26
- package/out/components/ui/outline.d.ts +0 -15
- package/out/components/ui/outline.luau +0 -116
- package/out/components/ui/padding.d.ts +0 -10
- package/out/components/ui/padding.luau +0 -19
- package/out/components/ui/scrolling-frame.d.ts +0 -14
- package/out/components/ui/scrolling-frame.luau +0 -44
- package/out/components/ui/text-field.d.ts +0 -12
- package/out/components/ui/text-field.luau +0 -60
- package/out/components/ui/text.d.ts +0 -17
- package/out/components/ui/text.luau +0 -61
- package/out/constants/options.d.ts +0 -2
- package/out/constants/options.luau +0 -27
- package/out/constants/text.d.ts +0 -3
- package/out/constants/text.luau +0 -9
- package/out/constants/util.d.ts +0 -1
- package/out/constants/util.luau +0 -7
- package/out/hooks/use-client.d.ts +0 -2
- package/out/hooks/use-client.luau +0 -17
- package/out/hooks/use-event.d.ts +0 -14
- package/out/hooks/use-event.luau +0 -58
- package/out/hooks/use-px.d.ts +0 -19
- package/out/hooks/use-px.luau +0 -78
- package/out/index.d.ts +0 -2
- package/out/init.luau +0 -10
- package/out/palette.d.ts +0 -47
- package/out/palette.luau +0 -42
- package/out/store.d.ts +0 -12
- package/out/store.luau +0 -26
- package/out/types.d.ts +0 -44
- package/out/types.luau +0 -2
- package/out/utils/string.d.ts +0 -5
- package/out/utils/string.luau +0 -25
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
local Enum = game and Enum or require "../test/mock".Enum :: never
|
|
2
|
+
local Color3 = game and Color3 or require "../test/mock".Color3 :: never
|
|
3
|
+
|
|
4
|
+
return {
|
|
5
|
+
Part = {
|
|
6
|
+
Material = Enum.Material.SmoothPlastic,
|
|
7
|
+
Size = vector.create(1, 1, 1),
|
|
8
|
+
Anchored = true
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
BillboardGui = {
|
|
12
|
+
ResetOnSpawn = false,
|
|
13
|
+
ZIndexBehavior = Enum.ZIndexBehavior.Sibling
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
CanvasGroup = nil,
|
|
17
|
+
|
|
18
|
+
Frame = {
|
|
19
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
20
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
21
|
+
BorderSizePixel = 0
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
ImageButton = {
|
|
25
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
26
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
27
|
+
BorderSizePixel = 0,
|
|
28
|
+
AutoButtonColor = false
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
ImageLabel = {
|
|
32
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
33
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
34
|
+
BorderSizePixel = 0,
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
ScreenGui = {
|
|
38
|
+
ResetOnSpawn = false,
|
|
39
|
+
ZIndexBehavior = Enum.ZIndexBehavior.Sibling
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
ScrollingFrame = {
|
|
43
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
44
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
45
|
+
BorderSizePixel = 0,
|
|
46
|
+
ScrollBarImageColor3 = Color3.new(0, 0, 0)
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
SurfaceGui = {
|
|
50
|
+
ResetOnSpawn = false,
|
|
51
|
+
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
|
52
|
+
|
|
53
|
+
PixelsPerStud = 50,
|
|
54
|
+
SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
TextBox = {
|
|
58
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
59
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
60
|
+
BorderSizePixel = 0,
|
|
61
|
+
ClearTextOnFocus = false,
|
|
62
|
+
Font = Enum.Font.SourceSans,
|
|
63
|
+
Text = "",
|
|
64
|
+
TextColor3 = Color3.new(0, 0, 0)
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
TextButton = {
|
|
68
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
69
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
70
|
+
BorderSizePixel = 0,
|
|
71
|
+
AutoButtonColor = false,
|
|
72
|
+
Font = Enum.Font.SourceSans,
|
|
73
|
+
Text = "",
|
|
74
|
+
TextColor3 = Color3.new(0, 0, 0)
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
TextLabel = {
|
|
78
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
79
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
80
|
+
BorderSizePixel = 0,
|
|
81
|
+
Font = Enum.Font.SourceSans,
|
|
82
|
+
Text = "",
|
|
83
|
+
TextColor3 = Color3.new(0, 0, 0)
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
UIListLayout = {
|
|
87
|
+
SortOrder = Enum.SortOrder.LayoutOrder
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
UIGridLayout = {
|
|
91
|
+
SortOrder = Enum.SortOrder.LayoutOrder
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
UITableLayout = {
|
|
95
|
+
SortOrder = Enum.SortOrder.LayoutOrder
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
UIPageLayout = {
|
|
99
|
+
SortOrder = Enum.SortOrder.LayoutOrder
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
VideoFrame = {
|
|
103
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
104
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
105
|
+
BorderSizePixel = 0
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
ViewportFrame = {
|
|
109
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
110
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
111
|
+
BorderSizePixel = 0
|
|
112
|
+
}
|
|
113
|
+
} :: { [string]: { [string]: unknown} }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
local graph = require "./graph"
|
|
2
|
+
local create_node = graph.create_node
|
|
3
|
+
local push_scope_as_child_of = graph.push_scope_as_child_of
|
|
4
|
+
local assert_stable_scope = graph.assert_stable_scope
|
|
5
|
+
local evaluate_node = graph.evaluate_node
|
|
6
|
+
|
|
7
|
+
local function derive<T>(source: () -> T): () -> T
|
|
8
|
+
local node = create_node(assert_stable_scope(), source, false :: any)
|
|
9
|
+
|
|
10
|
+
evaluate_node(node)
|
|
11
|
+
|
|
12
|
+
return function()
|
|
13
|
+
push_scope_as_child_of(node)
|
|
14
|
+
return node.cache
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
return derive
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
local graph = require "./graph"
|
|
2
|
+
local create_node = graph.create_node
|
|
3
|
+
local assert_stable_scope = graph.assert_stable_scope
|
|
4
|
+
local evaluate_node = graph.evaluate_node
|
|
5
|
+
|
|
6
|
+
local function effect<T>(callback: (T) -> T, initial_value: T)
|
|
7
|
+
local node = create_node(assert_stable_scope(), callback, initial_value)
|
|
8
|
+
|
|
9
|
+
evaluate_node(node)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
return effect :: (<T>(callback: (T) -> T, initial_value: T) -> ()) & ((callback: () -> ()) -> ())
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
local flags = require "./flags"
|
|
2
|
+
|
|
3
|
+
export type SourceNode<T> = {
|
|
4
|
+
cache: T,
|
|
5
|
+
[number]: Node<T>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type Node<T> = {
|
|
9
|
+
cache: T,
|
|
10
|
+
effect: ((T) -> T) | false,
|
|
11
|
+
cleanups: { () -> () } | false,
|
|
12
|
+
|
|
13
|
+
context: { [number]: unknown } | false,
|
|
14
|
+
|
|
15
|
+
owned: { Node<T> } | false,
|
|
16
|
+
owner: Node<T> | false,
|
|
17
|
+
|
|
18
|
+
parents: { SourceNode<T> },
|
|
19
|
+
[number]: Node<T> -- children
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
local scopes = { n = 0 } :: { [number]: Node<any>, n: number } -- scopes stack
|
|
23
|
+
|
|
24
|
+
local function efn(err: string)
|
|
25
|
+
local trace = debug.traceback(err, 2)
|
|
26
|
+
|
|
27
|
+
if string.find(err, "^effect error stacktrace") then -- if effect error is nested
|
|
28
|
+
trace = string.gsub(" " .. trace, "\n", function() -- indent entire error
|
|
29
|
+
return "\n "
|
|
30
|
+
end)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
trace ..= "\nsource update stacktrace:"
|
|
34
|
+
return trace
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
local function ycall<T, U>(fn: (T) -> U, arg: T): (boolean, string|U)
|
|
38
|
+
|
|
39
|
+
local thread = coroutine.create(xpcall)
|
|
40
|
+
--local function efn(err: string) return debug.traceback(err, 3) end
|
|
41
|
+
local resume_ok, run_ok, result = coroutine.resume(thread, fn, efn, arg)
|
|
42
|
+
|
|
43
|
+
assert(resume_ok)
|
|
44
|
+
|
|
45
|
+
if coroutine.status(thread) ~= "dead" then
|
|
46
|
+
return false, debug.traceback(thread, "attempt to yield in reactive scope")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
return run_ok, result
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
local function get_scope(): Node<unknown>?
|
|
53
|
+
return scopes[scopes.n]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
local function assert_stable_scope(): Node<unknown>
|
|
57
|
+
local scope = get_scope()
|
|
58
|
+
|
|
59
|
+
if not scope then
|
|
60
|
+
local caller_name = debug.info(2, "n")
|
|
61
|
+
return error(`cannot use {caller_name}() outside a stable or reactive scope`, 0)
|
|
62
|
+
elseif scope.effect then
|
|
63
|
+
error("cannot create a new reactive scope inside another reactive scope", 0)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
return scope
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
local function push_child<T>(parent: SourceNode<any>, child: Node<any>)
|
|
70
|
+
table.insert(parent, child)
|
|
71
|
+
table.insert(child.parents, parent)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
local function push_scope<T>(node: Node<T>)
|
|
75
|
+
local n = scopes.n + 1
|
|
76
|
+
scopes.n = n
|
|
77
|
+
scopes[n] = node
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
local function pop_scope()
|
|
81
|
+
local n = scopes.n
|
|
82
|
+
scopes.n = n - 1
|
|
83
|
+
scopes[n] = nil
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
local function push_cleanup<T>(node: Node<T>, cleanup: () -> ())
|
|
87
|
+
if node.cleanups then
|
|
88
|
+
table.insert(node.cleanups, cleanup)
|
|
89
|
+
else
|
|
90
|
+
node.cleanups = { cleanup }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
local function flush_cleanups<T>(node: Node<T>)
|
|
95
|
+
if node.cleanups then
|
|
96
|
+
for _, fn in node.cleanups do
|
|
97
|
+
local ok, err: string? = xpcall(fn, debug.traceback)
|
|
98
|
+
if not ok then error(`cleanup error: {err}`, 0) end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
table.clear(node.cleanups)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
local function find_and_swap_pop<T>(t: { T }, v: T)
|
|
106
|
+
local i = table.find(t, v) :: number
|
|
107
|
+
local n = #t
|
|
108
|
+
t[i] = t[n]
|
|
109
|
+
t[n] = nil
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
local function unparent<T>(node: Node<T>)
|
|
113
|
+
local parents = node.parents
|
|
114
|
+
|
|
115
|
+
for i, parent in parents do
|
|
116
|
+
find_and_swap_pop(parent, node)
|
|
117
|
+
parents[i] = nil
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
local function destroy<T>(node: Node<T>)
|
|
122
|
+
if flags.strict and table.find(scopes, node) then
|
|
123
|
+
error("attempt to destroy an active scope", 0)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
flush_cleanups(node)
|
|
127
|
+
unparent(node)
|
|
128
|
+
|
|
129
|
+
if node.owner then
|
|
130
|
+
find_and_swap_pop(node.owner.owned :: { Node<T> }, node)
|
|
131
|
+
node.owner = false
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if node.owned then
|
|
135
|
+
local owned = node.owned
|
|
136
|
+
while owned[1] do destroy(owned[1]) end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
local function destroy_owned<T>(node: Node<T>)
|
|
141
|
+
if node.owned then
|
|
142
|
+
local owned = node.owned
|
|
143
|
+
while owned[1] do destroy(owned[1]) end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
local update_queue = { n = 0 } :: { n: number, [number]: Node<any> }
|
|
148
|
+
|
|
149
|
+
local function evaluate_node<T>(node: Node<T>)
|
|
150
|
+
if flags.strict then
|
|
151
|
+
if table.find(scopes, node) then
|
|
152
|
+
error("a scope, that should rerun due to the update of a source, is already active", 0)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
local initial_value = node.cache
|
|
156
|
+
|
|
157
|
+
for i = 1, 2 do
|
|
158
|
+
local cur_value = node.cache
|
|
159
|
+
|
|
160
|
+
flush_cleanups(node)
|
|
161
|
+
destroy_owned(node)
|
|
162
|
+
|
|
163
|
+
push_scope(node)
|
|
164
|
+
local ok, new_value = ycall(node.effect :: (T) -> T, cur_value)
|
|
165
|
+
pop_scope()
|
|
166
|
+
|
|
167
|
+
if not ok then
|
|
168
|
+
table.clear(update_queue)
|
|
169
|
+
update_queue.n = 0
|
|
170
|
+
error(`effect error stacktrace\n{new_value :: string}`, 0)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
node.cache = new_value :: T
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
return initial_value ~= node.cache
|
|
177
|
+
else
|
|
178
|
+
local cur_value = node.cache
|
|
179
|
+
|
|
180
|
+
flush_cleanups(node)
|
|
181
|
+
destroy_owned(node)
|
|
182
|
+
|
|
183
|
+
push_scope(node)
|
|
184
|
+
local ok, new_value = pcall(node.effect :: (T) -> T, node.cache)
|
|
185
|
+
pop_scope()
|
|
186
|
+
|
|
187
|
+
if not ok then
|
|
188
|
+
table.clear(update_queue)
|
|
189
|
+
update_queue.n = 0
|
|
190
|
+
error(`effect error:\n{new_value}\n`, 0)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
node.cache = new_value
|
|
194
|
+
return cur_value ~= new_value
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
local function queue_children_for_update<T>(node: SourceNode<T>)
|
|
199
|
+
local i = update_queue.n
|
|
200
|
+
while node[1] do
|
|
201
|
+
i += 1
|
|
202
|
+
update_queue[i] = node[1]
|
|
203
|
+
unparent(node[1])
|
|
204
|
+
end
|
|
205
|
+
update_queue.n = i
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
local function get_update_queue_length()
|
|
209
|
+
return update_queue.n
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
local function flush_update_queue(from: number)
|
|
213
|
+
local i = from + 1
|
|
214
|
+
while i <= update_queue.n do
|
|
215
|
+
local node = update_queue[i]
|
|
216
|
+
--assert(node.effect)
|
|
217
|
+
|
|
218
|
+
if node.owner and evaluate_node(node) then
|
|
219
|
+
queue_children_for_update(node)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
update_queue[i] = false :: any
|
|
223
|
+
i += 1
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
update_queue.n = from
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
local function update_descendants<T>(root: SourceNode<T>)
|
|
230
|
+
local n0 = update_queue.n
|
|
231
|
+
queue_children_for_update(root)
|
|
232
|
+
|
|
233
|
+
if flags.batch then return end
|
|
234
|
+
|
|
235
|
+
local i = n0 + 1
|
|
236
|
+
while i <= update_queue.n do
|
|
237
|
+
local node = update_queue[i]
|
|
238
|
+
--assert(node.effect)
|
|
239
|
+
|
|
240
|
+
-- check if node is still owned in case destroyed after queued
|
|
241
|
+
if node.owner and evaluate_node(node) then
|
|
242
|
+
queue_children_for_update(node)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
update_queue[i] = false :: any -- false instead of nil to avoid sparse
|
|
246
|
+
i += 1
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
update_queue.n = n0
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
local function push_scope_as_child_of<T>(node: SourceNode<T>)
|
|
253
|
+
local scope = get_scope()
|
|
254
|
+
if scope and scope.effect then -- do not track nodes with no effect
|
|
255
|
+
push_child(node, scope)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
local function create_node<T>(owner: false | Node<any>, effect: false | (T) -> T, value: T): Node<T>
|
|
260
|
+
local node: Node<T> = {
|
|
261
|
+
cache = value,
|
|
262
|
+
effect = effect,
|
|
263
|
+
cleanups = false,
|
|
264
|
+
|
|
265
|
+
context = false,
|
|
266
|
+
|
|
267
|
+
owner = owner,
|
|
268
|
+
owned = false,
|
|
269
|
+
|
|
270
|
+
parents = {},
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if owner then
|
|
274
|
+
if owner.owned then
|
|
275
|
+
table.insert(owner.owned, node)
|
|
276
|
+
else
|
|
277
|
+
owner.owned = { node }
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
return node
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
local function create_source_node<T>(value: T): SourceNode<T>
|
|
285
|
+
return { cache = value }
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
local function get_children<T>(node: Node<T>): { Node<unknown> }
|
|
289
|
+
return { unpack(node) } :: { Node<any> }
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
local function set_context<T>(node: Node<T>, key: number, value: unknown)
|
|
293
|
+
if node.context then
|
|
294
|
+
node.context[key] = value
|
|
295
|
+
else
|
|
296
|
+
node.context = { [key] = value }
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
return table.freeze {
|
|
301
|
+
push_scope = push_scope,
|
|
302
|
+
pop_scope = pop_scope,
|
|
303
|
+
evaluate_node = evaluate_node,
|
|
304
|
+
get_scope = get_scope,
|
|
305
|
+
assert_stable_scope = assert_stable_scope,
|
|
306
|
+
push_cleanup = push_cleanup,
|
|
307
|
+
destroy = destroy,
|
|
308
|
+
flush_cleanups = flush_cleanups,
|
|
309
|
+
push_scope_as_child_of = push_scope_as_child_of,
|
|
310
|
+
update_descendants = update_descendants,
|
|
311
|
+
push_child = push_child,
|
|
312
|
+
create_node = create_node,
|
|
313
|
+
create_source_node = create_source_node,
|
|
314
|
+
get_children = get_children,
|
|
315
|
+
flush_update_queue = flush_update_queue,
|
|
316
|
+
get_update_queue_length = get_update_queue_length,
|
|
317
|
+
set_context = set_context,
|
|
318
|
+
scopes = scopes,
|
|
319
|
+
|
|
320
|
+
q = update_queue
|
|
321
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
local graph = require "./graph"
|
|
2
|
+
type Node<T> = graph.Node<T>
|
|
3
|
+
local create_node = graph.create_node
|
|
4
|
+
local assert_stable_scope = graph.assert_stable_scope
|
|
5
|
+
local get_scope = graph.get_scope
|
|
6
|
+
local evaluate_node = graph.evaluate_node
|
|
7
|
+
local push_cleanup = graph.push_cleanup
|
|
8
|
+
|
|
9
|
+
local function update_property_effect(p: {
|
|
10
|
+
instance: Instance,
|
|
11
|
+
property: string,
|
|
12
|
+
source: () -> unknown
|
|
13
|
+
})
|
|
14
|
+
(p.instance :: any)[p.property] = p.source()
|
|
15
|
+
return p
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
local function update_parent_effect(p: {
|
|
19
|
+
instance: Instance,
|
|
20
|
+
source: () -> Instance
|
|
21
|
+
})
|
|
22
|
+
p.instance.Parent = p.source()
|
|
23
|
+
return p
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
-- todo: investigate if "count" method used in indexes() and values() can improve performance here
|
|
27
|
+
local function update_children_effect(p: {
|
|
28
|
+
instance: Instance,
|
|
29
|
+
cur_children_set: { [Instance]: true },
|
|
30
|
+
new_children_set: { [Instance]: true },
|
|
31
|
+
source: () -> Instance | { Instance }
|
|
32
|
+
})
|
|
33
|
+
local cur_children_set: { [Instance]: true } = p.cur_children_set -- cache of all children parented before update
|
|
34
|
+
local new_children_set: { [Instance]: true } = p.new_children_set -- cache of all children parented after update
|
|
35
|
+
|
|
36
|
+
local new_children = p.source() -- all (and only) children that should be parented after this update
|
|
37
|
+
|
|
38
|
+
local function process_child(child: Instance | { Instance })
|
|
39
|
+
if type(child) == "userdata" then
|
|
40
|
+
if new_children_set[child] then return end -- stops redundant reparenting
|
|
41
|
+
|
|
42
|
+
new_children_set[child] = true -- record child set from this update
|
|
43
|
+
|
|
44
|
+
if not cur_children_set[child] then
|
|
45
|
+
child.Parent = p.instance -- if child wasn't already parented then parent it
|
|
46
|
+
else
|
|
47
|
+
cur_children_set[child] = nil -- remove child from cache if it was already in cache
|
|
48
|
+
end
|
|
49
|
+
elseif type(child) == "table" then
|
|
50
|
+
for _, child in child do
|
|
51
|
+
process_child(child)
|
|
52
|
+
end
|
|
53
|
+
elseif type(child) == "function" then
|
|
54
|
+
local node = create_node(assert(get_scope()), update_children_effect, {
|
|
55
|
+
instance = p.instance,
|
|
56
|
+
cur_children_set = {},
|
|
57
|
+
new_children_set = {},
|
|
58
|
+
source = child
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
evaluate_node(node)
|
|
62
|
+
|
|
63
|
+
push_cleanup(assert(get_scope()), function()
|
|
64
|
+
for child in node.cache.cur_children_set do
|
|
65
|
+
child.Parent = nil
|
|
66
|
+
end
|
|
67
|
+
end)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
process_child(new_children)
|
|
72
|
+
|
|
73
|
+
for child in cur_children_set do
|
|
74
|
+
child.Parent = nil -- unparent all children that weren't in the new children set
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
table.clear(cur_children_set) -- clear cache, preserve capacity
|
|
78
|
+
p.cur_children_set, p.new_children_set = new_children_set, cur_children_set
|
|
79
|
+
|
|
80
|
+
return p
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
property = function(instance, property, source)
|
|
85
|
+
local node = create_node(assert_stable_scope(), update_property_effect, {
|
|
86
|
+
instance = instance,
|
|
87
|
+
property = property,
|
|
88
|
+
source = source
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
evaluate_node(node)
|
|
92
|
+
|
|
93
|
+
return node
|
|
94
|
+
end,
|
|
95
|
+
|
|
96
|
+
parent = function(instance, parent)
|
|
97
|
+
local node = create_node(assert_stable_scope(), update_parent_effect, {
|
|
98
|
+
instance = instance,
|
|
99
|
+
source = parent
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
evaluate_node(node)
|
|
103
|
+
|
|
104
|
+
return node
|
|
105
|
+
end,
|
|
106
|
+
|
|
107
|
+
children = function(instance, children)
|
|
108
|
+
local node = create_node(assert_stable_scope(), update_children_effect, {
|
|
109
|
+
instance = instance,
|
|
110
|
+
cur_children_set = {},
|
|
111
|
+
new_children_set = {},
|
|
112
|
+
source = children
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
evaluate_node(node)
|
|
116
|
+
|
|
117
|
+
push_cleanup(assert_stable_scope(), function()
|
|
118
|
+
for child in node.cache.cur_children_set do
|
|
119
|
+
child.Parent = nil
|
|
120
|
+
end
|
|
121
|
+
end)
|
|
122
|
+
|
|
123
|
+
return node
|
|
124
|
+
end
|
|
125
|
+
}
|