more tests and diff fixes; wip
This commit is contained in:
parent
5f7f33bb02
commit
7779560b6b
3 changed files with 114 additions and 16 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")]
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue