Skip to content

Commit

Permalink
nrepl: add support for Figwheel Main
Browse files Browse the repository at this point in the history
  • Loading branch information
darwin committed Aug 31, 2019
1 parent bdb12c3 commit c57fd17
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 4 deletions.
6 changes: 4 additions & 2 deletions src/nrepl/dirac/nrepl/compilers.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[clojure.tools.logging :as log]
[dirac.lib.utils :as utils]
[dirac.nrepl.figwheel :as figwheel]
[dirac.nrepl.figwheel2 :as figwheel2]
[dirac.nrepl.sessions :as sessions]
[dirac.nrepl.state :as state])
(:import (java.util.regex Pattern)))
Expand Down Expand Up @@ -89,8 +90,9 @@
(let [session-compilers (state/get-session-compiler-descriptors session)
other-sessions-descriptors (sessions/get-other-sessions-descriptors session)
other-sessions-compilers (mapcat extract-session-compiler-descriptors other-sessions-descriptors)
figwheel-compilers (figwheel/collect-available-compiler-descriptors)]
(concat session-compilers other-sessions-compilers figwheel-compilers))) ; order is important here, we are matching compilers in this order
figwheel-compilers (figwheel/collect-available-compiler-descriptors)
figwheel2-compilers (figwheel2/collect-available-compiler-descriptors)]
(concat session-compilers other-sessions-compilers figwheel2-compilers figwheel-compilers))) ; order is important here, we are matching compilers in this order

(defn filter-available-matching-compiler-descriptors [session match]
(let [descriptors (collect-all-available-compiler-descriptors session)]
Expand Down
14 changes: 14 additions & 0 deletions src/nrepl/dirac/nrepl/controls.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns dirac.nrepl.controls
(:require [dirac.nrepl.compilers :as compilers]
[dirac.nrepl.figwheel :as figwheel]
[dirac.nrepl.figwheel2 :as figwheel2]
[dirac.nrepl.helpers :as helpers :refer [with-coalesced-output]]
[dirac.nrepl.messages :as messages]
[dirac.nrepl.sessions :as sessions]
Expand Down Expand Up @@ -214,6 +215,19 @@
(state/send-response! (utils/prepare-current-env-info-response))
response)))

; -- (dirac! :fig2) ---------------------------------------------------------------------------------------------------------

(defmethod dirac! :fig2 [_ & [api-name & args]]
; must not use with-coalesced-output, because builds can take longer time and user would not have feedback
(let [full-api-name (figwheel2/resolve-full-api-name api-name)
result (apply figwheel2/call-repl-api! full-api-name args)]
(let [response (case result
::figwheel2/not-found (error-println (messages/make-figwheel2-api-not-found-msg (str full-api-name)))
::figwheel2/not-callable (error-println (messages/make-figwheel2-bad-api-msg (str full-api-name)))
result)]
(state/send-response! (utils/prepare-current-env-info-response))
response)))

; -- default handler --------------------------------------------------------------------------------------------------------

