From 56a50679dee82bacbebf8e9dcb7a9632f90ef7b5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 19 Aug 2017 14:54:10 +0200 Subject: [PATCH] reactive sequence works now --- experiments/nextgen.nim | 67 ++++++++++++++++++++++++++++--------- src/karax.nim | 18 ++++++++++ src/reactive.nim | 73 +++++++++++++++++++++++++++++++++++------ src/vdom.nim | 4 +++ 4 files changed, 137 insertions(+), 25 deletions(-) diff --git a/experiments/nextgen.nim b/experiments/nextgen.nim index fb95a6b..4f955aa 100644 --- a/experiments/nextgen.nim +++ b/experiments/nextgen.nim @@ -2,24 +2,61 @@ import vdom, kdom, vstyles, karax, karaxdsl, jdict, jstrutils, reactive type - User = ref object + User = ref object of ReactiveBase firstname, lastname: cstring -var gu = @[newReactive(User(firstname: "Some", lastname: "Body")), - newReactive(User(firstname: "Some", lastname: "One")), - newReactive(User(firstname: "Some", lastname: "Two"))] +var gu = newRSeq(@[ (User(firstname: "Some", lastname: "Body")), + (User(firstname: "Some", lastname: "One")), + (User(firstname: "Some", lastname: "Two"))]) var clicks = 0 -proc renderUser(u: Reactive[User]): VNode {.track.} = - result = buildHtml(button): - text u.now.firstname & " " & u.now.lastname - proc onclick(ev: Event; n: VNode) = - inc clicks - u <- User(firstname: "Another", lastname: &clicks) +discard """ + # Text gets a *reactive* string in the first place! + # Text can register and knows how to update itself! + proc toReact(): RString = + observe(u): + u.firstname & u.lastname -proc main(): VNode = - result = buildHtml(tdiv): - for i in 0..high(gu): - renderUser(gu[i]) + let t = text(u.firstname & " " & u.lastname) + observe(u, t.update(u.firstname & " " & u.lastname)) + t +""" -setInitializer(main) +proc renderUser(u: User): VNode = #{.track.} = + proc inner(u: User): VNode = + result = buildHtml(tdiv): + text u.firstname & " " & u.lastname + button: + text "(X)" + proc onclick(ev: Event; n: VNode) = + #u.firstname = "kfdj" + #u.lastname = &clicks + #notifyObservers(u) + gu.deleteElem(u) + + result = inner(u) + doTrack(u, result, inner(u)) + +template vmap(x: RSeq; elem, f: untyped): VNode = + let tmp = buildHtml(elem): + for i in 0..= parent.len: + kxi.addPatch(pkAppend, current, nil, kid) + parent.add(kid) + else: + let before = current.childNodes[position] + kxi.addPatch(pkInsertBefore, current, before, kid) + parent.insert(kid, position) + applyPatch(kxi) + proc runDiff*(kxi: KaraxInstance; oldNode, newNode: VNode) = let olddom = oldNode.dom discard diff(newNode, oldNode, nil, olddom, kxi) diff --git a/src/reactive.nim b/src/reactive.nim index a1782f0..4fcc851 100644 --- a/src/reactive.nim +++ b/src/reactive.nim @@ -32,7 +32,7 @@ type s: seq[T] L: RInt -proc addSink[T](x: Reactive[T]; sink: proc(msg: Message; pos: int)) = +proc addSink(x: ReactiveBase; sink: proc(msg: Message; pos: int)) = x.sinks.add sink proc addSink(x: ReactiveBase; key: cstring; sink: proc(msg: Message; pos: int)) = @@ -88,7 +88,7 @@ proc `<-`*[T](x: Reactive[T], val: T) = x.broadcast(Mark) x.broadcast(Changed) -proc changed*(x: ReactiveBase) = +proc notifyObservers*(x: ReactiveBase) = x.broadcast(Mark) x.broadcast(Changed) @@ -125,10 +125,17 @@ proc newRSeq*[T](data: seq[T]): RSeq[T] = for i in 0..high(data): result.s[i] = data[i] -proc `[]=`[T](x: RSeq[T]; index: int; v: T) = +proc `[]=`*[T](x: RSeq[T]; index: int; v: T) = x.s[index] = v -proc `[]`[T](x: RSeq[T]; index: int): T = x.s[index] +proc `[]`*[T](x: RSeq[T]; index: int): T = x.s[index] +proc len*[T](x: RSeq[T]): int = x.s.len + +proc add*[T](x: RSeq[T]; y: T) = + let position = x.s.len + x.s.add(y) + x.broadcast(Mark) + x.broadcast(Inserted, position) proc insert*[T](x: RSeq[T]; y: T; position = 0) = x.s.insert(y, position) @@ -140,6 +147,16 @@ proc delete*[T](x: RSeq[T]; position = 0) = x.broadcast(Mark) x.broadcast(Deleted, position) +proc deleteElem*[T](x: RSeq[T]; y: T) = + var position = -1 + for i in 0..= 0: + x.s.delete(position) + x.broadcast(Mark) + x.broadcast(Deleted, position) + proc map*[T, U](x: RSeq[T], f: proc(x: T): U): RSeq[U] = let xl = x.L.value let res = newRSeq[U](xl) @@ -159,24 +176,60 @@ proc map*[T, U](x: RSeq[T], f: proc(x: T): U): RSeq[U] = import macros template trackImpl(r: ReactiveBase; key: cstring; a, b: untyped) = - when r is Reactive: + when r is ReactiveBase: addSink r, key, proc(m: Message; pos: int) = if m == Changed: runDiff(kxi, a, b) +template doTrack*(r: ReactiveBase; a, b: untyped) {.dirty.} = + bind addSink, Message, RSeq, Changed, Deleted, Inserted + addSink r, proc(m: Message; pos: int) = + #when r is RSeq: + #echo "Message: ", m, " ", pos + if m == Changed: karax.runDiff(kxi, a, b) + +template doTrackResize*(r: ReactiveBase; a, b: untyped) {.dirty.} = + bind addSink, Message, RSeq, Changed, Deleted, Inserted + addSink r, proc(m: Message; pos: int) = + #when r is RSeq: + #echo "Message: ", m, " ", pos + case m + of Deleted: karax.runDel(kxi, a, pos) + of Inserted: + karax.runIns(kxi, a, b, pos) + else: discard + +template notifyImpl(r: untyped) = + when r is ReactiveBase: + notifyObservers(r) + +proc root(n: NimNode): NimNode = + result = n + while result.kind in {nnkDotExpr, nnkBracketExpr}: result = result[0] + +proc analyse(n: NimNode; paramList: seq[NimNode]): NimNode = + result = n + #if n.kind in {nnkAsgn, nnkFastAsgn}: + # + # n[0] + #else: + # recurse() + macro track*(procDef: untyped): untyped = let params = params(procDef) let key = lineInfo(procDef) var call = newCall(procDef.name) var trackings = newStmtList() - for b in procDef.body: trackings.add b + var paramList: seq[NimNode] = @[] for j in 1..