Fork me on GitHub

@djblue How's the VS Code work coming along? Any early .vsix files for eager pre-alpha testers to try out? 🙂


Pretty close to being done. I've been trying to get the syntax colors but I guess those aren't available to extensions 😭


I can share the 0.16.0 jar/vsix here if you would like to try it out 👌


To avoid some confusion: The vscode extension mainly acts as a launcher so you only have to have it installed, there aren't any commands you run from within vscode


(portal.api/open {:launcher :vs-code})


For a fun feature preview, try using the new keyboard shortcut g d on a selected var 😏


But it runs as a view inside VS Code?

👍 1

Yup, it should launch inside of vs-code


Finally got around to actually trying it -- AWESOME!!!


I've added a task to my (Clover) config that launches Portal inside VS Code and bound it to ctrl-; shift-p and I'm off to the races! 🙂

yes 1

My own customizations no longer seem to work (that were messing with those internal hash maps of functions) -- I guess that's been changed now and I need to customize it a different way?


OK, figured that out and updated my setup.


go to definition 🙂 very nice!


Seems like the webview panel inside VS Code isn't a full browser, in terms of CSS rendering? When I slurp a Java doc URL and view as HTML, it's pretty much an unstyled HTML page. Still usable, but not as clean as viewing docs in a browser.


I was going to add a portal.api/register! fn


So the barrier to entry is basically just any clj/s var


I'll look into the webview issue 👌


Cool. I'm just calling portal.runtime/register! right now but will switch to an API version once it's there. It's working really nicely for me. Once 0.16.0 is on Clojars, I'll publish my updated dot-clojure and vscode-clover-setup.

👍 1

Here's what I have in Clover's config.cljs:

(defn portal-start []
  (p/let [here (editor/get-selection)]
     (assoc here :text
            (str "(do"
                 " (in-ns 'dev)"
                 " (def portal"
                 "  ((requiring-resolve 'portal.api/open)"
                 "   {:launcher :vs-code}))"
                 " (install-portal-extras)"
                 " (add-tap (requiring-resolve 'portal.api/submit)))")))))

(defn portal-clear []
  (p/let [here (editor/get-selection)]
    (editor/eval-and-render (assoc here :text "(portal.api/clear)"))))
and here's my key bindings for that
        "key": "ctrl+; shift+k",
        "command": "workbench.action.tasks.runTask",
        "args": "Clover: Portal clear"
        "key": "ctrl+; shift+P",
        "command": "workbench.action.tasks.runTask",
        "args": "Clover: Portal start"
and the dev/install-portal-extras is
(defn- install-portal-extras
    (let [r!   (requiring-resolve 'portal.runtime/register!)
          html (fn [url]
                    {:style {:background :white}}
                    [:portal.viewer/html (slurp url)]]
                   {:portal.viewer/default :portal.viewer/hiccup}))]
      ;; install extra functions:
      (run! (fn [[k f]] (r! f {:name k}))
            {'dev/->file   (requiring-resolve ')
             'dev/->html   html
             'dev/->map    (partial into {})
             'dev/->set    (partial into #{})
             'dev/->vector (partial into [])}))
    (catch Throwable _)))


I originally had :portal.launcher/window-title (System/getProperty "user.dir") in the open invocation which works great for the Chrome browser version but seems to prevent the VS Code panel from opening? Is there a way to set the Portal tab name in VS Code? Or could it just ignore options that don't work?


Interesting, I'm able to use :portal.launcher/window-title (System/getProperty "user.dir") without issue :thinking_face:


OK, I'll try that again.

👍 1

OK, it was an unrelated issue: if I open a workspace in VS Code rather than just a project, the launcher doesn't open.


So something like code . then connect to a REPL then portal.api/open -- that works.


But code some.code-workspace then connect to the same REPL then portal.api/open -- nothing happens.


I don't have a workspace that has just one folder (project). All my workspaces have multiple projects (folders). So that seems to be confusing the VS Code launcher. If I run the regular launcher, it works, and I get a browser window.


Yeah, I can reproduce that reliably. code some.code-workspace can open a regular (Chrome) Portal window but cannot open a VS Code window. The code "runs" but doesn't open a window.


portal.api/open returns #portal.runtime.jvm.client.Portal {:session-id #uuid "9807ae4d-5d75-489f-816f-2eeb5e1768d0"} as expected but nothing opens in VS Code.


