Fork me on GitHub
#unrepl
<
2017-04-03
>
cgrand07:04:53

@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/...

thheller07:04:48

@cgrand yeah, yet another logging lib probably isn't gonna see adoption

thheller07:04:07

but getting to the internals of all is not gonna be easy/possible

thheller07:04:57

but as a first step we just need a (log/spy my-value)

thheller07:04:48

but :log is also more system-wide really

thheller07:04:13

so having them as generic messages on a REPL connection might not make sense as they are not like :out

thheller07:04:48

instead you could do open a new REPL connection and call (log/tail maybe-some-kind-of-filter)

thheller07:04:30

which then upgrades that connection into a suitable (not strictly unrepl) protocol

thheller07:04:05

so the connection is specialized to only log related commands and not a generic REPL anymore

thheller07:04:05

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

thheller07:04:23

you should be asking for it

cgrand07:04:36

I agree that indeed redirecting all logs by default to the repl is a bad idea

cgrand07:04:08

(as capturing System wide out and err by default)

thheller07:04:49

as you said

thheller07:04:53

> REPL: the ultimate content negotiation protocol!

thheller07:04:12

so don't try to force everything into one protocol

thheller07:04:16

specialize instead

cgrand07:04:15

but no need to overspecialize either: open another unrepl session, activate log there and use its input for resolving log-related elisions

thheller07:04:37

but log becomes bi-directional if you want the elision part

thheller07:04:07

and I think everything is strictly RPC in that sense

thheller07:04:31

which is harder to realize in a streaming protocol

thheller07:04:33

I do think it is valuable to have /some/ messages also appear in the REPL

thheller07:04:08

ie. [:log "some-label" some-unique-id]

thheller07:04:20

so the REPL window can show that something was logged

thheller07:04:39

it can then take the id and open a new connection to interact with the actual log message

thheller07:04:46

should the user click on that

thheller07:04:50

I'm really used to the js/console.log style logging and just dumping everything in there

thheller07:04:01

should I want to expand even huge values I can

thheller07:04:12

most of the time I don't though

cgrand07:04:19

yeah but we really have to “decomplect” REPL-the-UI and REPL-as-pair-of-streams.

cgrand07:04:08

To sustain a good REPL ui, we need at least 2 REPLs-as-streams (main & control)

cgrand08:04:30

having log messages using either the control link or a dedicated link is a suitable option

thheller08:04:53

I don't agree

cgrand08:04:57

it’s to the UI to merge some or all of logs messgae back in the “console"

cgrand08:04:00

on which point?

thheller08:04:10

what we see as the user is only one stream

thheller08:04:43

that actions we do go over a second connection (or third) is an implementation detail

thheller08:04:43

so having the :log messages appear in the first stream only helps to establish context

thheller08:04:37

ie. this log happened in a thread I started in this REPL after :start-eval

thheller08:04:04

but I might also want a dedicated window somewhere

thheller08:04:10

that just tails the log in the background

thheller08:04:53

which I can sometimes look at, maybe click around on a webpage while some log accumulates in the background

thheller08:04:00

and then look at the log

thheller08:04:14

that has no REPL interaction at all, I want a dedicated window for that

thheller08:04:37

that we used a REPL to bootstrap this log tail is just an implementation detail. it is not a REPL.

thheller08:04:37

it cannot eval for example, so we don't need interrupt and other things

thheller08:04:13

(although you might add eval as a log protocol command, but that is not a REPL eval, it cannot read)

thheller08:04:18

I have no idea how all this would work in emacs since it is a bit limited UI-wise

thheller08:04:49

but something like Cursive could provide very fancy dedicated UI elements

thheller08:04:35

when I find the time to do so I will eventually implement some kind of solution for the browser

thheller08:04:48

just to be able to play with the UI a bit

thheller08:04:24

I'm too spoiled by the chrome devtools, even having something basic like its console for CLJ would be amazing

cgrand09:04:09

tools.logging assumes strings and uses pr-str

cgrand10:04:31

timbre is ok

cgrand13:04:15

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 💩)

pesterhazy13:04:52

Emojis? You're getting me ideas

cgrand13:04:27

that reco was kind of tongue-in-cheek

pesterhazy13:04:38

Well the damage is done

pesterhazy13:04:54

I can't unthink emojis now

cgrand13:04:10

what if code use emojis?

cgrand13:04:24

they are legal clojure symbols

cgrand13:04:58

@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))]))))))

cgrand13:04:24

(there should be variants for all commons backend, and try them all)

thheller13:04:46

neat if you can get access to that stuff

dominicm13:04:03

I'm a little confused by the actions. How do I send them?

cgrand13:04:16

@dominicm in general or for log stuff in particular?

dominicm13:04:35

@cgrand In general.

cgrand13:04:03

1/ you substitute parameters (if any) 2/ you send the resulting form to a repl

cgrand13:04:44

(in general another repl)

dominicm13:04:33

@cgrand So I type a literal: [:exit] for example?

cgrand14:04:51

Hmm no the idea is that the client UI has some capabilities

cgrand14:04:33

and they are enabled or disabled depending on what’s happening over the wire

cgrand14:04:30

(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)

cgrand14:04:06

so you get the :hello and it lists :exit and you client has some UI for :exit

cgrand14:04:46

