<commit_before>(ns com.stuartsierra.lazytest.watch
  (:use [clojure.contrib.find-namespaces
         :only (read-file-ns-decl
                find-clojure-sources-in-dir
                find-namespaces-in-dir)]
        [clojure.contrib.java :only (as-file)]
        [com.stuartsierra.lazytest :only (run-spec)]
        [com.stuartsierra.lazytest.report :only (spec-report)])
  (:import (java.io File)))

(defn ns-mod-times
  "Given a directory (java.io.File) containing .clj source files,
  returns map of namespace names (symbols) to Long timestamps."
  [d]
  (reduce (fn [m f]
            (if-let [sym (second (read-file-ns-decl f))]
              (assoc m sym (.lastModified f))))
          {} (find-clojure-sources-in-dir d)))

(defn stop
  "Sent as an agent action, stops a directory-watching agent."
  [state]
  (remove-watch *agent* ::watch)
  false)

(defn ns-mod-time-agent
  "Creates an agent that monitors namespaces for changes to their
  source files.  The state of the agent will always be a map from
  namespace names (symbols) to their last modified timestamp."
  [dir & options]
  {:pre [(instance? File dir)]}
  (let [{:keys [delay] :or {delay 500}} options
        updater (fn upd [state]
                  (when state
                    (Thread/sleep delay)
                    (send *agent* upd)
                    (ns-mod-times dir)))]
    (doto (agent true)
      (send updater))))

(defn- newer-namespaces [older newer]
  (filter #(not= (older %) (newer %)) (keys newer)))

(defn watch-dir
  "Watches directory d for namespaces whose files change, calls f on
  the sequence of changed namespaces.  Returns the watching agent;
  send stop to stop watching the directory.

  f will run with same bindings in effect as when watch-dir is
  called.

  options are 
     :delay - milliseconds between directory scans"
  [d f & options]
  (let [dir (as-file d)]
    (assert (.isDirectory dir))
    (let [agnt (apply ns-mod-time-agent dir options)
          bf (bound-fn* f)]
      (add-watch agnt ::watch
                 (fn [key agnt older newer]
                   (let [names (newer-namespaces older newer)]
                     (when (seq names) (bf names)))))
      agnt)))

(defn watch-spec
  "Runs all specs in directory d, then watches d for changing
  namespaces, reloads and runs their specs when they change.  Prints
  reports to current *out*.  Returns the watching agent, send stop to
  stop watching.

  Options are
    :reporter - report function, default is spec-report
    :delay - time (ms) between directory scans"
  [d & options]
  (let [{:keys [reporter] :or {reporter spec-report}} options]
    (reporter (run-spec (find-namespaces-in-dir d)))
    (apply watch-dir d
           (fn [names] (reporter (run-spec names :reload)))
           options)))
<commit_msg>Start watch with empty map<commit_after>(ns com.stuartsierra.lazytest.watch
  (:use [clojure.contrib.find-namespaces
         :only (read-file-ns-decl
                find-clojure-sources-in-dir
                find-namespaces-in-dir)]
        [clojure.contrib.java :only (as-file)]
        [com.stuartsierra.lazytest :only (run-spec)]
        [com.stuartsierra.lazytest.report :only (spec-report)])
  (:import (java.io File)))

(defn ns-mod-times
  "Given a directory (java.io.File) containing .clj source files,
  returns map of namespace names (symbols) to Long timestamps."
  [d]
  (reduce (fn [m f]
            (if-let [sym (second (read-file-ns-decl f))]
              (assoc m sym (.lastModified f))))
          {} (find-clojure-sources-in-dir d)))

(defn stop
  "Sent as an agent action, stops a directory-watching agent."
  [state]
  (remove-watch *agent* ::watch)
  nil)

(defn ns-mod-time-agent
  "Creates an agent that monitors namespaces for changes to their
  source files.  The state of the agent will always be a map from
  namespace names (symbols) to their last modified timestamp."
  [dir & options]
  {:pre [(instance? File dir)]}
  (let [{:keys [delay] :or {delay 500}} options
        updater (fn upd [state]
                  (when state
                    (Thread/sleep delay)
                    (send *agent* upd)
                    (ns-mod-times dir)))]
    (doto (agent {})
      (send updater))))

(defn- newer-namespaces [older newer]
  (filter #(not= (older %) (newer %)) (keys newer)))

(defn watch-dir
  "Watches directory d for namespaces whose files change, calls f on
  the sequence of changed namespaces.  Returns the watching agent;
  send stop to stop watching the directory.

  f will run with same bindings in effect as when watch-dir is
  called.

  options are 
     :delay - milliseconds between directory scans"
  [d f & options]
  (let [dir (as-file d)]
    (assert (.isDirectory dir))
    (let [agnt (apply ns-mod-time-agent dir options)
          bf (bound-fn* f)]
      (add-watch agnt ::watch
                 (fn [key agnt older newer]
                   (let [names (newer-namespaces older newer)]
                     (when (seq names) (bf names)))))
      agnt)))

(defn watch-spec
  "Runs all specs in directory d, then watches d for changing
  namespaces, reloads and runs their specs when they change.  Prints
  reports to current *out*.  Returns the watching agent, send stop to
  stop watching.

  Options are
    :reporter - report function, default is spec-report
    :delay - time (ms) between directory scans"
  [d & options]
  (let [{:keys [reporter] :or {reporter spec-report}} options]
    (apply watch-dir d
           (fn [names] 
             (reporter (run-spec names :reload)))
           options)))

{'fraction_non_alphanumeric': 0.11477761836441894, 'fraction_numerical': 0.0010760401721664275, 'mean_word_length': 2.457532548047117, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '3950359', 'n_tokens_mistral': 1765, 'n_tokens_neox': 1706, 'n_words': 611}