((requiring-resolve 'portal.api/open)) ; works in standalone project and workspace
  ((requiring-resolve 'portal.api/open) {:launcher :vs-code}) ; works in standalone project but NOT in a workspace
Hope that helps @djblue LMK if I can do more testing for you.

👍 1

That makes sense, I think my assumption of a root level folder is broken then.


I'll see if I can fix the launcher for workspaces 👌


Does those separate projects represent separate processes / repl connections? Or just multiple libraries as part of a single process?


(defn- get-workspace-folder []
  (-> vscode/workspace
      (aget 0)
      (.. -uri -path)))
This is the bit that's problematic in the vs-code extension


If it helps, I figured out that if I copy .portal/vs-code.edn from the workspace root folder (where it is created once Portal is activated) to the folder where the REPL is started, then it works.

👍 1

So it seems to get confused by the fact that the REPL is running in a different folder to the workspace root (which will just happen to be the first folder in the workspace).


Yeah, I might invert the relationship and have the repl process drop a file and the extension watch for it in all workspaces folders :thinking_face:


That won't work for setups where the REPL is started in a different folder -- that is not a root folder.


At work, we have a workspace with two folders: wsmain and worldsingles. But the REPL is always started in wsmain/clojure.


Interesting :thinking_face:


In the general case, I may well start a REPL in a folder that is completely outside any VS Code workspace.


The other idea I had was limiting the vs-code http-server port range and doing a broadcast from the repl process


So the server process started in Portal (as an extension) is a VS Code HTTP server? Interesting.


Well there are two. One in the extension waiting for a call to open a webview and another in the repl process hosting everything else.


Oh, I guess I was confused by the add-tap in the extension code... That's for you to debug the extension?

👍 1

Yeah, I was initially playing with everything inside the vs-code extension process


Well, I guess for my use case, it would work if the JVM launcher walked up the file system looking for .portal?


That would at least allow a REPL to be started in a subfolder which is common in monorepo situations.


That's interesting :thinking_face:


Is it common the have two process for a single mono repo? I wonder if one repl would clobber another


In the more general case, I don't think Portal could possibly know how to bridge a REPL started in an arbitrary different folder tho'...


For monorepos, you typically have just one REPL. It just isn't always started in the root.

👍 1

But I can imagine people having multiple Clojure projects in a workspace and having multiple REPLs -- one from each Clojure project -- but Calva/Clover is only going to be connected to one of them at any point in time.


If I leave the .portal/vs-code.edn file around (and .gitignore it), will Portal always just reuse that server/port?


Not currently, but it could. Although I do thing the parent walking trick should be easy enough to do


OK, so one problem you'd have is that if you started two instances of VS Code -- one in a folder and one for a workspace for which that is the root folder -- it would overwrite the vs-code.edn file in that folder.


And in fact if you reload VS Code, it reactivates the extensions and would overwrite the workspace root vs-code.edn file anyway with a new port number -- and then the REPL still running would try to talk to the old server which has gone away, right?


So I think you need to have the extension code reuse vs-code.edn to avoid that scenario?


(I reload/restart VS Code a lot more often than I restart a REPL)


Would I have to do portal.api/close on the JVM every time I restarted VS Code in order to clear the old server information and get the JVM Portal to connect to the (new) VS Code server?


The vs-code.edn file is only used for the initial portal.api/open function. So if you restart and a new file is available and you call portal.api/open again it should be wired against the new instance of vs-code.


A jar with the latest code if you are down to try it out again


I did a symlink between the two folders to test what happens if I reload VS Code (and that puts a different port into vs-code.edn) but leave the REPL running -- and the answer is: that works just fine, so the REPL does not "lose" the Portal connection.

💯 1

I'll remove the symlink and try that updated JAR next.


Perfect! Yup, that finds the .portal/vs-code.edn up the file tree and works "as expected". Thank you!

awesome 1

I'll have to think about what to do with the multi-project workspace thing. I suspect I can solve that with symlinks (so all the REPL-based projects in the workspace talk to the same VS Code Portal instance...).


Thanks for making sure the extension works!


With the multi-project workspace, it seems like I need to open a separate Portal view in VS Code for each backend REPL, if I want to switch between them -- because there's no way to just tell the backend "Oh, yeah, Portal is open and it's on port XYZ". But that's fine. I can definitely live with that (and it is a very unusual setup).


(and that was part of why I wanted the Portal window title to include the directory where the open call was made so I could keep track of which Portal was attached to which REPL anyway).

💯 1

(and, yes, that's with symlinks in place for all the folders in the workspace, pointing to a "master" .portal folder elsewhere 🙂 )


Yeah, currently the portal client ui can only handle a connection to a single runtime. In the future I might remove this limitation


Clover can only connect to one REPL so that's fine.


Here's what my OSS project workspace looks like with two REPLs in the background (for honeysql and next.jdbc) and two Portal views:


Ahh, now I get why you use workspaces 💯

👀 1

One more question if you have the time. Would you rather specify :launcher :vs-code or have portal assume you want to use vs-code if it can find the .portal/vs-code.edn file?


I would rather it was explicit. I may still want to launch a browser-based Portal window sometimes.

👍 1