<commit_before>(ns freactive.core
  (:refer-clojure :exclude [atom])
  (:require [goog.object]))

;; Core API

(deftype BindingInfo [raw-deref add-watch remove-watch clean])

(defprotocol IReactive
  (-get-binding-fns [this]))

(def ^:dynamic *register-dep* nil)

(defn register-dep [dep id binding-info]
  (when-let [rdep *register-dep*]
    (rdep dep id binding-info)))

;; END Core API

(def ^:dynamic *lazy* nil)

(def ^:dynamic *do-trace-captures* nil)

(def ^:dynamic *trace-capture* nil)

(def ^:private auto-reactive-id 0)

(defn new-reactive-id []
  (let [id auto-reactive-id]
    (set! auto-reactive-id (inc auto-reactive-id))
    (str "-r." id)))

(def ^:private iwatchable-binding-fns
  (BindingInfo. cljs.core/-deref cljs.core/-add-watch
          cljs.core/-remove-watch nil))

(def ^:private deref-only-binding-fns
  (BindingInfo. cljs.core/-deref nil nil nil))

(defn get-binding-fns [iref]
  (cond
   (satisfies? IReactive iref) (-get-binding-fns iref)
   (satisfies? IWatchable iref) iwatchable-binding-fns
   :default deref-only-binding-fns))

(defn apply-js-mixin [the-type mixin]
  (let [ptype (.-prototype the-type)]
    (goog.object/forEach
     mixin
     (fn [val key obj]
       (aset ptype key val)))))

(defn- -addFWatch [key f]
  (this-as this
           (when-not (aget (.-fwatches this) key)
             (set! (.-watchers this) (inc (.-watchers this)))
             (aset (.-fwatches this) key f))))

(defn- -removeFWatch [key]
  (this-as this
           (when (aget (.-fwatches this) key)
             (set! (.-watchers this) (dec (.-watchers this)))
             (js-delete (.-fwatches this) key))))

(defn- -notifyFWatches [oldVal newVal]
  (this-as this
           (goog.object/forEach
            (.-fwatches this)
            (fn [f key _]
              (f key this oldVal newVal)))
           (doseq [[key f] (.-watches this)]
             (f key this oldVal newVal))))

(def fwatch-mixin
  #js {:addFWatch -addFWatch
       :removeFWatch -removeFWatch
       :notifyFWatches -notifyFWatches})

(let [core-deref cljs.core/deref]
  (set! cljs.core/deref (fn [x]
                          (if (.-fastDeref x)
                            (.fastDeref x)
                            (core-deref x)))))

(def fwatch-binding-info
  (BindingInfo.
   #(.rawDeref %) #(.addFWatch % %2 %3) #(.removeFWatch % %2) #(.clean %)))

(deftype ReactiveAtom [id state meta validator watches fwatches watchers]
  Object
  (equiv [this other]
    (-equiv this other))
  (rawDeref [_] state)
  (fastDeref [this]
    (register-dep this id fwatch-binding-info) 
    state)
  (clean [this])
  cljs.core/IAtom

  IReactive
  (-get-binding-fns [this] fwatch-binding-info)

  cljs.core/IEquiv
  (-equiv [o other] (identical? o other))

  cljs.core/IDeref
  (-deref [this] (.fastDeref this))

  IMeta
  (-meta [_] meta)

  IPrintWithWriter
  (-pr-writer [a writer opts]
    (-write writer "#<ReactiveAtom: ")
    (pr-writer state writer opts)
    (-write writer ">"))

  IWatchable
  (-notify-watches [this oldval newval]
    (.notifyFWatches this oldval newval))
  (-add-watch [this key f]
    (set! (.-watches this) (assoc watches key f))
    this)
  (-remove-watch [this key]
    (set! (.-watches this) (dissoc watches key))
    this)

  IHash
  (-hash [this] (goog/getUid this))

  IReset
  (-reset! [a new-value]
    (let [old-value (.-state a)]
        (when-not (identical? old-value new-value)
          (let [validate (.-validator a)]
            (when-not (nil? validate)
              (assert (validate new-value) "Validator rejected reference state")))
          (set! (.-state a) new-value)
          (.notifyFWatches a old-value new-value))
        new-value))

  ISwap
  (-swap! [a f]
    (-reset! a (f (.-state a))))
  (-swap! [a f x]
    (-reset! a (f (.-state a) x)))
  (-swap! [a f x y]
    (-reset! a (f (.-state a) x y)))
  (-swap! [a f x y more]
    (-reset! a (apply f (.-state a) x y more))))

(apply-js-mixin ReactiveAtom fwatch-mixin)

;; (extend ReactiveAtom
;;   IWatchable
;;   IWatchable-extension)

(defn atom
  "Creates and returns a ReactiveAtom with an initial value of x and zero or
  more options (in any order):
  :meta metadata-map
  :validator validate-fn
  If metadata-map is supplied, it will be come the metadata on the
  atom. validate-fn must be nil or a side-effect-free fn of one
  argument, which will be passed the intended new state on any state
  change. If the new state is unacceptable, the validate-fn should
  return false or throw an Error. If either of these error conditions
  occur, then the value of the atom will not change."
  ([x] (ReactiveAtom. (new-reactive-id )x nil nil nil #js {} 0))
  ([x & {:keys [meta validator]}] (ReactiveAtom. (new-reactive-id) x meta validator nil #js {} 0)))

;; (defn- make-invalidate-fn [reactive id]
;;   (let [invalidate-fn
;;         (fn invalidate []
;;           (when-not (.-dirty reactive)
;;             (set! (.-dirty reactive) true)
;;             (if (> (.-watchers reactive) 0)
;;               ;; updates state and notifies watches
;;               (when (.compute reactive)
;;                 (.notifyInvalidationWatches reactive))
;;               ;; updates only invalidation watches
;;               (.notifyInvalidationWatches reactive))))]
;;     (set! (.-id invalidate-fn) id)
;;     (set! (.-deps invalidate-fn) #js {})
;;     invalidate-fn))

(defn- make-register-dep [rx]
  (fn do-register-dep [dep id binding-info]
    (when *trace-capture* (*trace-capture* dep))
    (aset (.-deps rx) id #js [dep binding-info])
    ;; (println "cur deps:" (.-deps rx))
    ((.-add-watch binding-info)
     dep (.-id rx)
     (fn []
       ((.-remove-watch binding-info) dep (.-id rx))
       (.invalidate rx)))))

(defn- lazy? [default-laziness]
  (if-not (nil? *lazy*) *lazy* default-laziness))

;; (defn- register-eager-rx-dep
;;   [id rx]
;;   (when-let [invalidate *invalidate-rx*]
;;     (when *trace-capture* (*trace-capture* rx))
;;     (aset (.-deps invalidate) id rx)
;;     (.addFWatch
;;      rx (.-id invalidate)
;;      (fn [_ _ _ _]
;;        (.removeFWatch rx id)
;;        (invalidate)))))

;; (defn- register-lazy-rx-dep<commit_msg>Have a dep automatically remove itself from the deps list when invalidated.<commit_after>(ns freactive.core
  (:refer-clojure :exclude [atom])
  (:require [goog.object]))

;; Core API

(deftype BindingInfo [raw-deref add-watch remove-watch clean])

(defprotocol IReactive
  (-get-binding-fns [this]))

(def ^:dynamic *register-dep* nil)

(defn register-dep [dep id binding-info]
  (when-let [rdep *register-dep*]
    (rdep dep id binding-info)))

;; END Core API

(def ^:dynamic *lazy* nil)

(def ^:dynamic *do-trace-captures* nil)

(def ^:dynamic *trace-capture* nil)

(def ^:private auto-reactive-id 0)

(defn new-reactive-id []
  (let [id auto-reactive-id]
    (set! auto-reactive-id (inc auto-reactive-id))
    (str "-r." id)))

(def ^:private iwatchable-binding-fns
  (BindingInfo. cljs.core/-deref cljs.core/-add-watch
          cljs.core/-remove-watch nil))

(def ^:private deref-only-binding-fns
  (BindingInfo. cljs.core/-deref nil nil nil))

(defn get-binding-fns [iref]
  (cond
   (satisfies? IReactive iref) (-get-binding-fns iref)
   (satisfies? IWatchable iref) iwatchable-binding-fns
   :default deref-only-binding-fns))

(defn apply-js-mixin [the-type mixin]
  (let [ptype (.-prototype the-type)]
    (goog.object/forEach
     mixin
     (fn [val key obj]
       (aset ptype key val)))))

(defn- -addFWatch [key f]
  (this-as this
           (when-not (aget (.-fwatches this) key)
             (set! (.-watchers this) (inc (.-watchers this)))
             (aset (.-fwatches this) key f))))

(defn- -removeFWatch [key]
  (this-as this
           (when (aget (.-fwatches this) key)
             (set! (.-watchers this) (dec (.-watchers this)))
             (js-delete (.-fwatches this) key))))

(defn- -notifyFWatches [oldVal newVal]
  (this-as this
           (goog.object/forEach
            (.-fwatches this)
            (fn [f key _]
              (f key this oldVal newVal)))
           (doseq [[key f] (.-watches this)]
             (f key this oldVal newVal))))

(def fwatch-mixin
  #js {:addFWatch -addFWatch
       :removeFWatch -removeFWatch
       :notifyFWatches -notifyFWatches})

(let [core-deref cljs.core/deref]
  (set! cljs.core/deref (fn [x]
                          (if (.-fastDeref x)
                            (.fastDeref x)
                            (core-deref x)))))

(def fwatch-binding-info
  (BindingInfo.
   #(.rawDeref %) #(.addFWatch % %2 %3) #(.removeFWatch % %2) #(.clean %)))

(deftype ReactiveAtom [id state meta validator watches fwatches watchers]
  Object
  (equiv [this other]
    (-equiv this other))
  (rawDeref [_] state)
  (fastDeref [this]
    (register-dep this id fwatch-binding-info) 
    state)
  (clean [this])
  cljs.core/IAtom

  IReactive
  (-get-binding-fns [this] fwatch-binding-info)

  cljs.core/IEquiv
  (-equiv [o other] (identical? o other))

  cljs.core/IDeref
  (-deref [this] (.fastDeref this))

  IMeta
  (-meta [_] meta)

  IPrintWithWriter
  (-pr-writer [a writer opts]
    (-write writer "#<ReactiveAtom: ")
    (pr-writer state writer opts)
    (-write writer ">"))

  IWatchable
  (-notify-watches [this oldval newval]
    (.notifyFWatches this oldval newval))
  (-add-watch [this key f]
    (set! (.-watches this) (assoc watches key f))
    this)
  (-remove-watch [this key]
    (set! (.-watches this) (dissoc watches key))
    this)

  IHash
  (-hash [this] (goog/getUid this))

  IReset
  (-reset! [a new-value]
    (let [old-value (.-state a)]
        (when-not (identical? old-value new-value)
          (let [validate (.-validator a)]
            (when-not (nil? validate)
              (assert (validate new-value) "Validator rejected reference state")))
          (set! (.-state a) new-value)
          (.notifyFWatches a old-value new-value))
        new-value))

  ISwap
  (-swap! [a f]
    (-reset! a (f (.-state a))))
  (-swap! [a f x]
    (-reset! a (f (.-state a) x)))
  (-swap! [a f x y]
    (-reset! a (f (.-state a) x y)))
  (-swap! [a f x y more]
    (-reset! a (apply f (.-state a) x y more))))

(apply-js-mixin ReactiveAtom fwatch-mixin)

;; (extend ReactiveAtom
;;   IWatchable
;;   IWatchable-extension)

(defn atom
  "Creates and returns a ReactiveAtom with an initial value of x and zero or
  more options (in any order):
  :meta metadata-map
  :validator validate-fn
  If metadata-map is supplied, it will be come the metadata on the
  atom. validate-fn must be nil or a side-effect-free fn of one
  argument, which will be passed the intended new state on any state
  change. If the new state is unacceptable, the validate-fn should
  return false or throw an Error. If either of these error conditions
  occur, then the value of the atom will not change."
  ([x] (ReactiveAtom. (new-reactive-id )x nil nil nil #js {} 0))
  ([x & {:keys [meta validator]}] (ReactiveAtom. (new-reactive-id) x meta validator nil #js {} 0)))

;; (defn- make-invalidate-fn [reactive id]
;;   (let [invalidate-fn
;;         (fn invalidate []
;;           (when-not (.-dirty reactive)
;;             (set! (.-dirty reactive) true)
;;             (if (> (.-watchers reactive) 0)
;;               ;; updates state and notifies watches
;;               (when (.compute reactive)
;;                 (.notifyInvalidationWatches reactive))
;;               ;; updates only invalidation watches
;;               (.notifyInvalidationWatches reactive))))]
;;     (set! (.-id invalidate-fn) id)
;;     (set! (.-deps invalidate-fn) #js {})
;;     invalidate-fn))

(defn- make-register-dep [rx]
  (fn do-register-dep [dep id binding-info]
    (when *trace-capture* (*trace-capture* dep))
    (aset (.-deps rx) id #js [dep binding-info])
    ;; (println "cur deps:" (.-deps rx))
    ((.-add-watch binding-info)
     dep (.-id rx)
     (fn []
       ((.-remove-watch binding-info) dep (.-id rx))
       (js-delete (.-deps rx) id)
       (.invalidate rx)))))

(defn- lazy? [default-laziness]
  (if-not (nil? *lazy*) *lazy* default-laziness))

;; (defn- register-eager-rx-dep
;;   [id rx]
;;   (when-let [invalidate *invalidate-rx*]
;;     (when *trace-capture* (*trace-capture* rx))
;;     (aset (.-deps invalidate) id rx)
;;     (.addFWatch
;;      rx (.-id invalidate)
;;      (fn [_ _ _ _]
;;        (.removeFWatch rx id)
;;        (invalidate)))))

;; (defn- register-lazy-rx-dep
{'fraction_non_alphanumeric': 0.15847962634884843, 'fraction_numerical': 0.0011273957158962795, 'mean_word_length': 2.481637230165405, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '215859', 'n_tokens_mistral': 4518, 'n_tokens_neox': 4327, 'n_words': 1276}