Vim
Quick Setup
If you don’t already have vim-fireplace setup head on over to the Github project and follow the installation guide.
To demonstrate how all the pieces fit together we’ll create a simple, minimal project with Leiningen.
Later on we’ll describe how to use the Clojure CLI and rebel-readline.
We’ll name our project fullstack. It will contain the following files and folders:
├── dev.cljs.edn
├── project.clj
├── resources
└── src
├── cljs
│ └── user.cljs
└── fullstack
├── main.clj
└── main.cljs
The following is a minimal Figwheel dev config, dev.cljs.edn
:
^{:watch-dirs ["src"]}
{:main fullstack.main}
Create a resources
folder. We’ll leave it empty for now.
Place the following in project.clj
:
(defproject fullstack "0.1"
:description "A minimal sample full-stack Clojure(Script) website"
:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.10.773"]]
:plugins [[cider/cider-nrepl "0.24.0"]]
:profiles {:dev
{:dependencies [[org.clojure/clojurescript "1.10.773"]
[com.bhauman/figwheel-main "0.2.18"]
[cider/piggieback "0.4.2"]]
:resource-paths ["target"]
:clean-targets ^{:protect false} ["target"]
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}}})
src/fullstack/main.clj
will be the entry-point into the server-side
(Clojure) application . We can start out with a minimal file:
(ns fullstack.main)
(defn -main []
(println "Hello from CLJ main"))
src/fullstack/main.cljs
will be the entry-point into the client-side
(ClojureScript) application. We can just start with:
(ns fullstack.main)
(println "Hello from CLJS main")
Starting the REPLs
Now we can start the Clojure REPL with:
$ lein repl
An nREPL server will automatically start. Verify that fireplace is able
to communicate with it by eval
ing something. E.g. you can place your
cursor over a symbol in Vim and press K
to see its doc string. If
you get an immediate result, you have successfully connected to the
nREPL server.
Now we can start Figwheel:
user=> (require 'figwheel.main.api)
user=> (figwheel.main.api/start {:mode :serve} "dev")
Some log messages will appear including something like:
[Figwheel] Starting Server at http://localhost:9500
Open your browser to this URL. This is the default Figwheel dev host page. A green “Connected” image should appear at the top-left indicating a successful connection between the browser and Figwheel.
Back in the Clojure REPL, we’re now ready to start a ClojureScript REPL within.
Be careful not to
eval
anything in a ClojureScript file from Vim yet. If you do this before connecting Piggieback, fireplace may start up its own ClojureScript REPL, disjoint from the browser!
Start the ClojureScript REPL:
user=> (figwheel.main.api/cljs-repl "dev")
Test the connection to the browser with an alert:
user=> (js/alert "Hello from the ClojureScript REPL")
If you see a pop-up dialog in your browser, it worked!
You must start this ClojureScript REPL before Piggieback can connect. Unlike other ClojureScript REPLs that Piggieback can start entirely on its own given just an nREPL port, the ClojureScript REPL within Vim relies on both nREPL and this primary ClojureScript REPL.
Now let’s connect fireplace. Run this command in Vim while in a ClojureScript buffer:
:Piggieback (figwheel.main.api/repl-env "dev")
This should return immediately if successful.
Let’s ensure we’re able to communicate with the browser from Vim. Add
the following in a ClojureScript file and eval it (cpp
):
(js/alert "Hello from Vim")
If that worked, we’re all set!
Rebel-Readline, Clojure CLI, Deps
If you’re happy with the workflow described above using Leiningen, you don’t have to read any further.
What’s described here is optional and for those who prefer to use the Clojure CLI, deps.edn and rebel-readline over Leiningen.
For the most part, we’ll be editing our .clj
, .cljs
and .cljc
files in Vim. vim-fireplace provides most of the functionality we need
for this purpose, however it’s
often necessary and even desirable
to interact directly with a “true” REPL - not the spartan “Quasi-REPL”
provided by fireplace.
Leiningen provides a pretty good REPL experience with tab-complete, etc. but rebel-readline takes things to the next level including syntax highlighting, inline docs, and particularly useful to Vim enthusiasts: Vim movements and custom key bindings!
Rebel-readline is able to work with Leiningen, but we’ll describe how to use it with the simpler Clojure CLI.
Why not just use Leiningen? See here for some rationale on why you might prefer using the native Clojure CLI and deps.edn.
So here’s a minimal deps.edn
(analogous to the previous project.clj
):
{:aliases
{:rebel {:main-opts ["-m" "rebel-readline.main"]}}
:paths ["src" "resources" "target"]
:deps
{org.clojure/clojure {:mvn/version "1.9.0"}
org.clojure/clojurescript {:mvn/version "1.10.773"}
com.bhauman/figwheel-main {:mvn/version "0.2.18"}
com.bhauman/rebel-readline {:mvn/version "0.1.4"}
com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}
org.clojure/tools.nrepl {:mvn/version "0.2.13"}
cider/cider-nrepl {:mvn/version "0.17.0"}
cider/piggieback {:mvn/version "0.3.8"}}}
Let’s create a helper namespace to make it easier to start and stop the
nREPL server. Place the following in src/fullstack/helpers.clj
:
(ns fullstack.helpers
"Helpers around starting/stopping an nREPL server."
(:require [clojure.tools.nrepl.server :as nrepl-server]
[clojure.java.io :as io]))
(def nrepl-port 7888)
(defonce nrepl-server (atom nil))
(defn nrepl-handler []
(require 'cider.nrepl)
(ns-resolve 'cider.nrepl 'cider-nrepl-handler))
(defn start-nrepl-server! []
(reset!
nrepl-server
(nrepl-server/start-server :port nrepl-port
:handler (nrepl-handler)))
(println "Cider nREPL server started on port" nrepl-port)
(spit ".nrepl-port" nrepl-port))
(defn stop-nrepl-server! []
(when (not (nil? @nrepl-server))
(nrepl-server/stop-server @nrepl-server)
(println "Cider nREPL server on port" nrepl-port "stopped")
(reset! nrepl-server nil)
(io/delete-file ".nrepl-port" true)))
Now we can start the Clojure REPL with rebel-readline:
$ clojure -A:rebel
And start the nREPL server:
user=> (require '[fullstack.helpers :refer :all])
user=> (start-nrepl-server!)
Now vim-fireplace can talk to the REPL. Verify this by eval
ing
something from Vim.
The remaining steps are just like what we did earlier with our
Leiningen setup. See this section (skipping the
lein repl
step of course).
That’s it. You now have a rebel-readline REPL (via the Clojure CLI and deps.edn) with no Leiningen in sight.
Dedicated Clojure REPL
With the two setups describe above, our REPL is left in a state suited to working in a ClojureScript environment. The Clojure environment is still there of course, but we can’t easily work with our Clojure code-base directly at the REPL.
But as we already have an nREPL server running we can just launch another REPL and connect to our running environment. Simply run:
$ lein repl :connect
Unfortunately rebel-readline doesn’t currently support working within an nREPL session. So we have to use Leiningen (or Boot if you prefer) for this second REPL instance.
Now we have a dedicated REPL for working with Clojure.