mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
virtual-dom: use a free list for handlers
This commit is contained in:
parent
b7802ec6ac
commit
34f6becaa2
1 changed files with 43 additions and 22 deletions
|
@ -30,12 +30,14 @@ PlatformState state initData : {
|
||||||
app : App state initData,
|
app : App state initData,
|
||||||
state,
|
state,
|
||||||
view : RenderedHtml,
|
view : RenderedHtml,
|
||||||
handlers : HandlerLookup state,
|
handlerLookup : HandlerLookup state,
|
||||||
isOddArena : Bool,
|
isOddArena : Bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: keep a list of free indices that we push and pop like a stack
|
HandlerLookup state : {
|
||||||
HandlerLookup state : List (Result (Handler state) [NoHandler])
|
handlers: List (Result (Handler state) [NoHandler]),
|
||||||
|
freeList: List Nat,
|
||||||
|
}
|
||||||
|
|
||||||
App state initData : {
|
App state initData : {
|
||||||
init : initData -> state,
|
init : initData -> state,
|
||||||
|
@ -43,7 +45,6 @@ App state initData : {
|
||||||
wasmUrl : Str,
|
wasmUrl : Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
# App code would only ever deal with the unrendered type. Diff is between old rendered and new unrendered
|
|
||||||
Html state : [
|
Html state : [
|
||||||
None,
|
None,
|
||||||
Text Str,
|
Text Str,
|
||||||
|
@ -249,12 +250,12 @@ JsEventResult state initData : {
|
||||||
## DANGER: this function does unusual stuff with memory allocation lifetimes. Be as careful as you would with Zig or C code!
|
## DANGER: this function does unusual stuff with memory allocation lifetimes. Be as careful as you would with Zig or C code!
|
||||||
dispatchEvent : Box (PlatformState state initData), Box (List (List U8)), Nat -> Effect (Box (JsEventResult state initData))
|
dispatchEvent : Box (PlatformState state initData), Box (List (List U8)), Nat -> Effect (Box (JsEventResult state initData))
|
||||||
dispatchEvent = \boxedPlatformState, boxedEventData, handlerId ->
|
dispatchEvent = \boxedPlatformState, boxedEventData, handlerId ->
|
||||||
{ app, state, view, handlers, isOddArena: wasOddArena } =
|
{ app, state, view, handlerLookup, isOddArena: wasOddArena } =
|
||||||
Box.unbox boxedPlatformState
|
Box.unbox boxedPlatformState
|
||||||
eventData =
|
eventData =
|
||||||
Box.unbox boxedEventData
|
Box.unbox boxedEventData
|
||||||
maybeHandler =
|
maybeHandler =
|
||||||
List.get handlers handlerId
|
List.get handlerLookup.handlers handlerId
|
||||||
|> Result.withDefault (Err NoHandler)
|
|> Result.withDefault (Err NoHandler)
|
||||||
{ action, stopPropagation, preventDefault } =
|
{ action, stopPropagation, preventDefault } =
|
||||||
when maybeHandler is
|
when maybeHandler is
|
||||||
|
@ -274,14 +275,19 @@ dispatchEvent = \boxedPlatformState, boxedEventData, handlerId ->
|
||||||
|
|
||||||
runInVdomArena isOddArena \_ ->
|
runInVdomArena isOddArena \_ ->
|
||||||
newViewUnrendered = app.render newState
|
newViewUnrendered = app.render newState
|
||||||
emptyHandlers = List.repeat (Err NoHandler) (List.len handlers)
|
numHandlers = List.len handlerLookup.handlers
|
||||||
|
emptyHandlerLookup = {
|
||||||
|
handlers: List.repeat (Err NoHandler) numHandlers,
|
||||||
|
freeList: List.range { start: At (numHandlers - 1), end: At 0 }
|
||||||
|
}
|
||||||
|
|
||||||
{ newHandlers, node: newViewRendered } <- diffAndUpdateDom emptyHandlers view newViewUnrendered |> Effect.after
|
{ newHandlers, node: newViewRendered } <-
|
||||||
|
diffAndUpdateDom emptyHandlerLookup view newViewUnrendered |> Effect.after
|
||||||
newBoxedPlatformState = Box.box {
|
newBoxedPlatformState = Box.box {
|
||||||
app,
|
app,
|
||||||
state: newState,
|
state: newState,
|
||||||
view: newViewRendered,
|
view: newViewRendered,
|
||||||
handlers: newHandlers,
|
handlerLookup: newHandlers,
|
||||||
isOddArena,
|
isOddArena,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,27 +303,36 @@ runInVdomArena = \useOddArena, run ->
|
||||||
_ <- Effect.disableVdomAllocator |> Effect.after
|
_ <- Effect.disableVdomAllocator |> Effect.after
|
||||||
Effect.always returnVal
|
Effect.always returnVal
|
||||||
|
|
||||||
insertHandler : List (Result (Handler state) [NoHandler]), Handler state -> { index : Nat, handlers : List (Result (Handler state) [NoHandler]) }
|
insertHandler : HandlerLookup state, Handler state -> { index : Nat, handlerLookup : HandlerLookup state }
|
||||||
insertHandler = \handlers, newHandler ->
|
insertHandler = \{ handlers, freeList }, newHandler ->
|
||||||
# TODO: speed this up using a free list
|
when List.last freeList is
|
||||||
when List.findFirstIndex handlers Result.isErr is
|
|
||||||
Ok index ->
|
Ok index ->
|
||||||
{
|
{ index,
|
||||||
index,
|
handlerLookup: {
|
||||||
handlers: List.set handlers index (Ok newHandler),
|
handlers: List.set handlers index (Ok newHandler),
|
||||||
|
freeList: List.dropLast freeList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err NotFound ->
|
Err _ ->
|
||||||
{
|
{ index: List.len handlers,
|
||||||
index: List.len handlers,
|
handlerLookup: {
|
||||||
handlers: List.append handlers (Ok newHandler),
|
handlers: List.append handlers (Ok newHandler),
|
||||||
|
freeList: freeList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceHandler : List (Result (Handler state) [NoHandler]), Nat, Handler state -> List (Result (Handler state) [NoHandler])
|
replaceHandler : HandlerLookup state, Nat, Handler state -> HandlerLookup state
|
||||||
replaceHandler = \handlers, index, newHandler ->
|
replaceHandler = \{ handlers, freeList }, index, newHandler ->
|
||||||
{ list } = List.replace handlers index (Ok newHandler)
|
{ list } = List.replace handlers index (Ok newHandler)
|
||||||
|
|
||||||
list
|
{ handlers: list,
|
||||||
|
freeList
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
# SERVER SIDE INIT
|
# SERVER SIDE INIT
|
||||||
|
@ -452,6 +467,12 @@ indexNodes = \{ list, index }, unrendered ->
|
||||||
index,
|
index,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# Emit patches first, then interpret those into Effects.
|
||||||
|
# It'll be way more debuggable and the code will probably be simpler.
|
||||||
|
# I can write tests more easily, and run more of it outside the browser.
|
||||||
|
# Fits with upcoming plans for how Effects will work anyway?
|
||||||
|
# First step is to allocate a NodeId without calling out to JS. Wasm needs to drive it.
|
||||||
diffAndUpdateDom : HandlerLookup state, RenderedHtml, Html state -> Effect { newHandlers : HandlerLookup state, node : RenderedHtml }
|
diffAndUpdateDom : HandlerLookup state, RenderedHtml, Html state -> Effect { newHandlers : HandlerLookup state, node : RenderedHtml }
|
||||||
diffAndUpdateDom = \newHandlers, oldNode, newNode ->
|
diffAndUpdateDom = \newHandlers, oldNode, newNode ->
|
||||||
when { oldNode, newNode } is
|
when { oldNode, newNode } is
|
||||||
|
@ -530,7 +551,7 @@ addAttribute : AddAttrWalk state, Attribute state -> AddAttrWalk state
|
||||||
addAttribute = \{ nodeIndex, style, newHandlers, renderedAttrs, effects }, attr ->
|
addAttribute = \{ nodeIndex, style, newHandlers, renderedAttrs, effects }, attr ->
|
||||||
when attr is
|
when attr is
|
||||||
EventListener name accessors handler ->
|
EventListener name accessors handler ->
|
||||||
{ handlers: updatedHandlers, index: handlerIndex } =
|
{ handlerLookup: updatedHandlers, index: handlerIndex } =
|
||||||
insertHandler newHandlers handler
|
insertHandler newHandlers handler
|
||||||
|
|
||||||
# Store the handlerIndex in the rendered virtual DOM tree, since we'll need it for the next diff
|
# Store the handlerIndex in the rendered virtual DOM tree, since we'll need it for the next diff
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue