From be0555de25b94d8ece679ab02d9202cb2877dad2 Mon Sep 17 00:00:00 2001 From: David Rolle Date: Wed, 22 Nov 2023 10:52:11 +0100 Subject: [PATCH] Simplify two space indentation config - replace the two config options with a single option, `:function-arguments-indentation`, with 3 possible values: `:community`, `:cursive`, or `:zprint` --- README.md | 23 ++++----- cljfmt/src/cljfmt/core.cljc | 65 +++++++++++++------------- cljfmt/src/cljfmt/main.clj | 9 ++-- cljfmt/test/cljfmt/core_test.cljc | 78 ++++++++++++++----------------- 4 files changed, 86 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 00d07b3..30feb03 100644 --- a/README.md +++ b/README.md @@ -250,20 +250,21 @@ In order to load the standard configuration file from Leiningen, add the other references in the `ns` forms at the top of your namespaces. Defaults to false. -* `:one-space-list-indent?` - - true if cljfmt should follow the [community style recommendation][] to indent - function/macro arguments by a single space when there are no arguments on the - same line as the function name. false if two spaces should be used. - Defaults to true. - -* `:one-space-list-indent-tags` - - When `:one-space-list-indent?` is false, re-enables one space indentation for - lists whose first element's [node tag][] is contained in this set. - Defaults to `#{:vector :map :list :set}` +* `:function-arguments-indentation` - + - `:community` if cljfmt should follow the [community style recommendation][] + to indent function/macro arguments by a single space when there + are no arguments on the same line as the function name. + - `:cursive` if two spaces should be used instead, unless the first + thing in the list (not counting metadata) is a data structure + literal. This should replicate Cursive's default behaviour. + - `:zprint` if two spaces should be used instead if the first thing + in the list is a symbol or keyword. This should replicate zprint's + default behaviour. + + Defaults to `:community` [indents.md]: docs/INDENTS.md [community style recommendation]: https://guide.clojure.style/#one-space-indent -[node tag]: https://cljdoc.org/d/rewrite-clj/rewrite-clj/1.1.47/api/rewrite-clj.node#tag ### Runtime Options diff --git a/cljfmt/src/cljfmt/core.cljc b/cljfmt/src/cljfmt/core.cljc index 2e80b30..1e9f0e8 100644 --- a/cljfmt/src/cljfmt/core.cljc +++ b/cljfmt/src/cljfmt/core.cljc @@ -188,24 +188,27 @@ (dec))) (defn- skip-meta [zloc] - (when (#{:meta :meta*} (z/tag zloc)) - (-> zloc z/down z/right))) + (if (#{:meta :meta*} (z/tag zloc)) + (-> zloc z/down z/right) + zloc)) -(defn- has-tag? [zloc tags] - (or (contains? tags (z/tag zloc)) - (some-> zloc skip-meta (has-tag? tags)))) +(defn- cursive-two-space-list-indent? [zloc] + (-> zloc z/leftmost* skip-meta z/tag #{:vector :map :list :set} not)) -(defn- list-indent [zloc context] - (cond - (> (index-of zloc) 1) - (-> zloc z/leftmost* z/right margin) +(defn- zprint-two-space-list-indent? [zloc] + (-> zloc z/leftmost* z/tag #{:token :list})) - (or (:one-space-list-indent? context) - (has-tag? (z/leftmost* zloc) (:one-space-list-indent-tags context))) - (coll-indent zloc) +(defn two-space-list-indent? [zloc context] + (case (:function-arguments-indentation context) + :community false + :cursive (cursive-two-space-list-indent? zloc) + :zprint (zprint-two-space-list-indent? zloc))) - :else - (inc (coll-indent zloc)))) +(defn- list-indent [zloc context] + (if (> (index-of zloc) 1) + (-> zloc z/leftmost* z/right margin) + (cond-> (coll-indent zloc) + (two-space-list-indent? zloc context) inc))) (def indent-size 2) @@ -310,6 +313,20 @@ (read-resource "cljfmt/indents/compojure.clj") (read-resource "cljfmt/indents/fuzzy.clj"))) +(def default-options + {:indentation? true + :insert-missing-whitespace? true + :remove-consecutive-blank-lines? true + :remove-multiple-non-indenting-spaces? false + :remove-surrounding-whitespace? true + :remove-trailing-whitespace? true + :split-keypairs-over-multiple-lines? false + :sort-ns-references? false + :function-arguments-indentation :community + :indents default-indents + :extra-indents {} + :alias-map {}}) + (defmulti ^:private indenter-fn (fn [_sym _context [type & _args]] type)) @@ -361,12 +378,11 @@ ([form indents] (indent form indents {})) ([form indents alias-map] - (indent form indents alias-map {:one-space-list-indent? true})) + (indent form indents alias-map default-options)) ([form indents alias-map opts] (let [ns-name (find-namespace (z/of-node form)) sorted-indents (sort-by indent-order indents) - context (merge (select-keys opts [:one-space-list-indent? - :one-space-list-indent-tags]) + context (merge (select-keys opts [:function-arguments-indentation]) {:alias-map alias-map :ns-name ns-name})] (transform form edit-all should-indent? @@ -508,21 +524,6 @@ (defn sort-ns-references [form] (transform form edit-all ns-reference? sort-arguments)) -(def default-options - {:indentation? true - :insert-missing-whitespace? true - :one-space-list-indent? true - :one-space-list-indent-tags #{:vector :map :list :set} - :remove-consecutive-blank-lines? true - :remove-multiple-non-indenting-spaces? false - :remove-surrounding-whitespace? true - :remove-trailing-whitespace? true - :split-keypairs-over-multiple-lines? false - :sort-ns-references? false - :indents default-indents - :extra-indents {} - :alias-map {}}) - (defn reformat-form ([form] (reformat-form form {})) diff --git a/cljfmt/src/cljfmt/main.clj b/cljfmt/src/cljfmt/main.clj index bca26c6..2442bf7 100644 --- a/cljfmt/src/cljfmt/main.clj +++ b/cljfmt/src/cljfmt/main.clj @@ -52,9 +52,12 @@ [nil "--[no-]sort-ns-references" :default (:sort-ns-references? defaults) :id :sort-ns-references?] - [nil "--[no-]one-space-list-indent" - :default (:one-space-list-indent? defaults) - :id :one-space-list-indent?]]) + [nil "--function-arguments-indentation STYLE" "STYLE may be community, cursive, or zprint" + :default (:function-arguments-indentation defaults) + :default-desc (name (:function-arguments-indentation defaults)) + :parse-fn keyword + :validate [#{:community :cursive :zprint} "Must be one of community, cursive, or zprint"] + :id :function-arguments-indentation]]) (defn- abort [& msg] (binding [*out* *err*] diff --git a/cljfmt/test/cljfmt/core_test.cljc b/cljfmt/test/cljfmt/core_test.cljc index 020fd3b..d6e7e71 100644 --- a/cljfmt/test/cljfmt/core_test.cljc +++ b/cljfmt/test/cljfmt/core_test.cljc @@ -1,7 +1,7 @@ (ns cljfmt.core-test (:require #?(:clj [cljfmt.test-util.clojure]) [#?@(:clj (clojure.test :refer) - :cljs (cljs.test :refer-macros)) [deftest testing is]] + :cljs (cljs.test :refer-macros)) [deftest testing is are]] [cljfmt.core :refer [reformat-string default-line-separator normalize-newlines find-line-separator replace-newlines wrap-normalize-newlines]] @@ -18,24 +18,22 @@ " baz" " quz)"])) - (is (reformats-to? - ["(foo" - "bar" - "baz)"] - ["(foo" - " bar" - " baz)"]) - "to first arg") - - (is (reformats-to? - ["(foo" - "bar" - "baz)"] - ["(foo" - " bar" - " baz)"] - {:one-space-list-indent? false}) - "2 spaces when `:one-space-list-indent?` is false")) + (are [fn-args-indent expected] + (reformats-to? + ["(foo" + "bar" + "baz)"] + expected + {:function-arguments-indentation fn-args-indent}) + :community ["(foo" + " bar" + " baz)"] + :cursive ["(foo" + " bar" + " baz)"] + :zprint ["(foo" + " bar" + " baz)"])) (testing "block indentation" (is (reformats-to? @@ -555,7 +553,7 @@ "" " ;b" " )"] - {:one-space-list-indent? false}))) + {:function-arguments-indentation :cursive}))) (testing "empty indent blocks" (is (reformats-to? @@ -605,7 +603,7 @@ ["(foo" " )"] {:remove-surrounding-whitespace? false - :one-space-list-indent? false})) + :function-arguments-indentation :cursive})) (is (reformats-to? ["(foo (bar (baz)))"] ["(foo (bar (baz)))"])) @@ -1049,8 +1047,8 @@ " )" " )"] {:remove-surrounding-whitespace? false - :one-space-list-indent? false}) - "indents properly with :one-space-list-indent? set to false"))) + :function-arguments-indentation :cursive}) + "indents properly with :function-arguments-indentation set to :cursive"))) (deftest test-options (is (reformats-to? @@ -1113,8 +1111,8 @@ " foo" " bar)"] {:indents {} - :one-space-list-indent? false}) - "can clear all indents rules with :one-space-list-indent? set to false") + :function-arguments-indentation :cursive}) + "can clear all indents rules with :function-arguments-indentation :cursive") (is (reformats-to? ["(do" "foo" @@ -1154,7 +1152,7 @@ " )"] {:remove-surrounding-whitespace? false :remove-trailing-whitespace? false - :one-space-list-indent? false})) + :function-arguments-indentation :cursive})) (is (reformats-to? ["( " "foo" @@ -1173,7 +1171,7 @@ " )"] {:remove-surrounding-whitespace? false :remove-trailing-whitespace? false - :one-space-list-indent? false})) + :function-arguments-indentation :cursive})) (is (reformats-to? ["(foo" " bar " @@ -1192,7 +1190,7 @@ " )"] {:remove-surrounding-whitespace? false :remove-trailing-whitespace? false - :one-space-list-indent? false})) + :function-arguments-indentation :cursive})) (is (reformats-to? ["{:one two :three four}"] ["{:one two" @@ -1305,7 +1303,7 @@ " 3 4)"] {:alias-map {"other" "another.lib"} :sort-ns-references? true - :one-space-list-indent? false + :function-arguments-indentation :cursive :indents {'block1 [[:block 1]] 'other.lib/overridden [[:block 2]] ;; This one is ignored 'another.lib/overridden [[:block 1]] ;; As this one overrides. @@ -1335,7 +1333,7 @@ "bar)"] ["#_(foo" " bar)"] - {:one-space-list-indent? false})) + {:function-arguments-indentation :cursive})) (is (reformats-to? ["(juxt +' -')"] ["(juxt +' -')"])) @@ -1476,7 +1474,7 @@ " b" " [c]))"] {:sort-ns-references? true - :one-space-list-indent? false})) + :function-arguments-indentation :cursive})) (is (reformats-to? ["(ns foo.bar" " (:require" @@ -1505,9 +1503,9 @@ " ^{:x 1} b" " [c]))"] {:sort-ns-references? true - :one-space-list-indent? false}))) + :function-arguments-indentation :cursive}))) -(deftest one-space-list-indent-tags +(deftest cursive-and-zprint-function-argument-indents-depend-on-first-element (let [input ["(foo" "bar)" "(:foo" @@ -1564,7 +1562,7 @@ "bar)" "(#:foo{:bar 1}" "baz)"]] - (testing "matches Cursive style by default" + (testing ":cursive style uses two spaces unless starting with a collection" (is (reformats-to? input ["(foo" @@ -1623,8 +1621,8 @@ " bar)" "(#:foo{:bar 1}" " baz)"] - {:one-space-list-indent? false}))) - (testing "can also match zprint style" + {:function-arguments-indentation :cursive}))) + (testing ":zprint uses two spaces when starting with a symbol, keyword, or list" (is (reformats-to? input ["(foo" @@ -1683,10 +1681,4 @@ " bar)" "(#:foo{:bar 1}" " baz)"] - {:one-space-list-indent? false - ;; everything except :token and :list - :one-space-list-indent-tags #{:meta :meta* :vector :map - :eval :uneval :fn - :set :deref :reader-macro :unquote - :var :quote :syntax-quote :unquote-splicing - :namespaced-map}}))))) + {:function-arguments-indentation :zprint})))))