From 5f3b502ee769d1dc6206dd7ae5b77f0b160b6e6b Mon Sep 17 00:00:00 2001 From: Cam Saul Date: Wed, 21 Aug 2024 22:48:25 +0000 Subject: [PATCH] Fix formatting when requires have metadata --- cljfmt/src/cljfmt/core.cljc | 37 +++++++++++++++++++++++++++++-- cljfmt/test/cljfmt/core_test.cljc | 27 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/cljfmt/src/cljfmt/core.cljc b/cljfmt/src/cljfmt/core.cljc index 561a1a4..1ebf050 100644 --- a/cljfmt/src/cljfmt/core.cljc +++ b/cljfmt/src/cljfmt/core.cljc @@ -568,10 +568,43 @@ #?(:clj (defn- as-zloc->alias-mapping [as-zloc] (let [alias (some-> as-zloc z/right z/sexpr) - current-namespace (some-> as-zloc z/leftmost z/sexpr) + ;; `leftmost` might be an `uneval` node e.g. in + ;; + ;; [#_{:clj-kondo/ignore [:x]} a.namespace :as n] + ;; + ;; so look for the first symbol node. The token can also have + ;; metadata on it e.g. + ;; + ;; [^{:clj-kondo/ignore [:x]} a.namespace :as n] + ;; + ;; which is why we need to `skip-meta` in find. + current-namespace (some-> as-zloc + z/leftmost + (z/find #(n/symbol-node? + (z/node (skip-meta %)))) + z/sexpr) grandparent-node (some-> as-zloc z/up z/up) + ;; For something like + ;; + ;; [a [namespace :as n]] + ;; + ;; `grandparent-node` points to [a [namespace :as n]]. In this case + ;; `a` is the parent namespace. + ;; + ;; But for something like + ;; + ;; ^{:x true} [a.namespace :as n] + ;; + ;; `grandparent-node` points to a `:meta` node e.g. + ;; + ;; [ ...] + ;; + ;; so make sure we're only looking at actual prefix forms like the + ;; former by checking whether it's a list or vector parent-namespace (when-not (ns-require-form? grandparent-node) - (first (z/child-sexprs grandparent-node)))] + (when (or (z/vector? grandparent-node) + (z/list? grandparent-node)) + (first (z/child-sexprs grandparent-node))))] (when (and (symbol? alias) (symbol? current-namespace)) {(str alias) (if parent-namespace (format "%s.%s" parent-namespace current-namespace) diff --git a/cljfmt/test/cljfmt/core_test.cljc b/cljfmt/test/cljfmt/core_test.cljc index a4c98f7..6827bbe 100644 --- a/cljfmt/test/cljfmt/core_test.cljc +++ b/cljfmt/test/cljfmt/core_test.cljc @@ -257,6 +257,33 @@ {:indents {'thing.core/defn [[:inner 0]]} #?@(:cljs [:alias-map {"t" "thing.core"}])}) "applies custom indentation to namespaced defn") + (testing "handles metadata on or comments before forms inside ns :require list" + (doseq [ignore-str ["" + "^{:clj-kondo/ignore [:discouraged-namespace]} " + "^:clj-kondo/ignore " + "^{some-symbol another-symbol} " + "#_{:clj-kondo/ignore [:discouraged-namespace]} " + "#_:clj-kondo/ignore " + "^tag " + "#_old-thing "] + ns-vec-str [(str ignore-str "[thing.core :as t]") + (str ignore-str "[thing [core :as t]]") + (str ignore-str "(thing [core :as t])") + (str "[" ignore-str "thing.core :as t]") + (str ignore-str " [" ignore-str "thing.core :as t]")] + :let [ns-str (str "(ns example (:require " ns-vec-str "))")]] + (testing ns-str + (is (reformats-to? + [ns-str + "" + "(t/defn foo [x]" + "(+ x 1))"] + [ns-str + "" + "(t/defn foo [x]" + " (+ x 1))"] + {:indents {'ns [[:block 1]], 'thing.core/defn [[:inner 0]]} + #?@(:cljs [:alias-map {"t" "thing.core"}])}))))) (is (reformats-to? ["(comment)" "(ns thing.core)"