code cleanups

This commit is contained in:
Andreas Rumpf 2017-06-23 22:21:06 +02:00
commit 74c07f0634

View file

@ -152,13 +152,12 @@ proc same(n: VNode, e: Node): bool =
proc replaceById(id: cstring; newTree: Node) = proc replaceById(id: cstring; newTree: Node) =
let x = document.getElementById(id) let x = document.getElementById(id)
x.parentNode.replaceChild(newTree, x) x.parentNode.replaceChild(newTree, x)
#newTree.id = id
type type
EqResult = enum EqResult = enum
changed, different, similar, identical changed, different, similar, identical
proc eq(a, b: VNode; deep: bool): EqResult = proc eq(a, b: VNode): EqResult =
if a.kind != b.kind: return different if a.kind != b.kind: return different
if a.id != b.id: return different if a.id != b.id: return different
result = identical result = identical
@ -169,60 +168,25 @@ proc eq(a, b: VNode; deep: bool): EqResult =
if a.text != b.text: return different if a.text != b.text: return different
if a.len != b.len: return different if a.len != b.len: return different
for i in 0..<a.len: for i in 0..<a.len:
if eq(a[i], b[i], deep) == different: return different if eq(a[i], b[i]) == different: return different
elif b.kind == VNodeKind.component: elif b.kind == VNodeKind.component:
# different component names mean different components: # different component names mean different components:
if a.text != b.text: return different if a.text != b.text: return different
let x = VComponent(b) let x = VComponent(b)
assert x.changedImpl != nil assert x.changedImpl != nil
return if x.changedImpl(x): changed else: identical return if x.changedImpl(x): changed else: identical
elif deep:
if a.len != b.len: return different
for i in 0..<a.len:
let res = eq(a[i], b[i], deep)
if res <= different: return different
elif res == similar:
# but continue, maybe something makes it 'different'!
result = similar
if not sameAttrs(a, b): return different if not sameAttrs(a, b): return different
if a.class != b.class: return different if a.class != b.class: return different
if not eq(a.style, b.style): return similar if not eq(a.style, b.style): return similar
# Do not test event listeners here! # Do not test event listeners here!
return result return result
when false: proc updateStyles(newNode, oldNode: VNode) =
proc updateDirtyElements(parent, current: Node, newNode: VNode,
kxi: KaraxInstance) =
if newNode.key >= 0 and isDirty(newNode.key):
unmarkDirty(newNode.key)
let n = vnodeToDom(newNode, kxi)
if parent == nil:
replaceById(kxi.rootId, n)
else:
parent.replaceChild(n, current)
elif newNode.kind != VNodeKind.text and newNode.kind != VNodeKind.vthunk and
newNode.kind != VNodeKind.dthunk:
for i in 0..newNode.len-1:
updateDirtyElements(current, current[i], newNode[i], kxi)
# leave early if we know there cannot be anything left to do:
#if dirtyCount <= 0: return
proc updateStyles(newNode, oldNode: VNode; deep: bool) =
# we keep the oldNode, but take over the style from the new node: # we keep the oldNode, but take over the style from the new node:
if oldNode.dom != nil: if oldNode.dom != nil:
if newNode.style != nil: applyStyle(oldNode.dom, newNode.style) if newNode.style != nil: applyStyle(oldNode.dom, newNode.style)
else: oldNode.dom.style = Style() else: oldNode.dom.style = Style()
oldNode.style = newNode.style oldNode.style = newNode.style
if deep:
doAssert newNode.len == oldNode.len
for i in 0 ..< newNode.len:
updateStyles(newNode[i], oldNode[i], deep)
proc updateDom(newNode, oldNode: VNode) =
newNode.dom = oldNode.dom
assert newNode.len == oldNode.len
for i in 0 ..< newNode.len:
updateDom(newNode[i], oldNode[i])
proc printV(n: VNode; depth: cstring = "") = proc printV(n: VNode; depth: cstring = "") =
kout depth, cstring($n.kind), n.myid, cstring"key ", n.key kout depth, cstring($n.kind), n.myid, cstring"key ", n.key
@ -264,55 +228,32 @@ proc apply(patches: seq[Patch]; kxi: KaraxInstance) =
proc diff(parent, current: Node; newNode, oldNode: VNode; patches: var seq[Patch]; proc diff(parent, current: Node; newNode, oldNode: VNode; patches: var seq[Patch];
kxi: KaraxInstance): EqResult = kxi: KaraxInstance): EqResult =
result = eq(newNode, oldNode, deep=false) result = eq(newNode, oldNode)
if result <= different: case result
var n: Node of identical:
if result == changed:
assert oldNode.kind == VNodeKind.component
let x = VComponent(oldNode)
let oldExpanded = x.expanded
x.expanded = x.renderImpl(x)
x.updatedImpl(x)
if oldExpanded == nil:
n = vnodeToDom(x.expanded, kxi)
else:
let res = diff(parent, current, x.expanded, oldExpanded, patches, kxi)
if res != different:
x.expanded = oldExpanded
n = oldExpanded.dom
doAssert n != nil, "old expanded.dom is nil"
else:
n = x.expanded.dom
doAssert n != nil, "expanded.dom is nil"
return
else:
detach(oldNode)
n = vnodeToDom(newNode, kxi)
patches.addPatch(pkReplace, parent, current, n)
elif result == similar:
updateStyles(newNode, oldNode, false)
else:
newNode.dom = oldNode.dom newNode.dom = oldNode.dom
let newLength = newNode.len let newLength = newNode.len
var oldLength = oldNode.len var oldLength = oldNode.len
let minLength = min(newLength, oldLength) let minLength = min(newLength, oldLength)
if minLength == 0: return result
assert oldNode.kind == newNode.kind assert oldNode.kind == newNode.kind
var commonPrefix = 0 var commonPrefix = 0
template eqAndUpdate(a: VNode; i: int; b: VNode; j: int; info, action: untyped) = template eqAndUpdate(a: VNode; i: int; b: VNode; j: int; info, action: untyped) =
let oldLen = patches.len let oldLen = patches.len
if oldNode.kind notin {VNodeKind.component, VNodeKind.vthunk, VNodeKind.dthunk}: when false:
assert current != nil if oldNode.kind notin {VNodeKind.component, VNodeKind.vthunk, VNodeKind.dthunk}:
assert current.childNodes[j] != nil, $info assert current != nil
assert oldNode.len == current.len assert current.childNodes[j] != nil, $info
assert oldNode.len == current.len
let r = if oldNode.kind in {VNodeKind.component, VNodeKind.vthunk, VNodeKind.dthunk}: let r = if oldNode.kind in {VNodeKind.component, VNodeKind.vthunk, VNodeKind.dthunk}:
diff(parent, current, a[i], b[j], patches, kxi) diff(parent, current, a[i], b[j], patches, kxi)
else: else:
diff(current, current.childNodes[j], a[i], b[j], patches, kxi) diff(current, current.childNodes[j], a[i], b[j], patches, kxi)
case r case r
of identical, changed: of identical, changed, similar:
a[i] = b[j] a[i] = b[j]
action action
of different: of different:
@ -320,10 +261,10 @@ proc diff(parent, current: Node; newNode, oldNode: VNode; patches: var seq[Patch
setLen(patches, oldLen) setLen(patches, oldLen)
if result != different: result = r if result != different: result = r
break break
of similar: #of similar:
updateStyles(a[i], b[j], false) # updateStyles(a[i], b[j])
a[i] = b[j] # a[i] = b[j]
action # action
while commonPrefix < minLength: while commonPrefix < minLength:
eqAndUpdate(newNode, commonPrefix, oldNode, commonPrefix, cstring"prefix"): eqAndUpdate(newNode, commonPrefix, oldNode, commonPrefix, cstring"prefix"):
@ -338,20 +279,18 @@ proc diff(parent, current: Node; newNode, oldNode: VNode; patches: var seq[Patch
var pos = min(oldPos, newPos) + 1 var pos = min(oldPos, newPos) + 1
for i in commonPrefix..pos-1: for i in commonPrefix..pos-1:
let res = diff(current, current.childNodes[i], if diff(current, current.childNodes[i],
newNode[i], oldNode[i], patches, kxi) newNode[i], oldNode[i], patches, kxi) != different:
if res != different:
newNode[i] = oldNode[i] newNode[i] = oldNode[i]
else: else:
result = different result = different
var nextChildPos = oldPos + 1 if oldPos + 1 == oldLength:
if nextChildPos == oldLength:
for i in pos..newPos: for i in pos..newPos:
patches.addPatch(pkAppend, current, nil, vnodeToDom(newNode[i], kxi)) patches.addPatch(pkAppend, current, nil, vnodeToDom(newNode[i], kxi))
result = different result = different
else: else:
let before = current.childNodes[nextChildPos] let before = current.childNodes[oldPos + 1]
for i in pos..newPos: for i in pos..newPos:
patches.addPatch(pkInsertBefore, current, before, patches.addPatch(pkInsertBefore, current, before,
vnodeToDom(newNode[i], kxi)) vnodeToDom(newNode[i], kxi))
@ -359,153 +298,48 @@ proc diff(parent, current: Node; newNode, oldNode: VNode; patches: var seq[Patch
# XXX call 'attach' here? # XXX call 'attach' here?
for i in pos..oldPos: for i in pos..oldPos:
detach(oldNode[i]) detach(oldNode[i])
doAssert i < current.childNodes.len #doAssert i < current.childNodes.len
patches.addPatch(pkRemove, current, current.childNodes[i], nil) patches.addPatch(pkRemove, current, current.childNodes[i], nil)
result = different result = different
of similar:
proc updateElement(parent, current: Node, newNode, oldNode: VNode; updateStyles(newNode, oldNode)
kxi: KaraxInstance): EqResult = of changed:
result = eq(newNode, oldNode, deep=false) assert oldNode.kind == VNodeKind.component
if result <= different: let x = VComponent(oldNode)
var n: Node let oldExpanded = x.expanded
var state = 0 x.expanded = x.renderImpl(x)
if result == changed: x.updatedImpl(x)
assert oldNode.kind == VNodeKind.component if oldExpanded == nil:
let x = VComponent(oldNode)
let oldExpanded = x.expanded
x.expanded = x.renderImpl(x)
x.updatedImpl(x)
if oldExpanded == nil:
n = vnodeToDom(x.expanded, kxi)
state = 1
else:
let res = updateElement(parent, current, x.expanded, oldExpanded, kxi)
if res != different:
x.expanded = oldExpanded
n = oldExpanded.dom
doAssert n != nil, "old expanded.dom is nil"
#kout cstring"produced old DOM: ", cstring($res)
#printV(oldExpanded)
#printV(x.expanded)
return
else:
n = x.expanded.dom
doAssert n != nil, "expanded.dom is nil"
state = 2
#kout cstring"produced new DOM:"
#printV(oldExpanded)
#printV(x.expanded)
return
else:
detach(oldNode) detach(oldNode)
n = vnodeToDom(newNode, kxi) let n = vnodeToDom(x.expanded, kxi)
state = 3 patches.addPatch(pkReplace, parent, current, n)
if parent == nil:
replaceById(kxi.rootId, n)
else: else:
#kout cstring"state ", state, parent, current let res = diff(parent, current, x.expanded, oldExpanded, patches, kxi)
if n != current: if res != different:
parent.replaceChild(n, current) x.expanded = oldExpanded
elif result == similar: assert oldExpanded.dom != nil, "old expanded.dom is nil"
updateStyles(newNode, oldNode, false)
else:
newNode.dom = oldNode.dom
if newNode.kind != VNodeKind.text:
let newLength = newNode.len
var oldLength = oldNode.len
let minLength = min(newLength, oldLength)
assert oldNode.kind == newNode.kind
when defined(simpleDiff):
for i in 0..min(newLength, oldLength)-1:
updateElement(current, current[i], newNode[i], oldNode[i], kxi)
if newLength > oldLength:
for i in oldLength..newLength-1:
current.appendChild(vnodeToDom(newNode[i]))
elif oldLength > newLength:
for i in countdown(oldLength-1, newLength):
detach(oldNode[i])
current.removeChild(current.lastChild)
else: else:
var commonPrefix = 0 assert x.expanded.dom != nil, "expanded.dom is nil"
of different:
template eqAndUpdate(a: VNode; i: int; b: VNode; action: untyped) = detach(oldNode)
let r = eq(a[i], b, true) let n = vnodeToDom(newNode, kxi)
case r patches.addPatch(pkReplace, parent, current, n)
of identical:
a[i] = b
#updateDom(a, b)
action
of different, changed:
if result != different: result = r
break
of similar:
#updateDom(a, b)
updateStyles(a[i], b, true)
a[i] = b
action
while commonPrefix < minLength:
eqAndUpdate(newNode, commonPrefix, oldNode[commonPrefix]):
inc commonPrefix
var oldPos = oldLength - 1
var newPos = newLength - 1
while oldPos >= commonPrefix and newPos >= commonPrefix:
eqAndUpdate(newNode, newPos, oldNode[oldPos]):
dec oldPos
dec newPos
var pos = min(oldPos, newPos) + 1
for i in commonPrefix..pos-1:
let res = updateElement(current, current.childNodes[i],
newNode[i], oldNode[i], kxi)
if res != different:
newNode[i] = oldNode[i]
else:
result = different
var nextChildPos = oldPos + 1
while pos <= newPos:
if nextChildPos == oldLength:
current.appendChild(vnodeToDom(newNode[pos], kxi))
else:
current.insertBefore(vnodeToDom(newNode[pos], kxi), current.childNodes[nextChildPos])
# added new Node, so old state of VDOM have one more Node
inc oldLength
inc pos
inc nextChildPos
result = different
for i in pos..oldPos:
detach(oldNode[i])
doAssert pos < current.childNodes.len
current.removeChild(current.childNodes[pos])
result = different
when false:
var drawTimeout: Timeout
proc dodraw(kxi: KaraxInstance) = proc dodraw(kxi: KaraxInstance) =
if kxi.renderer.isNil: return if kxi.renderer.isNil: return
let newtree = kxi.renderer() let newtree = kxi.renderer()
newtree.id = kxi.rootId newtree.id = kxi.rootId
kxi.toFocus = nil kxi.toFocus = nil
#if kxi.currentTree != nil:
# kout cstring"same? ", same(kxi.currentTree, document.getElementById(kxi.rootId))
#kout cstring"dodraw -----------------------------"
if kxi.currentTree == nil: if kxi.currentTree == nil:
kxi.currentTree = newtree kxi.currentTree = newtree
let asdom = vnodeToDom(kxi.currentTree, kxi) let asdom = vnodeToDom(kxi.currentTree, kxi)
replaceById(kxi.rootId, asdom) replaceById(kxi.rootId, asdom)
else: else:
let olddom = document.getElementById(kxi.rootId) let olddom = document.getElementById(kxi.rootId)
when false: var patches: seq[Patch] = @[]
discard updateElement(nil, olddom, newtree, kxi.currentTree, kxi) discard diff(nil, olddom, newtree, kxi.currentTree, patches, kxi)
else: patches.apply(kxi)
var patches: seq[Patch] = @[]
discard diff(nil, olddom, newtree, kxi.currentTree, patches, kxi)
patches.apply(kxi)
kxi.currentTree = newtree kxi.currentTree = newtree
#doAssert same(kxi.currentTree, document.getElementById(kxi.rootId)) #doAssert same(kxi.currentTree, document.getElementById(kxi.rootId))