diff --git a/cljfmt/resources/cljfmt/indents/clojure.clj b/cljfmt/resources/cljfmt/indents/clojure.clj index aee9cc56..b1d2e3de 100644 --- a/cljfmt/resources/cljfmt/indents/clojure.clj +++ b/cljfmt/resources/cljfmt/indents/clojure.clj @@ -1,7 +1,8 @@ -{alt! [[:block 0]] +{-> [[:thread] [:block 0]] + alt! [[:block 0]] alt!! [[:block 0]] are [[:block 2]] - as-> [[:block 2]] + as-> [[:thread 3] [:block 2]] binding [[:block 1]] bound-fn [[:inner 0]] case [[:block 1]] @@ -9,7 +10,7 @@ comment [[:block 0]] cond [[:block 0]] condp [[:block 2]] - cond-> [[:block 1]] + cond-> [[:thread :odd] [:block 1]] cond->> [[:block 1]] def [[:inner 0]] defmacro [[:inner 0]] @@ -50,6 +51,7 @@ ns [[:block 1]] proxy [[:block 2] [:inner 1]] reify [[:inner 0] [:inner 1]] + some-> [[:thread] [:block 0]] struct-map [[:block 1]] testing [[:block 1]] thread [[:block 0]] diff --git a/cljfmt/src/cljfmt/core.cljc b/cljfmt/src/cljfmt/core.cljc index 1e9f0e87..4d48f161 100644 --- a/cljfmt/src/cljfmt/core.cljc +++ b/cljfmt/src/cljfmt/core.cljc @@ -275,15 +275,41 @@ (symbol? key) (= key sym) (pattern? key) (re-find key (str sym))))) -(defn form-matches-key? [zloc key context] +(defn- form-symbol-matches? [zloc pred context] (let [possible-sym (form-symbol zloc)] - (or (symbol-matches-key? (fully-qualified-symbol possible-sym context) key) - (symbol-matches-key? (remove-namespace possible-sym) key)))) + (or (pred (fully-qualified-symbol possible-sym context)) + (pred (remove-namespace possible-sym))))) + +(defn- form-matches-key? [zloc key context] + (form-symbol-matches? zloc #(symbol-matches-key? % key) context)) + +(defn- form-matches-thread-macro? [zloc context] + (form-symbol-matches? zloc (:threads context) context)) + +(defn- get-zipper-position [zloc] + (loop [idx 0 + loop-zloc (z/leftmost zloc)] + (cond + (= zloc loop-zloc) idx + (= loop-zloc (z/rightmost zloc)) nil + :else (recur (inc idx) (z/right loop-zloc))))) + +(defn- in-thread-macro? [zloc context] + (when (= zloc (z/root zloc)) + (when-some [idx (form-matches-thread-macro? zloc context)] + (let [position (cond-> (get-zipper-position (z/up zloc)) + (in-thread-macro? (z/up (z/up zloc)) context) inc)] + (cond + (< position 2) false + (= idx :odd) (odd? position) + (= idx :even) (even? position) + :else (<= idx position)))))) (defn- inner-indent [zloc key depth idx context] - (let [top (nth (iterate z/up zloc) depth)] + (let [top (nth (iterate z/up zloc) depth) + adjusted-idx (cond-> idx (in-thread-macro? zloc context) (some-> dec))] (when (and (form-matches-key? top key context) - (or (nil? idx) (index-matches-top-argument? zloc depth idx))) + (or (nil? idx) (index-matches-top-argument? zloc depth adjusted-idx))) (let [zup (z/up zloc)] (+ (margin zup) (indent-width zup)))))) @@ -302,9 +328,10 @@ (defn- block-indent [zloc key idx context] (when (form-matches-key? zloc key context) - (let [zloc-after-idx (some-> zloc (nth-form (inc idx)))] + (let [adjusted-idx (cond-> idx (in-thread-macro? zloc context) (some-> dec (max 0))) + zloc-after-idx (some-> zloc (nth-form (inc adjusted-idx)))] (if (and (or (nil? zloc-after-idx) (first-form-in-line? zloc-after-idx)) - (> (index-of zloc) idx)) + (> (index-of zloc) adjusted-idx)) (inner-indent zloc key 0 nil context) (list-indent zloc context))))) @@ -336,6 +363,9 @@ (defmethod indenter-fn :block [sym context [_ idx]] (fn [zloc] (block-indent zloc sym idx context))) +(defmethod indenter-fn :thread [_ _ _] + (constantly nil)) + (defn- make-indenter [[key opts] context] (apply some-fn (map (partial indenter-fn key context) opts))) @@ -372,6 +402,13 @@ (defn- find-namespace [zloc] (some-> zloc root z/down (z/find z/right ns-form?) z/down z/next z/sexpr)) +(defn- get-thread-indents [indents] + (->> indents + (keep (fn [[k v]] + (when-first [[_ idx] (filter (fn [[rule]] (= rule :thread)) v)] + [k (or idx 2)]))) + (into {}))) + (defn indent ([form] (indent form default-indents {})) @@ -384,7 +421,8 @@ sorted-indents (sort-by indent-order indents) context (merge (select-keys opts [:function-arguments-indentation]) {:alias-map alias-map - :ns-name ns-name})] + :ns-name ns-name + :threads (get-thread-indents indents)})] (transform form edit-all should-indent? #(indent-line % sorted-indents context))))) diff --git a/cljfmt/test/cljfmt/core_test.cljc b/cljfmt/test/cljfmt/core_test.cljc index 7721d1e7..81518260 100644 --- a/cljfmt/test/cljfmt/core_test.cljc +++ b/cljfmt/test/cljfmt/core_test.cljc @@ -692,7 +692,48 @@ ["#:clj {:a :b" ":c :d}"] ["#:clj {:a :b" - " :c :d}"])))) + " :c :d}"]))) + + (testing "thread first" + (is (reformats-to? + ["(-> v" + "(cond-> a b" + "c d))"] + ["(-> v" + " (cond-> a b" + " c d))"])) + (is (reformats-to? + ["(cond-> v" + "a b" + "c d)"] + ["(cond-> v" + " a b" + " c d)"])) + (is (reformats-to? + ["(-> v" + "(cond-> a b" + "c d))"] + ["(-> v" + " (cond-> a b" + " c d))"])) + (is (reformats-to? + ["(-> (cond-> a" + "odd? inc)" + "inc)"] + ["(-> (cond-> a" + " odd? inc)" + " inc)"])) + (is (reformats-to? + ["(cond-> a" + "(cond-> 1 + odd? inc) + (cond-> a b + c d))"] + ["(cond-> a" + " (cond-> 1" + " odd? inc)" + " (cond-> a b" + " c d))"])))) (deftest test-remove-multiple-non-indenting-spaces (let [opts {:remove-multiple-non-indenting-spaces? true}]