clojure-mode
clojure-mode copied to clipboard
Indentation of `clojure.test/are`
Expected behavior
I was expecting clojure-mode's indentation to behave similar to cider-format and lein cljfmt. Is there some way to configure clojure-mode to indent the same way as cider-format?
Actual behavior
The macro clojure.test/are is indented one way with M-x cider-format (and lein cljfmt), but another way when using clojure-mode's indentation rules (with aggressive-indent-mode). The latter uses one space less than the former for the second argument to are (when it is on a new line).
Steps to reproduce the problem
The code is formatted as following using clojure-mode indentation:
(deftest are-test
(are [x y]
(= x y)
1 1
2 2))
And as following when using cider-format indentation (note the extra space on line 3):
((deftest are-test
(are [x y]
(= x y)
1 1
2 2))
Environment & Version information
clojure-mode version information
clojure-mode (version 5.11.0)
Emacs version
GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.12, cairo version 1.17.3) of 2019-11-26
Operating system
Arch Linux
I'd actually expect the clojure-mode result (not alignment, but two levels of indentation to separate it from the body).
I'd actually expect the clojure-mode result (not alignment, but two levels of indentation to separate it from the body).
I'm not sure I understand what you mean. But it would be best if there was some way to configure this...
Seems like it's not specific to are, all n-arg indent specs are broken(?):
;; in emacs:
(define-clojure-indent
(foo 3))
;; in clojure:
(foo 1
2
3
4
5)
;; in emacs:
(define-clojure-indent
(foo nil))
;; in clojure:
(foo 1
2
3
4
5)
This change to clojure-indent-function does the trick:
modified clojure-mode.el
@@ -1511,7 +1511,8 @@ This function also returns nil meaning don't specify the indentation."
(clojure--normal-indent last-sexp 'always-align))
;; Special arg. Rigidly indent with a large indentation.
(t
- (+ (* 2 lisp-body-indent) containing-form-column)))))
+ (+ 1 (* 2 lisp-body-indent) containing-form-column)
+ ))))
(`:defn
(+ lisp-body-indent containing-form-column))
((pred functionp)
Though I suspect a proper fix should take into account the value of clojure-indent-style var. Docstring for clojure-indent-function states:
- an integer N, meaning indent the first N arguments specially
like ordinary function arguments and then indent any further
arguments like a body;
...and how 'ordinary function arguments' are indented is controlled by clojure-indent-style: if it's always-align or align-arguments, then the first N arguments should be on the same column; if it's always-indent however, then logically according to the spec ("All args are indented like a macro body") the first N arguments should be indented the same as all the other ones, i.e. it should be a no-op.
I'm not sure if clojure-indent-function is supposed to be used that way though; chances are I'm overthinking this?
In any case, with the provided patch:
;; in emacs:
(define-clojure-indent
(foo 3))
;; in clojure:
(foo 1
2
3
4
5)
hi @j-cr
Thanks for posting this diff! I was to apply it to my (spac)emacs installation and it failed. Can you please explain how to apply this diff?
I've tried the following process:
- found that file(
~/.emacs.d/elpa/develop/clojure-mode-20200813.639/clojure-mode.el) - applied the fix
- executed
(spacemacs/recompile-elpa)which results in several warnings. - After emacs restart, regular modes stopped working - e.g. clj files are opened in fundamental(plain text) mode.
I've rolled back that diff and recompiled elpa. Everything seems to be working again but I was trying to fix this issue with clojure.test/are form weird formatting.
Seems like it's not specific to are, all n-arg indent specs are broken(?): From what I see, the biggest(only?) issue is with
areform because If I rename it to any other 3-character form(e.g.foo) formatting suddenly works again.
I don't use spacemacs so I don't know. Simply finding that function, changing it and evaluating it should work.
@j-cr thanks!
I'd actually expect the clojure-mode result (not alignment, but two levels of indentation to separate it from the body).
I'm not sure I understand what you mean.
@joncol, svantevonerichsen6906 meant that this is correct formatting.
Clojure mode indents special arguments by fixed amount of spaces, which is twice as large as indentation of ordinary macro parameter.
Which means that when you say (define-clojure-indent (foo 3)) you mean that first 3 arguments to foo should be considered special. These arguments are not aligned to the first one, but use 4 space indentation to indicate that these are indeed special args.
Similarly, if you put let binding vector onto a separate line, you'll see that it is indented by four spaces, where the body is indented by 2:
(let
[a 10]
a)
It may actually look that vector is aligned with where the first argument should go, but it's just a coincidence. If you use a longer macros like (define-clojure-indent (foo-bar-baz 2)) you'll see this:
(foo-bar-baz a
b
c)
Which helps seeing that a, and b are special args, and c is body form.
patch in https://github.com/clojure-emacs/clojure-mode/issues/548#issuecomment-568907128 introduces incorrect formatting, by making all special args indented by 5 spaces instead of 4, thus making those align with forms that have 3 letter long names, but it will still not work with foo-bar-baz macros, which I assume is what you want