trptcolin / reply

REPL-y: A fitter, happier, more productive REPL for Clojure.
Eclipse Public License 1.0
337 stars 44 forks source link

repl-y running in a leiningen repl? #170

Closed kkinnear closed 6 years ago

kkinnear commented 6 years ago

I'm wondering if it is possible to kick off a repl-y while running in a leiningen repl which was started by lein repl, and create a sort of "sub-environment" in the second repl-y.

I want to create a function someone can run at a regular lein repl that will create an environment and then kick off a repl-y repl that runs inside of that environment. When I try this, it does all of the initialization great, and then waits forever for input. I have to kill the terminal window, ultimately.

I can create this sort of embedded or second repl with clojure.main/repl and clojure.main/repl-read, but the user experience is, shall we say, a long way from that provided by repl-y.

My question: should I be able to do this with repl-y? If so, I'll keep trying. But I am wondering if I'm simply trying to do something that is never going to work. If you know of any examples of this being done, I'd love a pointer to an example. I haven't been able to find one.

I have successfully run repl-y standalone in an uberjar which I have made available to coworkers, and that works great (after you made it work for me some years ago). But that's just running one repl-y, not running one inside of another. But it does let me know that repl-y doesn't need to be running with an nrepl to operate. A single standalone repl-y works great. I just want to run a standalone one inside another one.

Thanks!

trptcolin commented 6 years ago

Hi Kim, nesting like this isn't currently possible (caveats below), but I agree it'd be great to have, if things could be reworked.

Nesting a standalone (non-nrepl) instance underneath another standalone instance appears to work (via lein run --standalone in the reply project source), but it does shares a Clojure runtime with the embedded reply instances, with all the same vars and everything (e.g. when I define user/x it's available in both places).

Nesting an attached nrepl instance (something like (reply.main/launch-nrepl (first (reply.main/parse-args ["--attach" "53102"])))) underneath a standalone instance also appears to more or less work (looks like an issue w/ the init code, but goes on to work ok), and lets there be distinct runtimes (but only works for 1 level, not recursively).

But as far as I can tell, nesting anything under an nrepl reply (which lein repl uses) has issues.

This might be a bit of a fundamental issue. The nrepl mode has to assume that execution happens in a separate process (even a remote machine!) - and since jline (and therefore reply) must use the terminal directly, there's no way to tell the server side of nrepl to access the client side's terminal.

The nrepl mode normally breaks out of its execution processing when the remote end signals that it needs input via an nrepl-protocol message "need-input". This is why things like clojure.main/repl will work: because they work with STDIN (which gets streamed in from another process) rather than the richer terminal interface.

I can imagine a design like #116 to be an extensible path forward for spinning up sub-repls, but not sure whether that kind of thing would solve your issue or if you would need evaluated code to be the thing that triggers the sub-repl. Alternatively: maybe there's a way I'm missing to redesign the nrepl client code here in reply to have a stack of nrepl servers it can pop through.

When you say "sub-environment", I'm not sure what level of sharing/separateness you'd want, but I imagine it'd be possible to either act on the same clojure runtime or spin up separate ones.

I'm not going to have the bandwidth to implement this anytime soon, but if someone else does that'd be awesome!

kkinnear commented 6 years ago

Thanks Colin. Since it is so seamless, I always manage to kind of ignore the nrepl nature of what repl-y does. Thanks for letting me know that I'm not going to get there by just some alternative approach to initiating repl-y.

I'm really only looking for a slightly more friendly clojure.main/repl, which has history, line editing, and maybe paren flashing for balance. The environment I want is to simply bind up a bunch of variables to create a local environment, and then kick off the repl. I'm not looking for another process or something -- I don't want isolation -- quite the contrary. I want some some environment that people can operate in, and will go away when they are done. Basically, I want to define a bunch of stuff, let people play with it, and then have it go away and not pollute whatever namespace they are in at the time. But while they are playing with it, they also need access to the existing environment at their repl.

I can live with clojure.main/repl, and if I really want history, I suppose I could implement it myself since I have to give it the read function anyway. The character by character stuff I suspect is not going to work.

My need isn't serious enough to try to dig into repl-y and its relationship with nrepl to implement something like you describe myself -- at least at this time.

Thanks for the information. I really appreciate it! And thanks for repl-y, its great!