From 6f37f7d677be6b604274507b80969770964d8652 Mon Sep 17 00:00:00 2001 From: David Pollak Date: Tue, 23 Dec 2014 15:10:54 -0800 Subject: [PATCH 1/4] Modified to allow for hooks into the code --- project.clj | 2 +- resources/public/js/main.js | 74 ++++++++++++++++++---------- resources/public/js/repl-ws.js | 38 +++++++++++--- resources/public/js/worksheet.js | 12 ++++- resources/public/worksheet.html | 7 +-- src/gorilla_repl/core.clj | 72 +++++++++++++++++++++++++-- src/gorilla_repl/websocket_relay.clj | 7 ++- 7 files changed, 168 insertions(+), 44 deletions(-) diff --git a/project.clj b/project.clj index 7476ca90e..3132bd938 100644 --- a/project.clj +++ b/project.clj @@ -2,7 +2,7 @@ ;;;; ;;;; gorilla-repl is licenced to you under the MIT licence. See the file LICENCE.txt for full details. -(defproject gorilla-repl "0.3.4" +(defproject gorilla-repl "0.3.5-visi" :description "A rich REPL for Clojure in the notebook style." :url "https://github.com/JonyEpsilon/gorilla-repl" :license {:name "MIT"} diff --git a/resources/public/js/main.js b/resources/public/js/main.js index cd9632283..d509c64d7 100644 --- a/resources/public/js/main.js +++ b/resources/public/js/main.js @@ -9,6 +9,8 @@ var app = function () { var self = {}; + self.conf = {}; + // Most importantly, the application has a worksheet! This is exposed so that the UI can bind to it, but note that // you should never change the worksheet directly, as this will leave the event handlers in an inconsistent state. // Rather you should use the `setWorksheet` function below. @@ -19,7 +21,7 @@ var app = function () { // whenever we change the filename, we update the URL to match self.filename.subscribe(function (filename) { if (filename !== "") history.pushState(null, null, "?filename=" + filename); - else history.pushState(null, null, "/worksheet.html"); + else history.pushState(null, null, self.config.worksheetName || "/worksheet.html"); }); // shows the name of the Leiningen project that gorilla was launched from, makes it easier to manage multiple // tabs with multiple gorilla sessions. @@ -39,7 +41,7 @@ var app = function () { // UI and, if appropriate, load an initial worksheet. self.start = function (initialFilename) { // get hold of configuration information from the backend - $.get("/config") + $.get(/^.*\//.exec(window.location.pathname)[0]+"config") .done(function (data) { self.config = data; // If we've got the configuration, then start the app @@ -55,6 +57,11 @@ var app = function () { commandProcessor.installCommands(self.config.keymap); ko.applyBindings(self, document.getElementById("document")); + // allow the filename to be passed as a parameter + if (!initialFilename && data.filename) { + initialFilename = data.filename; + } + if (initialFilename) loadFromFile(initialFilename); else setBlankWorksheet(); }) @@ -70,12 +77,15 @@ var app = function () { ws.segments().push( // Note that the variable ck here is defined in commandProcessor.js, and gives the appropriate // shortcut key (ctrl or alt) for the platform. - freeSegment("# Gorilla REPL\n\nWelcome to gorilla :-)\n\nShift + enter evaluates code. " + - "Hit " + ck + "+g twice in quick succession or click the menu icon (upper-right corner) " + - "for more commands ...\n\nIt's a good habit to run each worksheet in its own namespace: feel " + - "free to use the declaration we've provided below if you'd like.") + freeSegment(self.config.introMessage || + ("# Gorilla REPL\n\nWelcome to gorilla :-)\n\nShift + enter evaluates code. " + + "Hit " + ck + "+g twice in quick succession or click the menu icon (upper-right corner) " + + "for more commands ...\n\nIt's a good habit to run each worksheet in its own namespace: feel " + + "free to use the declaration we've provided below if you'd like.")) ); - ws.segments().push(codeSegment("(ns " + makeHipNSName() + "\n (:require [gorilla-plot.core :as plot]))")); + ws.segments().push(codeSegment(self.config.nsSegment || + ("(ns " + (self.config.nameSpace || makeHipNSName()) + + "\n (:require [gorilla-plot.core :as plot]))"))); self.setWorksheet(ws, ""); // make it easier for the user to get started by highlighting the empty code segment eventBus.trigger("worksheet:segment-clicked", {id: self.worksheet().segments()[1].id}); @@ -84,7 +94,7 @@ var app = function () { // bound to the window's title self.title = ko.computed(function () { - if (self.filename() === "") return "Gorilla REPL - " + self.project(); + if (self.filename() === "") return (self.conf.title || "Gorilla REPL - ") + self.project(); else return self.project() + " : " + self.filename(); }); @@ -124,11 +134,11 @@ var app = function () { // Helpers for loading and saving the worksheet - called by the various command handlers var saveToFile = function (filename, successCallback) { - $.post("/save", { + $.post(/^.*\//.exec(window.location.pathname)[0]+"save", { "worksheet-filename": filename, "worksheet-data": self.worksheet().toClojure() }).done(function () { - self.flashStatusMessage("Saved: " + filename); + if (!self.conf.hideSavedMsg) self.flashStatusMessage("Saved: " + filename); if (successCallback) successCallback(); }).fail(function () { self.flashStatusMessage("Failed to save worksheet: " + filename, 2000); @@ -137,21 +147,23 @@ var app = function () { var loadFromFile = function (filename) { // ask the backend to load the data from disk - $.get("/load", {"worksheet-filename": filename}) - .done(function (data) { - if (data['worksheet-data']) { - // parse and construct the new worksheet - var segments = worksheetParser.parse(data["worksheet-data"]); - var ws = worksheet(); - ws.segments = ko.observableArray(segments); - // show it in the editor - self.setWorksheet(ws, filename); - // highlight the first code segment if it exists - var codeSegments = _.filter(self.worksheet().segments(), function(s) {return s.type === 'code'}); - if (codeSegments.length > 0) - eventBus.trigger("worksheet:segment-clicked", {id: codeSegments[0].id}); - } - }) + $.get(/^.*\//.exec(window.location.pathname)[0]+"load", {"worksheet-filename": filename} + .done (function (data) { + if (data['worksheet-data']) { + // parse and construct the new worksheet + var segments = worksheetParser.parse(data["worksheet-data"]); + var ws = worksheet(); + ws.segments = ko.observableArray(segments); + // show it in the editor + self.setWorksheet(ws, filename); + // highlight the first code segment if it exists + var codeSegments = _.filter(self.worksheet().segments(), function(s) {return s.type === 'code'}); + if (codeSegments.length > 0) { + eventBus.trigger("worksheet:segment-clicked", {id: codeSegments[0].id}); + if (self.config.recalcAllOnLoad)eventBus.trigger("worksheet:evaluate-all"); + } + } + })) .fail(function () { self.flashStatusMessage("Failed to load worksheet: " + filename, 2000); }); @@ -178,7 +190,7 @@ var app = function () { self.palette.show("Scanning for files ...", []); $.ajax({ type: "GET", - url: "/gorilla-files", + url: /^.*\//.exec(window.location.pathname)[0]+"gorilla-files", success: function (data) { var paletteFiles = data.files.map(function (c) { return { @@ -204,6 +216,16 @@ var app = function () { } else self.saveDialog.show(); }); + // Save only if the worksheet is named. Used for autosave. + eventBus.on("app:save-named", function () { + var fname = self.filename(); + // if we already have a filename, save to it. Else, prompt for a name. + if (fname !== "" && self.conf.autosave) { + saveToFile(fname); + } + }); + + eventBus.on("app:saveas", function () { var fname = self.filename(); self.saveDialog.show(fname); diff --git a/resources/public/js/repl-ws.js b/resources/public/js/repl-ws.js index b68667e1a..a70a00a1f 100644 --- a/resources/public/js/repl-ws.js +++ b/resources/public/js/repl-ws.js @@ -17,12 +17,18 @@ var repl = (function () { self.ws.send(JSON.stringify(message)); }; + // the function that deals with pinging the server which serves as + // (1) keep-alive and (2) a way to piggyback pushes from the server on the ping + self.pinger = function () {}; + // Connect to the websocket nREPL bridge. // TODO: handle errors. self.connect = function (successCallback, failureCallback) { // hard to believe we have to do this var loc = window.location; - var url = "ws://" + loc.hostname + ":" + loc.port + "/repl"; + var protocol = window.location.protocol == "https:" ? "wss:" : "ws:"; + var url = protocol + "//" + loc.hostname + ":" + loc.port + + /^.*\//.exec(loc.pathname)[0] +"repl"; self.ws = new WebSocket(url); // we first install a handler that will capture the session id from the clone message. Once it's done its work @@ -38,11 +44,24 @@ var repl = (function () { // The first thing we do is send a clone op, to get a new session. self.ws.onopen = function () { + // we're got an open web socket... proceed with pinging + self.pinger = function () { + if (self.sessionID) { + var id = UUID.generate(); + self.ws.send(JSON.stringify({"op": "ping", + id: id, + "session": self.sessionID})); + } + + setTimeout(function() {self.pinger();}, 1000); + }; + self.pinger(); self.ws.send(JSON.stringify({"op": "clone"})); }; // If the websocket connection dies we're done for, message the app to tell it so. self.ws.onclose = function () { + self.pinger = function () {}; eventBus.trigger("app:connection-lost"); }; }; @@ -60,7 +79,11 @@ var repl = (function () { var id = UUID.generate(); // store the evaluation ID and the segment ID in the evaluationMap evaluationMap[id] = d.segmentID; - var message = {'op': 'eval', 'code': d.code, id: id, session: self.sessionID}; + + // include the segmentID in the message so data can be pushed to + // the segment in the future + var message = {'op': 'eval', 'segmentID': d.segmentID, + 'code': d.code, id: id, session: self.sessionID}; self.sendREPLCommand(message); }; @@ -108,13 +131,13 @@ var repl = (function () { var d = JSON.parse(message.data); // Is this a message relating to an evaluation triggered by the user? - var segID = evaluationMap[d.id]; + var segID = d.segmentID || evaluationMap[d.id]; if (segID != null) { // - evaluation result (Hopefully no other responses have an ns component!) if (d.ns) { self.currentNamespace = d.ns; - eventBus.trigger("evaluator:value-response", {ns: d.ns, value: d.value, segmentID: segID}); + eventBus.trigger("evaluator:value-response", {clear: d.clear, ns: d.ns, value: d.value, segmentID: segID}); return; } @@ -166,9 +189,12 @@ var repl = (function () { // If we get here, then we don't know what the message was for - just log it - console.log("Unknown response: " + JSON.stringify(d)); + // unless the message is explicitly set as ignore + if (!d.ignore && (d.status.indexOf("done") < 0)) { + console.log("Unknown response: " + JSON.stringify(d)); + } }; return self; -})(); \ No newline at end of file +})(); diff --git a/resources/public/js/worksheet.js b/resources/public/js/worksheet.js index 296990868..8672501c6 100644 --- a/resources/public/js/worksheet.js +++ b/resources/public/js/worksheet.js @@ -222,6 +222,10 @@ var worksheet = function () { var seg = self.getActiveSegment(); if (seg == null) return; evaluateSegment(seg); + + // save the sheet on evaluate + eventBus.trigger("app:save-named"); + // if this isn't the last segment, move to the next if (self.activeSegmentIndex != self.segments().length - 1) eventBus.trigger("command:worksheet:leaveForward"); // if it is the last, create a new one at the end @@ -237,6 +241,12 @@ var worksheet = function () { addEventHandler("evaluator:value-response", function (e, d) { var segID = d.segmentID; var seg = self.getSegmentForID(segID); + + // if the clear flag is set, clear the segment + if (d.clear) { + seg.clearOutput(); + } + try { // If you're paying attention, you'll notice that the value gets JSON.parse'd twice: once here, and again // in the output viewer. This is a workaround for a problem in the rendering nREPL middleware that results @@ -307,4 +317,4 @@ var worksheet = function () { }; return self; -}; \ No newline at end of file +}; diff --git a/resources/public/worksheet.html b/resources/public/worksheet.html index a77015139..418a4b50a 100644 --- a/resources/public/worksheet.html +++ b/resources/public/worksheet.html @@ -7,10 +7,11 @@ + - + @@ -18,7 +19,7 @@ + src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG-full.js&delayStartupUntil=configured"> @@ -175,4 +176,4 @@

- \ No newline at end of file + diff --git a/src/gorilla_repl/core.clj b/src/gorilla_repl/core.clj index 76837d6f8..6a57e24ff 100644 --- a/src/gorilla_repl/core.clj +++ b/src/gorilla_repl/core.clj @@ -46,6 +46,9 @@ _ (println "done.")] (res/response {:worksheet-data ws-data})))) +;; A hook so another system can capture the +;; saving of the file +(def capture-save (atom (fn [& _]))) ;; the client can post a request to have the worksheet saved, handled by the following (defn save @@ -54,6 +57,7 @@ (when-let [ws-data (:worksheet-data (:params req))] (when-let [ws-file (:worksheet-filename (:params req))] (print (str "Saving: " ws-file " ... ")) + (@capture-save ws-data ws-file) (spit ws-file ws-data) (println (str "done. [" (java.util.Date.) "]")) (res/response {:status "ok"})))) @@ -93,15 +97,60 @@ (route/resources "/") (route/files "/project-files" [:root "."])) +(defn fix-prefix + [{:keys [uri] :as request}] + (if-let [info (re-find #"^/visi/[^/]*(/.*)$" uri)] + (do + (println "Rewriting " uri " to " (get info 1)) + (assoc request :uri (get info 1))) + request + ) + ) + +(defn fix-slash + [{:keys [uri] :as request}] + (if (.endsWith uri "/") + (assoc request :uri (str uri "index.html")) + request) + ) + +(defn rw-app-routes + [request] + (-> request fix-prefix fix-slash app-routes) + ) + +(defn- mk-number + [x] + (if (string? x) (read-string x) x)) + +;; set to capture the configuration +(def config-capture-hook (atom (fn [& _]))) (defn run-gorilla-server [conf] ;; get configuration information from parameters - (let [version (or (:version conf) "develop") - webapp-requested-port (or (:port conf) 0) - ip (or (:ip conf) "127.0.0.1") + (@config-capture-hook conf) + (let [version (or (get conf "version") + (:version conf) + "develop") + + webapp-requested-port (mk-number + (or + (get conf "port") + (:port conf) + 0)) + ip (or + (get conf "ip") + (:ip conf) + "127.0.0.1") + nrepl-requested-port (or (:nrepl-port conf) 0) ;; auto-select port if none requested - project (or (:project conf) "no project") + + project (or + (get conf "project") + (:project conf) + "no project") + keymap (or (:keymap (:gorilla-options conf)) {}) _ (swap! excludes (fn [x] (set/union x (:load-scan-exclude (:gorilla-options conf)))))] ;; app startup @@ -114,10 +163,23 @@ ;; first startup nREPL (nrepl/start-and-connect nrepl-requested-port) ;; and then the webserver - (let [s (server/run-server #'app-routes {:port webapp-requested-port :join? false :ip ip}) + (let [s (server/run-server #'rw-app-routes {:port webapp-requested-port :join? false :ip ip}) webapp-port (:local-port (meta s))] (spit (doto (io/file ".gorilla-port") .deleteOnExit) webapp-port) (println (str "Running at http://" ip ":" webapp-port "/worksheet.html .")) + + ;; support "touching" a file on startup to let other + ;; processes know that the REPL is listening + (if-let [touch (get conf "touch")] + (do + (try + (let [fos (java.io.FileOutputStream. (java.io.File. touch))] + (.write fos (.getBytes "hello" "UTF-8")) + (.close fos) + ) + (catch Exception e nil)) + )) + (println "Ctrl+C to exit.")))) (defn -main diff --git a/src/gorilla_repl/websocket_relay.clj b/src/gorilla_repl/websocket_relay.clj index be06477d8..c9d0ade12 100644 --- a/src/gorilla_repl/websocket_relay.clj +++ b/src/gorilla_repl/websocket_relay.clj @@ -30,7 +30,10 @@ [channel data] (let [parsed-message (assoc (json/parse-string data true) :as-html 1) client (nrepl/client @conn Long/MAX_VALUE) - replies (nrepl/message client parsed-message)] + replies + (if (= "ping" (-> parsed-message :op)) + (nrepl/message client (assoc parsed-message :op "eval" :code "nil" :ignore "yes")) + (nrepl/message client parsed-message))] ;; send the messages out over the WS connection one-by-one. (doall (map (partial send-json-over-ws channel) replies)))) @@ -42,4 +45,4 @@ (server/with-channel request channel - (server/on-receive channel (partial process-message channel)))) \ No newline at end of file + (server/on-receive channel (partial process-message channel)))) From db4487abb9e543b8d0d8f92f9953eb1f6ef53f2b Mon Sep 17 00:00:00 2001 From: David Pollak Date: Thu, 29 Jan 2015 17:00:44 -0800 Subject: [PATCH 2/4] Triangulating towards Visi integration --- project.clj | 5 ++--- src/gorilla_repl/core.clj | 20 +------------------- src/gorilla_repl/nrepl.clj | 4 +++- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/project.clj b/project.clj index fc96aa3f8..5657e286e 100644 --- a/project.clj +++ b/project.clj @@ -2,7 +2,7 @@ ;;;; ;;;; gorilla-repl is licenced to you under the MIT licence. See the file LICENCE.txt for full details. -(defproject gorilla-repl "0.3.5-visi" +(defproject visi/gorilla-repl "0.3.5-SNAPSHOT" :description "A rich REPL for Clojure in the notebook style." :url "https://github.com/JonyEpsilon/gorilla-repl" :license {:name "MIT"} @@ -18,8 +18,7 @@ [javax.servlet/servlet-api "2.5"] [grimradical/clj-semver "0.2.0" :exclusions [org.clojure/clojure]] [cider/cider-nrepl "0.8.1"] - [visi/core "0.1.0-SNAPSHOT"] - [org.clojure/tools.nrepl "0.2.3"]] + [org.clojure/tools.nrepl "0.2.7"]] :main ^:skip-aot gorilla-repl.core :target-path "target/%s" :profiles {:uberjar {:aot :all}}) diff --git a/src/gorilla_repl/core.clj b/src/gorilla_repl/core.clj index 323bc7d3f..d564b6af3 100644 --- a/src/gorilla_repl/core.clj +++ b/src/gorilla_repl/core.clj @@ -113,31 +113,24 @@ [x] (if (string? x) (read-string x) x)) -;; set to capture the configuration -(def config-capture-hook (atom (fn [& _]))) (defn run-gorilla-server [conf] - ;; get configuration information from parameters - (@config-capture-hook conf) - (let [version (or (get conf "version") + (let [version (or (:version conf) "develop") webapp-requested-port (mk-number (or - (get conf "port") (:port conf) 0)) ip (or - (get conf "ip") (:ip conf) "127.0.0.1") nrepl-requested-port (or (:nrepl-port conf) 0) ;; auto-select port if none requested project (or - (get conf "project") (:project conf) "no project") @@ -158,17 +151,6 @@ (spit (doto (io/file ".gorilla-port") .deleteOnExit) webapp-port) (println (str "Running at http://" ip ":" webapp-port "/worksheet.html .")) - ;; support "touching" a file on startup to let other - ;; processes know that the REPL is listening - (if-let [touch (get conf "touch")] - (do - (try - (let [fos (java.io.FileOutputStream. (java.io.File. touch))] - (.write fos (.getBytes "hello" "UTF-8")) - (.close fos) - ) - (catch Exception e nil)) - )) (println "Ctrl+C to exit.")))) diff --git a/src/gorilla_repl/nrepl.clj b/src/gorilla_repl/nrepl.clj index a4924c571..e9c83b6a4 100644 --- a/src/gorilla_repl/nrepl.clj +++ b/src/gorilla_repl/nrepl.clj @@ -9,10 +9,12 @@ (def nrepl (atom nil)) +(def additional-middleware (atom nil)) + (defn start-and-connect [nrepl-requested-port] (let [cider-mw (map resolve cider/cider-middleware) - middleware (conj cider-mw #'render-mw/render-values) + middleware (conj (concat @additional-middleware cider-mw) #'render-mw/render-values) nr (nrepl-server/start-server :port nrepl-requested-port :handler (apply nrepl-server/default-handler middleware)) nrepl-port (:port nr) From 8e56b8d71963565e7b4aa27032c4909d39e5bbca Mon Sep 17 00:00:00 2001 From: David Pollak Date: Fri, 30 Jan 2015 16:46:04 -0800 Subject: [PATCH 3/4] Mostly done with Visi integration --- resources/public/index.html | 179 +++++++++++++++++++++++++++++++++ resources/public/js/main.js | 28 +++--- resources/public/js/repl-ws.js | 2 +- src/gorilla_repl/core.clj | 33 +++--- src/gorilla_repl/version.clj | 26 ----- 5 files changed, 208 insertions(+), 60 deletions(-) create mode 100644 resources/public/index.html delete mode 100644 src/gorilla_repl/version.clj diff --git a/resources/public/index.html b/resources/public/index.html new file mode 100644 index 000000000..418a4b50a --- /dev/null +++ b/resources/public/index.html @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gorilla REPL + + + + +

Loading ...

+ +
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/resources/public/js/main.js b/resources/public/js/main.js index d509c64d7..d8b58e79d 100644 --- a/resources/public/js/main.js +++ b/resources/public/js/main.js @@ -11,6 +11,8 @@ var app = function () { self.conf = {}; + self.conf.strings = {}; + // Most importantly, the application has a worksheet! This is exposed so that the UI can bind to it, but note that // you should never change the worksheet directly, as this will leave the event handlers in an inconsistent state. // Rather you should use the `setWorksheet` function below. @@ -21,7 +23,7 @@ var app = function () { // whenever we change the filename, we update the URL to match self.filename.subscribe(function (filename) { if (filename !== "") history.pushState(null, null, "?filename=" + filename); - else history.pushState(null, null, self.config.worksheetName || "/worksheet.html"); + else history.pushState(null, null, self.conf.worksheetName || "/worksheet.html"); }); // shows the name of the Leiningen project that gorilla was launched from, makes it easier to manage multiple // tabs with multiple gorilla sessions. @@ -43,9 +45,9 @@ var app = function () { // get hold of configuration information from the backend $.get(/^.*\//.exec(window.location.pathname)[0]+"config") .done(function (data) { - self.config = data; + self.conf = data; // If we've got the configuration, then start the app - self.project(self.config.project); + self.project(self.conf.project); // Prepare an empty worksheet so the UI has something to bind to. We do this as if we are loading a // worksheet on startup, we do it asynchronously, so need to have something in place before starting // the UI. This is easier than having two paths that both have UI startup code on them. (Although, the @@ -54,7 +56,7 @@ var app = function () { self.setWorksheet(ws, ""); // start the UI - commandProcessor.installCommands(self.config.keymap); + commandProcessor.installCommands(self.conf.keymap); ko.applyBindings(self, document.getElementById("document")); // allow the filename to be passed as a parameter @@ -74,17 +76,19 @@ var app = function () { // A helper function to create a new, blank worksheet with some introductory messages in. var setBlankWorksheet = function () { var ws = worksheet(); + var meinConf = self.conf; + var myStrings = self.conf.strings; ws.segments().push( // Note that the variable ck here is defined in commandProcessor.js, and gives the appropriate // shortcut key (ctrl or alt) for the platform. - freeSegment(self.config.introMessage || + freeSegment(self.conf.strings.introMessage || ("# Gorilla REPL\n\nWelcome to gorilla :-)\n\nShift + enter evaluates code. " + "Hit " + ck + "+g twice in quick succession or click the menu icon (upper-right corner) " + "for more commands ...\n\nIt's a good habit to run each worksheet in its own namespace: feel " + "free to use the declaration we've provided below if you'd like.")) ); - ws.segments().push(codeSegment(self.config.nsSegment || - ("(ns " + (self.config.nameSpace || makeHipNSName()) + + ws.segments().push(codeSegment(self.conf.strings.nsSegment || + ("(ns " + (self.conf.nameSpace || makeHipNSName()) + "\n (:require [gorilla-plot.core :as plot]))"))); self.setWorksheet(ws, ""); // make it easier for the user to get started by highlighting the empty code segment @@ -94,7 +98,7 @@ var app = function () { // bound to the window's title self.title = ko.computed(function () { - if (self.filename() === "") return (self.conf.title || "Gorilla REPL - ") + self.project(); + if (self.filename() === "") return (self.conf.strings.title || "Gorilla REPL - ") + self.project(); else return self.project() + " : " + self.filename(); }); @@ -147,8 +151,8 @@ var app = function () { var loadFromFile = function (filename) { // ask the backend to load the data from disk - $.get(/^.*\//.exec(window.location.pathname)[0]+"load", {"worksheet-filename": filename} - .done (function (data) { + $.get(/^.*\//.exec(window.location.pathname)[0]+"load", {"worksheet-filename": filename}) + .done(function (data) { if (data['worksheet-data']) { // parse and construct the new worksheet var segments = worksheetParser.parse(data["worksheet-data"]); @@ -160,10 +164,10 @@ var app = function () { var codeSegments = _.filter(self.worksheet().segments(), function(s) {return s.type === 'code'}); if (codeSegments.length > 0) { eventBus.trigger("worksheet:segment-clicked", {id: codeSegments[0].id}); - if (self.config.recalcAllOnLoad)eventBus.trigger("worksheet:evaluate-all"); + if (self.conf.recalcAllOnLoad) eventBus.trigger("worksheet:evaluate-all"); } } - })) + }) .fail(function () { self.flashStatusMessage("Failed to load worksheet: " + filename, 2000); }); diff --git a/resources/public/js/repl-ws.js b/resources/public/js/repl-ws.js index a70a00a1f..84820ec96 100644 --- a/resources/public/js/repl-ws.js +++ b/resources/public/js/repl-ws.js @@ -190,7 +190,7 @@ var repl = (function () { // If we get here, then we don't know what the message was for - just log it // unless the message is explicitly set as ignore - if (!d.ignore && (d.status.indexOf("done") < 0)) { + if (!d.ignore && (!d.status || d.status.indexOf("done") < 0)) { console.log("Unknown response: " + JSON.stringify(d)); } }; diff --git a/src/gorilla_repl/core.clj b/src/gorilla_repl/core.clj index d564b6af3..a6b6e6a0f 100644 --- a/src/gorilla_repl/core.clj +++ b/src/gorilla_repl/core.clj @@ -15,7 +15,6 @@ [gorilla-repl.websocket-relay :as ws-relay] [gorilla-repl.renderer :as renderer] ;; this is needed to bring the render implementations into scope [gorilla-repl.files :as files] - [gorilla-repl.version :as version] [clojure.set :as set] [clojure.java.io :as io]) (:gen-class)) @@ -116,24 +115,13 @@ (defn run-gorilla-server [conf] - (let [version (or - (:version conf) - "develop") - - webapp-requested-port (mk-number - (or - (:port conf) - 0)) - ip (or - (:ip conf) - "127.0.0.1") + (let [version (or (:version conf) "develop") + webapp-requested-port (mk-number (or (:port conf) 0)) + ip (or (:ip conf) "127.0.0.1") nrepl-requested-port (or (:nrepl-port conf) 0) ;; auto-select port if none requested - - project (or - (:project conf) - "no project") - + project (or (:project conf) "no project") + to-load (or (:load-file conf) "/worksheet.html") keymap (or (:keymap (:gorilla-options conf)) {}) _ (swap! excludes (fn [x] (set/union x (:load-scan-exclude (:gorilla-options conf)))))] ;; app startup @@ -141,16 +129,19 @@ ;; build config information for client (set-config :project project) (set-config :keymap keymap) - ;; check for updates - (version/check-for-update version) ;; runs asynchronously + (set-config :worksheetName to-load) + (set-config :params (:client-params conf)) + (set-config :strings (:client-strings conf)) + (set-config :autosave true) + (set-config :recalcAllOnLoad true) + ;; first startup nREPL (nrepl/start-and-connect nrepl-requested-port) ;; and then the webserver (let [s (server/run-server #'rw-app-routes {:port webapp-requested-port :join? false :ip ip}) webapp-port (:local-port (meta s))] (spit (doto (io/file ".gorilla-port") .deleteOnExit) webapp-port) - (println (str "Running at http://" ip ":" webapp-port "/worksheet.html .")) - + (println (str "Running at http://" ip ":" webapp-port to-load)) (println "Ctrl+C to exit.")))) diff --git a/src/gorilla_repl/version.clj b/src/gorilla_repl/version.clj deleted file mode 100644 index 91ec88736..000000000 --- a/src/gorilla_repl/version.clj +++ /dev/null @@ -1,26 +0,0 @@ -(ns gorilla-repl.version - "Checks whether a newer version of Gorilla REPL is availble." - (:require [org.httpkit.client :as http] - [clj-semver.core :as semver])) - -(def update-url "http://updates.gorilla-repl.org/latest-version?v=") - -(defn version-check - "Compares the latest version to the running version and prints a message to the console if there's an update - available." - [latest current] - (if (not= current "develop") - (if (semver/newer? latest current) - (println "A newer version of Gorilla REPL, version" latest ", is available.")))) - -(defn check-for-update - "This runs asynchronously, and can be fired off and forgotten." - [current] - (http/get - (str update-url current) - {:timeout 30000 :as :text} - (fn [{:keys [status headers body error]}] - (if error - (println "Unable to reach update server.") - (let [body-map (apply hash-map (read-string body))] - (version-check (:version body-map) current)))))) \ No newline at end of file From 739e341d9dad988d251b7e101b489f49c342ce4f Mon Sep 17 00:00:00 2001 From: David Pollak Date: Wed, 4 Mar 2015 21:36:52 -0800 Subject: [PATCH 4/4] Fixed some stuff that happens when there's no Visi involved --- resources/public/js/main.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/public/js/main.js b/resources/public/js/main.js index d8b58e79d..66f9c6305 100644 --- a/resources/public/js/main.js +++ b/resources/public/js/main.js @@ -76,19 +76,19 @@ var app = function () { // A helper function to create a new, blank worksheet with some introductory messages in. var setBlankWorksheet = function () { var ws = worksheet(); - var meinConf = self.conf; - var myStrings = self.conf.strings; + var meinConf = self.conf || {}; + var myStrings = self.conf.strings || {}; ws.segments().push( // Note that the variable ck here is defined in commandProcessor.js, and gives the appropriate // shortcut key (ctrl or alt) for the platform. - freeSegment(self.conf.strings.introMessage || + freeSegment(myStrings.introMessage || ("# Gorilla REPL\n\nWelcome to gorilla :-)\n\nShift + enter evaluates code. " + "Hit " + ck + "+g twice in quick succession or click the menu icon (upper-right corner) " + "for more commands ...\n\nIt's a good habit to run each worksheet in its own namespace: feel " + "free to use the declaration we've provided below if you'd like.")) ); - ws.segments().push(codeSegment(self.conf.strings.nsSegment || - ("(ns " + (self.conf.nameSpace || makeHipNSName()) + + ws.segments().push(codeSegment(myStrings.nsSegment || + ("(ns " + (meinConf.nameSpace || makeHipNSName()) + "\n (:require [gorilla-plot.core :as plot]))"))); self.setWorksheet(ws, ""); // make it easier for the user to get started by highlighting the empty code segment