more tests and diff fixes; wip

This commit is contained in:
Andreas Rumpf 2017-07-06 18:34:32 +02:00
commit 7779560b6b
3 changed files with 114 additions and 16 deletions

View file

@ -21,6 +21,9 @@ type
k: PatchKind k: PatchKind
parent, current: Node parent, current: Node
n: VNode n: VNode
PatchV = object
parent, newChild: VNode
pos: int
type type
KaraxInstance* = ref object ## underlying karax instance. Usually you don't have KaraxInstance* = ref object ## underlying karax instance. Usually you don't have
@ -34,7 +37,11 @@ type
renderId: int renderId: int
patches: seq[Patch] # we reuse this to save allocations patches: seq[Patch] # we reuse this to save allocations
patchLen: int patchLen: int
recursion: int patchesV: seq[PatchV]
patchLenV: int
runCount: int
when defined(stats):
recursion: int
var var
@ -251,6 +258,17 @@ proc addPatch(kxi: KaraxInstance; ka: PatchKind; parenta, currenta: Node;
kxi.patches[L].n = na kxi.patches[L].n = na
inc kxi.patchLen inc kxi.patchLen
proc addPatchV(kxi: KaraxInstance; parent: VNode; pos: int; newChild: VNode) =
let L = kxi.patchLenV
if L >= kxi.patchesV.len:
# allocate more space:
kxi.patchesV.add(PatchV(parent: parent, newChild: newChild, pos: pos))
else:
kxi.patchesV[L].parent = parent
kxi.patchesV[L].newChild = newChild
kxi.patchesV[L].pos = pos
inc kxi.patchLenV
proc apply(kxi: KaraxInstance) = proc apply(kxi: KaraxInstance) =
for i in 0..<kxi.patchLen: for i in 0..<kxi.patchLen:
let p = kxi.patches[i] let p = kxi.patches[i]
@ -276,15 +294,20 @@ proc apply(kxi: KaraxInstance) =
if x.onDetachImpl != nil: x.onDetachImpl(x) if x.onDetachImpl != nil: x.onDetachImpl(x)
n.dom = nil n.dom = nil
kxi.patchLen = 0 kxi.patchLen = 0
for i in 0..<kxi.patchLenV:
let p = kxi.patchesV[i]
p.parent[p.pos] = p.newChild
kxi.patchLenV = 0
proc diff(newNode, oldNode: VNode; parent, current: Node; kxi: KaraxInstance): EqResult = proc diff(newNode, oldNode: VNode; parent, current: Node; kxi: KaraxInstance): EqResult =
if kxi.recursion > 100: when defined(stats):
echo "newNode ", newNode.kind, " oldNode ", oldNode.kind, " eq ", eq(newNode, oldNode) if kxi.recursion > 100:
if oldNode.kind == VNodeKind.text: echo "newNode ", newNode.kind, " oldNode ", oldNode.kind, " eq ", eq(newNode, oldNode)
echo oldNode.text if oldNode.kind == VNodeKind.text:
#return echo oldNode.text
#doAssert false, "overflow!" #return
inc kxi.recursion #doAssert false, "overflow!"
inc kxi.recursion
result = eq(newNode, oldNode) result = eq(newNode, oldNode)
case result case result
of identical, similar: of identical, similar:
@ -310,6 +333,7 @@ proc diff(newNode, oldNode: VNode; parent, current: Node; kxi: KaraxInstance): E
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 = kxi.patchLen let oldLen = kxi.patchLen
let oldLenV = kxi.patchLenV
assert i < a.len assert i < a.len
assert j < b.len assert j < b.len
let r = if isSpecial: let r = if isSpecial:
@ -322,11 +346,13 @@ proc diff(newNode, oldNode: VNode; parent, current: Node; kxi: KaraxInstance): E
a[i] = b[j] a[i] = b[j]
action action
of usenewNode: of usenewNode:
b[j] = a[i] #b[j] = a[i]
kxi.addPatchV(b, j, a[i])
action action
of different: of different:
# undo what 'diff' would have done: # undo what 'diff' would have done:
kxi.patchLen = oldLen kxi.patchLen = oldLen
kxi.patchLenV = oldLenV
if result != different: result = r if result != different: result = r
break break
# compute common prefix: # compute common prefix:
@ -403,7 +429,8 @@ proc diff(newNode, oldNode: VNode; parent, current: Node; kxi: KaraxInstance): E
detach(oldNode) detach(oldNode)
kxi.addPatch(pkReplace, parent, current, newNode) kxi.addPatch(pkReplace, parent, current, newNode)
of usenewNode: doAssert(false, "eq returned usenewNode") of usenewNode: doAssert(false, "eq returned usenewNode")
dec kxi.recursion when defined(stats):
dec kxi.recursion
when defined(stats): when defined(stats):
proc depth(n: VNode; total: var int): int = proc depth(n: VNode; total: var int): int =
@ -416,6 +443,7 @@ when defined(stats):
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()
inc kxi.runCount
newtree.id = kxi.rootId newtree.id = kxi.rootId
kxi.toFocus = nil kxi.toFocus = nil
if kxi.currentTree == nil: if kxi.currentTree == nil:
@ -428,7 +456,7 @@ proc dodraw(kxi: KaraxInstance) =
#kout cstring"patch len ", patches.len #kout cstring"patch len ", patches.len
apply(kxi) apply(kxi)
kxi.currentTree = newtree kxi.currentTree = newtree
doAssert same(kxi.currentTree, document.getElementById(kxi.rootId)) #doAssert same(kxi.currentTree, document.getElementById(kxi.rootId))
if not kxi.postRenderCallback.isNil: if not kxi.postRenderCallback.isNil:
kxi.postRenderCallback() kxi.postRenderCallback()
@ -437,8 +465,8 @@ proc dodraw(kxi: KaraxInstance) =
if kxi.toFocus != nil: if kxi.toFocus != nil:
kxi.toFocus.focus() kxi.toFocus.focus()
kxi.renderId = 0 kxi.renderId = 0
kxi.recursion = 0
when defined(stats): when defined(stats):
kxi.recursion = 0
var total = 0 var total = 0
echo "depth ", depth(kxi.currentTree, total), " total ", total echo "depth ", depth(kxi.currentTree, total), " total ", total
@ -467,7 +495,8 @@ proc setRenderer*(renderer: proc (): VNode, root: cstring = "ROOT",
## Setup Karax. Usually the return value can be ignored. ## Setup Karax. Usually the return value can be ignored.
result = KaraxInstance(rootId: root, renderer: renderer, result = KaraxInstance(rootId: root, renderer: renderer,
postRenderCallback: clientPostRenderCallback, postRenderCallback: clientPostRenderCallback,
patches: newSeq[Patch](60)) patches: newSeq[Patch](60),
patchesV: newSeq[PatchV](30))
kxi = result kxi = result
window.onload = init window.onload = init

View file

@ -26,7 +26,6 @@ proc check(name: cstring; order: seq[int]) =
let result = checkOrder(order) let result = checkOrder(order)
results.add name & (if result: cstring" - OK" else: cstring" -FAIL") results.add name & (if result: cstring" - OK" else: cstring" -FAIL")
# result: 0 1 2 3 4 7 5
proc test1() = proc test1() =
results.add cstring"test1 started" results.add cstring"test1 started"
entries = @[cstring("0"), cstring("1"), cstring("2"), cstring("3"), cstring("4"), cstring("5")] entries = @[cstring("0"), cstring("1"), cstring("2"), cstring("3"), cstring("4"), cstring("5")]

View file

@ -43,8 +43,10 @@ proc testAppend() =
ul: ul:
li: text "A" li: text "A"
li: text "B" li: text "B"
li: text "C" li:
doDiff(a, b, "pkAppend li C") tdiv:
text "C"
doDiff(a, b, "pkAppend li div C")
proc testInsert() = proc testInsert() =
let a = buildHtml(tdiv): let a = buildHtml(tdiv):
@ -88,12 +90,80 @@ proc testDelete() =
"pkDetach li B", "pkRemove nil", "pkDetach li B", "pkRemove nil",
"pkDetach li C", "pkRemove nil") "pkDetach li C", "pkRemove nil")
proc testDeleteMiddle() =
let a = buildHtml(tdiv):
ul:
li:
tdiv: text "A"
li:
tdiv: text "B"
li:
tdiv: text "C"
li:
tdiv: text "D"
li:
tdiv: text "E"
li:
tdiv: text "F"
li:
tdiv: text "G"
li:
tdiv: text "H"
let b = buildHtml(tdiv):
ul:
li:
tdiv: text "A"
li:
tdiv: text "B"
li:
tdiv: text "C"
li:
tdiv: text "D"
li:
tdiv: text "E"
li:
tdiv: text "F"
li:
tdiv: text "H"
doDiff(a, b, "pkDetach li div G", "pkRemove nil")
proc createEntry(id: cstring): VNode =
result = buildHtml():
button(id="" & id):
text id
proc createEntries(entries: seq[cstring]): VNode =
result = buildHtml(tdiv()):
ul(id="ul"):
for e in entries:
createEntry(e)
for r in entries:
tdiv:
text r
proc testWild() =
var entries = @[cstring("0"), cstring("1"), cstring("2"), cstring("3"), cstring("4"), cstring"7", cstring("5")]
let a = createEntries(entries)
entries = @[cstring("0"), cstring("1"), cstring("2"), cstring("3"), cstring("4"), cstring("5")]
let b = createEntries(entries)
doDiff(a, b, "pkDetach button 7", "pkRemove nil", "pkDetach div 7", "pkRemove nil")
proc testWildInsert() =
var entries = @[cstring("0"), cstring("1"), cstring("2"), cstring("3"), cstring("4"), cstring("5")]
let a = createEntries(entries)
entries = @[cstring("0"), cstring("1"), cstring("2"), cstring("3"), cstring("4"), cstring"7", cstring("5")]
let b = createEntries(entries)
doDiff(a, b, "pkInsertBefore button 7", "pkInsertBefore div 7")
kxi = KaraxInstance(rootId: cstring"ROOT", renderer: proc (): VNode = discard) kxi = KaraxInstance(rootId: cstring"ROOT", renderer: proc (): VNode = discard)
testAppend() testAppend()
testInsert() testInsert()
testInsert2() testInsert2()
testDelete() testDelete()
testWild()
testWildInsert()
testDeleteMiddle()
if err == 0: if err == 0:
echo "Success" echo "Success"
else: else: