Fork me on GitHub
#portal
<
2023-03-30
>
caleb.macdonaldblack18:03:46

Is there a way to pin something in portal? When debugging I often use def within code and it’s easy to evaluate it for the last value that was defined. This is better than logging or printing for cases where that code may be executed repeatedly and spamming my logs. Is this something that could be achieved with portal? Maybe even with changes?

djblue18:03:00

Is the idea to use the value at the repl later or that you want to visually compare it to another?

djblue19:03:17

You can pull a selected value back into the repl via derefing the portal atom which is returned from portal.api/open

djblue19:03:28

(def p (portal.api/open))
;; select value in ui
(def selected @p)

djblue19:03:07

(ns user
  (:require [portal.api :as p]))

(def values (atom []))

(defn push [& args]
  (swap! values into args))

(p/register! #'push)
☝️ would allow you to collect as many values as you want over time. Just select the value or multiple values and call your custom command user/push via the command palette

seancorfield19:03:00

I have tried to write some fairly fancy code to ensure that the most recently tap>'d value always ends up as the most recent value in Portal with any middleware tap>'d values below it, so that I can programmatically get at that value from my editor. I don't want to have to interact with the Portal UI in general to select anything. I want to drive it all from my editor. And that requires a degree of predictability of where values are in the UI in order to get at them programmatically. I pretty much never want to interact with some arbitrary result produced by evaluation middleware -- but I want to drive Portal's UI from my editor to cause interactions with the most recent interesting value. In many ways, it would be ideal for me to have middleware eval output go into a separate panel in Portal and have just my direct tap> values go into the regular panel so the most recent was always what I explicitly tap>'d. I want the Portal UI to be more "drivable" by code, overall :)

seancorfield19:03:37

TL;DR: it's not so much about having a specific value in Portal accessible to the program, as having the program be able to drive the UI in Portal around a specific value.

seancorfield19:03:48

For example, I commonly want to cycle through viewers programmatically for the "most recently tap>'d value" but I want to ignore any middleware-produced data for that. Seeing that output is useful, and having the option to switch to the Portal UI and interact with it is nice -- and sometimes necessary -- but not my primary workflow when debugging. I have logging wired up to tap> into Portal, for example, and I already have a hot key set up to toggle that wiring on and off so I can eval a specific piece of code and not have logging clutter up Portal's UI, even tho' in general logging is useful. And I may end up doing something similar for middleware output, but it's still a pain to have to do this manually around specific evaluations. Having two separate sections in the UI for this would be my ideal choice: and then I could have logging and middleware go to the "secondary" panel and my explicit tap> results go to the "primary" panel -- and then programmatically interact with just that primary panel. Being able to start multiple VS Code launcher/viewer instances from a single JVM might solve this too -- not sure if that's possible?

djblue19:03:12

@U04V70XH6 would something like the following fit your workflow better?

(def tap-list (atom (list)))
(def eval-list (atom (list)))

(defn submit [value]
  (if (:portal.nrepl/eval value)
    (swap! eval-list conj value)
    (swap! tap-list conj value)))

(add-tap submit)

(portal.api/open
 {:window-title "tap-list"
  :value tap-list})
(portal.api/open
 {:window-title "eval-list"
  :value eval-list})

seancorfield19:03:43

Can I have two VS Code windows open?

djblue20:03:13

Yeah, you should be able to have multiple.

seancorfield20:03:33

That's cool... let me try that...

djblue20:03:36

And portal can be opened with any value, including atoms you can drive

djblue20:03:30

The tap-list that is loaded into portal by default is:

(defonce ^:private tap-list
  (atom (with-meta (list)
          {:portal.viewer/default :portal.viewer/inspector})))

seancorfield20:03:40

My setup is somewhat complicated by having my Portal start code be part of my Calva custom REPL snippets with logging wired up elsewhere but let me spend an hour playing with this... I had no idea I could manage my own tap list like that...

seancorfield20:03:22

What will the portal.api/* functions do when there are multiple instances?

djblue20:03:05

I think they do a broadcast, they should have an arity which includes passing in a specific instance

seancorfield20:03:28

And that "specific instance" is what comes back from open?

πŸ‘ 2
djblue20:03:36

close and clear are really the only ones that care

seancorfield20:03:12

Well, I have a lot of this sort of stuff:

{:name "Portal Viewer"
   :key "0"
   :snippet (portal.api/eval-str
             (str
              '(let [state portal.ui.state/state]
                 (-> (portal.ui.commands/select-none state)
                     (.then #(portal.ui.commands/select-child state))
                     (.then #(portal.ui.commands/select-child state))
                     (.then #(portal.ui.commands/select-next-viewer state))
                     (.then #(portal.ui.commands/select-none state))))))}

djblue20:03:43

eval-str also takes an instance πŸ‘Œ

seancorfield20:03:46

Ah, it accepts portal too... OK, this is looking very promising πŸ™‚

djblue20:03:04

Ohh, also I added {:await true} to eval-str so you can block on promises if you still wanted to use something like that

djblue20:03:55

(portal.api/eval-str portal "(.resolve js/Promise :hi)" {:await true}) => :hi

seancorfield20:03:40

OMG! THIS IS SO AWESOME!! This dramatically simplifies my Portal setup and provides so much more value!!!

awesome 2
πŸŽ‰ 4
bananadance 2
seancorfield20:03:37

Interesting tap>'d values on top; middleware output below:

djblue20:03:41

That's looking very nice πŸ‘Œ I think we have portal setup similarly for logging / taps at work

djblue20:03:47

Sorry I didn't mention it earlier

seancorfield20:03:42

The "danger" of an infinitely flexible toolchain is that you can almost never know what is possible πŸ™‚

πŸ’― 2
clojure-spin 2
djblue20:03:04

I think the upcoming notebook updates are gonna make things even more flexible / fun for vscode users

djblue22:03:44

Ohh, if your eval-str commands return :portal.api/ignore the nrepl middleware will not tap them πŸ‘Œ

djblue22:03:58

Also, the code does look much nicer awesome

seancorfield22:03:28

Good to know about :portal.api/ignore (allow that seems a strange value for the nREPL middleware to care about -- I'm surprised it isn't :portal.nrepl/ignore πŸ™‚ )

djblue22:03:00

That would be better, but I have some custom socket repl based tooling that I use that needs the same behavior so I use that keyword for both

djblue22:03:42

I could use both if it makes more sense :thinking_face:

seancorfield22:03:42

I updated my config.edn to use that. It caught another case I wasn't handling, so that's helpful.

seancorfield23:03:00

For folks using my https://github.com/seancorfield/vscode-calva-setup repo -- or interested in a "real-world" VS Code/Calva/Portal setup -- I've updated the custom REPL snippets (which drive Portal in my setup) to run two Portal windows in VS Code: one for middleware output (which looks mostly like logging) and one for dedicated tap> output, such as when you're debugging code or doing explicit ctrl+alt+t space tap> evals on code, so that you can view and interact with them separately: my custom REPL snippets (`ctrl+alt+space <key>`) all operate on the dedicated tap> output window, and you can "lift" the most recent middleware result into the tap> window (`ctrl+alt+space l`). In addition, if you're using my https://github.com/seancorfield/dot-clojure repo and the :dev/repl alias there, logging from clojure.tools.logging is also routed into the middleware output window now, rather than the main tap> window. I want to reiterate my gratitude gratitude gratitude for all the work @djblue puts in on Portal and for creating such an amazingly flexible and powerful tool!

❀️ 10
gratitude 2
djblue05:03:40

It's really exciting seeing the evolution of how you utilize Portal in your workflow so thanks for taking the time to share your experiences ❀️

seancorfield05:03:49

I need to write another blog entry πŸ™‚