(defmethod dirac! :default [action & _]
Expand Down
92 changes: 92 additions & 0 deletions src/nrepl/dirac/nrepl/figwheel2.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
(ns dirac.nrepl.figwheel2
"We are friends with Figwheel Main"
(:require [clojure.tools.logging :as log]
[clojure.string :as string]))

; TODO: would be nice to have some version checking (for sanity)
(def ^:dynamic *verbose* false)

(defn try-resolve-ns-symbol [ns-sym sym]
(try
(ns-resolve ns-sym sym)
(catch Throwable e
(when *verbose*
(log/trace e))
nil)))

(defn try-resolve-ns-var [ns-sym sym]
(let [v (try-resolve-ns-symbol ns-sym sym)]
(when (var? v)
(var-get v))))

(defn get-figwheel-build-registry []
(try-resolve-ns-var 'figwheel.main 'build-registry))

(defn get-all-figwheel-builds []
@(get-figwheel-build-registry))

(defn make-figwheel-compiler-id [build-id]
(str "figwheel.main/" (or build-id "?")))

(defn make-compiler-descriptor [figwheel-build]
(when-some [compiler-env (get-in figwheel-build [:repl-options :compiler-env])]
{:id (make-figwheel-compiler-id (:id figwheel-build))
:compiler-env compiler-env}))

(defn collect-available-compiler-descriptors []
(let [builds (get-all-figwheel-builds)]
(keep (fn [[_id build]] (make-compiler-descriptor build)) builds)))

(defn normalize-name [v]
(symbol (name v)))

(defn try-resolve-callable-api [qualified-sym-name]
(assert (qualified-symbol? qualified-sym-name))
(let [val (try-resolve-ns-symbol (symbol (namespace qualified-sym-name)) (symbol (name qualified-sym-name)))]
(when (var? val)
[(meta val) (var-get val)])))

(defn prepare-api-call-form [qualified-sym-name args]
`(do
(require (quote ~(symbol (namespace qualified-sym-name))))
(~qualified-sym-name ~@args)))

(defn macro? [var-descriptor]
(true? (:macro var-descriptor)))

(defn symbolize-name [n]
(cond
(symbol? n) n
(nil? n) (symbol "")
:else (symbol n)))

(defn non-blank [x]
(if-not (string/blank? x)
x))

(defn resolve-full-api-name [n]
(let [sym-name (symbolize-name n)
ns-name (or (non-blank (namespace sym-name)) "figwheel.main")
var-name (or (non-blank (name sym-name)) "status")]
(symbol ns-name var-name)))

(defn get-first-build []
(second (first (get-all-figwheel-builds))))

(defn evil-eval! [qualified-sym-name args]
(let [form (prepare-api-call-form qualified-sym-name args)
first-figwheel-repl-env (:repl-env (get-first-build))]
; binding cljs.repl/*repl-env* is needed for some figwheel calls, for example figwheel.repl/conns
; TODO: figure out a better way how to handle repl envs in multiple builds
(binding [cljs.repl/*repl-env* first-figwheel-repl-env]
(eval form))))

(defn call-repl-api! [full-api-name & args]
(assert (qualified-symbol? full-api-name))
(if-some [[var-descriptor var-value] (try-resolve-callable-api full-api-name)]
(cond
; in case of a macro we have to resort to evil eval
(macro? var-descriptor) (evil-eval! full-api-name args)
(fn? var-value) (apply var-value args)
:else ::not-callable)
::not-found))
10 changes: 10 additions & 0 deletions src/nrepl/dirac/nrepl/messages.clj
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@
"Please make sure you are loading the latest/expected Figwheel version in your nREPL server to prevent any mismatch:\n"
" => https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl"))

(defn ^:dynamic make-figwheel2-api-not-found-msg [api-name]
(str "Figwheel Main API '" api-name "' was not found.\n"
"Please make sure you have figwheel-main properly installed in your nREPL server:\n"
" => https://github.com/binaryage/dirac/tree/master/examples/figwheel-main"))

(defn ^:dynamic make-figwheel2-bad-api-msg [api-name]
(str "Figwheel Main API '" api-name "' is not a callable function/macro.\n"
"Please make sure you are loading the latest/expected Figwheel version in your nREPL server to prevent any mismatch:\n"
" => https://github.com/binaryage/dirac/tree/master/examples/figwheel-main"))

; -- joined session ---------------------------------------------------------------------------------------------------------

(defn ^:dynamic make-missing-nrepl-message-msg []
Expand Down
31 changes: 29 additions & 2 deletions src/nrepl/dirac/nrepl/usage.clj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
" `:match` list matching Dirac sessions"
""
" `:fig` Figwheel REPL API bridge"
" `:fig2` Figwheel Main REPL API bridge"
""
" `:version` print version info"
" `:help` print usage help"
Expand Down Expand Up @@ -164,7 +165,7 @@
" 1. `(dirac :fig)`"
" 2. `(dirac :fig api-fn & args)`"
""
"This is a bridge provided for convenince to allow controlling Figwheel directly from Dirac REPL."
"This is a bridge provided for convenience to allow controlling Figwheel directly from Dirac REPL."
""
"You may provide api-fn as a string, a keyword or a symbol. Figwheel API function is resolved dynamically."
"Function arguments must be specified precisely as expected by Figwheel API."
Expand All @@ -178,6 +179,31 @@
"Please refer to Figwheel docs for full list of control functions:"
" => https://github.com/bhauman/lein-figwheel#repl-figwheel-control-functions"])

(def ^:dynamic fig2-usage
["Call Figwheel Main REPL Controls API (if present)."
""
" 1. `(dirac :fig2)`"
" 2. `(dirac :fig2 api-fn & args)`"
""
"This is a bridge provided for convenience to allow controlling Figwheel directly from Dirac REPL."
""
"You may provide api-fn as a string, a keyword or a symbol. Figwheel API function is resolved dynamically."
"Function arguments must be specified precisely as expected by Figwheel API."
""
" Examples:"
" `(dirac :fig2 :status)` ; <= this is equivalent to `(dirac :fig)`"
" `(dirac :fig2 :clean)`"
" `(dirac :fig2 :reset \"my-build-id\")`"
" `(dirac :fig2 :build-once \"my-build-id\")`"
" `(dirac :fig2 :stop-builds \"my-build-id\")`"
" `(dirac :fig2 :start-builds \"my-build-id\")`"
""
" `(dirac :fig2 :figwheel.repl/conns)`"
" `(dirac :fig2 :figwheel.repl/focus \"session-name\")`"
""
"Please refer to Figwheel docs for full list of control functions:"
" => https://figwheel.org/docs"])

; -- public docs map --------------------------------------------------------------------------------------------------------

(defn render-usage [lines]
Expand All @@ -195,4 +221,5 @@
:switch (render-usage switch-usage)
:spawn (render-usage spawn-usage)
:kill (render-usage kill-usage)
:fig (render-usage fig-usage)})
:fig (render-usage fig-usage)
:fig2 (render-usage fig2-usage)})

0 comments on commit c57fd17

Please sign in to comment.