Fork me on GitHub
#calva
<
2022-10-13
>
maxt05:10:48

I’d like to use Portal as my repl, so I want to enable the nrepl-middleware. I have these lines in my ~/.clojure/deps

:aliases
 {:inspect/portal-cli
  {:extra-deps {djblue/portal {:mvn/version "0.31.0"}}
   :main-opts  ["-m" "nrepl.cmdline"
                  "--middleware" "[portal.nrepl/wrap-portal,cider.nrepl/cider-middleware]"]}}} 
But when I start with Calva jack-in, and select the above alias, it’ll start with this command
clojure -Sdeps '{:deps {nrepl/nrepl {:mvn/version,"1.0.0"},cider/cider-nrepl {:mvn/version,"0.28.5"}}}' -M:inspect/portal-cli -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware]"
It seems like the last --middleware is overriding the :main-opts from my alias. Can I configure Calva to not add it?

maxt06:10:36

Oh, I found an open bug about it 😞 It’s not possible to override middleware in ~/.clojure/deps.edn https://github.com/BetterThanTomorrow/calva/issues/1045

seancorfield06:10:39

I'm a heavy Portal user and I found the nREPL middleware to be very distracting -- you lose control of what you actually want to examine in Portal.

seancorfield06:10:56

My setup is here https://github.com/seancorfield/vscode-calva-setup if you're interested, and my https://github.com/seancorfield/dot-clojure repo includes my ~/.clojure/deps.edn file and my "smart REPL starter" dev.clj file.

❤️ 1
seancorfield06:10:04

I start my REPL manually -- because it nearly always outlives my editor sessions -- and then connect to it, and then start Portal inside VS Code using a custom REPL snippet (`ctrl+alt+space p`). After that, I can choose to either evaluate forms just to Calva's REPL (which I mostly have minimized) or tap> them via ctrl+shift+t t or ctrl+shift+t space -- and I have a lot of custom REPL snippets for doing other things via tap> as well (see those repos).

seancorfield06:10:26

Happy to discuss in more detail whenever you want.

seancorfield06:10:54

(modulo it being nearly midnight here right now and 9 am tomorrow for you 🙂 )

simple_smile 1
maxt06:10:31

Thank you for explaining your setup! I’m actually already using most of it, but I find it annoying that I still have to have the REPL-window open to catch a few things, error messages being one of those. Therefor I’d like to try to use the middleware for that, but still use tap to eval forms. If you’ve found another way around having both portal and repl open I’d be happy to hear about it. But I guess one solution is just to accept it. I don’t like looking in different places for output though.

maxt06:10:06

While we’re at it 🙂 One thing I’d like to do more of is customize viewers. It seems to me like the way to do that in portal is to add metadata to the forms before submitting them to portal. Have you found a good way to do that? For instance, tech.ml.datasets are not recognised by portal, and I’d like to add that.

pez07:10:50

@U02806HQX1C, was it you and I who talked about Portal and auto-tapping? Here's a thread about it, anyway. 😃

pez07:10:40

Could have been @U03DBC3BGAV, it was in that huddle session, at least, iirc.

Sainadh07:10:18

yes Peter, it was me. Thanks!

🙏 1
seancorfield16:10:24

@U0773UB6D The middleware isn't going to get your output tap>'d so you'd still have to look at Calva's REPL panel for that -- but I do have it minimized to just 5-6 lines (bottom right of my editor, below the Portal panel).

👍 1
seancorfield16:10:45

