-
-
Notifications
You must be signed in to change notification settings - Fork 648
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
Allow CIDER to load its own middleware via the sideloader #3037
Comments
With the above changes to cider/cider-nrepl/nrepl I can load the middleware, and invoke a |
Question regarding the sideloader design for @shen-tian. It seems like there's no way to distinguish between "file not found" and "empty file"? Maybe not the most common edge case but still seems significant... |
While I don't remember the details as well as Shen, I'm assuming that's just something that came from the early sideloader prototype that was created by @cgrand. Perhaps the reasoning was that an empty file and a missing file are more or less the same as far as the sideloader is concerned. We can probably make the responses more granular if that'd help somehow. |
With the last two PRs this actually seems to pretty much work as intended. It's a little slow (so might be worth considering caching responses, since currently every clj file gets served twice), and I didn't try every middleware (the deferred loading means there might still be errors lurking there), but all in all this is quite promising, although you still need a bit of low-level invocations to make it work. Maybe it's time to think about the ergonomics of this. Is the end goal to bundle cider-nrepl (and orchard) with cider? I think it would be preferable to download the jars from clojars, and either adapt our resource loading to deal with the zip files, or just unzip them in a cache location. So we'll need some logic that does that. and then I guess ultimately you would have something like When running directly from source I also had to edit Looping in @mk at this point |
Yep, that's the end goal. Or at least one of them - I envision that every client will use sideloading for their deps (e.g. I'd be fine with fetching deps from Clojars, although this might slow down the first jack-in/connect. I was also thinking there some be some configurable list of folders, from which you can sideload resources. (useful for user extensions based on eval) Anyways, I'm totally open to ideas how to make this optimal. |
in any case I think it should be opt-in, the current jack-in approach works fine and has benefits over the sideloading (speed, having resources actually on the classpath). |
@plexus great to see this being this far along already, thank you! 👏👏👏 Regarding fetching deps I'm wondering if there's a way to delegate this to another tool. Could downloading the jars could be handled by shelling out to |
You have to keep in mind that when someone is doing |
I was thinking that fetching the deps could work similarly to jack-in, where cider is also delegating this to another tool (clj, lein, shadow, boot…). Requiring e.g. The upside I would see is that it would put the jars in the same places as when using |
In theory it is, but people like me, who are heavy Lein users normally don't install Anyways, I've always been a believer in incremental progress, so I don't think we need to have everything super polished from the start. I'm not opposed to shelling out, I just think we should aim for utilities that are most likely to be around (e.g. generic Unix tools). In general I think we should aim for whatever requires the least amount of setup effort for the end users. |
Here are some quick PoC experiments: https://gist.github.com/720798947e999a2142390a702a9ebe0b
We have a lot of options here these days actually, some cross-platform (bb, deps) or platform-independent (deps.clj as standalone jar), and you could even throw in However these are all a bit unsatisfactory because they add stuff to classpath we don't want (src, clojure, spec.alpha), and
Since this jar is fully self contained (dependencies are inlined) this seems like the pragmatic solution here. It uses portable emacs lisp, and adds a one-time cost of downloading 2.6M. For me the first invocation was 0.86s, and after that it's just a check to see if the file exists. You could go a step further and check first if the file is perhaps already present in
Under the hood this uses elisp for listing the contents, but then shells out to extract a file. Still it seems this code is fairly portable, detecting the most appropriate zip tool and dealing with platform differences. Reading the So I think my next target should be
which
This should give users something that works with minimal setup or extra dependencies, without affecting any existing functionality or workflows. And people can combine some of those pieces themselves if they want to change e.g. where the jar comes from. I don't think the goal should be to make this behavior the default, we have a system that works well, and there are some benefits to adding the middleware directly to the server process. I would also hate to break existing workflows, I know how frustrating that can be. But we can make it easy to use and easy to find for people that can't or won't add the middleware directly to the server. Once this is working well for instance we can add a message to the current warning if cider-nrepl isn't found that people can upgrade their connection. |
I love the proposed plan - it's definitely the one of least resistance. We can also add some defcustom like |
I'm continuing to dogfood this, and have some more changes to nrepl incoming, including the caching we talked about. What I'm seeing in real-world usage though is that this slows down loading of any code to a point that will be unacceptable for most people. The problem is that any resource that isn't found through the regular classloader will get tried via the sideloader. This doesn't sound too bad, until you consider that when you
So that's already 2 network round-trips for every clj file. If it's a cljc file it's 3. ClojureScript is better, but it will still do a needless round-trip when it's a cljc file. So I think in practice we're going to have to either stop the sideloader again when we're done with it. Kind of problematic too with all the deferred loading. Or we need to have some kind of filter mechanism so we can tell it to only contact the client for certain resources. |
After more internal discussion we are going to focus currently on testing the I'm not really convinced that the sideloader is a good fit for this particular scenario, given that we have increasingly good options for dynamically loading jar dependencies, which are faster and more reliable. A more useful use case would be doing development over the network but being able to |
Whatever works best for you. For me the main appeal of the sideloader is for clients to be able to quickly customize their nREPL server without the need to use any external libraries, but obviously it's not the only way to inject some dependency dynamically.
Yeah, that makes perfect sense, especially in the context of commands like
What's the issue there?
I've also been thinking about this - not really a filter, but more of a list of namespace prefixes that will be invoking the sideloader. I think this will mostly solve the first problem that you mentioned. |
I don't remember any intentional design decision around this, so it's likely a carry-over from the unrepl design which Christophe authored. (Though I should have thought through this a bit more before adding it to nREPL). If we think this should change, let's do it before there's much use of this in the wild. |
I've tried this feature, as per the ticket description with
I've also tried with Is this ticket is the right place to submit these kinds of errors, or should I create a separate one? |
@andreyorst I think a separate ticket linking to this one would be best. |
nREPL now has dynamic insertion of middleware, as well a sideloader for requesting resources from the client. CIDER has basic support for sideloading, so it would seem that all the pieces are there to be able to take a vanilla nREPL connection, and upgrade it to a fully CIDER-compliant connection.
After initial testing it seems this isn't quite there yet, there are small tweaks needed in CIDER, cider-nrepl, and nREPL, so this is a meta-issue to keep track of the overall goal and progress.
Related
How to try it out
Start a vanilla nREPL server:
Connect to it from Emacs:
The text was updated successfully, but these errors were encountered: