greglook / cljstyle Goto Github PK
View Code? Open in Web Editor NEWA tool for formatting Clojure code
License: Eclipse Public License 1.0
A tool for formatting Clojure code
License: Eclipse Public License 1.0
$ cat .cljfmt
{:max-blank-lines 3
:single-import-break-width 1000
:indentation? false
:remove-surrounding-whitespace? false
:remove-trailing-whitespace? false
:insert-missing-whitespace? false
:remove-consecutive-blank-lines? false
:insert-padding-lines? true
:rewrite-namespaces? false}
$ cat foo.clj
(ns foo)
(defn foo-two []
nil)
$ cljfmt check foo.clj
--- a/foo.clj
+++ b/foo.clj
@@ -1,4 +1,6 @@
(ns foo)
-(defn foo-two []
+
+(defn foo-two
+ []
nil)
1 files formatted incorrectly
I would expect the diff to be:
$ cljfmt check foo.clj
--- a/foo.clj
+++ b/foo.clj
@@ -1,4 +1,6 @@
(ns foo)
+
(defn foo-two []
nil)
1 files formatted incorrectly
If I accidentally edit a file so it has no newline, cljstyle check
reports the problem like this:
1 files formatted incorrectly
That’s the whole output.
I was able to use cljstyle check --verbose
to find the file the problem was in, parser.cljc
, but the output still didn’t communicate the actual problem:
Using default cljstyle configuration for .
Source file src/cljsjs/marked.cljs is formatted correctly
[…]
Source file src/cljs/athens/core.cljs is formatted correctly
Source file src/cljc/athens/parser.cljc is formatted incorrectly
Source file src/cljs/athens/page.cljs is formatted correctly
[…]
{:files {:unrelated 9620, :correct 22, :incorrect 1}, :total 9643, :elapsed 544.152959}
Checked 9643 files in 544 ms
9620 unrelated
22 correct
1 incorrect
1 files formatted incorrectly
I was only able to figure out this was a newline problem by running cljstyle fix
and checking git diff
.
I’m using cljstyle 0.12.1, installed with brew cask install cljstyle
.
Since cljstyle check
normally reports differences as diffs, I would expect the output to be more like this:
--- a/src/cljc/athens/parser.cljc
+++ b/src/cljc/athens/parser.cljc
@@ -62,4 +62,4 @@
(defn parse-to-ast
"Converts a string of block syntax to an abstract syntax tree for Athens markup."
[string]
- (transform-to-ast (block-parser string)))
\ No newline at end of file
+ (transform-to-ast (block-parser string)))
1 files formatted incorrectly
The comment formatting which was introduced in #30 distinguishes between inline and so called "leading comments". However Lisp (and Clojure) uses comment conventions which are using even more semicolons, i.e. Heading and Top-Level comments which are using four and three ;
respectively (see https://guide.clojure.style/#four-semicolons-for-heading-comments and https://guide.clojure.style/#three-semicolons-for-top-level-comments). These comments shouldn't be reformatted with two semicolons. So we either need a way to detect these different comment types and provide separate prefix rules for them (i.e. Top-Level comments are usually followed by an empty line) or allow more flexible ways to specify the prefix rules for "leading comments".
Hi!
Thank you again for the cljstyle
!
Can you add align map values?
It would be great!
Something like this:
(def my-map
{:a "A value"
:blarg :another-value
:foo {:test "A nested map"
:c :d}})
The most notable diff is that for named functions it doesn't allow the arguments list to stay on the same line:
$ cljfmt check foo.clj
--- a/foo.clj
+++ b/foo.clj
@@ -1,2 +1,3 @@
-(fn foo [arg-one]
+(fn foo
+[arg-one]
nil)
1 files formatted incorrectly
$ cat foo.clj
(fn foo [arg-one]
nil)
(ns foo.bar
(:require
["something"]
["something" :as something]
["something" :default something])
Are all ns declarations that are used often in shadow-cljs that use external JS libs.
I haven't validated :default
yet but could be tested alongside this.
Error executing vim.schedule lua callback: ~.vim/plugged/conjure/lua/conjure/buffer.lua:88: Vim(function):E746: Function name does not match script file name: cljstyle#fix
Maybe this is related to Conjure, I'll dig in a bit and try to find a way to make it work but just wanted to flag this.
I added the following to ~/.config/nvim/after/ftplugin/clojure.vim
as described in the integrations doc.
" Add to file for vim or neovim:
" ~/.vim/after/ftplugin/clojure.vim
" ~/.config/nvim/after/ftplugin/clojure.vim
" NOTE: typically you'd set these to use a formatter, but in this case it fails
" since cljstyle usually can't run on partial forms.
"setlocal equalprg=cljstyle\ pipe
"setlocal formatprg=cljstyle\ pipe
" This can also go in autoload/cljstyle.vim
function cljstyle#fix()
let cwd = getcwd()
let winsave = winsaveview()
execute "cd" . expand('%:p:h')
:%!cljstyle pipe
execute "cd" . cwd
call winrestview(winsave)
endfunction
" Example shortcut to fix the current file
nnoremap <leader>cs :call cljstyle#fix()<cr>
Hey there!
I just came across the project (which looks pretty cool, btw) and I have a couple of small suggestions to improve the docs:
Thanks for the great project! 🙇
Hi,
I notice that the default indent size (in indent.clj
) is 2
. There does not appear to be a way to change this (afaik). According to this guide, clojure style guide, 2 space indents are bad.
Whilst I'm not advocating changing the indent-size to 1, please can we have the ability to change the indent size to 1 if we choose to do so :-)
Thank you.
-=david=-
While not as performant as the GraalVM-built binary, publishing cljstyle to Clojars would make integration with existing Clojure build tooling quite a bit easier, and would be well-suited to CI environments. What do you think?
Using this as a reference the indentation for forms within ns
are not customizable.
With this configuration:
{:rules
{:namespaces
{:indent-size 1}}}
it consistently formats to this style with 2 spaces instead of 1:
(ns format-test
(:require
[clojure.set :as set]
[clojure.string :as str]))
$ cat foo.clj
#:foo{:foo #::bar{}}
$ cljfmt check -v foo.clj
,,,
java.lang.NullPointerException: null
at java.util.concurrent.ConcurrentHashMap.get (ConcurrentHashMap.java:936)
clojure.lang.Namespace.find (Namespace.java:188)
clojure.core$find_ns.invokeStatic (core.clj:4096)
clojure.core$the_ns.invokeStatic (core.clj:4126)
clojure.core$ns_name.invokeStatic (core.clj:4130)
clojure.core$ns_name.invoke (core.clj:4130)
rewrite_clj.node.seq.NamespacedMapNode.sexpr (seq.clj:57)
rewrite_clj.node.protocols$fn__271$fn__315$G__277__317.invoke (protocols.clj:9)
rewrite_clj.node.protocols$fn__271$fn__315$G__276__320.invoke (protocols.clj:9)
clojure.core$map$fn__5587.invoke (core.clj:2747)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.Cons.next (Cons.java:39)
clojure.lang.RT.boundedLength (RT.java:1785)
clojure.lang.RestFn.applyTo (RestFn.java:130)
clojure.core$apply.invokeStatic (core.clj:657)
clojure.core$apply.invoke (core.clj:652)
rewrite_clj.node.seq$map_node$fn__1794.invoke (seq.clj:110)
rewrite_clj.node.seq.SeqNode.sexpr (seq.clj:16)
rewrite_clj.node.protocols$fn__271$fn__315$G__277__317.invoke (protocols.clj:9)
rewrite_clj.node.protocols$fn__271$fn__315$G__276__320.invoke (protocols.clj:9)
,,,
Note that it only happens if the value namespaced map is a relative namespaced key. This file checks okay, for example:
$ cat foo.clj
#:foo{:foo #:bar{}}
A rewrite-clj
bug: clj-commons/rewrite-clj#75
$ cat foo.clj
(:min m ##Inf)
$ cljstyle check -v foo.clj
Using cljstyle configuration from 1 sources for foo.clj:
<path redacted>/.cljstyle
Error while processing file foo.clj
java.lang.Exception: :reader-macro node expects 2 values. [at line 1, column 15]
at rewrite_clj.reader$throw_reader.invokeStatic (reader.clj:18)
rewrite_clj.reader$throw_reader.doInvoke (reader.clj:11)
clojure.lang.RestFn.invoke (RestFn.java:490)
rewrite_clj.reader$read_n.invokeStatic (reader.clj:157)
rewrite_clj.reader$read_n.invoke (reader.clj:145)
rewrite_clj.parser.core$parse_printables.invokeStatic (core.clj:50)
rewrite_clj.parser.core$parse_printables.doInvoke (core.clj:46)
clojure.lang.RestFn.invoke (RestFn.java:445)
rewrite_clj.parser.core$fn__2072.invokeStatic (core.clj:142)
rewrite_clj.parser.core/fn (core.clj:111)
clojure.lang.MultiFn.invoke (MultiFn.java:229)
rewrite_clj.reader$read_with_meta.invokeStatic (reader.clj:132)
rewrite_clj.reader$read_with_meta.invoke (reader.clj:128)
rewrite_clj.parser.core$parse_next.invokeStatic (core.clj:35)
rewrite_clj.parser.core$parse_next.invoke (core.clj:33)
rewrite_clj.reader$read_n.invokeStatic (reader.clj:153)
rewrite_clj.reader$read_n.invoke (reader.clj:145)
rewrite_clj.parser.core$parse_printables.invokeStatic (core.clj:50)
rewrite_clj.parser.core$parse_printables.doInvoke (core.clj:46)
clojure.lang.RestFn.invoke (RestFn.java:445)
rewrite_clj.parser.core$fn__2072.invokeStatic (core.clj:142)
rewrite_clj.parser.core/fn (core.clj:111)
clojure.lang.MultiFn.invoke (MultiFn.java:229)
rewrite_clj.reader$read_with_meta.invokeStatic (reader.clj:132)
rewrite_clj.reader$read_with_meta.invoke (reader.clj:128)
rewrite_clj.parser.core$parse_next.invokeStatic (core.clj:35)
rewrite_clj.parser.core$parse_next.invoke (core.clj:33)
rewrite_clj.parser.core$parse_delim$fn__2046.invoke (core.clj:43)
rewrite_clj.reader$read_repeatedly$fn__1450.invoke (reader.clj:141)
clojure.core$repeatedly$fn__6176.invoke (core.clj:5089)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.RT.seq (RT.java:528)
clojure.core$seq__5124.invokeStatic (core.clj:137)
clojure.core$take_while$fn__5638.invoke (core.clj:2896)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.Cons.next (Cons.java:39)
clojure.lang.RT.next (RT.java:706)
clojure.core$next__5108.invokeStatic (core.clj:64)
clojure.core$dorun.invokeStatic (core.clj:3134)
clojure.core$doall.invokeStatic (core.clj:3140)
clojure.core$doall.invoke (core.clj:3140)
rewrite_clj.reader$read_repeatedly.invokeStatic (reader.clj:143)
rewrite_clj.reader$read_repeatedly.invoke (reader.clj:137)
rewrite_clj.parser.core$parse_delim.invokeStatic (core.clj:44)
rewrite_clj.parser.core$parse_delim.invoke (core.clj:39)
rewrite_clj.parser.core$fn__2086.invokeStatic (core.clj:172)
rewrite_clj.parser.core/fn (core.clj:170)
clojure.lang.MultiFn.invoke (MultiFn.java:229)
rewrite_clj.reader$read_with_meta.invokeStatic (reader.clj:132)
rewrite_clj.reader$read_with_meta.invoke (reader.clj:128)
rewrite_clj.parser.core$parse_next.invokeStatic (core.clj:35)
rewrite_clj.parser.core$parse_next.invoke (core.clj:33)
rewrite_clj.parser$parse.invokeStatic (parser.clj:13)
rewrite_clj.parser$parse.invoke (parser.clj:10)
rewrite_clj.parser$parse_all$fn__2095.invoke (parser.clj:18)
clojure.core$repeatedly$fn__6176.invoke (core.clj:5089)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.RT.seq (RT.java:528)
clojure.core$seq__5124.invokeStatic (core.clj:137)
clojure.core$take_while$fn__5638.invoke (core.clj:2896)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.RT.seq (RT.java:528)
clojure.core$seq__5124.invokeStatic (core.clj:137)
clojure.core$dorun.invokeStatic (core.clj:3125)
clojure.core$doall.invokeStatic (core.clj:3140)
clojure.core$doall.invoke (core.clj:3140)
rewrite_clj.parser$parse_all.invokeStatic (parser.clj:20)
rewrite_clj.parser$parse_all.invoke (parser.clj:15)
rewrite_clj.parser$parse_string_all.invokeStatic (parser.clj:35)
rewrite_clj.parser$parse_string_all.invoke (parser.clj:32)
cljstyle.format.core$reformat_string.invokeStatic (core.clj:57)
cljstyle.format.core$reformat_string.invoke (core.clj:51)
cljstyle.format.core$reformat_file.invokeStatic (core.clj:68)
cljstyle.format.core$reformat_file.invoke (core.clj:62)
cljstyle.task.core$check_source.invokeStatic (core.clj:239)
cljstyle.task.core$check_source.invoke (core.clj:235)
cljstyle.task.process$processing_action$compute_BANG___3013.invoke (process.clj:105)
clojure.lang.AFn.applyToHelper (AFn.java:152)
clojure.lang.AFn.applyTo (AFn.java:144)
clojure.core$apply.invokeStatic (core.clj:657)
clojure.core$with_bindings_STAR_.invokeStatic (core.clj:1965)
clojure.core$with_bindings_STAR_.doInvoke (core.clj:1965)
clojure.lang.RestFn.invoke (RestFn.java:425)
clojure.lang.AFn.applyToHelper (AFn.java:156)
clojure.lang.RestFn.applyTo (RestFn.java:132)
clojure.core$apply.invokeStatic (core.clj:661)
clojure.core$bound_fn_STAR_$fn__5471.doInvoke (core.clj:1995)
clojure.lang.RestFn.invoke (RestFn.java:397)
cljstyle.task.process$processing_action$fn__3017.invoke (process.clj:133)
cljstyle.task.process.proxy$java.util.concurrent.RecursiveAction$ff19274a.compute (:-1)
java.util.concurrent.RecursiveAction.exec (RecursiveAction.java:189)
java.util.concurrent.ForkJoinTask.doExec (ForkJoinTask.java:289)
java.util.concurrent.ForkJoinPool$WorkQueue.runTask (ForkJoinPool.java:1056)
java.util.concurrent.ForkJoinPool.runWorker (ForkJoinPool.java:1692)
java.util.concurrent.ForkJoinWorkerThread.run (ForkJoinWorkerThread.java:157)
com.oracle.svm.core.thread.JavaThreads.threadStartRoutine (JavaThreads.java:479)
{:files {:process-error 1}, :total 1, :elapsed 7.742605}
Checked 1 files in 7.74 ms
1 process-error
Failed to process 1 files
$ cljfmt check foo.clj
--- a/foo.clj
+++ b/foo.clj
@@ -2,7 +2,6 @@
(:import
;; Single-class imports can be collapsed into a symbol
clojure.lang.Keyword
- ;; Otherwise break the class names out onto new lines
(java.io
InputStream
OutputStream)))
1 files formatted incorrectly
$ cat foo.clj
(ns foo
(:import
;; Single-class imports can be collapsed into a symbol
clojure.lang.Keyword
;; Otherwise break the class names out onto new lines
(java.io
InputStream
OutputStream)))
Hi,
Thanks for the utility. I was wondering if there was a way to consider adding the ability to apply the styling as discussed here:
(ns foo.bar
(:require
[honeysql.core :as sql]
[clojure.string :as str]))
Applying cljstyle
at the moment displays this:
--- a/src/foo/bar.clj
+++ b/src/foo/bar.clj
@@ -1,8 +1,8 @@
(ns foo.bar
(:require
- [honeysql.core :as sql]
- [clojure.string :as str]))
+ [clojure.string :as str]
+ [honeysql.core :as sql]))
You'll notice that the require
is 2 spaces in, but then the statements after are only 1 space in (this is also the same for imports).
I do understand that the Clojure Style guide is one person's work, but it does have traction within the community and whilst not everyone agrees 100% of what it says, there are parts that are nice - and this particular formatting of the imports/requires is nice :-)
Thank you for your time and consideration.
-=david=-
Hi Greg,
I'm looking to package up this utility for Arch (I already maintain a few others, like clj-kondo and babashka). Is there a license for this utility? I can't seem to see it in the README or in the source.
Thanks.
-=david=-
file.clj
...
(defn- data
[]
#::{:id 1
:name "Some title"}
...
Error while processing file src/path/to/flie.clj
clojure.lang.ExceptionInfo: [line 81, col 6] Invalid keyword: .
Thank you for your great improvements.
I consider to use it as standard formatter for whole products in my company.
But the name of cljfmt
cause some awkward situations such as following.
Do you have the possibility of renaming it?
After doing a bit of profiling with clj-async-profiler, I noticed that some of the new formatting rules consume more CPU than they should need to. Of a sample flame graph where reformat-form
took 43.47% of the measured CPU time, that time was spent:
cljstyle.format.type/reformat
cljstyle.format.indent/reindent
cljstyle.format.fn/line-break-functions
cljstyle.format.var/line-break-vars
This means that the type rules take almost half of the CPU time, despite type definitions being a small fraction of the actual code! It would be better to consider a more targeted approach, where the form is first searched for a relevant type form, then edits are applied to just that subform. The functions in rewrite-clj.zip.subedit
might be useful for this.
$ cljfmt check foo.clj
--- a/foo.clj
+++ b/foo.clj
@@ -1,5 +1,5 @@
(ns foo)
-(def bar #:asdf{:one 1
+(def bar #:asdf {:one 1
:two 2})
1 files formatted incorrectly
$ cat foo.clj
(ns foo)
(def bar #:asdf{:one 1
:two 2})
Currently it inserts a space between the default namespace and the map, but doesn't indent the map to match.
It should either insert a space and indent the map or do nothing.
Version 0.8.1.
Comment as first element breaks argument alignment:
❯ cat cljstyle.clj
(defn req->user
[req]
(or ;; Hello world
(foo-1 req)
(foo-2 req)
(foo-3 req)))
~/tmp master*
❯ cljstyle check cljstyle.clj
--- a/cljstyle.clj
+++ b/cljstyle.clj
@@ -1,6 +1,6 @@
(defn req->user
[req]
- (or ;; Hello world
- (foo-1 req)
- (foo-2 req)
- (foo-3 req)))
+ (or ; Hello world
+ (foo-1 req)
+ (foo-2 req)
+ (foo-3 req)))
1 files formatted incorrectly
Without the comment element, check accepts the formatting.
If the comment form is between the other elements, alignment works.
The same happens inside :require
forms, if :namespaces :break-libs?
is false
and the first element on require list is a comment.
(defn fuga
[x]
(+ 1 2)
)
(let [p 1]
#_(+ 1 2))
(defn hoge
[x]
(+ 1 2)
)
(defn fuga
[x]
(+ 1 2))
(let [p 1]
#_(+ 1 2))
(defn hoge
[x]
(+ 1 2))
Styling is ignored after #_
.
(defn fuga
[x]
(+ 1 2))
(let [p 1]
#_(+ 1 2))
(defn hoge
[x]
(+ 1 2)
)
Some Clojure files have shebang like #!/usr/bin/env clj -M
or #!/usr/bin/env bb
(it's https://github.com/babashka/babashka ). cljstyle can't process these files with clojure.lang.ExceptionInfo: Invalid symbol: !/usr/bin/env.
. When the first line starts with #!
it would be a shebang so it'll be ignored.
Hi!
Thanks for the library. 🔥
Any plans for installing with brew
? It would be great.
This mostly happens in CI, but I've observed sometimes the cljstyle check
task hangs with no output for 10+ minutes and gets killed for inactivity. Occasionally we do see some output warning that not all of the threads have completed in the expected time interval.
Investigate, and potentially add some internal state dumping if we go way beyond the expected runtime.
While running cljfmt check/fix
on files which have misaligned code in defn-
the program passes over it silently without complaining
Hi there,
When following the usage instructions with deps.edn I get two different classes of errors, even on an empty dir.
Using just :tag
doesn't seem to be supported anymore:
filipesilva@Filipes-Air ~/s/empty-dir [1]> clj -Sdeps '{:deps {mvxcvi/cljstyle {:git/url "https://github.com/greglook/cljstyle.git", :tag "0.15.0"}}}' \
-m cljstyle.main \
check
Error building classpath. Library mvxcvi/cljstyle has coord with missing sha
If I use the sha I get a ClassNotFoundException instead:
filipesilva@Filipes-Air ~/s/empty-dir [1]> clj -Sdeps '{:deps {mvxcvi/cljstyle {:git/url "https://github.com/greglook/cljstyle.git", :sha "13fd2a3ff4097f1ed5c7ec96baf13e547ac2a26b"}}}' \
-m cljstyle.main \
check
WARNING: Implicit use of clojure.main with options is deprecated, use -M
Syntax error (ClassNotFoundException) compiling at (cljstyle/format/zloc.clj:1:1).
rewrite_clj.node.stringz.StringNode
Full report at:
/var/folders/hx/dl3d0pqn6615z78g83gc26300000gn/T/clojure-1489479610023568038.edn
Follow up from #7.
Currently, when checking a file with imports like so:
(:import
[org.flywaydb.core
Flyway]
[org.flywaydb.core.api.configuration
FluentConfiguration])
cljstyle will complain like so:
- [org.flywaydb.core
- Flyway]
- [org.flywaydb.core.api.configuration
- FluentConfiguration]))
+ (org.flywaydb.core
+ Flyway)
+ (org.flywaydb.core.api.configuration
+ FluentConfiguration))
i.e., flagging the use of [
as incorrect. It's totally acceptable to perfer [
in keeping with the use of require
.
Cljstyle should allow as a configuration option the option to perfer one or the other.
It would be nice to be able to require that files are POSIX-compliant.
(ns hoge
;; require start
(:require
[java-time :as jt])
;; import start
(:import
(java.sql
Timestamp)
(java.time
DateTimeException
Instant
ZonedDateTime)
(java.time.temporal
ChronoUnit)))
cljstyle removes ;; import start
and ;; require start
comment. Is this intended?
pre-commit is a framework for managing and maintaining multi-language pre-commit hooks. I've written a custom pre-commit
hook configuration that works with my locally installed cljstyle, but it would be nice to have this configuration to this repository so pre-commit can be automatically run without requiring a local user install of cljstyle.
An ns form like:
(ns foo.bar
{:baz "baz"})
results in the file being skipped with:
clojure.lang.ExceptionInfo: Unknown ns node form :map
The attr-map is an alternate way to specify metadata for the ns.
I started implementing this, but got stuck on how to correctly transform a node that represents the map. I'm happy to keep working on it, but could use some guidance.
(letfn [(foo []
(fn [] ::foo))
(bar []
::bar)]
::baz)
::bar
's indentation is broken.
(letfn [(foo []
(fn [] ::foo))
(bar []
::bar)]
::baz)
When the formatting functions in cljstyle
encounter a form they can't process, the error is some arbitrary exception like NullPointerException
or ClassCastException
which bubbles up from deep within the code. This means that the end user doesn't get any feedback about what form caused the error - the granularity is only on the file level.
An improvement would be to have an intermediate layer in transform
or somewhere equivalent that would catch these errors and re-throw a wrapped exception which contained information about the location in the file (using rewrite-clj
's custom zipper) as well as a string with a tightly-scoped form that reproduces the error. Minimally, you could zip up to the enclosing top-level form and report that in the error.
I like to keep the first require at the same line with :require
form:
(ns example.foo
(:require [reagent.core :as r]
[re-frame.core :as rf]))
Currently cljstyle adds linebreak after :require
. I'd like to use the ns formatting, but keeping existing linebreaks.
Not sure what the ideal solution here is but Mac's tar
command does not support the --overwrite
option.
h/t to @alexdao3 for this
When running ./install-cljstyle
, you may get the following error on Mac:
tar: Option --overwrite is not supported"
If so, open the install-cljstyle
file and remove the --overwrite
option. Then re-run the ./install-cljstyle
command.
This should install the cljstyle
binary into your /usr/local/bin
directory.
$ cljfmt check foo.clj
--- a/foo.clj
+++ b/foo.clj
@@ -1,3 +1,3 @@
{:one 1
;; this is a comment
- }
+}
1 files formatted incorrectly
$ cat foo.clj
{:one 1
;; this is a comment
}
After running check
this is the result for multiple arity functions:
(defn a
([x]
- (a x 2))
+ (a x 2))
([x y]
- (* x y)))
+ (* x y)))
Is this the expected behavior?
https://github.com/bbatsov/clojure-style-guide#multiple-arity-indentation
Currently it chokes on the :reader-macro
conditionals:
java.lang.IllegalArgumentException: No matching clause: :reader-macro
cljstyle splits import statements:
(:import
(java.text
SimpleDateFormat)
(java.util
Date))
but i would like to be:
(:import
(java.text SimpleDateFormat)
(java.util Date))
I tried with this config
{
:rules {:blank-lines {:max-consecutive 1
:padding-lines 1}
:indentation {:indents {#"^[^ \[]" [[:inner 0]]}}
:namespaces {:import-break-width 200
:single-import-break-width 200}}
}
but it doesn't work
How could I obtain this behavior?
$ cljfmt check foo.clj
--- a/foo.clj
+++ b/foo.clj
@@ -1,1 +1,1 @@
-(ns ^:a foo)
+(ns foo)
1 files formatted incorrectly
I saw from the readme: "The native binaries are self-contained, so to install them simply place them on your path." which perked my attention, so I tried a binary on my NixOS box, but (as usual with NixOS) it fails to run with "no such file or directory", which means that it's dynamically linked (apparently it is only libz
that I was missing on my platform, but for various reasons that's non trivial to get working in the system.
According to https://www.graalvm.org/reference-manual/native-image/StaticImages/ it should be possible to make a static image by passing in --static
to the native-image
call. I would make a PR to do that, but since I don't have a running version of graalvm I wouldn't be able to test it. Perhaps it's worth a try?
Install script like clj-kondo would be helpful in general.
It is helpful if it can be used as a lightweight version manager.
https://github.com/borkdude/clj-kondo/blob/master/doc/install.md#installation-script-macos-and-linux
https://github.com/borkdude/clj-kondo/blob/master/script/install-clj-kondo
If you have a namespaced map using keyword aliasing inside of a namespaced map that also uses keyword aliasing, cljstyle will throw a NullPointerException.
As a minimal example, this will throw a NullPointerException when you try to run cljstyle fix
on the file:
(ns test
(:require
[foo :as foo]))
(def problem
#::foo{:config
#::foo{:id 1}})
Stack trace:
java.lang.NullPointerException: null
at java.util.concurrent.ConcurrentHashMap.get (ConcurrentHashMap.java:936)
clojure.lang.Namespace.find (Namespace.java:188)
clojure.core$find_ns.invokeStatic (core.clj:4096)
clojure.core$the_ns.invokeStatic (core.clj:4126)
clojure.core$ns_name.invokeStatic (core.clj:4130)
clojure.core$ns_name.invoke (core.clj:4130)
rewrite_clj.node.seq.NamespacedMapNode.sexpr (seq.clj:57)
rewrite_clj.node.protocols$fn__271$fn__315$G__277__317.invoke (protocols.clj:9)
rewrite_clj.node.protocols$fn__271$fn__315$G__276__320.invoke (protocols.clj:9)
clojure.core$map$fn__5587.invoke (core.clj:2747)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.Cons.next (Cons.java:39)
clojure.lang.RT.boundedLength (RT.java:1785)
clojure.lang.RestFn.applyTo (RestFn.java:130)
clojure.core$apply.invokeStatic (core.clj:657)
clojure.core$apply.invoke (core.clj:652)
rewrite_clj.node.seq$map_node$fn__1794.invoke (seq.clj:110)
rewrite_clj.node.seq.SeqNode.sexpr (seq.clj:16)
rewrite_clj.node.protocols$fn__271$fn__315$G__277__317.invoke (protocols.clj:9)
rewrite_clj.node.protocols$fn__271$fn__315$G__276__320.invoke (protocols.clj:9)
clojure.core$map$fn__5587.invoke (core.clj:2745)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.RT.seq (RT.java:528)
clojure.lang.RT.countFrom (RT.java:643)
clojure.lang.RT.count (RT.java:636)
rewrite_clj.node.seq$assert_namespaced_map_children.invokeStatic (seq.clj:40)
rewrite_clj.node.seq$assert_namespaced_map_children.invoke (seq.clj:37)
rewrite_clj.node.seq$namespaced_map_node.invokeStatic (seq.clj:115)
rewrite_clj.node.seq$namespaced_map_node.invoke (seq.clj:112)
rewrite_clj.parser.core$fn__2076.invokeStatic (core.clj:123)
rewrite_clj.parser.core/fn (core.clj:111)
clojure.lang.MultiFn.invoke (MultiFn.java:229)
rewrite_clj.reader$read_with_meta.invokeStatic (reader.clj:132)
rewrite_clj.reader$read_with_meta.invoke (reader.clj:128)
rewrite_clj.parser.core$parse_next.invokeStatic (core.clj:35)
rewrite_clj.parser.core$parse_next.invoke (core.clj:33)
rewrite_clj.parser.core$parse_delim$fn__2050.invoke (core.clj:43)
rewrite_clj.reader$read_repeatedly$fn__1454.invoke (reader.clj:141)
clojure.core$repeatedly$fn__6176.invoke (core.clj:5089)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.RT.seq (RT.java:528)
clojure.core$seq__5124.invokeStatic (core.clj:137)
clojure.core$take_while$fn__5638.invoke (core.clj:2896)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.Cons.next (Cons.java:39)
clojure.lang.RT.next (RT.java:706)
clojure.core$next__5108.invokeStatic (core.clj:64)
clojure.core$dorun.invokeStatic (core.clj:3134)
clojure.core$doall.invokeStatic (core.clj:3140)
clojure.core$doall.invoke (core.clj:3140)
rewrite_clj.reader$read_repeatedly.invokeStatic (reader.clj:143)
rewrite_clj.reader$read_repeatedly.invoke (reader.clj:137)
rewrite_clj.parser.core$parse_delim.invokeStatic (core.clj:44)
rewrite_clj.parser.core$parse_delim.invoke (core.clj:39)
rewrite_clj.parser.core$fn__2090.invokeStatic (core.clj:172)
rewrite_clj.parser.core/fn (core.clj:170)
clojure.lang.MultiFn.invoke (MultiFn.java:229)
rewrite_clj.reader$read_with_meta.invokeStatic (reader.clj:132)
rewrite_clj.reader$read_with_meta.invoke (reader.clj:128)
rewrite_clj.parser.core$parse_next.invokeStatic (core.clj:35)
rewrite_clj.parser.core$parse_next.invoke (core.clj:33)
rewrite_clj.parser$parse.invokeStatic (parser.clj:13)
rewrite_clj.parser$parse.invoke (parser.clj:10)
rewrite_clj.parser$parse_all$fn__2099.invoke (parser.clj:18)
clojure.core$repeatedly$fn__6176.invoke (core.clj:5089)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.RT.seq (RT.java:528)
clojure.core$seq__5124.invokeStatic (core.clj:137)
clojure.core$take_while$fn__5638.invoke (core.clj:2896)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.Cons.next (Cons.java:39)
clojure.lang.RT.next (RT.java:706)
clojure.core$next__5108.invokeStatic (core.clj:64)
clojure.core$dorun.invokeStatic (core.clj:3134)
clojure.core$doall.invokeStatic (core.clj:3140)
clojure.core$doall.invoke (core.clj:3140)
rewrite_clj.parser$parse_all.invokeStatic (parser.clj:20)
rewrite_clj.parser$parse_all.invoke (parser.clj:15)
rewrite_clj.parser$parse_string_all.invokeStatic (parser.clj:35)
rewrite_clj.parser$parse_string_all.invoke (parser.clj:32)
cljstyle.format.core$reformat_string.invokeStatic (core.clj:57)
cljstyle.format.core$reformat_string.invoke (core.clj:51)
cljstyle.format.core$reformat_file.invokeStatic (core.clj:68)
cljstyle.format.core$reformat_file.invoke (core.clj:62)
cljstyle.task.core$pipe.invokeStatic (core.clj:324)
cljstyle.task.core$pipe.invoke (core.clj:319)
cljstyle.tool.main$_main$fn__3428.invoke (main.clj:72)
cljstyle.tool.main$_main.invokeStatic (main.clj:66)
cljstyle.tool.main$_main.doInvoke (main.clj:37)
clojure.lang.RestFn.applyTo (RestFn.java:137)
cljstyle.tool.main.main (:-1)
Using version 0.12.0.
Clojure Mode for Emacs supports vertical alignment, as does Calva's formatter. Any thoughts on what it might take to add this to cljstyle
?
clj-kondo has a nice feature where you can configure it to lint a particular macro as if it were a different macro, so you don't have to write custom linters for macros if they're not necessary. That got me thinking—it would be really nice if cljstyle supported something like that in the :indents
configuration, e.g.
;; .cljstyle
{:indents {def-special-fn defn}}
Would indent def-special-fn
like defn
. It might even be worth adding a new configuration key so you could opt into certain special formats, as the example above wouldn't format its arg vector liked defn.
$ cat foo.cljs
(ns foo
(:import
[goog.async Debouncer]))
$ cljfmt check -v foo.cljs
,,,
java.lang.UnsupportedOperationException: null
at rewrite_clj.node.whitespace.WhitespaceNode.sexpr (whitespace.clj:28)
cljfmt.format.ns$expand_imports$fn__2718$fn__2722.invoke (ns.clj:167)
clojure.core$map$fn__5587.invoke (core.clj:2747)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:56)
clojure.lang.RT.seq (RT.java:528)
,,,
So far cljstyle
mostly avoids moving comments around or otherwise formatting them, but there are a few rules that come to mind as useful additions:
;;
) - this matches convention and keeps Emacs from pushing the comment far to the right as it does with single-semicolon comments.A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.