diff --git a/src/reactive.nim b/src/reactive.nim index 307d317..34c630e 100644 --- a/src/reactive.nim +++ b/src/reactive.nim @@ -1,4 +1,6 @@ +import jdict + type Message = enum Unchanged @@ -14,9 +16,11 @@ type phantom: bool type + SinkSeq = seq[proc(msg: Message, pos: int)] ReactiveBase* = ref object of RootObj ## everything that is a "reactive" ## value derives from that - sinks*: seq[proc(msg: Message, pos: int)] + sinks*: SinkSeq + dups: JDict[cstring, bool] id: int Reactive*[T] = ref object of ReactiveBase value*: T @@ -33,10 +37,16 @@ type var rid: int +proc addSink(x: ReactiveBase; key: cstring; sink: proc(msg: Message; pos: int)) = + if x.dups == nil: x.dups = newJDict[cstring, bool]() + if not x.dups.contains(key): + x.dups[key] = true + x.sinks.add sink + if x.id == 0: + inc rid + x.id = rid + proc addSink(x: ReactiveBase; sink: proc(msg: Message; pos: int)) = - #if x.sinks.len > 0: - # x.sinks[0] = sink - #else: x.sinks.add sink if x.id == 0: inc rid @@ -78,6 +88,29 @@ template wrapObserver(f: untyped) = #state.phantom = true helper +template glitchFree(f: untyped) = + var state: State + proc helper(msg: Message; pos: int) = + case msg.kind: + of Changed, Unchanged: + state.outdated = state.outdated or (msg.kind == Changed) + dec state.stale + if state.stale == 0: + if state.outdated: + x.value = f + x.broadcast(msg.kind, pos) + state.outdated = false + else: + x.broadcast(Unchanged, pos) + of Mark: + if state.stale == 0: x.broadcast(msg, pos) + inc state.stale + of Inserted, Deleted, Replaced: + dec state.stale + if state.stale == 0: + x.broadcast(msg, pos) + helper + proc `:=`[T](x: Reactive[T], f: proc(): T) = toTrack = wrapObserver(f()) x.value = f() @@ -186,17 +219,29 @@ proc map*[T, U](x: RSeq[T], f: proc(x: T): U): RSeq[U] = import macros -template trackImpl(r: ReactiveBase; a, b: untyped) = +template protect(r: ReactiveBase; body: untyped) = + var tmp: seq[proc(msg: Message, pos: int)] + swap(r.sinks, tmp) + body + swap(r.sinks, tmp) + +template trackImpl(r: ReactiveBase; key: cstring; a, b: untyped) = when r is ReactiveBase: - addSink r, proc(m: Message; pos: int) = - if m == Changed: karax.runDiff(kxi, a, b) + addSink r, key, proc(m: Message; pos: int) = + if m == Changed: + protect r: + echo "runDiff A" + karax.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) + if m == Changed: + protect r: + echo "runDiff B" + karax.runDiff(kxi, a, b) template doTrackResize*(r: ReactiveBase; a, b: untyped) {.dirty.} = bind addSink, Message, RSeq, Changed, Deleted, Inserted @@ -226,10 +271,75 @@ macro track*(procDef: untyped): untyped = let param = x[i] call.add(param) + let key = lineInfo(procDef) for j in 1..