Skip to content
This repository was archived by the owner on Oct 21, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions lein-light-nrepl/src/lighttable/nrepl/eval.clj
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,18 @@
(if (instance? clojure.lang.IObj thing)
(meta thing)))

(defn clean-serialize [res & [opts]]
(defn clean-serialize
"Stringify the result `res` into a form appropiate to its type. Defaults to
using function `pr-str` for most types.

Allowed options:

- `:print-length` - Restricts length of stringified results to it. Defaults to 1000.
- `:allow-var?` - Whether to return a var itself or its stringified version.
Defaults to nil.
- `:result` - Whether to return an atom itself or its content. Defaults to nil.
- `:verbatim` - Whether to return an string itself or its 'pr-str' version."
[res & [opts]]
(binding [*print-length* (or (:print-length opts) *print-length* 1000)]
(cond
(fn? res) 'fn
Expand All @@ -70,7 +81,8 @@
(and (string? res) (:verbatim opts)) res
:else (pr-str res))))

(defn truncate [v]
(defn truncate
[v]
v)

(defn ->result [opts f]
Expand Down Expand Up @@ -118,7 +130,10 @@
(str (reduce str "" (repeat (:start meta 0) "\n"))
code)))

(defn watch [v meta]
(defn watch
"Stringify result-value `v` with pretty-print, send it back to Light Table,
and then return the value itself to continue the evaling call."
[v meta]
(let [ppv (with-out-str (pprint v))
data {:meta meta :result (subs ppv 0 (dec (count ppv)))}]
(core/safe-respond-to (:obj meta) :editor.eval.clj.watch data))
Expand Down
100 changes: 80 additions & 20 deletions src/lt/plugins/clojure.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,15 @@
(run-local-server (clients/client! :nrepl.client))))))

(behavior ::on-eval.clj
:desc "Clojure: Eval editor content"
:triggers #{:eval}
:reaction (fn [editor]
(object/raise clj-lang :eval! {:origin editor
:info (assoc (@editor :info)
:print-length (object/raise-reduce editor :clojure.print-length+ nil)
:code (watches/watched-range editor nil nil nil))})))
(behavior ::on-eval.cljs
:desc "Clojure: Eval editor content"
:triggers #{:eval}
:reaction (fn [editor]
(object/raise clj-lang :eval! {:origin editor
Expand All @@ -87,6 +89,7 @@
"(set! js/COMPILED js/COMPILED-temp)"))})))

(behavior ::on-eval.one
:desc "Clojure: Eval a single form in editor"
:triggers #{:eval.one}
:reaction (fn [editor]
(let [code (watches/watched-range editor nil nil nil)
Expand All @@ -103,12 +106,21 @@
:info info}))))


