Skip to content

Commit

Permalink
align associative
Browse files Browse the repository at this point in the history
  • Loading branch information
rsh-blip committed Mar 13, 2023
1 parent 3418b7f commit a9c0f05
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ pom.xml.asc
.lein-*
.nrepl-port
reports
*.cpcache
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ selectively enabled or disabled:
other references in the `ns` forms at the top of your namespaces.
Defaults to false.

* `:align-associative?` -
true if cljfmt should left align the values of maps and binding
special forms (let, loop, binding). This will convert
`{:foo 1\n:barbaz 2}` to `{:foo 1\n :barbaz 2}`
and `(let [foo 1\n barbaz 2])` to `(let [foo 1\n barbaz 2])`.
Defaults to false.

You can also configure the behavior of cljfmt:

* `:paths` - determines which directories to include in the
Expand Down
67 changes: 67 additions & 0 deletions cljfmt/src/cljfmt/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,73 @@
(not (namespaced-map? (z/up* zloc)))
(element? (z/right* zloc))))

(def ^:private binding-keywords #{"doseq" "let" "loop" "binding" "with-open"
"go-loop" "if-let" "when-some" "if-some" "for"
"with-local-vars" "with-redefs"})

(defn ks->max-length [ks]
(if (empty? ks)
0
(->> ks
(apply max-key (comp count str))
str
count)))

(defn- aligner [zloc max-length align?]
(cond
(zero? max-length) (z/up zloc)
(z/rightmost? zloc) (z/up zloc)
align? (let [to-add (->> zloc
z/sexpr
str
count
(- max-length))
new-zloc (z/insert-space-right zloc to-add)]
(aligner (z/right new-zloc) max-length false))
:else (aligner (z/right zloc) max-length true)))

(defn- align-binding [zloc]
(let [se (z/sexpr zloc)
ks (take-nth 2 se)
max-length (ks->max-length ks)
bindings (z/down zloc)]
(if bindings
(aligner bindings max-length true)
zloc)))

(defn- align-map [zloc]
(let [se (z/sexpr zloc)
ks (keys se)
max-length (ks->max-length ks)
kvs (z/down zloc)]
(if kvs
(aligner kvs max-length true)
zloc)))

(defn- binding? [zloc]
(and (z/vector? zloc)
(-> zloc z/sexpr count even?)
(->> zloc
z/left
z/string
binding-keywords)))

(defn align-map-or-binding [zloc]
(cond
(binding? zloc) (align-binding zloc)
(z/map? zloc) (align-map zloc)
:else zloc))

(defn- align-associative? [zloc]
(or (binding? zloc)
(z/map? zloc)))

(defn insert-missing-whitespace [form]
(transform form edit-all missing-whitespace? z/insert-space-right))

(defn align-associative [form]
(transform form edit-all align-associative? align-map-or-binding))

(defn- space? [zloc]
(= (z/tag zloc) :whitespace))

Expand Down Expand Up @@ -492,6 +556,7 @@
:remove-surrounding-whitespace? true
:remove-trailing-whitespace? true
:split-keypairs-over-multiple-lines? false
:align-associative? false
:sort-ns-references? false
:indents default-indents
:alias-map {}})
Expand All @@ -512,6 +577,8 @@
remove-surrounding-whitespace)
(cond-> (:insert-missing-whitespace? opts)
insert-missing-whitespace)
(cond-> (:align-associative? opts)
align-associative)
(cond-> (:remove-multiple-non-indenting-spaces? opts)
remove-multiple-non-indenting-spaces)
(cond-> (:indentation? opts)
Expand Down
87 changes: 87 additions & 0 deletions cljfmt/test/cljfmt/core_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -1336,3 +1336,90 @@
" ^{:x 1} b"
" [c]))"]
{:sort-ns-references? true})))

(deftest test-align-associative
(testing "sanity"
(is (reformats-to?
["(def x 1)"]
["(def x 1)"]
{:align-associative? true})))
(testing "no op 0"
(is (reformats-to?
["(let [x 1"
" y 2])"]
["(let [x 1"
" y 2])"]
{:align-associative? true})))
(testing "no op 1"
(is (reformats-to?
["(let [x 1])"]
["(let [x 1])"]
{:align-associative? true})))
(testing "empty"
(is (reformats-to?
["(let [])"]
["(let [])"]
{:align-associative? true})))
(testing "simple binding"
(is (reformats-to?
["(let [x 1"
" longer 2])"]
["(let [x 1"
" longer 2])"]
{:align-associative? true})))
(testing "simple map"
(is (reformats-to?
["{:x 1"
" :longer 2}"]
["{:x 1"
" :longer 2}"]
{:align-associative? true})))
(testing "nested simple map"
(is (reformats-to?
["{:x {:x 1}"
" :longer 2}"]
["{:x {:x 1}"
" :longer 2}"]
{:align-associative? true})))
(testing "nested align map"
(is (reformats-to?
["{:x {:x 1"
" :longer 2}"
" :longer 2}"]
["{:x {:x 1"
" :longer 2}"
" :longer 2}"]
{:align-associative? true})))
(testing "nested align binding"
(is (reformats-to?
["(let [x (let [x 1"
" longer 2])"
" longer 2])"]
["(let [x (let [x 1"
" longer 2])"
" longer 2])"]
{:align-associative? true})))
(testing "align many map"
(is (reformats-to?
["{:a 1"
" :longer 2"
" :b 3}"]
["{:a 1"
" :longer 2"
" :b 3}"]
{:align-associative? true})))
(testing "binding align preserves comments"
(is (reformats-to?
["(let [a 1 ;; comment"
" longer 2])"]
["(let [a 1 ;; comment"
" longer 2])"]
{:align-associative? true}
)))
(testing "map align preserves comments"
(is (reformats-to?
["{:a 1 ;; comment"
" :longer 2}"]
["{:a 1 ;; comment"
" :longer 2}"]
{:align-associative? true}))))

0 comments on commit a9c0f05

Please sign in to comment.