Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix formatting when requires have metadata #352

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

camsaul
Copy link
Contributor

@camsaul camsaul commented Aug 21, 2024

Fixes #351

While working on this I tried to think of all the possible weird edge cases I could and found that it also broke in a few even weirder situations e.g. if you did something like put metadata on or comments before the x in [x :as y] itself. I added a pretty comprehensive test makes sure things work as expected no matter what shenanigans you're up to in your ns form.

@@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to write out the reasoning behind the changes here as opposed to in the PR, hope that's ok. Maybe it will save the next person who comes along some time so they don't have to work out what's going on the hard way

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case it's a lot of information that isn't relevant to most readers. Consider instead something like:

current-namespace (some-> as-zloc z/leftmost find-first-symbol z/sexpr)

This informs the reader of the intent, without needing to go into detail. If the reader is curious of why, they can look up the information in the associated commit.

It's important to explain the source code, but also to be concise. Any information that isn't universally relevant is noise to those that don't need it.

grandparent-node (some-> as-zloc z/up z/up)
parent-namespace (when-not (ns-require-form? grandparent-node)
(first (z/child-sexprs grandparent-node)))]
(when (or (z/vector? grandparent-node)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we have something like

^{:clj-kondo/ignore [:discouraged-namespace} [my.namespace :as n]

then grandparent-node in this case is a :meta node so by adding the vector or string check we prevent that from accidentally returning an alias map like

{"n" "{:clj-kondo/ignore [:discouraged-namespace]}.my.namespace"}

Comment on lines 571 to 575
current-namespace (some-> as-zloc
z/leftmost
(z/find #(n/symbol-node?
(z/node (skip-meta %))))
z/sexpr)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have something like

[#_my.old.namespace my.namespace :as n]

then z/leftmost will be an :uneval node (see clj-commons/rewrite-clj#70) so we need to skip it until we find the first symbol node, hence the use of z/find. I didn't use token? here since keywords are tokens too and we don't want it to accidentally return :as if it runs into some other issue.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about factoring that out:

(defn- symbol-node? [zloc]
  (some-> zloc z/node n/symbol-node?))

(defn- leftmost-symbol [zloc]
  (some-> zloc z/leftmost (z/find (comp symbol-node? skip-meta))))

That might make it a little more readable:

current-namespace (some-> as-zloc leftmost-symbol z/sexpr)

@@ -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 [""
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope using doseq to test different combinations of stuff here is ok. Writing out 40 separate test cases seemed a bit noisy

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, but why did you use doseq instead of are?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason was that I wanted to try all the different permutations of ignore-str and ns-vec-str which would have required a nested ares which besides being hard to read would then macroexpand into nested is assertions as well. doseq is clearer and doesn't do dumb things

@@ -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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case it's a lot of information that isn't relevant to most readers. Consider instead something like:

current-namespace (some-> as-zloc z/leftmost find-first-symbol z/sexpr)

This informs the reader of the intent, without needing to go into detail. If the reader is curious of why, they can look up the information in the associated commit.

It's important to explain the source code, but also to be concise. Any information that isn't universally relevant is noise to those that don't need it.

cljfmt/src/cljfmt/core.cljc Outdated Show resolved Hide resolved
Comment on lines 571 to 575
current-namespace (some-> as-zloc
z/leftmost
(z/find #(n/symbol-node?
(z/node (skip-meta %))))
z/sexpr)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about factoring that out:

(defn- symbol-node? [zloc]
  (some-> zloc z/node n/symbol-node?))

(defn- leftmost-symbol [zloc]
  (some-> zloc z/leftmost (z/find (comp symbol-node? skip-meta))))

That might make it a little more readable:

current-namespace (some-> as-zloc leftmost-symbol z/sexpr)

@@ -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 [""
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, but why did you use doseq instead of are?

#?(: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)
current-namespace (some-> as-zloc leftmost-symbol z/sexpr)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with suggestions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Indentation rules break if namespace form in :require has metadata on it
2 participants