(defn fill-placeholders [editor exp]
(defn fill-placeholders
"Replace editor-selection-flags, that is `__SELECTION*__`, inside `exp` with the currently
selected code in `editor`."
[editor exp]
(-> exp
(string/replace "__SELECTION*__" (pr-str (ed/selection editor)))
(string/replace "__SELECTION__" (ed/selection editor))))

(behavior ::on-eval.custom
:desc "Clojure: Eval a form that has been wrapped by custom code"
:doc "Example:

```
(eval (time form)) ;; instead of (eval form)
```"
:triggers #{:eval.custom}
:reaction (fn [editor exp opts]
(let [code (fill-placeholders editor exp)
Expand Down Expand Up @@ -168,14 +180,16 @@

(defn lighttable-ui-project?
"Determine if path is part of a project that evals to LightTable's process
e.g. LightTable plugin or LightTable itself"
(i.e., LightTable plugin or LightTable itself)."
[path]
(or (files/walk-up-find path "plugin.edn")
(files/walk-up-find path "plugin.json")
(when-let [project-file (files/walk-up-find path "project.clj")]
(= 'lighttable (second (reader/read-string (:content (files/open-sync project-file))))))))

(behavior ::eval!
:desc "Clojure: Send event information for evaluation."
:doc "This event information goes to the appropriate nREPL or LightTable-UI client."
:triggers #{:eval!}
:reaction (fn [this event]
(let [{:keys [info origin]} event
Expand Down Expand Up @@ -263,6 +277,7 @@
(notifos/done-working)))

(behavior ::cljs-result
:desc "Clojure: Receive a cljs result and dispatch it"
:triggers #{:editor.eval.cljs.result}
:reaction (fn [obj res]
(notifos/done-working)
Expand All @@ -271,20 +286,24 @@
(object/raise obj ev res))))

(behavior ::cljs-result.replace
:desc "Clojure: Replace current selection with the result of an evaluation"
:triggers #{:editor.eval.cljs.result.replace}
:reaction (fn [obj res]
(if-let [err (or (:stack res) (:ex res))]
(notifos/set-msg! err {:class "error"})
(ed/replace-selection obj (unescape-unicode (or (:result res) ""))))))

(behavior ::cljs-result.statusbar
:desc "Clojure: Show the result of an evaluation on the statusbar"
:triggers #{:editor.eval.cljs.result.statusbar}
:reaction (fn [obj res]
(if-let [err (or (:stack res) (:ex res))]
(notifos/set-msg! err {:class "error"})
(notifos/set-msg! (unescape-unicode (or (:result res) "")) {:class "result"}))))

(behavior ::cljs-result.inline
:desc "Clojure: Show the resulting evaluation on inline widget"
:doc "The resulting evaluation is dispatched either as an exception or an inline-result."
:triggers #{:editor.eval.cljs.result.inline}
:reaction (fn [obj res]
(let [meta (:meta res)
Expand All @@ -295,6 +314,8 @@
(object/raise obj :editor.result (unescape-unicode (or (:result res) "")) loc)))))

(behavior ::cljs-result.inline-at-cursor
:desc "Clojure: Show the resulting evaluation inline at cursor location"
:doc "This is similar to `::cljs-result.inline`, but puts the result as the cursor location."
:triggers #{:editor.eval.cljs.result.inline-at-cursor}
:reaction (fn [obj res]
(let [meta (:meta res)
Expand All @@ -316,6 +337,8 @@
:meta meta})))))

(behavior ::clj-result
:desc "Clojure: Receive an eval! result and dispatch it"
:doc "The dispatch is according to the appropiate result type. Defaults to `:inline`."
:triggers #{:editor.eval.clj.result}
:reaction (fn [obj res]
(notifos/done-working)
Expand All @@ -324,6 +347,7 @@
(object/raise obj ev res))))

(behavior ::clj-result.replace
:desc "Clojure: Replace current selection with the result"
:triggers #{:editor.eval.clj.result.replace}
:reaction (fn [obj res]
(doseq [result (-> res :results)
Expand All @@ -335,6 +359,7 @@
(ed/replace-selection obj (:result result))))))

(behavior ::clj-result.statusbar
:desc "Clojure: Show evaluation result on the statusbar"
:triggers #{:editor.eval.clj.result.statusbar}
:reaction (fn [obj res]
(doseq [result (-> res :results)
Expand All @@ -346,6 +371,8 @@
(notifos/set-msg! (:result result) {:class "result"})))))

(behavior ::clj-result.inline
:desc "Clojure: Display eval result inline"
:doc "Dispatches as an exception or an inline-result."
:triggers #{:editor.eval.clj.result.inline}
:reaction (fn [obj res]
(doseq [result (-> res :results)
Expand All @@ -354,10 +381,11 @@
:start-line (dec (:line meta))}]]
(if (:stack result)
(object/raise obj :editor.eval.clj.exception result :passed)
(do
(object/raise obj :editor.result (:result result) loc))))))
(object/raise obj :editor.result (:result result) loc)))))

(behavior ::clj-result.inline-at-cursor
:desc "Clojure: Display the eval result inline at cursor location"
:doc "This is similar to `::clj-result.inline`, but puts the result at the cursor location."
:triggers #{:editor.eval.clj.result.inline-at-cursor}
:reaction (fn [obj res]
(doseq [result (-> res :results)
Expand All @@ -366,8 +394,7 @@
:start-line (-> res :meta :start)}]]
(if (:stack result)
(object/raise obj :editor.eval.clj.exception result :passed)
(do
(object/raise obj :editor.result (:result result) loc))))))
(object/raise obj :editor.result (:result result) loc)))))

(behavior ::clj-result.return
:triggers #{:editor.eval.clj.result.return}
Expand All @@ -382,6 +409,7 @@
:meta meta})))))

(behavior ::clj-exception
:desc "Clojure: Display stacktrace information and summary in statusbar"
:triggers #{:editor.eval.clj.exception}
:reaction (fn [obj res passed?]
(when-not passed?
Expand All @@ -390,10 +418,13 @@
loc {:line (dec (:end-line meta)) :ch (:end-column meta 0)
:start-line (dec (:line meta 1))}]
(notifos/set-msg! (:result res) {:class "error"})
(object/raise obj :editor.exception (:stack res) loc))
))
(object/raise obj :editor.exception (:stack res) loc))))

(behavior ::cljs-exception
:desc "Clojure: Display stacktrace information and summary in statusbar"
;; Line below is too long to include, but will be useful after https://github.com/LightTable/LightTable/issues/2197
:doc "Take the result of evaling a cljs form, which resulted in an exception,
and displays a message in the status bar and an exception widget with the stacktrace."
:triggers #{:editor.eval.cljs.exception}
:reaction (fn [obj res passed?]
(when-not passed?
Expand All @@ -413,8 +444,7 @@
msg
"Unknown error")]
(notifos/set-msg! msg {:class "error"})
(object/raise obj :editor.exception stack loc))
))
(object/raise obj :editor.exception stack loc))))

(behavior ::eval-print
:triggers #{:editor.eval.clj.print}
Expand All @@ -424,8 +454,7 @@
:line (when (object/has-tag? this :nrepl.client)
"stdout")
:id (:id str)
:content (:out str)}
))))
:content (:out str)}))))

