Skip to content

Commit

Permalink
Merge pull request #83 from mauricioszabo/shadow-custom-commands
Browse files Browse the repository at this point in the history
Shadow custom commands
  • Loading branch information
mauricioszabo committed Jun 25, 2020
2 parents 82e11b5 + a928c50 commit 1eb079a
Show file tree
Hide file tree
Showing 29 changed files with 176 additions and 148 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,8 @@
# CHANGELOG

## 0.5.1
- Shadow-CLJS can call its own internal API by passing a specific command

## 0.5.0
- Performance improvement while parsing Clojure code
- Runnable config
Expand Down
2 changes: 1 addition & 1 deletion project.clj
@@ -1,4 +1,4 @@
(defproject repl-tooling "0.5.0"
(defproject repl-tooling "0.5.1"
:dependencies [[org.clojure/core.async "0.4.490"]
[com.cognitect/transit-cljs "0.8.264"]
[reagent "0.10.0"]
Expand Down
20 changes: 10 additions & 10 deletions src/repl_tooling/commands_to_repl/all_cmds.cljs
Expand Up @@ -32,32 +32,32 @@
(let [[_ namespace] (helpers/ns-range-for contents (first eval-range))]
(e-eval/eval-cmd state code namespace eval-range data opts)))))

(defn- eval-block [state data opts]
(defn- eval-block [state data]
(p/let [d data]
(eval-range state d opts helpers/block-for)))
(eval-range state d {} helpers/block-for)))

(defn- eval-top-block [state data opts]
(defn- eval-top-block [state data]
(p/let [d data]
(eval-range state d opts helpers/top-block-for)))
(eval-range state d {} helpers/top-block-for)))

(defn- eval-selection [state data opts]
(defn- eval-selection [state data]
(p/let [{:keys [range] :as d} data]
(eval-range state d opts (fn [contents _]
[range (helpers/text-in-range contents range)]))))
(eval-range state d {} (fn [contents _]
[range (helpers/text-in-range contents range)]))))

