-
Notifications
You must be signed in to change notification settings - Fork 51
Figwheel REPL plugin
Antonin Hildebrand edited this page Dec 19, 2016
·
8 revisions
This method is obsolete, check out Dirac DevTools
With cljs-devtools installed, this allows you to:
- navigate native javascript values in devtools console
- drill down more complex cljs data structures interactively
- you want to run Figwheel REPL with rlwrap - this will give you history, bracket matching and other great enhancements
- it is handy to have REPL terminal available on a global keyboard shortcut - for example "Hotkey Window" in iTerm2
- Figwheel supports nREPL, so you can connect to its REPL remotely. I did this with IntelliJ and it worked like a charm. This way you can get additional sugary features from your IDE.
By default Figwheel REPL is silent on browser side. Luckily, thanks to Figwheel configurability, you are allowed to specify a custom REPL plugin. Let's implement a REPL plugin which will echo evaluated expressions entered in REPL into devtools javascript console (to be presented by cljs-devtools).
Here is an example implementation:
(ns your-project.figwheel
(:require [figwheel.client :as figwheel]))
(defonce ^:const repl-marker-style "color:white; background-color:black; padding:0px 2px; border-radius:1px;")
(defonce ^:const figwheel-pattern #"^\(function \(\)\{try\{return cljs\.core\.pr_str\.call")
(defonce ^:const figwheel-replacement "(function (){try{return cljs.core.identity.call")
(defonce ^:const intellij-pattern #"^try\{cljs\.core\.pr_str\.call")
(defonce ^:const intellij-replacement "try{cljs.core.identity.call")
(defonce ^:dynamic *inside-repl-plugin* false)
(defn should-be-ignored? [code]
(boolean (.match code #"^goog\.(addDependency|require|provide)"))) ; for some reason we are getting goog.* calls from figwheel inside repl plugin
(defn detect-repl-kind [code]
(cond
(.match code figwheel-pattern) :figwheel
(.match code intellij-pattern) :intellij
:else :unknown))
(defn unwrap-code [repl-kind code]
(case repl-kind
:figwheel (.replace code figwheel-pattern figwheel-replacement)
:intellij (.replace code intellij-pattern intellij-replacement)
code))
(defn wrap-result [repl-kind result]
(case repl-kind
:figwheel (pr-str result)
:intellij (pr-str result)
result))
(defn eval [code]
(js* "eval(~{code})"))
(defn echo-result [result]
(.log js/console "%cREPL" repl-marker-style result))
(defn eval-with-echoing [code]
(let [repl-kind (detect-repl-kind code)
rewritten-code (unwrap-code repl-kind code)
result (eval rewritten-code)]
(echo-result result)
(wrap-result repl-kind result)))
(defn echoing-eval [code]
(if (and *inside-repl-plugin* (not (should-be-ignored? code)))
(eval-with-echoing code)
(eval code)))
(defn repl-plugin [& args]
(let [standard-impl (apply figwheel/repl-plugin args)]
(fn [& args]
(binding [*inside-repl-plugin* true]
(apply standard-impl args)))))
(defn start-figwheel []
(figwheel/start
{; your config goes here...
:eval-fn echoing-eval
:merge-plugins {:repl-plugin repl-plugin}}))
- we are hijacking evaluation by specifying our own
eval-fn
which does the echoing - echoing is achieved by rewriting incoming javascript code snippets
- code snippets are produced by REPL backends and may differ between nREPL implementations
- usually a raw expression is just wrapped in
pr-str
call => so we unwrap it - above we provided unwrapping for Figwheel's REPL and IntelliJ's nREPL (yours can differ slightly)
- because
eval-fn
can be called by Figwheel in different situations than REPL evaluations, we have to implement arepl-plugin
which marks situations when eval-fn is being called from*inside-repl-plugin*
The above code is meant to be an example for you to implement your own version. It worked for me with Figwheel 0.5.0-SNAPSHOT, but I believe it should be compatible with official 0.4.1 release as well. It is likely that this code will break in future. I have existing maintained implementation in the plastic project. Feel free to steal it.