^^ quick and dirty version of an Emacs unrepl client
nice!!
note that this is literally the result of an evening+a morning hacking around, with little to no attempt to do things "right". Consider it alpha/experimental.
that said, do try it out of course 😉
can you eval a buffer?
trying it right now
no, no buffer or in-buffer eval stuff is there
that would be cool at some point
I'm curious if the features to display correct line/column etc work well for features
sending stuff to the process from a buffer would be easy to add, but to display results in the buffer instead of in the repl will require a lot more book keeping
hm results in the minibuffer below like inf-clojure?
also no support for multiple repls/connections at this point, it just hard codes the repl buffer name
like I said this is a proof of concept, it will need a bit of reworking to better handle its state management
sorry I'm a newb after 2 years of emacs, how do I load this?
yeah look at the comments at the bottom
do an M-x eval-buffer to load the code
then do (unrepl-start '("lein" "run" "-m" "unrepl.repl/start"))
or (unrepl-start '("telnet" "localhost" "3848"))
if you're running on a socket repl
then switch to the *unrepl-repl*
buffer
you can inspect the output coming from unrepl in the *unrepl-output*
buffer
ok
had to start using this command
JAVA_OPTS="-Dclojure.server.unrepl={:address,\"127.0.0.1\",:port,3848,:accept,unrepl.repl/start}" lein run -m clojure.main/main
also telnet didn't worked, I replaced it with nc
instead
which is better anyway I think
yeah that's what I did as well. the other should also work assuming it's in the right directory
note the commas instead of spaces
some weird quoting issue with lein
I think
yeah nc
is actually cleaner, I had to add some code to skip past telnet's initial output
ah interesting, must be a dfference in shell logic
works ^^
I'm uzing zsh on linux
nice!
here it's zsh on osx
anyway good to know, I'll try to get some kind of unrepl-jack-in
working at some point
hehe cool
what unravel does is to send the unrepl payload on connect
so you don't need to run a specially prepared repl, any socket server should do
ah I see, you upgrade a plain old repl to unrepl
makes sense
right
it's not perfect yet, because it uses a single ns so multiple connections could interfere if they use different unrepl versions
but at some point we can gensym the ns name to rule out this possibility
now the next cool thing would be to install unrepl if it isn't there yet
honestly I'd just ship the unrepl payload with the emacs module, so the two are always in sync
at least for now
ah yeah so with "payload" you actually mean the full source, I was assuming you just sent a (unrepl.repl/start)
but that makes sense
right I mean the two src files (print.clj and repl.clj)
cool, I'll look into that
now onto some non-hobby hacking 😉
really cool that your project shows than edn.el is up to the task
that's what you use, right?
yes!
yeah works quite well. just have to get used to using vectors and hash-maps in elisp, you don't see those every day 🙂
there was some worry about that project (bbatsov mentioned it wasn't maintained or something like that, I don't recall)
no problems so far, obviously haven't really pushed its limits yet. but I assume edn is stable and well defined enough
yeah well, not around the edges I think
like what is a valid symbol char etc
all those things are up for interpretation 🙂
right
You mean the multiple namespaces for multiple clients?
@pesterhazy to confirm my understanding from code: you mirror unrepl into your local project?
Yeah for now
@pesterhazy I think it's a good idea really. Although maybe with some vendoring for the ns.
If you don’t mind I’ll explain what I have in mind out of the thread
I believe that the general shape of unrepl is not going to change so it’s time to take care of other aspects, especially the upgrade from a standard repl (or from a nrepl)
I’m going to add a helper ns to unrepl (well to the reference impl of unrepl 😉) for clients to perform the upgrade
So a client won’t have to duplicate unrepl code unless it modifies it (and then yeah the ns should be changed)
not sure I understand
to my mind, shipping the unrepl code with the client is a virtue
because it means that the target system doesn't need to be instrumented
you ship it (as a dep not as a copy)
ah yes, that makes sense
shipping it as a dep means you become part of the jvm though, unravel is independent. So is cider/vim/etc.
there are resources files, meant to be sent over the wire
@dominicm, I can pull in the dependency as part of the unravel build process, no problem. Shouldn't affect users
Hmm, I think I'm missing something. How does that work @pesterhazy?
if you install unravel via npm install -g unravel-repl
, that will include the repl.clj + print.clj from the unrepl repo
# How to upgrade a repl to unrepl
(Audience: repl client authors)
Once you have a connection to a REPL, you should send the content of the resource file `unrepl/upgrade/blob.clj` to it.
Once sent, start reading the output and wait for vectors. Here are the vectors you may get:
* `[:unrepl.upgrade/require some.ns.name :some/thing]` then send the content of the ns identified by `some.ns.name` to the repl, followed by `:some/thing` to delimit the end of the ns.
* `[:unrepl.upgrade/failed "pr-str of an exception"]` something went wrong you are back in the plain repl
* `[:unrepl/hello ...]` success!
* anything else should be ignored.
So the blob performs an inversion of control to request missing resources to the client.
I already do this
I mean except for the error handling 🙂
ok cool, anything you do that I don’t?
I wait for the message [:unrepl/bye]
and stop interpreting the stream as edn after that
because in an upgraded repl, I get the regular user=>
prompt after the end
I only documented how to upgrade to unrepl (how to get to unrepl/hello
), what happens after is not for UPGRADE.md
DOWNGRADE.md
🙂
but seriously this is useful information
can you elaborate on unrepl.upgrade/require
?
I can't quite parse that sentence
ok so basically the blob upgrades to a short-lived “upgrade-repl” before upgrading to unrepl/start
this “upgrade-repl” is the one emitting the :unrepl.upgrade/*
messages
why do we need this?
I mean, we want to lower the barrier for tooling authors
I thought it was great that @plexus could write a client in an evening
so I think one thing that would be cool would be to aim to keep that and make additional features optional
The other option was to systematically send the two files.
Which files do you mean? repl.clj and print.clj?
@pesterhazy yes these two
JVM client can do:
(defn upgrade!
“Upgrades a repl to unrepl. Upon success the first form read on the input stream should be
a :unrepl/hello message.
in is the input stream from the repl (so its *out*)
out is the output stream to the repl (so its *in*)”
[in out]
(binding [*in* in
*out* out]
(-> “unrepl.upgrade.blob” resource slurp println)
(loop []
(let [x (read)]
(case (when (vector? x) (first x))
:unrepl.upgrade/require
(let [[_ ns eom] x]
(-> ns resource slurp println)
(println eom))
:unrepl.upgrade/success true
:unrepl.upgrade/failed (throw (ex-info “Can’t upgrade.” {:reason (second x)}))
(recur))))))
@pesterhazy is your code for upgrading the repl simpler?
hm I just use the string [:unrepl/hello
as a sentinel
you blindly send the two files? and wait for the sentinel?
https://github.com/pesterhazy/unravel/blob/master/src/unravel/network.cljs#L15
yeah exactly 🙂
So what I propose is a replacement for <https://github.com/pesterhazy/unravel/blob/b687edb188aaab58b9ac0ce7a143172dc3e256a6/src/unravel/loop.cljs#L145-L147>
cool
yeah that was definitely just an ad hoc concatenation of the files
maybe maybe maybe you are right
I think I’m going to write a custom lein task to create the blob by concatenation
sounds good. Does that handle renaming the namespaces too?
why rename?
(if you don’t change them :-))
to avoid conflicts between, say, unravel and emacs's unrepl-mode?
there’s no conflict if none patched unrepl
did you patch it?
I did (to add a :unrepl/bye
action), but that can be easily upstreamed
but inevitably at some point two clients will use different versions of unrepl, no?
so they might overwrite each other
creating a race conditions - who connects last wins
there are two cases to consider: • two different versions of unrepl (one older than the other) • two forks
First case I can deal with it in the blob (and developing unrepl with no breaking change)
hm yeah but that seems like you're creating more work for yourself
instead of just having two independent versions of unrepl
Second case: bad bad forker & unrepl should provide ways to be extended without forking (eg for actions)
I don’t know
I agree with backward-compatibility as a goal
maybe yes I should just rename for now on
and consider a more satisfying solution later
that sounds like a good plan
or I can spend all my time devising a clever solution (which qualifies as an advanced form of procrastination)
maybe unrepl turns out to be super stable, so we realize soon enough those worries are all unfounded
some of those advanced procrastination experiments have already yielded fantastic results, like the ellipsis feature 🙂
Cool Emacs mode 😀😀
Starring the file
@pesterhazy congrats for scoring an unravel talk at euroclojure
thanks 🙂
are you coming for the conference @cgrand? how about everyone else?
it might be nice to get an unrepl unsession organized 🙂
Unlikely
@pesterhazy I am going to be there
@richiardiandrea nice!
still trying to find the time to work on a universal js/cljs repl...man, I wish I had some bureaucracy to take care of 😄
I'll be there
@pesterhazy @cgrand something we could adopt, is that there's some build process for mangling the ns of the unrepl clj files during build. Then it would be unique for each project: no clobbering
@richiardiandrea structured procrastination eh?
we should meet up in Berlin when you guys are here and talk repls 🙂
oh that's for sure
😄
@dominicm, yeah I think that would be a good option
Vital.vim does this, which I find infinitely curious
cgrand: third case : you're working on unrepl itself over an unrepl session, and want to change things without interfering your running session