(defn all [state {:keys [editor-data] :as opts} repl-kind]
(p/let [orchard-cmds (orchard/cmds state)
config-file (-> @state :editor/callbacks :config-file-path)]
(cond->
{:evaluate-top-block {:name "Evaluate Top Block"
:description "Evaluates top block block on current editor's selection"
:command #(eval-top-block state (editor-data) opts)}
:command #(eval-top-block state (editor-data))}
:evaluate-block {:name "Evaluate Block"
:description "Evaluates current block on editor's selection"
:command #(eval-block state (editor-data) opts)}
:command #(eval-block state (editor-data))}
:evaluate-selection {:name "Evaluate Selection"
:description "Evaluates current editor's selection"
:command #(eval-selection state (editor-data) opts)}
:command #(eval-selection state (editor-data))}
:run-tests-in-ns {:name "Run tests in NS"
:description "Run all tests on the current namespace"
:command #(e-eval/run-tests-in-ns! state)}
Expand Down
2 changes: 1 addition & 1 deletion src/repl_tooling/commands_to_repl/orchard.cljs
@@ -1,6 +1,6 @@
(ns repl-tooling.commands-to-repl.orchard
(:require-macros [repl-tooling.repl-client.clj-helper :as h])
(:require [repl-tooling.eval :as eval]
[repl-tooling.repl-client.clj-helper :as h]
[repl-tooling.editor-helpers :as helpers]
[clojure.string :as str]
[repl-tooling.editor-integration.evaluation :as e-eval]
Expand Down
9 changes: 5 additions & 4 deletions src/repl_tooling/editor_integration/connection.cljs
Expand Up @@ -37,15 +37,16 @@
data
(assoc opts :pass pass)
(constantly [range code])))))
:evaluate-and-render (fn [{:keys [text range pass]}]
(p/let [data (editor-data)]
:evaluate-and-render (fn [options]
(p/let [data (editor-data)
{:keys [text range]} options]
(cmds/eval-range state
data
(assoc opts :pass pass)
(dissoc options :text :range)
(constantly [range text]))))
:eval (fn [options]
(let [code (:text options)
[[row col]] (:range opts)
[[row col]] (:range options)
eval-opts (cond-> (dissoc options :text)
row (assoc :row row)
col (assoc :col col))]
Expand Down
2 changes: 1 addition & 1 deletion src/repl_tooling/editor_integration/doc.cljs
@@ -1,6 +1,6 @@
(ns repl-tooling.editor-integration.doc
(:require-macros [repl-tooling.repl-client.clj-helper :refer [contents-for-fn]])
(:require [repl-tooling.editor-helpers :as helpers]
[repl-tooling.repl-client.clj-helper :refer [contents-for-fn]]
[promesa.core :as p]
[repl-tooling.eval :as eval]
[repl-tooling.editor-integration.evaluation :as e-eval]))
Expand Down
19 changes: 9 additions & 10 deletions src/repl_tooling/editor_integration/evaluation.cljs
Expand Up @@ -60,16 +60,16 @@
(treat-error #(cmds/run-callback! state :notify %) cljs? (:clj/repl @state))
repl)))

(s/defn eval-cmd [state
(s/defn eval-cmd [state :- schemas/EditorState
code :- s/Str
namespace
range :- schemas/Range
editor-data :- schemas/EditorData
opts]
opts :- schemas/EvalOpts]
(when code
(let [prom (p/deferred)
filename (:filename editor-data)
{:keys [on-start-eval on-eval]} opts
{:keys [on-start-eval on-eval]} (:editor/callbacks @state)
[[row col]] range
;; TODO: Remove UNREPL and always evaluate on primary
repl (repl-for state filename (-> opts :pass :aux))
Expand All @@ -81,13 +81,12 @@
(and on-start-eval (on-start-eval eval-data))
(eval/evaluate repl
code
{:filename filename
:id id
:row (inc row)
:col (inc col)
:namespace namespace
;; FIXME: this is kinda bad, we're re-using opts...
:pass (:pass opts)}
(assoc opts
:filename filename
:id id
:row (inc row)
:col (inc col)
:namespace namespace)
#(when (and on-eval @state)
(let [parsed (helpers/parse-result %)]
(on-eval (assoc eval-data
Expand Down
7 changes: 3 additions & 4 deletions src/repl_tooling/editor_integration/schemas.cljs
Expand Up @@ -99,17 +99,16 @@
(s/optional-key :column) s/Int
(s/optional-key :pass) {s/Any s/Any}
(s/optional-key :ignore) s/Bool
(s/optional-key :aux) AuxOptions})
(s/optional-key :aux) AuxOptions
s/Keyword s/Any})

(def PromisedEvalOpts (assoc EvalOpts
:text s/Str
(s/optional-key :auto-detect) s/Bool))

(def EditorFeatures {:autocomplete s/Any
:eval-and-render (s/=> s/Any s/Any s/Any s/Any)
:evaluate-and-render (s/=> s/Any {:text s/Str
:range Range
(s/optional-key :pass) s/Any})
:evaluate-and-render (s/=> s/Any PromisedEvalOpts)
:eval (s/=> s/Any PromisedEvalOpts)
:result-for-renderer (s/=> js/Promise)
:go-to-var-definition (s/=> s/Any {:var-name s/Str
Expand Down
2 changes: 1 addition & 1 deletion src/repl_tooling/features/shadow_cljs.cljs
@@ -1,6 +1,6 @@
(ns repl-tooling.features.shadow-cljs
(:require-macros [repl-tooling.repl-client.clj-helper :as h])
(:require [cljs.reader :as edn]
[repl-tooling.repl-client.clj-helper :as h]
[clojure.string :as str]
["path" :as path]
["fs" :refer [existsSync readFileSync]]
Expand Down
4 changes: 2 additions & 2 deletions src/repl_tooling/integrations/connection.cljs
@@ -1,6 +1,6 @@
(ns repl-tooling.integrations.connection
(:require-macros [repl-tooling.repl-client.clj-helper :refer [cljs-blob-contents]])
(:require [promesa.core :as p :include-macros true]
(:require [promesa.core :as p]
[repl-tooling.repl-client.clj-helper :refer [cljs-blob-contents]]
[cljs.reader :as edn]
[repl-tooling.repl-client.clojure :as clj-repl]
[repl-tooling.eval :as eval]
Expand Down
2 changes: 1 addition & 1 deletion src/repl_tooling/repl_client/clojure.cljs
@@ -1,6 +1,6 @@
(ns repl-tooling.repl-client.clojure
(:require-macros [repl-tooling.repl-client.clj-helper :refer [blob-contents]])
(:require [repl-tooling.editor-helpers :as helpers]
[repl-tooling.repl-client.clj-helper :refer [blob-contents]]
[repl-tooling.eval :as eval]
[cljs.reader :as reader]
[clojure.string :as str]
Expand Down
95 changes: 61 additions & 34 deletions src/repl_tooling/repl_client/shadow_ws.cljs
Expand Up @@ -3,6 +3,7 @@
[clojure.string :as str]
[promesa.core :as p]
[repl-tooling.editor-helpers :as helpers]
[clojure.reader :as edn]
[repl-tooling.eval :as eval]
[repl-tooling.integrations.repls :as repls]
[cognitect.transit :as t]
Expand Down Expand Up @@ -50,12 +51,26 @@
[[file "" build-id row]]))))
prom))

(defn- send-custom-command! [state message id opts]
(let [prom (p/deferred)
row (:row opts 0)
file (:filename opts "[EVAL]")
message (assoc (edn/read-string message) :call-id id)]
(swap! state update :pending-evals assoc (:call-id message) {:promise prom
:file file
:row row
:pass (:pass opts)})
(send! (:ws @state) message)
prom))

(defrecord ShadowCLJS [state]
eval/Evaluator
(evaluate [this command opts callback]
(p/let [id (:id opts (gensym "shadow-eval-"))
namespace (-> opts :namespace str not-empty (or "cljs.user"))
prom (evaluate! state namespace (str command) (assoc opts :id id))]
prom (if (:shadow-command opts)
(send-custom-command! state command id opts)
(evaluate! state namespace (str command) (assoc opts :id id)))]
(callback prom)
id))

Expand Down Expand Up @@ -116,12 +131,17 @@
(remove-id % client-id)))
(listen-to-events! state))

(defn- capture-result! [state {:keys [result call-id]}]
(when-let [{:keys [promise success? pass]} (-> @state :pending-evals (get call-id))]

(defn- resolve-pending! [state {:keys [call-id]} result]
(let [{:keys [promise]} (-> @state :pending-evals (get call-id))]
(swap! state update :pending-evals dissoc call-id)
(p/resolve! promise (assoc pass
(if success? :result :error) result
:as-text result))))
(p/resolve! promise result)))

(defn- capture-result! [state {:keys [result call-id] :as msg}]
(when-let [{:keys [success? pass]} (-> @state :pending-evals (get call-id))]
(resolve-pending! state msg (assoc pass
(if success? :result :error) result
:as-text result))))

(defn- get-result! [state msg]
(swap! state update-in [:pending-evals (:call-id msg)] assoc :success? true)
Expand All @@ -139,25 +159,24 @@
:request-op :edn
:oid (:ex-oid msg)}))

(defn- send-as-error! [state {:keys [warnings call-id]}]
(when-let [{:keys [promise row file]} (-> @state :pending-evals (get call-id))]
(defn- send-as-error! [state {:keys [warnings call-id] :as msg}]
(when-let [{:keys [row file]} (-> @state :pending-evals (get call-id))]
(let [trace (->> warnings
(mapv (fn [{:keys [msg line]}]
[(str/replace msg #"Use of.* (.*/.*)$" "$1")
""
file
(dec (+ row line))])))]
(swap! state update :pending-evals dissoc call-id)
(p/resolve! promise (helpers/error-result "Compile Warning"
(->> warnings (map :msg) (str/join "\n"))
trace)))))
(resolve-pending! state msg
(helpers/error-result "Compile Warning"
(->> warnings (map :msg) (str/join "\n"))
trace)))))

(defn- obj-not-found! [state {:keys [call-id]}]
(when-let [{:keys [promise row file]} (-> @state :pending-evals (get call-id))]
(swap! state update :pending-evals dissoc call-id)
(p/resolve! promise (helpers/error-result "404"
"Result not found"
[[file "" "" row]]))))
(defn- obj-not-found! [state {:keys [call-id] :as msg}]
(when-let [{:keys [row file]} (-> @state :pending-evals (get call-id))]
(resolve-pending! state msg (helpers/error-result "404"
"Result not found"
[[file "" "" row]]))))

(defn- send-output! [state {:keys [stream text]}]
(let [on-out (:on-output @state)
Expand Down Expand Up @@ -193,22 +212,30 @@
(.end ^js (:ws @state))
(p/resolve! (:evaluator @state) {:error :access-denied}))

(s/defn ^:private treat-ws-message! [state :- State, {:keys [op] :as msg}]
(case op
:welcome (send-hello! state)
:clients (parse-clients! state msg)
:notify (update-builds! state msg)
:ping (send! (:ws @state) {:op :pong})
:eval-result-ref (get-result! state msg)
:eval-runtime-error (get-error! state msg)
:obj-result (capture-result! state msg)
:eval-compile-warnings (send-as-error! state msg)
:eval-compile-error (get-error! state (assoc msg :from (:ex-client-id msg 1)))
:obj-not-found (obj-not-found! state msg)
:runtime-print (send-output! state msg)
:shadow.cljs.model/sub-msg (compile-error! state msg)
:access-denied (access-denied! state)
(prn :unknown-op op)))
(defn- send-result! [state {:keys [pass]} msg]
(let [res (pr-str msg)
result (assoc pass :result res :as-text res)]
(resolve-pending! state msg result)))

(s/defn ^:private treat-ws-message! [state :- State, {:keys [op call-id] :as msg}]
(if-let [pending (-> @state :pending-evals (get call-id))]
(case op
:obj-result (capture-result! state msg)
:eval-runtime-error (get-error! state msg)
:eval-compile-error (get-error! state (assoc msg :from (:ex-client-id msg 1)))
:eval-compile-warnings (send-as-error! state msg)
:eval-result-ref (get-result! state msg)
:obj-not-found (obj-not-found! state msg)
(send-result! state pending msg))
(case op
:welcome (send-hello! state)
:clients (parse-clients! state msg)
:notify (update-builds! state msg)
:ping (send! (:ws @state) {:op :pong})
:runtime-print (send-output! state msg)
:shadow.cljs.model/sub-msg (compile-error! state msg)
:access-denied (access-denied! state)
(prn :unknown-op op))))

(defn- create-ws-conn! [id url state]
(try
Expand Down
2 changes: 1 addition & 1 deletion src/repl_tooling/repl_client/source.cljs
@@ -1,6 +1,6 @@
(ns repl-tooling.repl-client.source
(:require-macros [repl-tooling.repl-client.clj-helper :refer [generic-eval-wrapper]])
(:require [clojure.string :as str]
[repl-tooling.repl-client.clj-helper :refer [generic-eval-wrapper]]
[rewrite-clj.parser :as parser]
[rewrite-clj.node :as node]))

Expand Down

0 comments on commit 1eb079a

Please sign in to comment.