diff --git a/src/cider/nrepl/middleware/format.clj b/src/cider/nrepl/middleware/format.clj index cd3e2d9d1..5bf029057 100644 --- a/src/cider/nrepl/middleware/format.clj +++ b/src/cider/nrepl/middleware/format.clj @@ -6,12 +6,29 @@ [cljfmt.core :as fmt] [clojure.string :as str] [clojure.tools.reader.edn :as edn] - [clojure.tools.reader.reader-types :as readers])) + [clojure.tools.reader.reader-types :as readers] + [clojure.walk :as walk])) ;;; Code formatting +(defn- keyword->symbol [kw] + (.sym ^clojure.lang.Keyword kw)) + +(defn- generate-user-indents [indents] + (reduce-kv + (fn [acc kw rule] + (assoc acc + (keyword->symbol kw) + (walk/postwalk #(cond-> % (string? %) keyword) rule))) + fmt/default-indents + indents)) + (defn format-code-reply - [{:keys [code] :as msg}] - {:formatted-code (fmt/reformat-string code)}) + [{:keys [code options] :as msg}] + (let [opts (some-> options + (select-keys [:indents :alias-map]) + (update :indents generate-user-indents) + (update :alias-map #(reduce-kv (fn [m k v] (assoc m (name k) v)) {} %)))] + {:formatted-code (fmt/reformat-string code opts)})) ;;; EDN formatting (defn- read-edn diff --git a/test/clj/cider/nrepl/middleware/format_test.clj b/test/clj/cider/nrepl/middleware/format_test.clj index 7c9dfa583..cdd6ceb35 100644 --- a/test/clj/cider/nrepl/middleware/format_test.clj +++ b/test/clj/cider/nrepl/middleware/format_test.clj @@ -60,12 +60,50 @@ (is (= #{"done"} status)) (is (= formatted-code-sample formatted-code)))) + (testing "format-code works with indents option" + (let [{:keys [formatted-code status]} (session/message {:op "format-code" + :code ugly-code-sample + :options {"indents" {"let" [["block" 2]]}}})] + (is (= #{"done"} status)) + (is (= "(let [x 3 + y 4] + (+ (* x x) (* y y)))" + formatted-code)))) + + (testing "format-code works with alias-map option" + (let [alias-sample "(foo/bar 1\n2)" + default-options {"indents" {"foo.core/bar" [["inner" 0]]}} + normal-reply (session/message {:op "format-code" :code alias-sample + :options default-options}) + alias-map-reply (session/message {:op "format-code" :code alias-sample + :options (assoc default-options + "alias-map" {"foo" "foo.core"})})] + (is (= #{"done"} (:status normal-reply) (:status alias-map-reply))) + (is (= "(foo/bar 1\n 2)" (:formatted-code normal-reply))) + (is (= "(foo/bar 1\n 2)" (:formatted-code alias-map-reply))))) + (testing "format-code op error handling" (let [{:keys [status err ex]} (session/message {:op "format-code" :code "*/*/*!~v"})] (is (= #{"format-code-error" "done"} status)) (is (.startsWith err "clojure.lang.ExceptionInfo: Invalid")) - (is (= ex "class clojure.lang.ExceptionInfo"))))) + (is (= ex "class clojure.lang.ExceptionInfo")))) + + (testing "format-code returns an error if indents option is invalid" + (let [{:keys [status err ex] :as reply} (session/message {:op "format-code" + :code "(+ 1 2 3)" + :options {"indents" "INVALID"}})] + (is (= #{"format-code-error" "done"} status)) + (is (.startsWith err "java.lang.IllegalArgumentException:")) + (is (= ex "class java.lang.IllegalArgumentException")))) + + (testing "format-code returns an error if alias-map option is invalid" + (let [{:keys [status err ex] :as reply} (session/message {:op "format-code" + :code "(+ 1 2 3)" + :options {"alias-map" "INVALID"}})] + (is (= #{"format-code-error" "done"} status)) + (is (.startsWith err "java.lang.IllegalArgumentException:")) + (is (= ex "class java.lang.IllegalArgumentException"))))) (deftest format-edn-op-test (testing "format-edn works"