(behavior ::eval-print-err
:triggers #{:editor.eval.clj.print.err}
Expand Down Expand Up @@ -517,18 +546,36 @@
;; watches
;;****************************************************

;; For more information on watches check
;;
;; Original anouncement: https://groups.google.com/forum/#!msg/light-table-discussion/lyFzPGI2XMs/ec8T1OUPvMsJ
;;
;; Blog posts: http://scattered-thoughts.net/blog/2014/01/27/were-not-even-trying/?utm_source=dlvr.it&utm_medium=twitter
;; https://medium.com/@zindlerb/guide-to-light-table-watches-fad560f698d3#.oqwq991sx
;;
;; Rolex watches plugin: https://groups.google.com/forum/#!topic/light-table-discussion/NQWGC0vVHMY

(behavior ::cljs-watch-src
:desc "Clojure: Wraps the watched source code"
:doc "Wraps watched code to catch its result and send it back to LightTable,
while continuing normal evaluation of an expression."
:triggers #{:watch.src+}
:reaction (fn [editor cur meta src]
(let [meta (assoc meta :ev :editor.eval.cljs.watch)]
(str "(js/lttools.watch " src " (clj->js " (pr-str meta) "))"))))

(behavior ::clj-watch-src
:desc "Clojure: Wraps the watched source code"
:doc "Wraps watched code to catch its result and send it back to LightTable,
while continuing normal evaluation of an expression."
:triggers #{:watch.src+}
:reaction (fn [editor cur meta src]
(str "(lighttable.nrepl.eval/watch " src " " (pr-str meta) ")")))

(defn fill-watch-placeholders [exp src meta watch]
(defn fill-watch-placeholders
"Replace editor-selection-flags (placeholders) for custom watches inside `exp`
with the src-code to be watched."
[exp src meta watch]
(-> exp
(string/replace "\n" " ")
(string/replace "__SELECTION*__" (pr-str src))
Expand All @@ -537,12 +584,20 @@
(string/replace #"__\|(.*)\|__" watch)))

(behavior ::cljs-watch-custom-src
:desc "Clojure: Prepare expression for watching"
:doc "The expression is prepared by filling its placeholders and wrapping its watcher-code
with custom call to `:editor.eval.cljs.watch`."
:triggers #{:watch.custom.src+}
:reaction (fn [editor cur meta opts src]
(let [watch (str "(js/lttools.raise " (:obj meta) " :editor.eval.cljs.watch {:meta " (pr-str (merge (dissoc opts :exp) meta)) " :result $1})")]
(let [watch (str "(js/lttools.raise " (:obj meta)
" :editor.eval.cljs.watch {:meta " (pr-str (merge (dissoc opts :exp) meta))
" :result $1})")]
(fill-watch-placeholders (:exp opts) src meta watch))))

(behavior ::clj-watch-custom-src
:desc "Clojure: Prepare expression for watching"
:doc "The exp is prepared by filling its placeholders and wrapping its watcher-code
with custom call to `:editor.eval.clj.watch`"
:triggers #{:watch.custom.src+}
:reaction (fn [editor cur meta opts src]
(let [wrapped (if (:verbatim opts)
Expand Down Expand Up @@ -578,6 +633,7 @@
;;****************************************************

(behavior ::clj-doc
:desc "Clojure: Request docstring for symbol at cursor from nREPL"
:triggers #{:editor.doc}
:reaction (fn [editor]
(let [token (find-symbol-at-cursor editor)
Expand All @@ -593,8 +649,7 @@
:info info
:origin editor
:create try-connect})
command info :only editor)))
))
command info :only editor)))))

(behavior ::print-clj-doc
:triggers #{:editor.clj.doc}
Expand All @@ -617,6 +672,7 @@
(assoc token-left :loc loc)))))

(behavior ::cljs-doc
:desc "Clojure: Request docstring for symbol at cursor from nREPL"
:triggers #{:editor.doc}
:reaction (fn [editor]
(let [token (find-symbol-at-cursor editor)
Expand All @@ -643,16 +699,20 @@
(object/raise editor :editor.doc.show! result)))))

(behavior ::clj-doc-search
:desc "Clojure: Add trigger for Clojure in language documentation search"
:doc "Links the 'Search language docs' input-text on the sidebar with a trigger to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please convert to :desc. While I'm ok using :doc as an additive key we shouldn't be using it as a replacement for :desc. Same feedback for other behaviors that only have :doc

`:docs.clj.search` to retrieve all the documentation on a user-input."
:triggers #{:types+}
:reaction (fn [this cur]
(conj cur {:label "clj" :trigger :docs.clj.search :file-types #{"Clojure"}})
))
(conj cur {:label "clj" :trigger :docs.clj.search :file-types #{"Clojure"}})))

(behavior ::cljs-doc-search
:desc "Clojure: Add trigger for ClojureScript in language documentation search"
:doc "Links the 'Search language docs' input-text on the sidebar with a trigger to
`:docs.cljs.search` to retrieve all the documentation on a user-input."
:triggers #{:types+}
:reaction (fn [this cur]
(conj cur {:label "cljs" :trigger :docs.cljs.search :file-types #{"ClojureScript"}})
))
(conj cur {:label "cljs" :trigger :docs.cljs.search :file-types #{"ClojureScript"}})))

;;****************************************************
;; autocomplete
Expand Down