VSCode/Calva

VSCode / Calva with side window for REPL output.

Introduction

In my previous blog posts Configuring VSCode/Calva for Clojure programming and Configuring VSCode/Calva for Clojure programming - Part 2 I did some basic VSCode/Calva configurations. In this new blog post I’m describing how to streamline connecting to the REPLs.

I am using my Github 4Clojure exercises repo as an example. I do the 4clojure.oxal.org exercises for building my Clojure programming skills. I highly recommend these exercises, or the equivalent PEZ/rich4clojure exercises. The reason that I use the first one at the moment is that I just didn’t know about the rich4clojure repo at the time when beginning doing those exercises. I have written another blog post regarding doing the 4clojure exercises, you might want to read it as well: 4Clojure Exercises Part 1.

Why Do I Need Both Clojure and Clojurescript REPLs?

The reason I’m starting both Clojure JVM REPL and Clojurescript Node REPL is that although I do the exercises in the Clojure JVM REPL side, 4clojure.oxal.org runs the exercises using Clojurescript. Most of the solutions created in the Clojure JVM side work just fine, but occasionally some functions like abs (the absolute value of a number) does not work in the 4clojure.oxal.org tester. In these cases I have used the Clojurescript Node REPL to test my solution (e.g. in this case (.abs js/Math x)).

Start Clojure and Clojurescript REPLs

I might be a bit of an old-timer, but I like to start the REPLs in the terminal and then connect to the REPLs in the editor, instead of starting the REPL as part of the editor process (i.e. as a Jack-in).

I instructed in my previous blog post 4Clojure Exercises Part 1 how to start the REPLs, but let’s iterate it here once again (you need just or copy-paste the bash commands from the Justfile):

Open three terminals. In the first terminal run just backend. In the second terminal run just init, then just shadow-node, and wait until the build is ready. In the third terminal run just run-node. Now you are ready to connect your IDE’s REPL integration both to the Clojure on JVM process and to the Clojurescript on Node process.

REPL Connect Sequences

Calva provides nice customization to the REPL Connect Sequences. I created a couple of connect sequences to make connecting to the clojure backend and clojurescript frontend REPLs faster:

    "calva.replConnectSequences": [
      {
        "name": "clojure-backend",
        "nReplPortFile": [".nrepl-port"],
        "projectType": "deps.edn",
        "cljsType": "none"
      },   
      {
        "name": "clojurescript-frontend",
        "projectType": "shadow-cljs",
        "cljsType": {
            "dependsOn": "shadow-cljs",
            "connectCode": "(shadow.cljs.devtools.api/repl :app)",
        }
      }
    ]

Now in VSCode I just have to give commands (I have a keyboard shortcut for Calva: Connect to a Running REPL Server in the Project, of course):

  • Calva: Connect to a Running REPL Server in the Project => Choose clojure-backend, next for the suggested host:port press Enter.
  • Calva: Connect to a Running REPL Server in the Project => Choose clojurescript-frontend, next for the suggested host:port press Enter.

BUT: In the above mentioned configuration you can either connect to the backend REPL or to frontend REPL, but not to both REPLs at the same time, since Calva can only connect to one REPL at the time. Therefore, you have two choices:

  1. Open two VSCode windows in the same project, one for the backend and one for the frontend, as explained in the Workspace Layouts Calva documentation. Connect to those two REPLs respectively in those two VSCode windows.
  2. Start shadow-cljs REPL and let it start Clojure REPL, and have one Calva connection to the shadow-cljs REPL and via it to the Clojure REPL as well.

Later on, in the Clojurians Slack, regarding option 2, Peter Strömberg kindly told me that if I’m using using shadow-cljs with deps.edn, I can simplify the above configuration like:

    "calva.replConnectSequences": [
      {
        "name": "backend + frontend",
        "projectType": "shadow-cljs",
        "cljsType": "shadow-cljs",
        "menuSelections": {
          "cljsLaunchBuilds": [
            ":app",
            ":test",
          ],
          "cljsDefaultBuild": ":app"
        }
      }
    ]

… and then: Calva: Connect to a Running REPL Server in the Project => Choose backend + frontend, next for the suggested host:port press Enter. That’s it. Your editor is connected to the frontend Clojurescript REPL running in the project, and the backend REPL is started by shadow-cljs.

If this is a bit confusing, I recommend to read more about how Calva handles REPL connections, e.g.:

Run Clojure Snippets

I use often Integrant for state managamenent in my Clojure applications. When you configure all your state (web server, db connections) using Integrant it is really easy to reset the state e.g. after some code changes. In my Cursive setup I have keyboard shortcut alt+j for calling Integrant reset. Therefore I added the same keyboard shortcut also in my Calva setup:

  {
    "key": "alt+j",
    "command": "calva.runCustomREPLCommand",
    "when": "calva:connected && calva:keybindingsEnabled && editorLangId == 'clojure'",
    "args": {
      "ns": "user",
      "snippet": "(integrant.repl/reset)",
    }
  },

Conclusions

I really like that VSCode and Calva are very configurable. You can create your REPL environments the way you like and it is fast either to use Jack-in or connect to a REPL running outside the editor.

Kari Marttila