rewrite-clj

https://github.com/clj-commons/rewrite-clj
Dimitar Uzunov 2021-04-01T07:55:41.117400Z

Hi! I’m interested in implementing a SLOC counter for clojure (if it is not too difficult)

Dimitar Uzunov 2021-04-01T07:57:01.118800Z

it needs to count blank lines, comments (including (comment ) and #_

Dimitar Uzunov 2021-04-01T07:57:33.119100Z

I will need the parser + node api right?

borkdude 2021-04-01T07:59:03.119300Z

correct

borkdude 2021-04-01T07:59:18.119700Z

you can inspect the node/tag to see if the top level node is a comment or not

borkdude 2021-04-01T07:59:26.120Z

#_ is called :uneval I think

Dimitar Uzunov 2021-04-01T08:01:33.120300Z

thanks for the pointers!

lread 2021-04-01T11:10:46.122200Z

@dimitar.ouzounoff please let us know how it goes!

Dimitar Uzunov 2021-04-01T11:21:20.123Z

will do, I have to wrap one task first and I will get on this; my first impression is that most likely I will need to parse this line by line

borkdude 2021-04-01T11:23:26.124500Z

@dimitar.ouzounoff No, you can just use parser/parse-string-all and this will give you a :forms node. Then you can go over its :children and skip all :comment and :uneval or empty lines, etc. And then just call str on the remaining nodes and count the number of lines from those.

Dimitar Uzunov 2021-04-01T11:24:49.125800Z

well, I saw parse-string-all but it doesn’t return a seq; I guess I need to read on the nodes to understand them better

borkdude 2021-04-01T11:28:48.127800Z

@dimitar.ouzounoff It returns one node, a :forms node

borkdude 2021-04-01T11:28:57.128100Z

and that has :children which are all of the top level nodes

Dimitar Uzunov 2021-04-01T11:35:25.128800Z

hmm this is what a comment looks like in cider-inspect: Class: rewrite_clj.node.seq.SeqNode Meta Information: :row = 87 :col = 1 :end-row = 91 :end-col = 5 Contents: :tag = :list :format-string = "(%s)" :wrap-length = 2 :seq-fn = rewrite_clj.node.seq$list_node$fn__7201@66935f35 :children = ( { :value comment, :string-value "comment", :map-qualifier nil } { :newlines "\n" } { :whitespace " " } { :tag :list, :format-string "(%s)", :wrap-length 2, :seq-fn rewrite_clj.node.seq$list_node$fn__7201@fab763f, :children ( { :value do,

Dimitar Uzunov 2021-04-01T11:36:41.129300Z

I’m not sure how to access it, I can’t seem to use nth

Dimitar Uzunov 2021-04-01T11:36:59.129900Z

in order to use a comment? predicate

borkdude 2021-04-01T11:39:13.132Z

ah right

borkdude 2021-04-01T11:39:37.132600Z

so the first child of this form is a symbol node with :value 'token

lread 2021-04-01T11:39:47.132800Z

@dimitar.ouzounoff you can achieve what your goal with the node API, but the zip API is a bit higher level.

borkdude 2021-04-01T11:40:32.133700Z

something like:

(defn comment-node? [node]
  (and (= :list (node/tag node)) (some-> node :children first :value (= 'comment))))

borkdude 2021-04-01T11:41:13.134700Z

Personally I don't see a need for the zipper API here, since it's pretty straightforward to only iterate over the top level nodes, there isn't a need to visit any deeper nodes and/or rewrite/remove them

lread 2021-04-01T11:43:58.137600Z

No need, just an alternative that might offer easier nav through tree. Not sure exactly what @dimitar.ouzounoff wants to count here yet.

borkdude 2021-04-01T11:44:21.138100Z

He want to count lines of code, but exclude comment forms

Dimitar Uzunov 2021-04-01T11:45:43.140200Z

yes, I guess this is why something like rewrite-clj is necessary as it needs to exclude whole multiline comment forms from the line count

lread 2021-04-01T11:46:42.140900Z

Oh I thought he also wanted to count comments and (comment and #_.

borkdude 2021-04-01T11:48:59.141300Z

that doesn't change the problem very much, those are just different predicates

lread 2021-04-01T11:49:22.141500Z

Yup

borkdude 2021-04-01T11:50:34.142700Z

and you're also able to count whitespace lines, so rewrite-clj is a great tool for this problem to implement clocl (count line of clojure)

borkdude 2021-04-01T11:50:57.143400Z

which will soon be available as a GraalVM binary? with proper command line interface please? :P

lread 2021-04-01T11:51:12.143600Z

Feel free to carry on with node API, just offering up an alternative.

lread 2021-04-01T11:52:40.144300Z

Ha! Let’s not get you started! 🙂

Dimitar Uzunov 2021-04-01T11:58:55.145500Z

I’m still at a beginner level, but it sounds like fun 🙂

Dimitar Uzunov 2021-04-01T11:58:58.145700Z

well I’m not sure how to go over the nodes

Dimitar Uzunov 2021-04-01T11:59:08.146Z

rewrite-clj.zip/next might be something I can use

Dimitar Uzunov 2021-04-01T12:01:24.147600Z

another thing that I’m not sure about is that if take this line: (set! *warn-on-reflection* true) ;; avoid reflexion so you can use graalvm

Dimitar Uzunov 2021-04-01T12:01:45.148Z

it is two children on the first depth

Dimitar Uzunov 2021-04-01T12:02:01.148700Z

so it would count as a line of code + a line of column

lread 2021-04-01T12:03:58.150500Z

It is! Borkdude and I curate some tips over at https://github.com/lread/clj-graal-docs. I was more joking with borkdude about proper cmd lines, which is something he has been recently irked by.

borkdude 2021-04-01T12:13:09.151200Z

@dimitar.ouzounoff

user=> (require '[clojure.string :as str] '[rewrite-clj.parser :as p])
nil
user=> (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)"))
(<list: (+ 1 2 3)> <newline: "\n"> <comment: ";;hello\n"> <list: (comment 1 2 3)>)
user=> (map str (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)")))
("(+ 1 2 3)" "\n" ";;hello\n" "(comment 1 2 3)")
user=> (map (comp count str/split-lines str) (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)")))
(1 0 1 1)
user=> (apply + (map (comp count str/split-lines str) (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)"))))
3

👍 1
borkdude 2021-04-01T12:14:31.151400Z

user=> (require '[rewrite-clj.node :as node])
nil
user=> (defn comment-node? [node]
  (and (= :list (node/tag node)) (some-> node :children first :value (= 'comment))))
#'user/comment-node?
user=> (apply + (map (comp count str/split-lines str) (remove comment-node? (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)")))))
2

👍 1
borkdude 2021-04-01T12:18:13.152300Z

@lee I'm pretty tempted to just add rewrite-clj to babashka so you can write little scripts like this ;)

borkdude 2021-04-01T12:19:08.152900Z

I think when bb was only a few weeks old @sogaiu proposed it already, haha

lread 2021-04-01T12:19:38.153300Z

hey, I’d use it!

Dimitar Uzunov 2021-04-01T12:22:56.153900Z

ok, I think I get it now, thanks @borkdude!

ericdallo 2021-04-01T18:06:02.154600Z

Any chance of rewrite-clj support :babashka: interpreter? 🧵

2021-04-02T23:15:09.179400Z

oh, where did you try that out? atm i don't think tree-sitter gives a grammar author the ability to tune how the correction works. consequently, i haven't found it to be so flexible for coping with broken code. the work that you all are doing on rewrite-clj, clj-kondo, clojure-lsp, etc. is much better for editor users from this perspective.

❤️ 1
lread 2021-04-03T14:17:11.179700Z

Oh @sogaiu, I am a total tree-sitter noob and have only read/watched introductory stuff. I thought that https://github.com/tree-sitter/tree-sitter/issues/224, but again have not dug deep at all.

2021-04-03T22:30:28.193500Z

yeah what you mentioned has a nice explanation (which i confess i do not understand that well 🙂 ) here is a more recent discussion that may be relevant for lisp-likes: https://github.com/tree-sitter/tree-sitter/issues/923 i've been working on trying to spell out specifically what one can do in the case of unbalanced delimiters. afaict, in general, when there is a missing closing delimiter, there are mutiple possible places it might go. if one can trust existing indentation i think this can be narrowed down a bit, but still hammocking / researching. anyway, sorry to have drifted off topic.

lread 2021-04-04T12:26:38.194600Z

All interesting to me @sogaiu! Thanks for sharing.

🙂 1
ericdallo 2021-04-01T18:06:18.154700Z

(z/of-string "!/usr/bin/env bb\n\n(ns foo)")
=> Execution error (ExceptionInfo) at clojure.tools.reader.impl.errors/throw-ex (errors.clj:34).
Invalid symbol: !/usr/bin/env.

ericdallo 2021-04-01T18:06:53.154900Z

Is just that if a user open a babashka file with that interpreter and is using clojure-lsp, it will throw a lot of those exceptions 😕

borkdude 2021-04-01T18:07:13.155200Z

ouch! I think rewrite-clj should be able to handle this, but you need to start it with a #

borkdude 2021-04-01T18:07:35.155400Z

so: #!/usr/bin/env bb

ericdallo 2021-04-01T18:07:44.155600Z

oh yeah, my bad, but it happens with the # as well

borkdude 2021-04-01T18:09:10.156Z

that surprises me, since I'm supporting this in clj-kondo too

borkdude 2021-04-01T18:09:18.156200Z

but it could be that it's only supported in my fork. I'll check

🤞 1
lread 2021-04-01T18:10:28.156500Z

yeah I can confirm it throws for rewrite-clj @ericdallo

👍 1
😢 1
lread 2021-04-01T18:10:56.156900Z

cheer up buddy, we’ll deal with it.

ericdallo 2021-04-01T18:11:05.157100Z

Thank you! 😄

ericdallo 2021-04-01T18:12:14.157800Z

oh, it makes sense!

lread 2021-04-01T18:12:15.158Z

I shall take a peek @borkdude, thanks.

lread 2021-04-01T18:14:16.158200Z

So kondo just skips, yeah?

lread 2021-04-01T18:14:51.158400Z

What would we like rewrite-clj to do?

borkdude 2021-04-01T18:15:04.158600Z

yes, #! is read exactly the same as ;

lread 2021-04-01T18:15:29.158800Z

Oh, just something rewrite-clj does not understand yet.

borkdude 2021-04-01T18:15:49.159Z

Maybe a new :shebang type node?

borkdude 2021-04-01T18:15:57.159200Z

Similar to ; I guess

ericdallo 2021-04-01T18:16:17.159400Z

yes, a new node looks useful

borkdude 2021-04-01T18:16:38.159600Z

user=> (p/parse-string ";; foo")
<comment: ";; foo">
user=> (p/parse-string "#! foo") ;;=> <shebang "#! foo">

lread 2021-04-01T18:16:51.159800Z

Sure, sounds good.

borkdude 2021-04-01T18:17:18.160Z

Note:

user=> (+ 1 2 3 #! foo
4)
10
so yeah, it's exactly like ;

lread 2021-04-01T18:18:07.160200Z

Huh, so where is #! documented?

borkdude 2021-04-01T18:18:50.160400Z

it might not be documented, but this is how it works ;)

lread 2021-04-01T18:19:33.160600Z

tis the way of things, I like shebang for a name, but I’ll see if it is called something specific in the reader

lread 2021-04-01T18:21:11.161600Z

You are fast man! Tx! So technically the zipper should skip these guys too.

borkdude 2021-04-01T18:21:29.161800Z

we could also just make it a :comment node perhaps

borkdude 2021-04-01T18:21:38.162Z

yes

lread 2021-04-01T18:21:51.162200Z

That would fit in more easily.

👍 1
lread 2021-04-01T18:22:50.162500Z

Alrighty! I new rewrite-clj feature! Very exciting.

lread 2021-04-01T18:22:59.162700Z

Or a bug fix.

borkdude 2021-04-01T18:23:02.162900Z

New challenge: https://github.com/borkdude/deps.clj/blob/master/deps.bat#L1-L7

lread 2021-04-01T18:23:05.163200Z

Still exciting

borkdude 2021-04-01T18:23:24.163500Z

Yeah, exciting that rewrite-clj is finally catching up with clj-kondo's fork after 2 years :P

borkdude 2021-04-01T18:23:52.163700Z

hehe, sorry, just kidding

lread 2021-04-01T18:24:40.163900Z

I think that windows command line argument might have set your tone for the day. :simple_smile:

borkdude 2021-04-01T18:25:11.164100Z

Here is the old issue for clj-kondo: https://github.com/clj-kondo/clj-kondo/issues/294

lread 2021-04-01T18:25:47.164500Z

cool, I’ll write up an issue for rewrite-clj and fix.

ericdallo 2021-04-01T18:25:53.164700Z

yay a new feature!

lread 2021-04-01T18:25:55.164900Z

now about that .bat file…

lread 2021-04-01T18:26:47.165100Z

ya, thanks for raising @ericdallo!

🚀 1
borkdude 2021-04-01T18:27:51.165300Z

are you using argument in two senses here?

borkdude 2021-04-01T18:28:22.165600Z

I didn't feel like I was arguing with someone, because nobody really replied, except the guy who was the "victim" of string quoting

lread 2021-04-01T18:31:32.165900Z

It was a bad pun!

lread 2021-04-01T18:32:27.166200Z

No, you were empathizing not arguing.

borkdude 2021-04-01T18:32:58.166400Z

haha ok.

borkdude 2021-04-01T18:33:34.166600Z

I'm strangely excited about this Windows shebang though. It's always nice to support some archaic niche use case

😆 3
borkdude 2021-04-01T18:35:06.166900Z

Me having a Windows machine is really paying off here ;P

lread 2021-04-01T18:36:11.167100Z

128gb at the ready… for any arcane issue…

lread 2021-04-01T21:18:35.167400Z

@ericdallo, https://github.com/clj-commons/rewrite-clj/commit/187497a5d017ee21e81c674938f2427707807ca4, lemme know if it works for ya.

ericdallo 2021-04-01T21:20:50.167600Z

Nice, thank you very much! Just don't giving the exception will certainly work 😄 I'll make the change on clojure-lsp on next rewrite-clj release

lread 2021-04-01T21:23:25.167900Z

Coolio, I’ll likely cut a release tomorrow then.

🤘 1
borkdude 2021-04-01T21:26:27.168200Z

@ericdallo btw, you mentioned that the integration tests were broken with the newest clj-kondo. could you follow up on this?

ericdallo 2021-04-01T21:39:00.168400Z

yep, for some reason the order of the elements of the list were not in the expected order with the new version, it's not a issue and it seems now the order is "more" correct, following the asc pattern of the elements positions

ericdallo 2021-04-01T21:39:33.168600Z

I didn't investigate that much, but I'll keep an eye if next clj-kondo releases impact that again

borkdude 2021-04-01T21:42:36.168800Z

the version from earlier in march had a bug which reported unresolved symbols in the wrong order. maybe you captured this in an integration test

1
1
lread 2021-04-01T21:43:04.169Z

@borkdude, just reviewing https://github.com/clj-kondo/clj-kondo/commits/master/parser/clj_kondo/impl/rewrite_clj, I don’t think there is anything else immediately relevant that we are not already tracking for rewrite-clj v1, do you?

lread 2021-04-01T21:44:35.169600Z

yup, tx

borkdude 2021-04-01T21:46:19.169800Z

@lee I already had some patches around namespaced maps in the first commit, unfortunately I didn't specify that explicitly

borkdude 2021-04-01T21:47:10.170Z

but I think that was the major one before this inlined fork

lread 2021-04-01T21:47:15.170200Z

I think we might be ok in that area

borkdude 2021-04-01T21:47:31.170400Z

yeah. so I think it's pretty safe to drop the alpha suffix

lread 2021-04-01T21:47:41.170600Z

yeah you were probably working around the half-finished namespaced map support in v0

borkdude 2021-04-01T21:48:19.170800Z

yeah, also the *ns*thing bugged me

lread 2021-04-01T21:48:37.171Z

yeah, it was annoying

lread 2021-04-01T21:49:38.171200Z

I guess we could drop alpha. I would have liked more feedback around sexpr work around namespaced elements from real usage, but if it does not come naturally, then… can’t force it.

borkdude 2021-04-01T21:51:08.171400Z

can also wait some more

lread 2021-04-01T21:51:53.171600Z

I might do that… doesn’t hurt. Spec set a precedent. :simple_smile:

borkdude 2021-04-01T21:52:54.171800Z

not sure if that's a good example to follow though ;)

lread 2021-04-01T21:53:32.172Z

:simple_smile:

2021-04-01T23:00:21.172200Z

fwiw tree-sitter-clojure recognizes #! too: https://github.com/sogaiu/tree-sitter-clojure/blob/master/grammar.js#L40

lread 2021-04-01T23:03:12.174200Z

@sogaiu! Nice to hear from you! I finally took the time to introduce myself to tree-sitters the other day. Very interesting!

2021-04-01T23:05:24.174400Z

ah cool!

lread 2021-04-01T23:36:39.176500Z

Yeah, I found the smart error detection pretty darn awesome. Not exactly sure how it works but the effect is nice.