- Notifications
You must be signed in to change notification settings - Fork 50
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-fnwhich 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-strcall => so we unwrap it - above we provided unwrapping for Figwheel's REPL and IntelliJ's nREPL (yours can differ slightly)
- because
eval-fncan be called by Figwheel in different situations than REPL evaluations, we have to implement arepl-pluginwhich 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.