Skip to content
Open
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
545 changes: 295 additions & 250 deletions ruby instarepl_compiled.js

Large diffs are not rendered by default.

165 changes: 91 additions & 74 deletions ruby instarepl_compiled.js.map

Large diffs are not rendered by default.

23 changes: 12 additions & 11 deletions ruby.behaviors
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
{:+ {:app [(:lt.objs.plugins/load-js ["codemirror/ruby.js" "ruby instarepl_compiled.js"])]
:clients []
:editor.ruby [:lt.objs.langs.ruby/ruby-exception :lt.objs.langs.ruby/on-eval
:lt.objs.langs.ruby/on-eval.one
:lt.objs.langs.ruby/ruby-result
:lt.objs.langs.ruby/ruby-success
:lt.objs.langs.ruby/ruby-printer
:lt.objs.langs.ruby/ruby-watch
:lt.objs.langs.ruby/ruby-incomplete
:lt.objs.langs.ruby/live-toggle
:editor.ruby [:lt.objs.langs.ruby.eval/ruby-exception
:lt.objs.langs.ruby.eval/on-eval
:lt.objs.langs.ruby.eval/on-eval.one
:lt.objs.langs.ruby.eval/ruby-result
:lt.objs.langs.ruby.eval/ruby-success
:lt.objs.langs.ruby.eval/ruby-printer
:lt.objs.langs.ruby.watch/ruby-watch
:lt.objs.langs.ruby.eval/ruby-incomplete
:lt.objs.langs.ruby.live/live-toggle
;; :lt.objs.langs.ruby/ruby-image
:lt.objs.langs.ruby/watch-src
:lt.objs.langs.ruby.watch/watch-src
;; we don't seem to be able to unload part of watchable, so we're manually adding the pieces we need
;; [:lt.object/add-tag :watchable]
:lt.plugins.watches/watch!
:lt.plugins.watches/unwatch!
]
:editor.ruby.live [:lt.objs.langs.ruby/eval-on-change]
:editor.ruby.live [:lt.objs.langs.ruby.live/eval-on-change]

:files [(:lt.objs.files/file-types [{:name "Ruby" :exts [:rb, :gemspec, :Rakefile, :Gemfile] :mime "text/x-ruby" :tags [:editor.ruby]}])]
:ruby.lang [:lt.objs.langs.ruby/eval! :lt.objs.langs.ruby/connect]}
:ruby.lang [:lt.objs.langs.ruby.eval/eval! :lt.objs.langs.ruby.client/connect]}

;; :- {:editor.ruby [:lt.plugins.watches/eval-on-watch-or-unwatch]}

Expand Down
165 changes: 165 additions & 0 deletions src/lt/plugins/client.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
(ns lt.objs.langs.ruby.client
(:require [lt.object :as object]
[lt.objs.command :as cmd]
[lt.objs.files :as files]
[lt.objs.popup :as popup]
[lt.objs.plugins :as plugins]
[lt.objs.proc :as proc]
[lt.objs.clients :as clients]
[lt.objs.notifos :as notifos]
[lt.util.load :as load]
[lt.objs.sidebar.clients :as scl]
[lt.objs.dialogs :as dialogs]
[lt.objs.clients.tcp :as tcp]

[lt.objs.langs.ruby :as ruby :refer [ruby]])
(:require-macros [lt.macros :refer [behavior]]))

(def plugin-dir (plugins/find-plugin "Ruby Instarepl"))
(def shell (load/node-module "shelljs"))
(def rb-path (files/join plugin-dir "rb-src/lt_client.rb"))
(def runner-path (files/join plugin-dir "rb-src/lt_client_runner.sh"))

(behavior ::on-out
:triggers #{:proc.out}
:reaction (fn [this data]
(let [out (.toString data)]
(object/update! this [:buffer] str out)
(if (> (.indexOf out "Connected") -1)
(do
(notifos/done-working)
(object/merge! this {:connected true}))
(object/update! this [:buffer] str data)))))

(behavior ::on-error
:triggers #{:proc.error}
:reaction (fn [this data]
(let [out (.toString data)]
(when-not (> (.indexOf (:buffer @this) "Connected") -1)
(object/update! this [:buffer] str data)
))
))

(behavior ::on-exit
:triggers #{:proc.exit}
:reaction (fn [this data]
;(object/update! this [:buffer] str data)
(when-not (:connected @this)
(notifos/done-working)
(popup/popup! {:header "We couldn't connect."
:body [:span "Looks like there was an issue trying to connect
to the project. Here's what we got:" [:pre (:buffer @this)]]
:buttons [{:label "close"}]})
(clients/rem! (:client @this)))
(proc/kill-all (:procs @this))
(object/destroy! this)
))

(object/object* ::connecting-notifier
:triggers []
:behaviors [::on-exit ::on-error ::on-out]
:init (fn [this client]
(object/merge! this {:client client :buffer ""})
nil))

(defn escape-spaces [s]
(if (= files/separator "\\")
(str "\"" s "\"")
s))

(defn bash-escape-spaces [s]
(clojure.string/replace s " " "\\ "))

(defn run-rb [{:keys [path project-path name client] :as info}]
(let [n (notifos/working "Connecting..")
obj (object/create ::connecting-notifier client)
use-runner (or (::use-rvm? @ruby) (::use-rbenv? @ruby))
keys->env {::use-rbenv? :LT_USE_RBENV, ::use-rvm? :LT_USE_RVM, ::enable-client-logging? :LT_ENABLE_CLIENT_LOGGING}
env (zipmap (map keys->env
(keys (select-keys @ruby (keys keys->env))))
(cycle [true]))
env (if (::rvm-path @ruby) (assoc env :LT_RVM_PATH (::rvm-path @ruby)) env)
command (if use-runner
"bash"
(or (::ruby-exe @ruby) "ruby"))
args (if use-runner
[runner-path project-path (bash-escape-spaces rb-path) tcp/port (clients/->id client)]
[(escape-spaces rb-path) tcp/port (clients/->id client)])]
(proc/exec {:command command
:args args
:cwd project-path
:env env
:obj obj})))

(defn check-ruby [obj]
(assoc obj :ruby (or (::ruby-exe @ruby)
(.which shell "ruby"))))

(defn check-client [obj]
(assoc obj :ruby-client (files/exists? rb-path)))

(defn find-project [obj]
(let [p (:path obj)
roots (files/get-roots)]
(loop [cur p
prev ""]
(if (or (empty? cur)
(roots cur)
(= cur prev))
(assoc obj :project-path
(if (files/dir? p) p (files/parent p)))
(if (and (files/dir? cur)
(files/exists? (files/join cur "Gemfile")))
(assoc obj :project-path cur)
(recur (files/parent cur) cur))))))

(defn notify [obj]
(let [{:keys [ruby project-path path ruby-client client]} obj]
(cond
(or (not ruby) (empty? ruby)) (do
(clients/rem! client)
(notifos/done-working)
(popup/popup! {:header "We couldn't find Ruby."
:body "In order to evaluate in Ruby files, a Ruby interpreter has to be installed and on your system PATH."
:buttons [{:label "Download Ruby"
:action (fn []
(platform/open "https://www.ruby-lang.org/en/downloads/"))}
{:label "ok"}]}))
(not project-path) (do
(clients/rem! client)
(notifos/done-working)
(popup/popup! {:header "We couldn't find this file."
:body "In order to evaluate in Ruby files, the file has to be on disk somewhere."
:buttons [{:label "Save this file"
:action (fn []
(cmd/exec! :save)
(try-connect obj))}
{:label "Cancel"
:action (fn []
)}]}))
:else (run-rb obj))
obj))

(behavior ::connect
:triggers #{:connect}
:reaction (fn [this path]
(try-connect {:info {:path path}})))

(defn check-all [obj]
(-> obj
(check-ruby)
(check-client)
(find-project)
(notify)))

(defn try-connect [{:keys [info]}]
(let [path (:path info)
client (clients/client! :ruby.client)]
(check-all {:path path
:client client})
client))

(scl/add-connector {:name "Ruby"
:desc "Select a directory to serve as the root of your ruby project."
:connect (fn []
(dialogs/dir ruby :connect))})
50 changes: 50 additions & 0 deletions src/lt/plugins/config.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
(ns lt.objs.langs.ruby.config
(:require [lt.object :as object]
[lt.objs.langs.ruby :as ruby :refer [ruby]])

(:require-macros [lt.macros :refer [behavior]]))

(behavior :lt.objs.langs.ruby/ruby-exe
:triggers #{:object.instant}
:desc "Ruby: Set the path to the ruby executable for clients"
:type :user
:params [{:label "path"
:type :path}]
:exclusive true
:reaction (fn [this exe]
(object/merge! ruby {:lt.objs.langs.ruby.client/ruby-exe exe})))


(behavior :lt.objs.langs.ruby/use-rvm
:triggers #{:object.instant}
:desc "Ruby: Use RVM when loading REPL"
:type :user
:exclusive true
:reaction (fn [this]
(object/merge! ruby {:lt.objs.langs.ruby.client/use-rvm? true})))


(behavior :lt.objs.langs.ruby/rvm-path
:triggers #{:object.instant}
:desc "Ruby: Path to RVM init script"
:type :user
:params [{:label "path", :type :path}]
:exclusive true
:reaction (fn [this path]
(object/merge! ruby {:lt.objs.langs.ruby.client/rvm-path path})))

(behavior :lt.objs.langs.ruby/use-rbenv
:triggers #{:object.instant}
:desc "Ruby: Use rbenv when loading REPL"
:type :user
:exclusive true
:reaction (fn [this]
(object/merge! ruby {:lt.objs.langs.ruby.client/use-rbenv? true})))

(behavior :lt.objs.langs.ruby/client-enable-logging
:triggers #{:object.instant}
:desc "Ruby: log ruby client output to lt_client.log"
:type :user
:exclusive true
:reaction (fn [this]
(object/merge! ruby {:lt.objs.langs.ruby.client/enable-client-logging? true})))
92 changes: 92 additions & 0 deletions src/lt/plugins/eval.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
(ns lt.objs.langs.ruby.eval
(:require [lt.object :as object]
[lt.objs.editor :as ed]
[lt.objs.eval :as eval]
[lt.plugins.watches :as watches]
[lt.objs.clients :as clients]
[lt.objs.notifos :as notifos]
[lt.objs.console :as console]
[lt.objs.files :as files]

[lt.objs.langs.ruby :as ruby :refer [ruby]]
[lt.objs.langs.ruby.client :as client])

(:require-macros [lt.macros :refer [behavior]]))

(behavior ::on-eval
:triggers #{:eval}
:reaction (fn [editor]
(object/raise ruby :eval! {:origin editor
:info (assoc (@editor :info)
:code (watches/watched-range editor nil nil lt.objs.langs.ruby.watch/ruby-watch)
:meta {:start 0, :end (ed/last-line editor)})})))

(behavior ::on-eval.one
:triggers #{:eval.one}
:reaction (fn [editor]
(let [pos (ed/->cursor editor)
code (if (ed/selection? editor)
(watches/watched-range editor nil nil lt.objs.langs.ruby.watch/ruby-watch)
(ed/line editor (:line pos)))
info (:info @editor)
info (if (ed/selection? editor)
(assoc info
:code (ed/selection editor)
:meta {:start (-> (ed/->cursor editor "start") :line)
:end (-> (ed/->cursor editor "end") :line)})
(assoc info :pos pos :code code :meta {:start (:line pos) :end (:line pos)}))]
(object/raise ruby :eval! {:origin editor
:info info}))))

(behavior ::eval!
:triggers #{:eval!}
:reaction (fn [this event]
(let [{:keys [info origin]} event
client (-> @origin :client :default)]
(notifos/working "")
(clients/send (eval/get-client! {:command :editor.eval.ruby
:origin origin
:info info
:create client/try-connect})
:editor.eval.ruby
info
:only
origin))))

(behavior ::ruby-result
:triggers #{:editor.eval.ruby.result}
:reaction (fn [editor res]
(notifos/done-working)
(object/raise editor :editor.result (:result res) {:line (:end (:meta res))
:start-line (-> res :meta :start)})))

(behavior ::ruby-success
:triggers #{:editor.eval.ruby.success}
:reaction (fn [editor res]
(notifos/done-working)
(object/raise editor :editor.result "✓" {:line (-> res :meta :end)
:start-line (-> res :meta :start)})))

(behavior ::ruby-incomplete
:triggers #{:editor.eval.ruby.incomplete}
:reaction (fn [editor res]
(notifos/done-working)
(object/raise editor :editor.result "…" {:line (-> res :meta :end)
:start-line (-> res :meta :start)})))

(behavior ::ruby-exception
:triggers #{:editor.eval.ruby.exception}
:reaction (fn [editor ex]
(notifos/done-working)
(object/raise editor :editor.exception (:ex ex) {:line (-> ex :meta :end)
:start-line (-> ex :meta :start)})
))

(behavior ::ruby-printer
:triggers #{:editor.eval.ruby.print}
:reaction (fn [editor p]
(console/loc-log {:file (files/basename (:file p))
:line "stdout"
:content (:msg p)})))


18 changes: 18 additions & 0 deletions src/lt/plugins/image.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
(ns lt.objs.langs.ruby.image
(:require [lt.object :as object])

(:require-macros [lt.macros :refer [behavior defui]]))

(defui image [src]
[:img {:src (str "data:image/png;base64," src)}])

(defui canvas []
[:canvas])

(behavior ::ruby-image
:triggers #{:editor.eval.ruby.image}
:reaction (fn [editor img]
;(console/log (pr-str img))
(object/raise editor :editor.result.underline (image (:image img)) {:line (-> img :meta :end)
:start-line (-> img :meta :start)})
))
Loading