I've recently added a custom submit that wraps Portal's submit and I use that as a tap> listener. It tracks each value passing through so I can access @dev/*v for the "most recent" Portal value without needing to select/navigate within Portal itself: https://github.com/seancorfield/vscode-calva-setup/blob/develop/calva/config.edn#L65 (requires Calva 2.0.307)

seancorfield16:10:40

You could do something similar and have it detect certain types of data and attach metadata before submitting the value to Portal. I used to do that for Clover/Reveal back in the day.

seancorfield16:10:48

(I used to turn Vars into views that showed their metadata, their docstring, and their value if appropriate, amongst other things)

djblue17:10:12

The main reason the nrepl middleware produces extra information is that it's in a place where it can collect more info such as runtime type (very hand for cljc files), eval exceptions, test assertions, stdio, timing and other handy info. I do see how it could be annoying if you don't want that info. Currently the middleware just submits the value directly to portal via portal.api/submit, but it could tap> values to allow users to select exactly what info they care about in a custom submit handler. For tech.ml.datasets datatypes, I think the way for portal to recognize them is via https://clojuredocs.org/clojure.datafy/datafy and automatically datafying all values submitted to portal with something like: (add-tap (comp portal.api/submit clojure.datafy/datafy))

👍 1
maxt06:10:35

My experiment with using both the middleware and tap> for eval did not work as I wanted becasue 1. tap> sends nil to the repl, which portal picks up. 2. output still is not visible in portal (as Sean said, but I didn’t get until I saw it myself 🙂

maxt06:10:46

Something I think I would like would be to have the middleware dispatch a multimethod, so that I can load different handlers into it.

maxt06:10:50

But maybe tap>:ing is the way to go

seancorfield15:10:08

Capturing (and tap>ing) out/err is probably the biggest gap I'd like to "solve", which nREPL middleware could do I think... But I don't want all results tap>d, nor empty out/err. I don't know if there's a single solution that would fit everyone's needs.

djblue20:10:31

What kind of ux are you thinking for out/err?

seancorfield20:10:19

Ah, nREPL only has stdio -- it doesn't separate out and err?

djblue20:10:42

It does, stderr will be colored in red

seancorfield20:10:23

Oh, stdio is a sequence of outputs?

djblue20:10:43

Yeah, the data looks like:

[{:tag :out :val "123"} {:tag :err :val "456"}]
I went with the prepl data format 👌

seancorfield20:10:22

So I guess what would be "ideal" for me here would be to have a way to intercept and post-process that packet before it goes to Portal (and have the option to not submit it).

seancorfield20:10:55

Because what I'd want would be just the :stdio value, not the whole map, and only when it is non-empty.

djblue20:10:58

Yeah, the middleware is very rigid currently. What kind of API do you think would make the most sense here?

djblue20:10:57

The https://github.com/djblue/portal/blob/master/src/portal/nrepl.clj#L117isn't currently too large, so the easiest thing right now would be to fork it :thinking_face:

seancorfield20:10:37

How about some sort of middleware initialization that lets users specify what function to call instead of p/submit in that middleware? By default, it would still use p/submit, but users could provide their own nREPL data handler instead that could pick apart the data and submit whatever they want to Portal?

seancorfield20:10:47

In particular, I'd want to submit pieces of that data separately to Portal so the history would show: • result data (or error data) • stdio data (if not empty) • previous result data • previous stdio data ...

djblue20:10:47

Previously, the middleware would use tap> which would allow users to intercept that value in a handler and provide arbitrary logic. What that work for you? I could add a key like {:portal.nrepl/eval true} to make dispatch explicit

djblue20:10:13

Also, stdio will not be in the map if there was no out/err 👍

seancorfield20:10:56

Hmm, I already have a custom submitter (`tap>` listener) so I guess that would work better -- as long as Portal itself wasn't a tap> listener by default.

seancorfield20:10:32

Wouldn't that change break existing Portal nREPL middleware users tho'? Or is that a roughly zero-size set right now?

djblue20:10:33

Portal by default does not add itself as a tap handler 👍

seancorfield20:10:18

So, yeah, having the middleware tap> stuff with an extra key would definitely give me the flexibility I would want.

djblue20:10:19

I think most users are doing a (add-tap p/submit) so if that has been setup, it should be equivalent

djblue20:10:41

If they haven't, this would be a breaking change, but with a trivial fix 😬

seancorfield20:10:35

Here's what I currently do when starting Portal https://github.com/seancorfield/vscode-calva-setup/blob/develop/calva/config.edn#L67-L78 so I'd just make my custom submit "smarter", but out of the box.

👍 1
seancorfield20:10:01

And I'd have to figure out how to prevent the m/w from doubling up everything because all my code that I eval that contains tap> calls would also go through the Calva REPL and get tap>'d due to the m/w 🙂 But that's my problem at that point, and very malleable.

djblue20:10:02

FYI you will also get :report which will contain test assertions if you ran any

seancorfield20:10:29

Does that play nice with other tooling that modifies clojure.test/report?

seancorfield20:10:48

(I guess I'll find out)

djblue20:10:30

It will intercept values and propagate them to the previous report handler 👍 which is why the data also shows up in stdout

1
djblue20:10:38

If you want to play with this before the next release, you can update this https://github.com/djblue/portal/blob/master/src/portal/nrepl.clj#L86 to tap> and should be able to get going with it locally 👍

seancorfield20:10:37

Yeah, I can copy it into my dev REPL code and modify how I start things up to use it instead of the core Portal one...

djblue20:10:49

Any suggestions for the sentential key/val for identifying these result maps? Or should it be part of metadata?

djblue20:10:58

Another thing to note is that the middleware will only tap the result if the portal ui is open, this was done to prevent issues of collecting results without the user knowing and causing memory issues

seancorfield20:10:04

If it's a key, it shows up in the portal view by default which is a bit weird, so maybe metadata is better here?

👍 1
skylize16:10:42

Is this call to registerSignatureHelpProvider working as expected?

vscode.languages.registerSignatureHelpProvider(
      config.documentSelector,
      new CalvaSignatureHelpProvider(),
      ' ',
      ' '
    )
https://github.com/BetterThanTomorrow/calva/blob/fd9575862df7489b3130b30d751c196c7e48a352/src/extension.ts#L530-L537 It seems to contradict Code's API Docs, which indicate the function takes only 3 arguments, not 4, with the 3rd being a list of strings. https://code.visualstudio.com/api/references/vscode-api#SignatureHelpProvider Assuming it's "right", I'm also quite curious what the duplicate whitespace entry is supposed to be for.

pez19:10:14

The call is fine syntactically. The list/array of strings are declared as a spread argument so it's similar to Clojure's & args. Why we have space twice as a trigger character, I don't know. Should suffice with one of them.

skylize00:10:03

I've gathered up most of the register-whatever-and-push-to-subscriptions stuff in extension.tsinto a few collections, which can be iterated over instead of manually doing that for each one. If this change interests you, I can roll that minor edit in with it. Please check this out briefly, and let me know if I should polish it up for a PR. https://github.com/skylize/calva/blob/consolidate-subs/src/extension.ts The first draft brings the file down from 699 lines down to 483. Each unit now has its name in front, and is alphabetically sorted to more easily browse what is there. --- Do you have a strong preference for { } around 1 line arrow-fn bodies? I see some of both, and Prettier doesn't seem to complain either way. (Personally, I'm heavily biased toward removing all punctuation that is not strictly necessary.) Since many of of the commands, etc. have functions of varying length defined inline, it seems to me other functions only used as a call site for one of these registries could also be inlined into these new data structures. E.g. onDidSave, onDidOpen, etc. What do you think?

pez06:10:25

Sounds like good changes to extension.ts. It's easier to review it if you file a PR. Make it a draft if you don't think it's ready for immediate merge. > I can roll that minor edit in with it. Not sure what this refers to, but generally I like PRs to be focused on one thing at a time, and to have an issue that states the problem and that this issue is linked in the Changelog as part of the PR.

pez06:10:26

That said, in this case, since you mainly want feedback on the suggested changes. You can wait with issue and changelog stuff, if you like. (Though, it might make it easier to parse the suggestion if I have a clear problem statement, so there's that 😄.)

pez06:10:12

> Do you have a strong preference for { } around 1 line arrow-fn bodies? I see some of both, I haven't made up my mind here. @U02N27RK69K usually can help me with trade-offs like this. I lean towards consistency with our preference/rule about always using brackets with if/else statements, but I also know that lately I've been skipping the brackets for the arrow functions because it somehow clarifies to me the nature of them.... In any case, we probably should codify it, and if Prettier can help us with it, we probably should make it do so.

Cora (she/her)08:10:23

I'm indifferent and typically just let the formatter decide. however in some cases it does matter because not having braces around the function body changes the semantics of the code in a meaningful way and also can disambiguate syntax when that's relevant

skylize13:10:53

> It's easier to review it if you file a PR Wasn't looking for a review, yet. Just something along the lines of "yeah, that looks promising". So, 😎 >> I can roll that minor edit in with it. > Not sure what this refers to, but Refers to the duplicated " ". Since I'm already touching that function anyway, thinking to go ahead and clean up what basically amounts to a typo, which would be hardly even be worth a PR on its own.

pez22:10:04

> Wasn't looking for a review, yet. Let me rephrase then: It will be easier to read the changes and to give feedback if this is sent as a PR. Use a draft PR to signal that you don't think it's ready for merge.

skylize22:10:13

Yep, yep. I got that. 😸 All I wanted was to make sure you weren't gonna be like "No. No. No. We're definitely not messing with all that."

Kari Marttila17:10:30

In Calva when I put my cursor above some Clojure function like range, Calva shows the clojure.core/range documentation in a pop-up window. Is it possible to get this documentation in a tab editor as in IntelliJ/Cursive? In IntelliJ/Cursive there is a nice feature called QuickDoc. With a hotkey association, by pressing the hotkey the first time IntelliJ shows the documentation in a pop-up window, but if you press the hotkey again, IntelliJ shows the documentation in a separate editor tab in which the whole document is more pleasent to read.

Kari Marttila17:10:56

Learning "Calva power user capabilities" 🙂

Kari Marttila18:10:10

Maybe I write another blog post with that name later on. 🙂

Kari Marttila18:10:00

Ah, I found something similar from @U04V70XH6 => https://github.com/seancorfield/vscode-calva-setup > clojuredocs.cljs -- with a Clojure symbol selected, this will open VS Code's Simple Browser, directly inside VS Code, at the corresponding ClojureDocs page. That will be an interesting exercise for me to try joyride 🙂

Kari Marttila18:10:41

Wow, wow, 🚀 to the 🌔 ! It works. Nice! Thanks @U0ETXRFEW and @U04V70XH6!

calva 1
pez18:10:43

There are also Calva commands for printing the ClojureDocs to the Output Window as well as to a Rich comment. These commands are available in the hover, and from the command palette. Here's a video showing the ClojureDocs -> RCF feature: https://www.youtube.com/watch?v=a4sm_B_mqhs We probably should build in something like @U04V70XH6 #joyride scripts into Calva.

❤️ 1
calva 1
Kari Marttila20:10:42

In Cursive there is one nice feature which is equivalent with Calva's Paredit: Kill/Delete Sexp Forward . If you have e.g.:

(def 
  ¤
  
  "Jee!")
... and if your cursor is in the position marked with ¤ above, and then you give the command Paredit: Kill/Delete Sexp Forward you first get:
(def
  ¤"Jee!")
... i.e. all whitespace gets deleted before the next sexp, and then if you give that command again:
(def
  ¤)
... i.e. the actual sexp gets killed. In Calva:
(def 
  ¤
  
  "Jee!")
=>
(def)
I guess this is up to taste. But I kinda like more Cursive's possibilty first to delete the whitespace and then the sexp (and also leaving the line-break). (`¤` is not part of the text in editor, just marking the cursor in the examples).

pez20:10:33

Calva has something similar with the command Paredit: Delete/Kill Right. Default keybinding ctrl+k.