so you take the form mapped to :exit and you send it (generally through a second repl linkà

dominicm14:04:03

Hmm, still not fully understanding what is listening to the form mapped to :exit

dominicm14:04:09

& why it can't be sent manually.

cgrand15:04:05

@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.
~$

dominicm15:04:12

I hadn't realised that was what the forms looked like in :actions. That makes loads of sense now.

dominicm15:04:48

And I suppose that theoretically I could send the (unrepl.repl/exit! …) from the first term, even if that's not the intent?

cgrand15:04:12

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.

cgrand15:04:29

Heh, just read your last message. Great minds think alike

dominicm15:04:52

Okay, this is great.

dominicm15:04:57

I fear Slack might be misrepresenting my location for you to send that 😛

dominicm15:04:46

Is the plan for unrepl to handle multi-plexing still?

cgrand15:04:04

no I mixed you and @pesterhazy locations

cgrand15:04:23

re: multiplexing, no. One can provide a multiplexing layer but unrepl-the-spec is independent of it.

dominicm15:04:37

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.

cgrand15:04:37

That was the whole point of going to multiple connections, not having to bake multiplexing in

cgrand15:04:25

a multiplexing layer may be quite simple as long as you don’t want to handle backpressure

dominicm15:04:12

Curious: Is there a mechanism for custom actions yet?

cgrand15:04:30

give me an example

dominicm16:04:55

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).

cgrand16:04:13

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?

dominicm16:04:54

My thoughts behind action was having it generate custom tags, so if that can be done without a custom action, that is okay too.

dominicm16:04:31

Essentially I'm looking for something like [:out] but for results (like [:eval])

dominicm16:04:24

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]

thheller16:04:25

> find-usages in refactor-nrepl

thheller16:04:32

this is not a REPL interaction

dominicm16:04:51

@thheller not sure I understand?

thheller16:04:04

REPL is read-eval-print-loop

thheller16:04:42

your example would be better served by a custom protocol

thheller16:04:07

but that protocol has different concerns than a REPL would have

thheller16:04:13

ie :streamed

thheller16:04:36

but it doesn't need some of the other things unrepl provides anymore

thheller16:04:03

no read no :prompt ...

cgrand16:04:49

@dominicm between eval and out there may be log

thheller16:04:44

something like that should be used for tooling stuff .. it also has find-usages built-in

thheller16:04:39

@dominicm this probably doesnt make much sense

thheller16:04:43

> {: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}}}]}

thheller16:04:58

but in essence this is what it would look like for the lang server protocol

thheller16:04:10

note that this a sync RPC request

thheller16:04:42

you could also do it in a streaming fashion although the protocol doesn't define that

thheller16:04:10

it does however define {:method "$/cancelRequest" :params {:id 1}} to cancel the reference request

thheller16:04:58

you may also send notifications while processing the request

thheller16:04:15

you can do anything really. the protocol itself is fairly simple

thheller16:04:42

its just JSON-RPC the lang server protocol just tries to define a standard protocol for editors

thheller16:04:56

whether or not that will catch on permanently remains to be seen

thheller16:04:01

looks promising though

thheller16:04:19

but none of that is related to a REPL and should not be confused with it

dominicm17:04:00

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.

thheller17:04:02

thats the point of the language server as well

thheller17:04:55

I want to work on my lang server impl a bit soon

thheller17:04:18

I expect to use some of the tools behind cider-nrepl, I really don't wnat to write all that stuff by myself 🙂

thheller17:04:30

just not the nrepl bits

dominicm17:04:59

https://github.com/autozimu/LanguageClient-neovim cool 😄 Get LanguageServer working then 😉

dominicm17:04:53

@thheller would you build it upon unrepl? Or something else?

thheller18:04:23

probably not unrepl no

thheller18:04:21

well you could start it unrepl and it will upgrade the connection

thheller18:04:59

but currently I think thats much more complicated than just connecting to a dedicated port

thheller18:04:13

FWIW the language server is working

thheller18:04:29

it just doesn't ahve many features yet but the protocol is implemented

thheller18:04:48

only got to the proof-of-concept stage so far

thheller18:04:09

but when you open a .cljs file it will compile that file and publish some diagnostics

thheller18:04:15

ie. warnings in that file

thheller18:04:40

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 😉

pesterhazy18:04:59

Published unravel 0.1.4, with tab completion and prettier exceptions

pesterhazy18:04:54

@cgrand I'm wondering how to track evals

pesterhazy19:04:37

for tab completion I send out a command and get back [:eval '(filter filterv first) 1]

pesterhazy19:04:02

but how do I know which number I should be looking for?

cgrand19:04:06

@pesterhazy elaborate

pesterhazy19:04:15

currently I just count how many forms I've sent over the wire

pesterhazy19:04:24

but that seems hacky?

cgrand19:04:48

Right after the form you receive a started-eval msg

pesterhazy19:04:19

ah I'm still on the older version as of ~5 days ago

pesterhazy19:04:32

so I should listen for started-eval and compare that to what I sent out?

pesterhazy19:04:51

so I learn the eval-id?

cgrand19:04:37

There's an echo message I haven't implemented yet

cgrand19:04:28

But echo is more meant to map evals to expression when they are batched.

pesterhazy19:04:28

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

cgrand19:04:37

Why can't you do that? Put the application id in the return value and you are done.