From dae6d946087c8c0fd8295e3f41775205f0afd551 Mon Sep 17 00:00:00 2001 From: Connor Kilgore Date: Tue, 25 Jun 2024 08:45:33 -0700 Subject: [PATCH 1/8] saved second attempt --- src/speclj/freshener.clj | 217 ++++++++++++++++++++++++++++++++++++ src/speclj/run/vigilant.clj | 2 +- 2 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 src/speclj/freshener.clj diff --git a/src/speclj/freshener.clj b/src/speclj/freshener.clj new file mode 100644 index 0000000..6b3b5a7 --- /dev/null +++ b/src/speclj/freshener.clj @@ -0,0 +1,217 @@ +(ns speclj.freshener + (:use + [clojure.java.io :only (file)]) + (:require + [clojure.set :as set]) + (:import + [java.io PushbackReader FileReader File])) + +(defn find-files-in + "Returns a seq of all files (matching the regex) contained in the given directories." + [pattern & dirs] + (let [dirs (map #(.getCanonicalFile %) dirs) + files (reduce #(into %1 (file-seq (file %2))) [] dirs) + files (remove #(.isHidden %) files) + clj-files (filter #(re-matches pattern (.getName %)) files)] + clj-files)) + +(def clj-file-regex #".*\.clj(c)?") +(defn clj-files-in + "Returns a seq of all clojure source files contained in the given directories." + [& dirs] (apply find-files-in clj-file-regex dirs)) + +;; Resolving ns names --------------------------------------------------------------------------------------------------- + +(def clj-extensions [".clj" ".cljc"]) + +(defn ns-to-filenames + "Converts the namespace name into a relative path for the corresponding clojure src file." + ([ns] (ns-to-filenames ns clj-extensions)) + ([ns extensions] (map #(str (apply str (replace {\. \/ \- \_} (name ns))) %) extensions))) + +(defn ns-to-file + "Returns a java.io.File corresponding to the clojure src file for the + given namespace. nil is returned if the file is not found in the classpath + or if the file is not a raw text file." + ([ns] (ns-to-file ns clj-extensions)) + ([ns extensions] + (let [relative-filenames (ns-to-filenames ns extensions) + loader (clojure.lang.RT/baseLoader) + url (first (filter identity (map #(.getResource loader %) relative-filenames)))] + (if (and url (= "file" (.getProtocol url))) + (file (.getFile url)) + nil)))) + +(defn ns-form? + "Returns true if the given form is a namespace form." + [form] + (and (list? form) (= 'ns (first form)))) + +(defn read-ns-form + "Returns the namespace form on the specified clojure src file, nil if none is found." + [file] + (try + (let [reader (PushbackReader. (FileReader. file))] + (try + (loop [form (read {:read-cond :allow} reader)] + (if (ns-form? form) + form + (recur (read {:read-cond :allow} reader)))) + (finally (.close reader)))) + (catch Exception e nil))) +; +;; Parsing the ns form -------------------------------------------------------------------------------------------------- +; +(defn- compose-ns [prefix lib] + (if prefix + (symbol (str prefix \. lib)) + lib)) + +(defn- ns-for-part [prefix arg] + (cond + (symbol? arg) (compose-ns prefix arg) + (and (vector? arg) (or (nil? (second arg)) (keyword? (second arg)))) (compose-ns prefix (first arg)) + :else (map #(ns-for-part (compose-ns prefix (first arg)) %) (rest arg)))) + +(defn- depending-names-of-part [args] + (map #(ns-for-part nil %) (filter (complement keyword?) (rest args)))) + +(defn depending-ns-names-from + "Returns a seq of symbols that are the names of the namespaces that the provided + namespace form depends on." + [ns-form] + (let [dependency-parts (filter #(and (list? %) (#{:use :require} (first %))) ns-form) + ns-list (map #(depending-names-of-part %) dependency-parts)] + (set (flatten ns-list)))) + +(defn depending-files-from + "Returns a seq of java.io.File objects that the namespace form depends on." + [ns-form] + (if ns-form + (let [dependency-names (depending-ns-names-from ns-form) + dependency-filenames (map #(ns-to-file %) dependency-names)] + (vec (filter identity dependency-filenames))) + [])) + +(defn ns-name-from + "Returns the name of the namespace form" + [ns-form] + (if ns-form + (second ns-form) + nil)) +; +;; File tracking -------------------------------------------------------------------------------------------------------- + +(deftype FileTracker [ns mod-time dependencies] + Object + (toString [this] (str "ns: " ns " mod-time: " mod-time " dependencies: " dependencies))) + +(defn- new-file-tracker [ns mod-time dependencies] + (FileTracker. ns mod-time dependencies)) + +(defn- modified? [file tracker] + (> (.lastModified file) (.mod-time tracker))) + +(declare update-tracking-for-files) +(defn- update-tracking-for-file [listing file batch] + (let [tracker (get listing file) + no-update-required (not (or (nil? tracker) (modified? file tracker)))] + (if no-update-required + [listing batch] + (let [ns-form (read-ns-form file) + dependencies (depending-files-from ns-form) + [listing batch] (update-tracking-for-files listing dependencies batch) + ns (ns-name-from ns-form) + updated-tracker (new-file-tracker ns (.lastModified file) dependencies)] + [(assoc listing file updated-tracker) batch])))) + +(defn- update-tracking-for-files + ([listing files] (first (update-tracking-for-files listing files #{}))) + ([listing files batch] + (loop [[listing batch] [listing batch] files files] + (if (not (seq files)) + [listing batch] + (let [file (first files)] + (if (contains? batch file) + (recur [listing batch] (rest files)) + (recur (update-tracking-for-file listing file (conj batch file)) (rest files)))))))) + +(defn- depends-on? [dependency listing dependent] + (some (partial = dependency) (.dependencies (get listing dependent)))) + +(defn- has-dependent? [listing file] + (some #(depends-on? file listing %) (keys listing))) + +(defn- with-dependency [new-dependents dependents file tracker] + (if (some dependents (.dependencies tracker)) + (conj new-dependents file) + new-dependents)) + +(defn- dependents-of + ([listing files] (dependents-of listing (set files) #{})) + ([listing files dependents] + (loop [files files dependents dependents] + (let [new-dependents (reduce (fn [new-dependents [file tracker]] (with-dependency new-dependents files file tracker)) #{} listing)] + (if (seq new-dependents) + (recur new-dependents (into dependents new-dependents)) + dependents))))) + +(defn- clean-deleted-files + ([listing] (clean-deleted-files listing (filter #(not (.exists %)) (keys listing)))) + ([listing files-to-delete] + (if (not (seq files-to-delete)) + listing + (let [dependencies (reduce #(into %1 (.dependencies (get listing %2))) [] files-to-delete) + listing (apply dissoc listing files-to-delete) + unused-dependencies (filter #(not (has-dependent? listing %)) dependencies)] + (clean-deleted-files listing unused-dependencies))))) + +(defn- unload-nses [nses] + (doseq [ns nses] (remove-ns ns)) + (dosync (alter @#'clojure.core/*loaded-libs* set/difference (set nses)))) + +(defn- load-nses [nses] + (apply require nses)) + +(defn- doto-nses [listing files & actions] + (let [trackers (vec (filter identity (map listing files))) + nses (vec (filter identity (map #(.ns %) trackers)))] + (when (seq nses) + (doseq [action actions] + (action nses))))) + +(defn make-fresh + "Does the work of freshener functions." + [listing-atom files auditor] + (let [listing (clean-deleted-files @listing-atom) + tracked-files (set (keys listing)) + deleted (set/difference (set (keys @listing-atom)) tracked-files) + new-tracked-files (set/difference (set files) tracked-files) + modified-tracked-files (set (filter #(modified? % (get listing %)) tracked-files)) + updates (concat new-tracked-files modified-tracked-files) + listing (update-tracking-for-files listing updates) + new (set/difference (set (keys listing)) tracked-files) + files-to-reload (sort (into (dependents-of listing updates) updates)) + result {:new new :deleted deleted :modified modified-tracked-files :reloaded files-to-reload}] + (when (auditor result) + (doto-nses @listing-atom deleted unload-nses) + (reset! listing-atom listing) + (doto-nses listing files-to-reload unload-nses load-nses)) + result)) + +(defn freshener + "Returns a freshener function that, when invoked, will ensure +the freshness of all files provided by the provider function. +The provider must be a no-arg function that returns a seq of java.io.File +objects. If any of the files have been modified, they (and all +their dependent files), will be reloaded. New files will be loaded and +tracked. Deleted files will be unloaded along with any dependant files +that are no longer referenced. The freshener function returns a report map +of seqs containing File objects: {:new :modified :deleted :reloaded}. +The optional auditor function is called, passing in the report map, +before the state of the runtime has been modified. Only when the auditor +returns a truthy value will the runtime be modified." + ([provider] (freshener provider (fn [_] true))) + ([provider auditor] + (let [listing-atom (atom {})] + (fn [] (make-fresh listing-atom (provider) auditor))))) diff --git a/src/speclj/run/vigilant.clj b/src/speclj/run/vigilant.clj index ec998c2..1df3902 100644 --- a/src/speclj/run/vigilant.clj +++ b/src/speclj/run/vigilant.clj @@ -1,6 +1,6 @@ (ns speclj.run.vigilant (:require [clojure.java.io :as io] - [fresh.core :refer [clj-files-in make-fresh ns-to-file]] + [speclj.freshener :refer [clj-files-in make-fresh ns-to-file]] [speclj.config :as config] [speclj.platform :refer [current-time endl enter-pressed? format-seconds secs-since]] [speclj.reporting :as reporting] From 98a8031d8d71bea3c8406220bedcc073a0110cbf Mon Sep 17 00:00:00 2001 From: Connor Kilgore Date: Tue, 25 Jun 2024 11:19:19 -0700 Subject: [PATCH 2/8] updated deps --- deps.edn | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deps.edn b/deps.edn index 06c6ec1..b5f5f31 100644 --- a/deps.edn +++ b/deps.edn @@ -5,6 +5,9 @@ mmargs/mmargs {:mvn/version "1.2.0"} org.clojure/clojure {:mvn/version "1.11.3"} trptcolin/versioneer {:mvn/version "0.1.1"} + org.clojure/tools.namespace {:mvn/version "1.5.0"} + clj-commons/pomegranate {:mvn/version "1.2.24"} + leiningen-core/leiningen-core {:mvn/version "2.9.6"} } :aliases { :test {:extra-deps { From 6f40bf7cfa73e22c95e8c994bbb27063fb4307f9 Mon Sep 17 00:00:00 2001 From: Connor Kilgore Date: Tue, 25 Jun 2024 16:27:04 -0700 Subject: [PATCH 3/8] working state for new lib --- spec/speclj/freshener_spec.clj | 11 ++++++ src/speclj/freshener.clj | 23 ++++++++++++- src/speclj/run/vigilant.clj | 63 +++++++++++++++------------------- 3 files changed, 61 insertions(+), 36 deletions(-) create mode 100644 spec/speclj/freshener_spec.clj diff --git a/spec/speclj/freshener_spec.clj b/spec/speclj/freshener_spec.clj new file mode 100644 index 0000000..d50af17 --- /dev/null +++ b/spec/speclj/freshener_spec.clj @@ -0,0 +1,11 @@ +(ns speclj.freshener-spec + (:require [speclj.core :refer :all] + [speclj.freshener :refer :all] + [speclj.spec-helper :as spec-helper])) + +(describe "Freshener" + + (it "returns n" + (should= 1 (return-n 1)) + ) + ) \ No newline at end of file diff --git a/src/speclj/freshener.clj b/src/speclj/freshener.clj index 6b3b5a7..7c72e2a 100644 --- a/src/speclj/freshener.clj +++ b/src/speclj/freshener.clj @@ -2,7 +2,14 @@ (:use [clojure.java.io :only (file)]) (:require - [clojure.set :as set]) + [clojure.set :as set] + [clojure.string :as str] + [clojure.tools.namespace.repl :as repl] + [clojure.tools.namespace.reload :as reload] + [clojure.tools.namespace.dir :as dir] + [clojure.tools.namespace.track :as track] + [clojure.tools.namespace.find :as find] + [clojure.tools.namespace.parse :as parse]) (:import [java.io PushbackReader FileReader File])) @@ -180,6 +187,20 @@ (doseq [action actions] (action nses))))) +(defn get-dependencies [deps-map] + (let [nses (apply set/union (for [ns deps-map] + (set (conj (val ns) (key ns)))))] + (for [ns nses] (ns-to-file ns)))) + +(defn return-n [n] + n) + +(defn freshen [listing-atom file-dirs] + (repl/scan) + (alter-var-root #'repl/refresh-tracker reload/track-reload) + (apply repl/set-refresh-dirs []) + ) + (defn make-fresh "Does the work of freshener functions." [listing-atom files auditor] diff --git a/src/speclj/run/vigilant.clj b/src/speclj/run/vigilant.clj index 1df3902..a1a23ee 100644 --- a/src/speclj/run/vigilant.clj +++ b/src/speclj/run/vigilant.clj @@ -1,53 +1,45 @@ (ns speclj.run.vigilant (:require [clojure.java.io :as io] - [speclj.freshener :refer [clj-files-in make-fresh ns-to-file]] + [clojure.tools.namespace.repl :as repl] + [speclj.freshener :refer [make-fresh ns-to-file freshen]] [speclj.config :as config] [speclj.platform :refer [current-time endl enter-pressed? format-seconds secs-since]] [speclj.reporting :as reporting] [speclj.results :as results] - [speclj.running :as running]) + [speclj.running :as running] + [clojure.tools.namespace.repl :as repl] + [clojure.tools.namespace.reload :as reload]) (:import (java.util.concurrent ScheduledThreadPoolExecutor TimeUnit))) (def start-time (atom 0)) -(defn- ns-for-results [results] - (set (map #(str (.ns @(.. % characteristic parent))) results))) +(def current-error-data (atom nil)) -(defn- report-update [report] - (let [reporters (config/active-reporters) - reloads (:reloaded report)] - (when (seq reloads) - (reporting/report-message* reporters (str endl "----- " (str (java.util.Date.) " -----"))) - (reporting/report-message* reporters (str "took " (format-seconds (secs-since @start-time)) " to determine file statuses.")) - (reporting/report-message* reporters "reloading files:") - (doseq [file reloads] (reporting/report-message* reporters (str " " (.getCanonicalPath file)))))) - true) - -(defn- reload-files [runner current-results] - (let [previous-failed-files (map ns-to-file (ns-for-results @(.previous-failed runner))) - files-to-reload (set (concat previous-failed-files current-results))] - (swap! (.file-listing runner) #(apply dissoc % previous-failed-files)) - (make-fresh (.file-listing runner) files-to-reload report-update))) - -(defn- reload-report [runner report] - (let [reloads (:reloaded report)] - (when (seq reloads) - (reload-files runner reloads))) - false) +(defn get-error-data [e] + (:data (first (:via (Throwable->map e))))) (defn- tick [configuration] (with-bindings configuration - (let [runner (config/active-runner) + (let [runner (config/active-runner) reporters (config/active-reporters)] (try (reset! start-time (current-time)) - (make-fresh (.file-listing runner) (set (apply clj-files-in @(.directories runner))) (partial reload-report runner)) - (when (seq @(.descriptions runner)) - (reset! (.previous-failed runner) (:fail (results/categorize (seq @(.results runner))))) - (running/run-and-report runner reporters)) + (freshen (.file-listing runner) @(.directories runner)) + (cond + (::reload/error repl/refresh-tracker) + (throw (::reload/error repl/refresh-tracker)) + (seq @(.descriptions runner)) + (do + (reset! current-error-data nil) + (reset! (.previous-failed runner) (:fail (results/categorize (seq @(.results runner))))) + (running/run-and-report runner reporters))) (catch java.lang.Throwable e - (running/process-compile-error runner e) - (reporting/report-runs* reporters @(.results runner)))) + (let [error-data (get-error-data e)] + (when (not= error-data @current-error-data) + (running/process-compile-error runner e) + (reporting/report-runs* reporters @(.results runner))) + (reset! current-error-data error-data)) + )) (reset! (.descriptions runner) []) (reset! (.results runner) [])))) @@ -64,11 +56,12 @@ (deftype VigilantRunner [file-listing results previous-failed directories descriptions] running/Runner (run-directories [this directories _reporters] - (let [scheduler (ScheduledThreadPoolExecutor. 1) + (let [scheduler (ScheduledThreadPoolExecutor. 1) configuration (config/config-bindings) - runnable (fn [] (tick configuration)) - dir-files (map io/file directories)] + runnable (fn [] (tick configuration)) + dir-files (map io/file directories)] (reset! (.directories this) dir-files) + (apply repl/set-refresh-dirs dir-files) (.scheduleWithFixedDelay scheduler runnable 0 500 TimeUnit/MILLISECONDS) (.scheduleWithFixedDelay scheduler (fn [] (listen-for-rerun configuration)) 0 500 TimeUnit/MILLISECONDS) (.awaitTermination scheduler Long/MAX_VALUE TimeUnit/SECONDS) From 06c4d97f74c0d4190cfc65f1ee82938a7f6fe168 Mon Sep 17 00:00:00 2001 From: Connor Kilgore Date: Tue, 25 Jun 2024 16:38:19 -0700 Subject: [PATCH 4/8] clean up and fixed reset feature --- deps.edn | 1 - src/speclj/freshener.clj | 216 +---------------------------------- src/speclj/run/standard.cljc | 2 +- src/speclj/run/vigilant.clj | 7 +- 4 files changed, 9 insertions(+), 217 deletions(-) diff --git a/deps.edn b/deps.edn index b5f5f31..afe3eb1 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,6 @@ { :paths ["src"] :deps { - fresh/fresh {:mvn/version "1.1.2"} mmargs/mmargs {:mvn/version "1.2.0"} org.clojure/clojure {:mvn/version "1.11.3"} trptcolin/versioneer {:mvn/version "0.1.1"} diff --git a/src/speclj/freshener.clj b/src/speclj/freshener.clj index 7c72e2a..69fb2fb 100644 --- a/src/speclj/freshener.clj +++ b/src/speclj/freshener.clj @@ -2,16 +2,8 @@ (:use [clojure.java.io :only (file)]) (:require - [clojure.set :as set] - [clojure.string :as str] [clojure.tools.namespace.repl :as repl] - [clojure.tools.namespace.reload :as reload] - [clojure.tools.namespace.dir :as dir] - [clojure.tools.namespace.track :as track] - [clojure.tools.namespace.find :as find] - [clojure.tools.namespace.parse :as parse]) - (:import - [java.io PushbackReader FileReader File])) + [clojure.tools.namespace.reload :as reload])) (defn find-files-in "Returns a seq of all files (matching the regex) contained in the given directories." @@ -27,212 +19,10 @@ "Returns a seq of all clojure source files contained in the given directories." [& dirs] (apply find-files-in clj-file-regex dirs)) -;; Resolving ns names --------------------------------------------------------------------------------------------------- - -(def clj-extensions [".clj" ".cljc"]) - -(defn ns-to-filenames - "Converts the namespace name into a relative path for the corresponding clojure src file." - ([ns] (ns-to-filenames ns clj-extensions)) - ([ns extensions] (map #(str (apply str (replace {\. \/ \- \_} (name ns))) %) extensions))) - -(defn ns-to-file - "Returns a java.io.File corresponding to the clojure src file for the - given namespace. nil is returned if the file is not found in the classpath - or if the file is not a raw text file." - ([ns] (ns-to-file ns clj-extensions)) - ([ns extensions] - (let [relative-filenames (ns-to-filenames ns extensions) - loader (clojure.lang.RT/baseLoader) - url (first (filter identity (map #(.getResource loader %) relative-filenames)))] - (if (and url (= "file" (.getProtocol url))) - (file (.getFile url)) - nil)))) - -(defn ns-form? - "Returns true if the given form is a namespace form." - [form] - (and (list? form) (= 'ns (first form)))) - -(defn read-ns-form - "Returns the namespace form on the specified clojure src file, nil if none is found." - [file] - (try - (let [reader (PushbackReader. (FileReader. file))] - (try - (loop [form (read {:read-cond :allow} reader)] - (if (ns-form? form) - form - (recur (read {:read-cond :allow} reader)))) - (finally (.close reader)))) - (catch Exception e nil))) -; -;; Parsing the ns form -------------------------------------------------------------------------------------------------- -; -(defn- compose-ns [prefix lib] - (if prefix - (symbol (str prefix \. lib)) - lib)) - -(defn- ns-for-part [prefix arg] - (cond - (symbol? arg) (compose-ns prefix arg) - (and (vector? arg) (or (nil? (second arg)) (keyword? (second arg)))) (compose-ns prefix (first arg)) - :else (map #(ns-for-part (compose-ns prefix (first arg)) %) (rest arg)))) - -(defn- depending-names-of-part [args] - (map #(ns-for-part nil %) (filter (complement keyword?) (rest args)))) - -(defn depending-ns-names-from - "Returns a seq of symbols that are the names of the namespaces that the provided - namespace form depends on." - [ns-form] - (let [dependency-parts (filter #(and (list? %) (#{:use :require} (first %))) ns-form) - ns-list (map #(depending-names-of-part %) dependency-parts)] - (set (flatten ns-list)))) - -(defn depending-files-from - "Returns a seq of java.io.File objects that the namespace form depends on." - [ns-form] - (if ns-form - (let [dependency-names (depending-ns-names-from ns-form) - dependency-filenames (map #(ns-to-file %) dependency-names)] - (vec (filter identity dependency-filenames))) - [])) - -(defn ns-name-from - "Returns the name of the namespace form" - [ns-form] - (if ns-form - (second ns-form) - nil)) -; -;; File tracking -------------------------------------------------------------------------------------------------------- - -(deftype FileTracker [ns mod-time dependencies] - Object - (toString [this] (str "ns: " ns " mod-time: " mod-time " dependencies: " dependencies))) - -(defn- new-file-tracker [ns mod-time dependencies] - (FileTracker. ns mod-time dependencies)) - -(defn- modified? [file tracker] - (> (.lastModified file) (.mod-time tracker))) - -(declare update-tracking-for-files) -(defn- update-tracking-for-file [listing file batch] - (let [tracker (get listing file) - no-update-required (not (or (nil? tracker) (modified? file tracker)))] - (if no-update-required - [listing batch] - (let [ns-form (read-ns-form file) - dependencies (depending-files-from ns-form) - [listing batch] (update-tracking-for-files listing dependencies batch) - ns (ns-name-from ns-form) - updated-tracker (new-file-tracker ns (.lastModified file) dependencies)] - [(assoc listing file updated-tracker) batch])))) - -(defn- update-tracking-for-files - ([listing files] (first (update-tracking-for-files listing files #{}))) - ([listing files batch] - (loop [[listing batch] [listing batch] files files] - (if (not (seq files)) - [listing batch] - (let [file (first files)] - (if (contains? batch file) - (recur [listing batch] (rest files)) - (recur (update-tracking-for-file listing file (conj batch file)) (rest files)))))))) - -(defn- depends-on? [dependency listing dependent] - (some (partial = dependency) (.dependencies (get listing dependent)))) - -(defn- has-dependent? [listing file] - (some #(depends-on? file listing %) (keys listing))) - -(defn- with-dependency [new-dependents dependents file tracker] - (if (some dependents (.dependencies tracker)) - (conj new-dependents file) - new-dependents)) - -(defn- dependents-of - ([listing files] (dependents-of listing (set files) #{})) - ([listing files dependents] - (loop [files files dependents dependents] - (let [new-dependents (reduce (fn [new-dependents [file tracker]] (with-dependency new-dependents files file tracker)) #{} listing)] - (if (seq new-dependents) - (recur new-dependents (into dependents new-dependents)) - dependents))))) - -(defn- clean-deleted-files - ([listing] (clean-deleted-files listing (filter #(not (.exists %)) (keys listing)))) - ([listing files-to-delete] - (if (not (seq files-to-delete)) - listing - (let [dependencies (reduce #(into %1 (.dependencies (get listing %2))) [] files-to-delete) - listing (apply dissoc listing files-to-delete) - unused-dependencies (filter #(not (has-dependent? listing %)) dependencies)] - (clean-deleted-files listing unused-dependencies))))) - -(defn- unload-nses [nses] - (doseq [ns nses] (remove-ns ns)) - (dosync (alter @#'clojure.core/*loaded-libs* set/difference (set nses)))) - -(defn- load-nses [nses] - (apply require nses)) - -(defn- doto-nses [listing files & actions] - (let [trackers (vec (filter identity (map listing files))) - nses (vec (filter identity (map #(.ns %) trackers)))] - (when (seq nses) - (doseq [action actions] - (action nses))))) - -(defn get-dependencies [deps-map] - (let [nses (apply set/union (for [ns deps-map] - (set (conj (val ns) (key ns)))))] - (for [ns nses] (ns-to-file ns)))) - (defn return-n [n] n) -(defn freshen [listing-atom file-dirs] +(defn freshen [] (repl/scan) (alter-var-root #'repl/refresh-tracker reload/track-reload) - (apply repl/set-refresh-dirs []) - ) - -(defn make-fresh - "Does the work of freshener functions." - [listing-atom files auditor] - (let [listing (clean-deleted-files @listing-atom) - tracked-files (set (keys listing)) - deleted (set/difference (set (keys @listing-atom)) tracked-files) - new-tracked-files (set/difference (set files) tracked-files) - modified-tracked-files (set (filter #(modified? % (get listing %)) tracked-files)) - updates (concat new-tracked-files modified-tracked-files) - listing (update-tracking-for-files listing updates) - new (set/difference (set (keys listing)) tracked-files) - files-to-reload (sort (into (dependents-of listing updates) updates)) - result {:new new :deleted deleted :modified modified-tracked-files :reloaded files-to-reload}] - (when (auditor result) - (doto-nses @listing-atom deleted unload-nses) - (reset! listing-atom listing) - (doto-nses listing files-to-reload unload-nses load-nses)) - result)) - -(defn freshener - "Returns a freshener function that, when invoked, will ensure -the freshness of all files provided by the provider function. -The provider must be a no-arg function that returns a seq of java.io.File -objects. If any of the files have been modified, they (and all -their dependent files), will be reloaded. New files will be loaded and -tracked. Deleted files will be unloaded along with any dependant files -that are no longer referenced. The freshener function returns a report map -of seqs containing File objects: {:new :modified :deleted :reloaded}. -The optional auditor function is called, passing in the report map, -before the state of the runtime has been modified. Only when the auditor -returns a truthy value will the runtime be modified." - ([provider] (freshener provider (fn [_] true))) - ([provider auditor] - (let [listing-atom (atom {})] - (fn [] (make-fresh listing-atom (provider) auditor))))) + (apply repl/set-refresh-dirs [])) diff --git a/src/speclj/run/standard.cljc b/src/speclj/run/standard.cljc index 14a32cd..d5b195a 100644 --- a/src/speclj/run/standard.cljc +++ b/src/speclj/run/standard.cljc @@ -1,6 +1,6 @@ (ns speclj.run.standard (:require #?(:clj [clojure.java.io :as io]) - #?(:clj [fresh.core :as fresh]) + #?(:clj [speclj.freshener :as fresh]) #?(:cljs [speclj.report.progress]) #?(:cljs [speclj.components :as components]) [speclj.config :as config] diff --git a/src/speclj/run/vigilant.clj b/src/speclj/run/vigilant.clj index a1a23ee..78fdae0 100644 --- a/src/speclj/run/vigilant.clj +++ b/src/speclj/run/vigilant.clj @@ -1,7 +1,7 @@ (ns speclj.run.vigilant (:require [clojure.java.io :as io] [clojure.tools.namespace.repl :as repl] - [speclj.freshener :refer [make-fresh ns-to-file freshen]] + [speclj.freshener :refer [freshen]] [speclj.config :as config] [speclj.platform :refer [current-time endl enter-pressed? format-seconds secs-since]] [speclj.reporting :as reporting] @@ -24,7 +24,7 @@ reporters (config/active-reporters)] (try (reset! start-time (current-time)) - (freshen (.file-listing runner) @(.directories runner)) + (freshen) (cond (::reload/error repl/refresh-tracker) (throw (::reload/error repl/refresh-tracker)) @@ -44,6 +44,9 @@ (reset! (.results runner) [])))) (defn- reset-runner [runner] + (reset! current-error-data nil) + (repl/clear) + (apply repl/set-refresh-dirs @(.directories runner)) (reset! (.previous-failed runner) []) (reset! (.results runner) []) (reset! (.file-listing runner) {})) From 1ac8e721be99b076d23e83b86e12dabb72a05f62 Mon Sep 17 00:00:00 2001 From: Connor Kilgore Date: Wed, 26 Jun 2024 09:48:22 -0700 Subject: [PATCH 5/8] Added in tests --- examples/sample/a.clj | 1 + examples/sample/b.cljc | 1 + examples/sample/c.cljx | 1 + examples/sample/portable.cljx | 1 + spec/speclj/freshener_spec.clj | 53 ++++++++++++++++++++++++++++++---- src/speclj/freshener.clj | 6 ++-- 6 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 examples/sample/a.clj create mode 100644 examples/sample/b.cljc create mode 100644 examples/sample/c.cljx create mode 100644 examples/sample/portable.cljx diff --git a/examples/sample/a.clj b/examples/sample/a.clj new file mode 100644 index 0000000..2ded8b8 --- /dev/null +++ b/examples/sample/a.clj @@ -0,0 +1 @@ +I'm a clojure file \ No newline at end of file diff --git a/examples/sample/b.cljc b/examples/sample/b.cljc new file mode 100644 index 0000000..1b430e7 --- /dev/null +++ b/examples/sample/b.cljc @@ -0,0 +1 @@ +I'm a clojure common file \ No newline at end of file diff --git a/examples/sample/c.cljx b/examples/sample/c.cljx new file mode 100644 index 0000000..375dd0b --- /dev/null +++ b/examples/sample/c.cljx @@ -0,0 +1 @@ +I'm neither \ No newline at end of file diff --git a/examples/sample/portable.cljx b/examples/sample/portable.cljx new file mode 100644 index 0000000..f7479f8 --- /dev/null +++ b/examples/sample/portable.cljx @@ -0,0 +1 @@ +I'm antiquated \ No newline at end of file diff --git a/spec/speclj/freshener_spec.clj b/spec/speclj/freshener_spec.clj index d50af17..c02b9cb 100644 --- a/spec/speclj/freshener_spec.clj +++ b/spec/speclj/freshener_spec.clj @@ -1,11 +1,54 @@ (ns speclj.freshener-spec (:require [speclj.core :refer :all] [speclj.freshener :refer :all] - [speclj.spec-helper :as spec-helper])) + [clojure.tools.namespace.repl :as repl] + [clojure.tools.namespace.dir :as dir]) + (:use [clojure.java.io :only (file copy make-input-stream delete-file make-parents)])) + +(def sample-dir (.getCanonicalFile (file "examples/sample"))) + +(defn sample-file [dir name] + (file dir name)) + +(defn write-file [dir name content] + (let [file (sample-file dir name)] + (make-parents file) + (copy (make-input-stream (.getBytes content) {}) file) + file)) + +(defn tweak-mod-time [file tweak] + (let [mod-time (+ (.lastModified file) (* 1000 tweak))] + (.setLastModified file mod-time))) (describe "Freshener" - (it "returns n" - (should= 1 (return-n 1)) - ) - ) \ No newline at end of file + (it "finds specified files by default" + (write-file sample-dir "portable.cljx" "I'm antiquated") + (let [files (find-files-in #".*\.cljx" sample-dir)] + (should-contain "portable.cljx" (set (map #(.getName %) files))))) + + (it "first freshening adds files to listing" + (write-file sample-dir "a.clj" "I'm a clojure file") + (write-file sample-dir "b.cljc" "I'm a clojure common file") + (write-file sample-dir "c.cljx" "I'm neither") + (let [files (clj-files-in sample-dir)] + (should-contain "a.clj" (set (map #(.getName %) files))) + (should-contain "b.cljc" (set (map #(.getName %) files))) + (should-not-contain "c.cljx" (set (map #(.getName %) files))))) + + (context "freshen" + (before + (repl/set-refresh-dirs sample-dir)) + + (it "new files are detected and added to tracker" + (repl/clear) + (freshen) + (should= 2 (count (::dir/files repl/refresh-tracker))) + (should-contain "a.clj" (set (map #(.getName %) (::dir/files repl/refresh-tracker)))) + (should-contain "b.cljc" (set (map #(.getName %) (::dir/files repl/refresh-tracker))))) + + (it "refresh dirs are updated to nil indicating the classpath" + (freshen) + (should= nil repl/refresh-dirs))) + + (repl/clear)) \ No newline at end of file diff --git a/src/speclj/freshener.clj b/src/speclj/freshener.clj index 69fb2fb..d31214c 100644 --- a/src/speclj/freshener.clj +++ b/src/speclj/freshener.clj @@ -2,6 +2,7 @@ (:use [clojure.java.io :only (file)]) (:require + [clojure.tools.namespace.dir :as dir] [clojure.tools.namespace.repl :as repl] [clojure.tools.namespace.reload :as reload])) @@ -19,10 +20,7 @@ "Returns a seq of all clojure source files contained in the given directories." [& dirs] (apply find-files-in clj-file-regex dirs)) -(defn return-n [n] - n) - (defn freshen [] (repl/scan) (alter-var-root #'repl/refresh-tracker reload/track-reload) - (apply repl/set-refresh-dirs [])) + (repl/set-refresh-dirs)) From fd03beb5f1d09754df302b2f7ed70bfd9b228874 Mon Sep 17 00:00:00 2001 From: Connor Kilgore Date: Mon, 22 Jul 2024 09:17:13 -0700 Subject: [PATCH 6/8] errors re-assert themselves --- CHANGES.md | 5 +++++ README.md | 8 ++++---- VERSION | 2 +- package.json | 2 +- src/speclj/run/vigilant.clj | 6 +++++- src/speclj/running.cljc | 29 ++++++++++++++--------------- 6 files changed, 30 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 59f29c9..e693434 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +# 3.4.9 + +* migrates from deprecated make-fresh library to clojure.tools.namespace +* fixes bug where changes to src files in a directory that includes a space char does not register updates to the vigilant runner + # 3.4.8 * can now be built and executed under `:advanced` ClojureScript optimizations diff --git a/README.md b/README.md index eb8df34..4f3a756 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ Include speclj in your `:dev` profile `:dependencies` and`:plugins`. Then change ```clojure ; - snip :dependencies [[org.clojure/clojure "1.11.3"]] -:profiles {:dev {:dependencies [[speclj "3.4.8"]]}} -:plugins [[speclj "3.4.8"]] +:profiles {:dev {:dependencies [[speclj "3.4.9"]]}} +:plugins [[speclj "3.4.9"]] :test-paths ["spec"] ``` @@ -140,7 +140,7 @@ Add a `spec` alias to your `deps.edn`. ```clojure { :aliases {:spec {:main-opts ["-m" "speclj.main" "-c"] - :extra-deps {speclj/speclj {:mvn/version "3.4.8"}} + :extra-deps {speclj/speclj {:mvn/version "3.4.9"}} :extra-paths ["spec"]}} } ``` @@ -312,7 +312,7 @@ Here's an example alias for your `deps.edn`. ```clojure {:aliases {:cov {:main-opts ["-m" "cloverage.coverage" "--runner" ":speclj" "-p" "src" "-s" "spec" ] :extra-deps {cloverage/cloverage {:mvn/version "1.2.4"} - speclj/speclj {:mvn/version "3.4.8"}}}}} + speclj/speclj {:mvn/version "3.4.9"}}}}} ``` Sadly, Cloverage doesn't offer a way to pass arguments to the runner (Speclj in this case). Speclj will use the diff --git a/VERSION b/VERSION index 7921bd0..7bcbb38 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.4.8 +3.4.9 diff --git a/package.json b/package.json index 6ffc2b4..cb544b7 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "puppeteer": "^13.5.1" + "puppeteer": "^22.12.1" } } diff --git a/src/speclj/run/vigilant.clj b/src/speclj/run/vigilant.clj index 78fdae0..d4b6208 100644 --- a/src/speclj/run/vigilant.clj +++ b/src/speclj/run/vigilant.clj @@ -35,7 +35,11 @@ (running/run-and-report runner reporters))) (catch java.lang.Throwable e (let [error-data (get-error-data e)] - (when (not= error-data @current-error-data) + (alter-var-root #'repl/refresh-tracker + (constantly (assoc repl/refresh-tracker :clojure.tools.namespace.track/load []))) + (running/process-compile-error runner e) + (reporting/report-runs* reporters @(.results runner)) + #_(when (not= error-data @current-error-data) (running/process-compile-error runner e) (reporting/report-runs* reporters @(.results runner))) (reset! current-error-data error-data)) diff --git a/src/speclj/running.cljc b/src/speclj/running.cljc index c7ec458..4bbd1e7 100644 --- a/src/speclj/running.cljc +++ b/src/speclj/running.cljc @@ -100,20 +100,20 @@ (defn- report-result [result-constructor characteristic start-time reporters failure] (let [present-args (filter identity [characteristic (secs-since start-time) failure]) - result (apply result-constructor present-args)] + result (apply result-constructor present-args)] (report-run result reporters) result)) (defn- do-characteristic [characteristic reporters] - (let [description @(.-parent characteristic) - befores (collect-components #(deref (.-befores %)) description) - afters (collect-components #(deref (.-afters %)) description) - core-body (.-body characteristic) + (let [description @(.-parent characteristic) + befores (collect-components #(deref (.-befores %)) description) + afters (collect-components #(deref (.-afters %)) description) + core-body (.-body characteristic) before-and-after-body (fn [] (eval-characteristic befores core-body afters)) - arounds (collect-components #(deref (.-arounds %)) description) - full-body (nested-fns before-and-after-body (map #(.-body %) arounds)) - withs (collect-components #(deref (.-withs %)) description) - start-time (current-time)] + arounds (collect-components #(deref (.-arounds %)) description) + full-body (nested-fns before-and-after-body (map #(.-body %) arounds)) + withs (collect-components #(deref (.-withs %)) description) + start-time (current-time)] (try (do (full-body) @@ -123,7 +123,7 @@ (report-result pending-result characteristic start-time reporters e) (report-result fail-result characteristic start-time reporters e))) (finally - (reset-withs withs))))) ;MDM - Possible clojure bug. Inlining reset-withs results in compile error + (reset-withs withs))))) ;MDM - Possible clojure bug. Inlining reset-withs results in compile error (defn- do-characteristics [characteristics reporters] (doall @@ -133,7 +133,7 @@ (declare do-description) (defn- do-child-contexts [context results reporters] - (loop [results results + (loop [results results children @(.-children context)] (if (seq children) (recur (concat results (do-description (first children) reporters)) (rest children)) @@ -148,9 +148,9 @@ #?(:clj (defn- with-withs-bound [description body] - (let [withs (concat @(.-withs description) @(.-with-alls description)) - ns (the-ns (symbol (.-ns description))) - with-mappings (reduce #(assoc %1 (ns-resolve ns (.-name %2)) %2) {} withs) + (let [withs (concat @(.-withs description) @(.-with-alls description)) + ns (the-ns (symbol (.-ns description))) + with-mappings (reduce #(assoc %1 (ns-resolve ns (.-name %2)) %2) {} withs) with-and-ns-mappings (assoc with-mappings #'*ns* ns)] (with-bindings* with-and-ns-mappings body))) @@ -187,7 +187,6 @@ (finally (reset-withs @(.-with-alls description)))))))))) - (defn process-compile-error [runner e] (let [error-result (error-result e)] (swap! (.-results runner) conj error-result) From 5cc4435cd4d3657ff2da6f05186151881bafe96e Mon Sep 17 00:00:00 2001 From: Connor Kilgore Date: Mon, 22 Jul 2024 12:09:02 -0700 Subject: [PATCH 7/8] Files that contain saved state information are no longer reloaded during vigilant run --- src/speclj/config.cljc | 2 +- src/speclj/freshener.clj | 24 +++++++++++++++++++++++- src/speclj/run/vigilant.clj | 8 +++----- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/speclj/config.cljc b/src/speclj/config.cljc index 823990f..6e030d0 100644 --- a/src/speclj/config.cljc +++ b/src/speclj/config.cljc @@ -116,4 +116,4 @@ *omit-pending?* (:omit-pending config) *full-stack-trace?* (some? (:stacktrace config)) *tag-filter* (parse-tags (:tags config))] - (action))) + (action))) \ No newline at end of file diff --git a/src/speclj/freshener.clj b/src/speclj/freshener.clj index d31214c..d23eb95 100644 --- a/src/speclj/freshener.clj +++ b/src/speclj/freshener.clj @@ -4,7 +4,9 @@ (:require [clojure.tools.namespace.dir :as dir] [clojure.tools.namespace.repl :as repl] - [clojure.tools.namespace.reload :as reload])) + [clojure.tools.namespace.track :as track] + [clojure.tools.namespace.reload :as reload] + [clojure.tools.namespace.file :as file])) (defn find-files-in "Returns a seq of all files (matching the regex) contained in the given directories." @@ -20,7 +22,27 @@ "Returns a seq of all clojure source files contained in the given directories." [& dirs] (apply find-files-in clj-file-regex dirs)) +(defn remove-value [val coll] + (remove #(= % val) coll)) + +(defn remove-ignore [tracker namespace] + (when-let [file (first (some #(when (= (val %) namespace) %) (::file/filemap tracker)))] + (alter-var-root #'repl/refresh-tracker + (constantly + (assoc tracker + ::track/load (remove-value namespace (::track/load tracker)) + ::track/unload (remove-value namespace (::track/unload tracker)) + ::file/filemap (dissoc (::file/filemap tracker) file) + ::dir/files (set (remove-value file (::dir/files tracker)))))))) + +(def ignored-namespaces ['speclj.config 'speclj.run.vigilant + 'speclj.results 'speclj.core + 'speclj.reporting 'speclj.running]) + (defn freshen [] (repl/scan) + (doseq [namespace ignored-namespaces] + (remove-ignore repl/refresh-tracker namespace) + ) (alter-var-root #'repl/refresh-tracker reload/track-reload) (repl/set-refresh-dirs)) diff --git a/src/speclj/run/vigilant.clj b/src/speclj/run/vigilant.clj index d4b6208..3e02847 100644 --- a/src/speclj/run/vigilant.clj +++ b/src/speclj/run/vigilant.clj @@ -8,11 +8,11 @@ [speclj.results :as results] [speclj.running :as running] [clojure.tools.namespace.repl :as repl] + [clojure.tools.namespace.track :as track] [clojure.tools.namespace.reload :as reload]) (:import (java.util.concurrent ScheduledThreadPoolExecutor TimeUnit))) (def start-time (atom 0)) - (def current-error-data (atom nil)) (defn get-error-data [e] @@ -25,6 +25,7 @@ (try (reset! start-time (current-time)) (freshen) + ;(prn repl/refresh-tracker) (cond (::reload/error repl/refresh-tracker) (throw (::reload/error repl/refresh-tracker)) @@ -36,12 +37,9 @@ (catch java.lang.Throwable e (let [error-data (get-error-data e)] (alter-var-root #'repl/refresh-tracker - (constantly (assoc repl/refresh-tracker :clojure.tools.namespace.track/load []))) + (constantly (assoc repl/refresh-tracker ::track/load []))) (running/process-compile-error runner e) (reporting/report-runs* reporters @(.results runner)) - #_(when (not= error-data @current-error-data) - (running/process-compile-error runner e) - (reporting/report-runs* reporters @(.results runner))) (reset! current-error-data error-data)) )) (reset! (.descriptions runner) []) From e0c375f5ed8012849aa093aa086a5a515fb5bed4 Mon Sep 17 00:00:00 2001 From: Connor Kilgore Date: Mon, 22 Jul 2024 14:49:07 -0700 Subject: [PATCH 8/8] Fixed report issue --- deps.edn | 2 -- src/speclj/freshener.clj | 24 ++++++++++++++++-------- src/speclj/run/vigilant.clj | 34 +++++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/deps.edn b/deps.edn index afe3eb1..e3c14de 100644 --- a/deps.edn +++ b/deps.edn @@ -5,8 +5,6 @@ org.clojure/clojure {:mvn/version "1.11.3"} trptcolin/versioneer {:mvn/version "0.1.1"} org.clojure/tools.namespace {:mvn/version "1.5.0"} - clj-commons/pomegranate {:mvn/version "1.2.24"} - leiningen-core/leiningen-core {:mvn/version "2.9.6"} } :aliases { :test {:extra-deps { diff --git a/src/speclj/freshener.clj b/src/speclj/freshener.clj index d23eb95..fdc3acf 100644 --- a/src/speclj/freshener.clj +++ b/src/speclj/freshener.clj @@ -1,12 +1,14 @@ (ns speclj.freshener - (:use - [clojure.java.io :only (file)]) (:require [clojure.tools.namespace.dir :as dir] + [clojure.tools.namespace.file :as file] + [clojure.tools.namespace.reload :as reload] [clojure.tools.namespace.repl :as repl] [clojure.tools.namespace.track :as track] - [clojure.tools.namespace.reload :as reload] - [clojure.tools.namespace.file :as file])) + [speclj.config] + [speclj.reporting]) + (:use + [clojure.java.io :only [file]])) (defn find-files-in "Returns a seq of all files (matching the regex) contained in the given directories." @@ -35,6 +37,9 @@ ::file/filemap (dissoc (::file/filemap tracker) file) ::dir/files (set (remove-value file (::dir/files tracker)))))))) +(defn find-key-by-value [m val] + (some (fn [[k v]] (when (= v val) k)) m)) + (def ignored-namespaces ['speclj.config 'speclj.run.vigilant 'speclj.results 'speclj.core 'speclj.reporting 'speclj.running]) @@ -42,7 +47,10 @@ (defn freshen [] (repl/scan) (doseq [namespace ignored-namespaces] - (remove-ignore repl/refresh-tracker namespace) - ) - (alter-var-root #'repl/refresh-tracker reload/track-reload) - (repl/set-refresh-dirs)) + (remove-ignore repl/refresh-tracker namespace)) + (let [reloaded-files + (for [ns (::track/load repl/refresh-tracker)] + (find-key-by-value (::file/filemap repl/refresh-tracker) ns))] + (alter-var-root #'repl/refresh-tracker reload/track-reload) + (repl/set-refresh-dirs) + reloaded-files)) diff --git a/src/speclj/run/vigilant.clj b/src/speclj/run/vigilant.clj index 3e02847..c1d2c8e 100644 --- a/src/speclj/run/vigilant.clj +++ b/src/speclj/run/vigilant.clj @@ -1,15 +1,16 @@ (ns speclj.run.vigilant (:require [clojure.java.io :as io] + [clojure.tools.namespace.reload :as reload] [clojure.tools.namespace.repl :as repl] - [speclj.freshener :refer [freshen]] + [clojure.tools.namespace.dir :as dir] + [clojure.tools.namespace.file :as file] + [clojure.tools.namespace.track :as track] [speclj.config :as config] - [speclj.platform :refer [current-time endl enter-pressed? format-seconds secs-since]] + [speclj.freshener :refer [freshen]] + [speclj.platform :refer [current-time enter-pressed? endl format-seconds secs-since]] [speclj.reporting :as reporting] [speclj.results :as results] - [speclj.running :as running] - [clojure.tools.namespace.repl :as repl] - [clojure.tools.namespace.track :as track] - [clojure.tools.namespace.reload :as reload]) + [speclj.running :as running]) (:import (java.util.concurrent ScheduledThreadPoolExecutor TimeUnit))) (def start-time (atom 0)) @@ -18,19 +19,31 @@ (defn get-error-data [e] (:data (first (:via (Throwable->map e))))) +(defn- report-update [files start-time] + (let [reporters (config/active-reporters)] + (when (seq files) + (reporting/report-message* reporters (str endl "----- " (str (java.util.Date.) " -----"))) + (reporting/report-message* reporters (str "took " (format-seconds (secs-since start-time)) " to determine file statuses.")) + (reporting/report-message* reporters "reloading files:") + (doseq [file files] + (do + (reporting/report-message* reporters (str " " (.getCanonicalPath file)))) + ))) + true) + (defn- tick [configuration] (with-bindings configuration (let [runner (config/active-runner) - reporters (config/active-reporters)] + reporters (config/active-reporters) + reloaded-files (freshen)] (try (reset! start-time (current-time)) - (freshen) - ;(prn repl/refresh-tracker) (cond (::reload/error repl/refresh-tracker) (throw (::reload/error repl/refresh-tracker)) (seq @(.descriptions runner)) (do + (report-update reloaded-files @start-time) (reset! current-error-data nil) (reset! (.previous-failed runner) (:fail (results/categorize (seq @(.results runner))))) (running/run-and-report runner reporters))) @@ -40,8 +53,7 @@ (constantly (assoc repl/refresh-tracker ::track/load []))) (running/process-compile-error runner e) (reporting/report-runs* reporters @(.results runner)) - (reset! current-error-data error-data)) - )) + (reset! current-error-data error-data)))) (reset! (.descriptions runner) []) (reset! (.results runner) []))))