This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # announcements (7)
- # babashka (13)
- # beginners (98)
- # biff (20)
- # calva (3)
- # clj-kondo (5)
- # clj-otel (6)
- # cljs-dev (96)
- # clojure (22)
- # clojure-austin (29)
- # clojure-conj (4)
- # clojure-europe (53)
- # clojure-nl (2)
- # clojure-norway (63)
- # clojure-uk (3)
- # clojurescript (18)
- # cursive (10)
- # data-science (11)
- # datalevin (2)
- # datomic (7)
- # deps-new (1)
- # fulcro (3)
- # graphql (1)
- # gratitude (4)
- # hyperfiddle (43)
- # kaocha (4)
- # malli (15)
- # pathom (6)
- # polylith (2)
- # reagent (3)
- # reitit (2)
- # releases (6)
- # remote-jobs (1)
- # rewrite-clj (45)
- # ring (4)
- # shadow-cljs (47)
- # sql (5)
- # xtdb (8)
I just wrote a linting engine using rewrite-clj (actually clj-kondo's rewrite-clj output), and it runs really fast compared to the hand-rolled stuff I was playing with beforehand. i'm reading through various defunct clojure projects that do hand-rolled parsing, and it's really surprising to me how simple they would be if they had relied on rewrite-clj (kibit and marginalia specifically). thanks to y'all for working so hard on this library.
Thanks for the kind words @UEENNMX0T!
sort of, yeah
I originally was building an s-expression pattern matching library for use in clj-kondo, but i think it's not quite appropriate given how @U04V15CAJ likes to write his code, and then i saw how slow kibit is and in the process of taking over the project, i ended up finishing my own version, which lints much faster lol
$ time lein kibit ... real 41m41.018s user 42m18.347s sys 0m3.742s
$ time clojure -M:run ../netrunner/ ... real 0m14.986s user 1m47.699s sys 0m1.404s
> but i think it's not quite appropriate given how @U04V15CAJ likes to write his code Not sure what you mean by that :P
sorry, i don't mean to insult or denigrate. you've said that you prefer to perform each lint check exactly where it needs to happen as you process the files in an effort to keep speed as fast as possible, instead of pre-processing a given hunk of code and running over it a second time to find errors
that's correct. this is not a matter of preference, but a matter of performance. clj-kondo has to be fast to be a viable option to execute on every keystroke
I'm still thinking about a way to let people have the whole file's rewrite-clj structure and do whatever they want, as a hook. But having kibit as a hook, is probably a little bit on the heavy side, right
Also kibit's matching is a little bit course, since it doesn't really see if a symbol is a local or a var reference, for example, right?
yeah, it doesn't care about that, it's purely focused on shape. if i were to attempt to integrate this into clj-kondo, a bunch of stuff would need to change
it is pretty fast tho. linting the https://github.com/mtgred/netrunner/ with native clj-kondo takes 28 seconds, and linting the same repo with my library as an uberjar takes 10 seconds.
macros to generate functions that compare the shape: given
(not (empty? coll)), it'll generate something like
(and (= :list (:tag form)) (let [children (:children form)] (and (= 1 (count children)) (= 'not (:sym (nth children 0))) ...)
wraps it in a function, and then calling that function is pretty quick
does it come down to: not using core.logic is faster than rolling your own matching logic? that's what I would have expected :)
Haha yes, it is due to that
I'm not sure how rewrite-clj makes stuff simpler for you compared to working with s-expressions directly. in clj-kondo I only use rewrite-clj so I have more control over the locations (e.g. numbers can have location metadata) and invalid expressions still parse, etc.
you are aware of grasp right? https://github.com/borkdude/grasp it works directly on s-expressions using clojure.spec expressions
I relied on clj-kondo’s parser because i started this to satisfy my curiosity about alternate methods of writing clj-kondo linters lol
well, clj-kondo still has access to the rewrite-clj nodes so in theory it could give you access to every s-expression it lints to do your custom kibit like matching. Are you aware of the macroexpand hook? It turns a macro rewrite-clj node into an s-expression, which the user can then transform (or lint) and after that it's turned back into a rewrite-clj node.
I just switched from using clj-kondo's rewrite-clj to the rewrite-clj library itself and running takes longer: 7.6 seconds to 19.2 seconds
i suspect because i had to add a small bit of logic to skip over comment and whitespace nodes
(let [~children-form (vec (:children ~new-form))] to
(let [~children-form (vec (remove n/whitespace-or-comment? (n/children ~new-form)))]
I've alluded to it before but why aren't you just matching against s-expressions instead of using rewrite-clj?
Oh god no, edn/read-string doesn't work for clojure code. That is very close to what grasp is doing (which I also mentioned before in this thread), if you want to take that approach, you can take a look there.
i don't want to fall down the well of having to update my code to handle new edge cases in clojure parsing, you know? better to rely on the existing libraries with nice apis
kibit does that, and is currently broken if you use
grasp uses edamame to parse clojure code: https://github.com/borkdude/edamame just look at its source, it's very simple.
ah excellent, i forgot about that library
i'll read it over
here's my library: https://github.com/NoahTheDuke/spat
warning, i wrote it for myself lol
I didn't know that was something that kibit did, but I think you could take the same approach with this library: the matching can happen on regular s-expressions like kibit does (which will make it faster than working with rewrite node, I think and the matching logic becomes simpler) and the rewriting can be done using rewrite-clj
I’m only rewriting in the printing to out, not in the file itself
Interesting, I’ll try it on edamame output, see how that feels. The primary logic doesn’t change much thankfully, the output of the macro barely relies on the rewrite-clj form