@thheller thanks, logging was a blatant oversight. so we can imagine log messages like [:log [“ns.or.class.or.whatever.hierarchical.key.as.string” inst & args] group-id?]
now there’s the issue of bridging to any combination of timbre/jul/log4j/logback/slf4j/...
@cgrand yeah, yet another logging lib probably isn't gonna see adoption
but getting to the internals of all is not gonna be easy/possible
but as a first step we just need a (log/spy my-value)
but :log
is also more system-wide really
so having them as generic messages on a REPL connection might not make sense as they are not like :out
instead you could do open a new REPL connection and call (log/tail maybe-some-kind-of-filter)
which then upgrades that connection into a suitable (not strictly unrepl) protocol
so the connection is specialized to only log related commands and not a generic REPL anymore
I don't feel like connecting to a REPL, upgrading a unrepl and then getting a bunch of log messages just because the system is logging is useful
you should be asking for it
I agree that indeed redirecting all logs by default to the repl is a bad idea
(as capturing System wide out and err by default)
as you said
> REPL: the ultimate content negotiation protocol!
so don't try to force everything into one protocol
specialize instead
but no need to overspecialize either: open another unrepl session, activate log there and use its input for resolving log-related elisions
but log becomes bi-directional if you want the elision part
and I think everything is strictly RPC in that sense
which is harder to realize in a streaming protocol
I do think it is valuable to have /some/ messages also appear in the REPL
ie. [:log "some-label" some-unique-id]
so the REPL window can show that something was logged
it can then take the id
and open a new connection to interact with the actual log message
should the user click on that
I'm really used to the js/console.log
style logging and just dumping everything in there
should I want to expand even huge values I can
most of the time I don't though
yeah but we really have to “decomplect” REPL-the-UI and REPL-as-pair-of-streams.
To sustain a good REPL ui, we need at least 2 REPLs-as-streams (main & control)
having log messages using either the control link or a dedicated link is a suitable option
I don't agree
it’s to the UI to merge some or all of logs messgae back in the “console"
on which point?
what we see as the user is only one stream
that actions we do go over a second connection (or third) is an implementation detail
so having the :log
messages appear in the first stream only helps to establish context
ie. this log happened in a thread I started in this REPL after :start-eval
but I might also want a dedicated window somewhere
that just tails the log in the background
which I can sometimes look at, maybe click around on a webpage while some log accumulates in the background
and then look at the log
that has no REPL interaction at all, I want a dedicated window for that
that we used a REPL to bootstrap this log tail is just an implementation detail. it is not a REPL.
it cannot eval for example, so we don't need interrupt and other things
(although you might add eval
as a log protocol command, but that is not a REPL eval, it cannot read
)
I have no idea how all this would work in emacs
since it is a bit limited UI-wise
but something like Cursive could provide very fancy dedicated UI elements
when I find the time to do so I will eventually implement some kind of solution for the browser
just to be able to play with the UI a bit
I'm too spoiled by the chrome devtools, even having something basic like its console
for CLJ would be amazing
tools.logging
assumes strings and uses pr-str
timbre
is ok
So (map #(/ %) (iterate dec 3))
now machine prints as
(<#C4C63FWP5|unrepl>/ratio [1 3] <#C4C63FWP5|unrepl>/ratio [1 2] 1 <#C4C63FWP5|unrepl>/lazy-error #error {:cause "Divide by zero", :via [{:type <#C4C63FWP5|unrepl>.java/class java.lang.ArithmeticException, :message "Divide by zero", :at <#C4C63FWP5|unrepl>/object [#unrepl.java/class java.lang.StackTraceElement "0x272298a" "clojure.lang.Numbers.divide(Numbers.java:158)"]}], :trace [#unrepl/... nil]})
and the client can render it as (1/3 1/2 1 💩)
Emojis? You're getting me ideas
that reco was kind of tongue-in-cheek
Well the damage is done
I can't unthink emojis now
what if code use emojis?
they are legal clojure symbols
Heh
@thheller something like:
(let [^ch.qos.logback.classic.Logger logbackroot (org.slf4j.LoggerFactory/getLogger ch.qos.logback.classic.Logger/ROOT_LOGGER_NAME)]
(.addAppender logback
(proxy [ch.qos.logback.core.AppenderBase] []
(doAppend [^ch.qos.logback.classic.spi.ILoggingEvent e]
(when *repl-issued* ; does not exist yet
(unrepl/write [:log (into [(keyword (.getLevel e)) ; todo normalize
(.getTimeStamp e) ; todo to inst
(.getMessage e)] ; todo split and interleave with arguments
(.getArgumentArray e))]))))))
(there should be variants for all commons backend, and try them all)
neat if you can get access to that stuff
I'm a little confused by the actions. How do I send them?
@dominicm in general or for log stuff in particular?
@cgrand In general.
1/ you substitute parameters (if any) 2/ you send the resulting form to a repl
(in general another repl)
@cgrand So I type a literal: [:exit]
for example?
Hmm no the idea is that the client UI has some capabilities
and they are enabled or disabled depending on what’s happening over the wire
(to reduce the coupling between the UI and the upgrage blob/unrepl impl (eg a CLJS unrepl would not offer the same actions as a CLJ one)
so you get the :hello
and it lists :exit
and you client has some UI for :exit
so you take the form mapped to :exit
and you send it (generally through a second repl linkà
Hmm, still not fully understanding what is listening to the form mapped to :exit
& why it can't be sent manually.
@dominicm : First term
$ telnet localhost 5555
user=> (require 'unrepl.repl)(unrepl.repl/start)
nil
[:unrepl/hello {:session :session282, :actions {:exit (unrepl.repl/exit! :session282)}}]
[:prompt {}]
[:prompt {}]
Second term:
$ telnet localhost 5555
user=>(unrepl.repl/exit! :session282)
true
First term:
Connection closed by foreign host.
~$
I hadn't realised that was what the forms looked like in :actions
. That makes loads of sense now.
And I suppose that theoretically I could send the (unrepl.repl/exit! …)
from the first term, even if that's not the intent?
If the main repl is not in a dirty state (busy or in the middle of a form), suicide is possible:
user=> (require 'unrepl.repl)(unrepl.repl/start)
nil
[:unrepl/hello {:session :session292, :actions {:exit (unrepl.repl/exit! :session292)}}]
[:prompt {}]
[:prompt {}]
(unrepl.repl/exit! :session292)
[:started-eval {:actions {:interrupt (unrepl.repl/interrupt! :session292 1), :background (unrepl.repl/background! :session292 1)}} 1]
Connection closed by foreign host.
Heh, just read your last message. Great minds think alike
Okay, this is great.
I fear Slack might be misrepresenting my location for you to send that 😛
Is the plan for unrepl to handle multi-plexing still?
no I mixed you and @pesterhazy locations
re: multiplexing, no. One can provide a multiplexing layer but unrepl-the-spec is independent of it.
I thought not, just confirming. I was trying to plan how I'd do my multi-hop funkage to stations in the wild. I'll have to have a think about what a multi-plexing layer might look like.
That was the whole point of going to multiple connections, not having to bake multiplexing in
a multiplexing layer may be quite simple as long as you don’t want to handle backpressure
Curious: Is there a mechanism for custom actions yet?
give me an example
Interested in how the find-usages in refactor-nrepl would be ported. It's a particularly slow operation, so you want to "stream" the values as you get them (channel-like semantics).
Does it need to be an action? Can't it be something running on a dedicated repl or on a thread spawned from the control repl?
My thoughts behind action was having it generate custom tags, so if that can be done without a custom action, that is okay too.
Essentially I'm looking for something like [:out]
but for results (like [:eval]
)
I'm not sure I've explained that well. If I do:
(do (println 1) (Thread/sleep 10000) (println 2))
I get
[:out "1"]
;; some time later…
[:out "2"]
[:eval nil]
I want to try do something like:
[:streamed 1]
;; sleep
[:streamed 2]
[:eval nil]
> find-usages in refactor-nrepl
this is not a REPL interaction
@thheller not sure I understand?
REPL is read-eval-print-loop
your example would be better served by a custom protocol
but that protocol has different concerns than a REPL would have
ie :streamed
but it doesn't need some of the other things unrepl provides anymore
no read
no :prompt
...
@dominicm between eval and out there may be log
didn't bring this up in a while https://github.com/Microsoft/language-server-protocol 😉
something like that should be used for tooling stuff .. it also has find-usages built-in
@dominicm this probably doesnt make much sense
> {:id 1 :method "textDocument/references" :params {:textDocument {:uri "foo.clj"}
:position {:line 1 :character 2}
:context {:includeDeclaration true}}}
< {:id 1 :result [{:uri "foo.clj" :range {:start {:line 1 :character 2} :end {:line 1 :character 6}}}]}
but in essence this is what it would look like for the lang server protocol
note that this a sync RPC request
you could also do it in a streaming fashion although the protocol doesn't define that
it does however define {:method "$/cancelRequest" :params {:id 1}}
to cancel the reference request
you may also send notifications while processing the request
you can do anything really. the protocol itself is fairly simple
its just JSON-RPC the lang server protocol just tries to define a standard protocol for editors
whether or not that will catch on permanently remains to be seen
looks promising though
but none of that is related to a REPL and should not be confused with it
I like the way cider looks at these operations as plain clojure functions. They're essentially helper functions, that tooling is able to interpret the result of.
thats the point of the language server as well
I want to work on my lang server impl a bit soon
I expect to use some of the tools behind cider-nrepl
, I really don't wnat to write all that stuff by myself 🙂
just not the nrepl bits
https://github.com/autozimu/LanguageClient-neovim cool 😄 Get LanguageServer working then 😉
@thheller would you build it upon unrepl? Or something else?
probably not unrepl no
well you could start it unrepl and it will upgrade the connection
but currently I think thats much more complicated than just connecting to a dedicated port
FWIW the language server is working
it just doesn't ahve many features yet but the protocol is implemented
only got to the proof-of-concept stage so far
but when you open a .cljs
file it will compile that file and publish some diagnostics
ie. warnings in that file
this is from Visual Studio Code, no idea how that would work in neovim
but if that language client support textDocument/didOpen
and publishDiagnostics
it probably just works 😉
Published unravel 0.1.4, with tab completion and prettier exceptions
@cgrand I'm wondering how to track evals
for tab completion I send out a command and get back [:eval '(filter filterv first) 1]
but how do I know which number I should be looking for?
@pesterhazy elaborate
currently I just count how many forms I've sent over the wire
but that seems hacky?
Right after the form you receive a started-eval msg
ah I'm still on the older version as of ~5 days ago
so I should listen for started-eval
and compare that to what I sent out?
so I learn the eval-id?
There's an echo message I haven't implemented yet
But echo is more meant to map evals to expression when they are batched.
Ideally I would be able to send (with-eval-id #uuid "e35a9235-0517-4017-b9f3-d16b76e5cc44" (get-completions))
and then map the result back to that
Why can't you do that? Put the application id in the return value and you are done.
sure I can