-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Outline work needed for cljs and cljc support #195
Comments
awesome you opened this. let me collect my thoughts and previous discussions around this before answering in detail |
|
Just after a bit of analysis the list of
and now your questions
Essentially the current approach does not work for cljs because the analyzer we've choosen (clojure contrib analyzer: org.clojure/tools.analyzer) does not have a working cljs/js implementation. The existing cljs/js implementation has been abandoned due to lack of time to keep up with cljs, see https://github.com/clojure/tools.analyzer.js#this-project-has-been-abandoned. So as simple as that we decided to "buy" an analyzer solution and build features on top of it expecting the cljs part working too which is not the case unfortunately. There is one exception, clean-ns which uses its own means to gather information about the namespace it works on. It does not use the AST and supports clj, cljs and cljc. This answers your 2nd question too I suppose. In the context of clojure.contrib analyzer there is no such thing as cljs AST at the moment unfortunately.
In short find an other solution the get the necessary information about the clj/cljs code we work with. Apart from the missing cljs story of the currently used analyzer there are other problems with it. A good discussion of these problems by some much brighter ppl than myself can be found here: #134. The above discussion touches on a load of things so perhaps a very careful thinking over all those points raised could be benefitial. Might raise new questions etc. (I guess it would be also possible to pick up the maintenance of A bit about options on potential solutionsOne option is to run our own minimalistic analyzer. As the cljs story is really getting more and more interesting these days we might have a look on self hosted cljs repls like lumo and planck if they could help us out for cljs. Also joker looks very interesting. If I understand the impliciations there it should support both clj and cljs. Also as implemented in disclaimer: although i am very excited to do a POC with joker i have very limited time nowadays to hack on these things unfortunately. disclaimer: this is more like a vision like thing than anything else and mainly mine. other people in the team and around all things clojure in emacs may not agree with this last section. i appreciate i might not answered all your questions but hopefully gave some food for thoughts. let me know what you think and if this in any way helpful or just even more confusing... |
@benedekfazekas thanks fot the great response. Please allow me to not address your points right away, because I realize there are some big gaps in my understanding of the various analyzer toolchains. I was under the impression we'd have no choice but to use https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/analyzer/api.cljc or maybe https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/analyzer.cljc I found an old post that does clarify some of the goals of tools.analyzer here and puts thnigs in perspective. It is not clear to me whether using the cljs analyzer directly would be possible. I guess this is a downside of the data-driven approach right here: I cannot look at the refactor-nrepl codebase and easily determine what analyzer functionality it depends on. There is just one or two calls into the library but the rest of the project depends on specific ast keys. I think what would go a long way towards determining the right path forward would be to have an exhaustive reference of the ast data that's actually used throughout the project. Then we could look at the outputs of different alternatives and easily determine what's different and/or missing. Am I correct that this would help? If so, maybe a test namespace where each middleware would have to assert against an analysis pass checking for its dependencies, or maybe better, a set of specs for the ast? Edit: I'm going off another assumption here, which is that with some undetermined amount of shims, we'd be able to build upon the clojurescript analyzer directly and recreate the ast that refactor-nrepl currently requires. I now realize sounds a lot like rewriting a subset of tools.analyzer.js, and without familiarity with those tools it's unclear how much work this represents. |
I don't think so. The AST is the API. That was part of the reason in choosing tools.analyzer to begin with, that we could get a general AST (whether the source was clj, cljs or cljc) and then do work on that AST. If that's no longer viable, we should look into replicating the capabilities afforded by tools.analyzer. @benedekfazekas listed those above. I'm not aware of another library that does analysis, like tools.analyzer, but we might be able to get the same capabilities by running the cljs compiler. This takes us to point 4. in your OP:
No, there isn't. And this is starting to look like the only way forward. The lack of progress is mostly because neither @benedekfazekas or myself write cljs in our day to day job and nobody else has shown any interest in helping us out (until now? :)) |
Right, the point I was trying to make is: if I run a cljc file through both So imagine we were to diff those two asts and make fixing those discrepancies our todo list, we potentially could ignore a bunch of those if we limited ourselves to what refactor-nrep uses, the API would then become a subset of the tools.analyzer AST. Does that make any sense or am I missing something? |
I think you're right that the ASTs will be nothing alike, and that's fine. Even if it was possible to find common ground in the two ASTs and normalize them into some more generalized thing, I don't think that would be a good idea. We'd have to track changes in both projects to keep our normalization step in sync. I think a much better approach would be to just add a new code path to e.g. |
If you want to talk in real-time, you can find us both on gitter. I'm around on freenode and I think @benedekfazekas still hangs out on slack (in the clojure channel) |
So now, what about: https://github.com/clojure/jvm.tools.analyzer#usage-clojurescript? The landscape is even more confusing than I initially though, thinking out loud here in hopes that you guys can correct me if I'm missing something:
At first glance |
Your concerns for For that reason, we might as well use If you want to get involved @julienfantin, I suggest the following route forward:
I don't think we should care about duplicating code while investigating. I'd rather have some extra stuff, than break the currently working clj features while adding cljs support. It's also too early to worry about supporting multiple versions of cljs. Tracking the latest major release is miles better than no support at all. Most likely the cljs analyzer isn't moving very fast, even if cljs itself is! |
Ok that sounds like a plan! I do have a couple of remaining questions and a comment:
Lastly I'll say that I have very limited yak-shaving time over the next few months, so progress on this will be slow. |
@benedekfazekas is planning to cut a stable release sometime later this month, which makes it easy for people to opt-out of any alpha-quality features that are coming to cljs.
Slow is infinitely better than nothing! |
And there's the option of writing a simplified portable parser - something that won't really try to expand macros and so on. On having more of the refactorings depend on just the loaded code instead of analyzing all the source code. In general, there's also the bigger question of how to better support ClojureScript in CIDER as well. Most of the limitations come from the stagnation in nREPL's development - in theory if first call support for ClojureScript was added there instead of relying on piggieback a lot of things would become simpler. Also in theory - supporting some evaluation backend that nREPL would simplify the ClojureScript situation a lot. But that's also a lot of work that no one really wants/has the time to tackle. |
I like that fact that there is a plan formulating how attack this. and agree with @expez that |
Guess you can always ping the maintainer about its state. |
also there are several self hosted cljs options now. they might worth a check. |
It's just a wrapper around the clojure and cljs compiler, so unless the API of those two change, the wrapper doesn't have to be updated. |
ah right. thanks for clarification @expez |
Unfortunately haven't had time to work on this at all, but I did gain some insight on what will need to happen, so I'll leave some notes here, if not only for myself: Add support for
|
ClojureScript patch submitted: https://dev.clojure.org/jira/browse/CLJS-2051 |
awesome! |
I have to say that when I was wondering how to do this as well I basically ended up considering the above the easiest approach. In particular, Antonio Monteiro has already ported the ClojureScript analyzer to self-host (node) and this would be a very valuable library to spin off. I might be the only one here, but I think, and I am really trying hard in Spinning off a new analyzer library has so many upsides that I am considering doing it myself...maybe merging code from I have also looked into https://github.com/thunknyc/crntl and I had an old idea of using By the way this is a great discussion so thanks @julienfantin! |
The conforming work of the ClojureScript analyzer AST to the tools.analyzer spec which shipped as part of ClojureScript 1.10.439 doesn't help here? https://dev.clojure.org/jira/browse/CLJS-1461 |
Think that patch was news to all of us :) It should be largely plug and play, I think! |
while playing with this, realised that https://dev.clojure.org/jira/browse/CLJS-2051 actually never got merged |
after working around the missing |
as outlined in #195, see specially #195 (comment) Take aways: - one integration test is duplicated for cljs (together with the source file) - `cljs.analyzer` used directly instead of `jvm.tools.analyzer` -- latter errored with latest cljs, did not investigate further - workarounds needed for - no `end-line`, `end-column` info in CLJS AST (also see https://dev.clojure.org/jira/browse/CLJS-2051) - no `:raw-forms` in cljs AST containing the stages of macro expansion including the original form - :op = `:binding` nodes in CLJS ASTs seems to be missing `:children` entry so the AST can not be walked properly
see POC for |
I like the POC 😄! Also that piece of functionality could be a very good candidate for the |
@arichiardi Although maybe we can achieve pretty much the same result using |
Thanks @arichiardi also recorded my finding here https://dev.clojure.org/jira/browse/CLJS-1461?focusedCommentId=51581#comment-51581 next step is doing a POC for the same |
as outlined in #195, see specially #195 (comment) Take aways: - one integration test is duplicated for cljs (together with the source file) - `cljs.analyzer` used directly instead of `jvm.tools.analyzer` -- latter errored with latest cljs, did not investigate further - workarounds needed for - no `end-line`, `end-column` info in CLJS AST (also see https://dev.clojure.org/jira/browse/CLJS-2051) - no `:raw-forms` in cljs AST containing the stages of macro expansion including the original form - :op = `:binding` nodes in CLJS ASTs seems to be missing `:children` entry so the AST can not be walked properly
and a PoC with a |
https://github.com/clojure-lsp/clojure-lsp/ became a thing in the meantime. Since very recently it features an API. It works with clojurescript. So I'd propose that refactor-nrepl is defined in terms of defprotocols, with typically two possible implementations: "traditional" (i.e. refactor-nrepl's current one) and LSP.
@expez , what would you think? Personally I'd see it as one of the very few options ahead. As usual in OSS "who's doing the work" is an important factor: refactor-nrepl only has sporadic maintainance and tools.analyzer less so. So delegating the work seems sensible. Protocols have a certain cleanliness to them anyway. e.g. what if the clojurescript compiler itself becomes a nice "ast backend" in a future :) Final note: a defprotocol allows us to even not bother with integrating LSP ourselves at all. It could be done in an external project gluing our protocol to an impl. |
I like the idea in general, although it would have been nicer if the
functionality in question was fully decoupled from Clojure-lsp. That’s why
Orchard is nREPL agnostic. I assume this API you mention should be usable
without an LSP server, right?
On Sat, 3 Jul 2021 at 17:05, vemv ***@***.***> wrote:
https://github.com/clojure-lsp/clojure-lsp/ became a thing in the
meantime. Since very recently it features an API.
It works with clojurescript.
So I'd propose that refactor-nrepl is defined in terms of defprotocols,
with typically two possible implementations: "traditional" (i.e.
refactor-nrepl's current one) and LSP.
- traditional for projects targeting the JVM
- LSP for projects (also) targeting cljs, or for projects too hard to
analyze with t.ana
- There could be extra options/heuristics for making the right choice
on a per-project basis.
@expez <https://github.com/expez> , what would you think?
Personally I'd see it as one of the very few options ahead. As usual in
OSS "who's doing the work" is an important factor: refactor-nrepl only has
sporadic maintainance and tools.analyzer less so. So delegating the work
seems sensible.
Protocols have a certain cleanliness to them anyway. e.g. what if the
clojurescript compiler itself becomes a nice "ast backend" in a future :)
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#195 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAZLSXBWOM6BWZRMVE7TYDTV4RKNANCNFSM4DHZMXDQ>
.
--
Best Regards,
Bozhidar Batsov
|
Yes I'd implement the functionality purely in terms of protocols which would abstract over the choice of tools.analyzer, clojure-lsp or others. Does that answer the q? |
I meant I’d prefer if this functionality in Clojure LSP was detached from
LSP. Now the dep is going to be bigger than it could be. Not a big deal for
me, just a concern.
On Sat, 3 Jul 2021 at 19:24, vemv ***@***.***> wrote:
Yes I'd implement the functionality purely in terms of protocols which
would abstract over the choice of tools.analyzer, clojure-lsp or others.
Does that answer the q?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#195 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAZLSVMW3YWY7OVMAMXNT3TV5BTDANCNFSM4DHZMXDQ>
.
--
Best Regards,
Bozhidar Batsov
|
I agree with you both :) That said I haven't done enough work to have an opinion about the best way to add support for cljs. The alternative of using something like |
Personally I like that clojure-lsp with its new API is kind of "just a lib". The fact that it was originally meant for LSP is incidental. If we were to write some sort of broader full-flown protocol compatible with all sort of LSP impls, wouldn't we be reinventing the LSProtocol? I'd say there's already https://github.com/emacs-lsp/lsp-mode / etc for that |
Yes I get what you say and share the sentiment to some degree. refactor-nrepl could bundle whatever impls seem high-quality and still leave the door open for user-provided impls. |
I'd like to use this issue to start a discussion on what needs to be done in order to add support for cljs and cljc.
Most of what I consider to be the important functionality involves the analyzer and is unavailable outside of clj files.
I'm positive others would like to improve this state too, but for someone who's not familiar with the project trying to tackle these issues seems a bit hopeless.
It would be helpful to start outlining what the current problems are, what solutions could work and lay a path for people who are interested in working on this but don't know where to start.
Some questions I currently have in mind:
Why does the current approach cannot work for cljs?
This is not specific to refactor-nrepl, but some pointers on how exactly does the analysis process differ between clj and cljs would make a good resource.
In principle, are all the existing middlewares applicable to a cljs ast?
It is unclear if there are platform-specific blockers that would prevent this from happening.
What are some potential solutions for implementing the existing functionality in cljs?
Some high level on how the cljs runtime could integrate with the rest of the middleware.
Are there any potential problems with having to deal with two distinct analysis results?
Cheers!
The text was updated successfully, but these errors were encountered: