 (defn- noop-macroexpand-1 [form]
   form)
 
-(defn string-ast [string]
+(defn- get-ast-from-cache
+  [ns file-content]
+  (-> (get @ast-cache ns)
+      (get (hash file-content))))
+
+(defn- update-ast-cache
+  [file-content ns ast]
+  (swap! ast-cache update-in [ns] merge {(hash file-content) ast})
+  ast)
+
+(defn- build-ast
+  [ns aliases]
+  (binding [ana/macroexpand-1 noop-macroexpand-1]
+    (assoc-in (ana.jvm/analyze-ns ns) [0 :alias-info] aliases)))
+
+(defn- cachable-ast [file-content]
+  (let [[ns aliases] (parse-ns file-content)]
+    (when ns
+      (if-let [cached-ast (get-ast-from-cache ns file-content)]
+        cached-ast
+        (update-ast-cache file-content ns (build-ast ns aliases))))))
+
+(defn string-ast
+  [file-content]
   (try
-    (let [[ns aliases] (parse-ns string)]
-      (binding [ana/macroexpand-1 noop-macroexpand-1]
-        (when ns
-          (assoc-in (ana.jvm/analyze-ns ns) [0 :alias-info] aliases))))
+    (cachable-ast file-content)
     (catch Exception ex
-      (println "error when building AST for" (first (parse-ns string)))
+      (println "error when building AST for" (first (parse-ns file-content)))
       (.printStackTrace ex)
       [])))

            [clojure.tools.analyzer.jvm :as ana.jvm]
            [clojure.tools.namespace.parse :refer [read-ns-decl]])
  (:import java.io.PushbackReader))

;; these two fns could go to clojure.tools.namespace.parse: would worth a pull request
(defn get-alias [as v]


(defn parse-ns
  "Returns tuples with the ns as the first element and
  a map of the aliases for the namespace as the second element
  in the same format as ns-aliases"
  [body]
  (let [ns-decl (read-ns-decl (PushbackReader. (java.io.StringReader. body)))
        aliases (->> ns-decl
