Permalink
Browse files

File editor for web repl:

1. Create a CodeMirror-based file editor (in webedit.cljs)
2. Create webrepl3.cljs and repl3.html, which use the file editor
3. build2.sh builds the console-only version of clojurescript.net;
   build3.sh builds the version including both console and file editor.

The file editor's contents can be evaluated in the REPL by pressing
Ctrl/Cmd+E, and the editor itself can be shown and hidden by pressing
the Show/Hide file editor link.
  • Loading branch information...
1 parent aea80af commit f0cbeb9dcb7ee4cde11c31529bdc62737d7b23ac @arthuredelstein arthuredelstein committed Feb 21, 2013
Showing with 381 additions and 5 deletions.
  1. +3 −0 .gitmodules
  2. +5 −4 src/cljs/webconsole.cljs
  3. +86 −0 src/cljs/webedit.cljs
  4. +26 −0 src/cljs/webrepl3.cljs
  5. +6 −0 web/build2.sh
  6. +6 −0 web/build3.sh
  7. +221 −0 web/repl3.html
  8. +27 −1 web/stylesheets/himera.css
  9. +1 −0 web/vendor/codemirror
View
@@ -1,3 +1,6 @@
[submodule "web/vendor/jq-console"]
path = web/vendor/jq-console
url = https://github.com/replit/jq-console
+[submodule "web/vendor/codemirror"]
+ path = web/vendor/codemirror
+ url = https://github.com/marijnh/CodeMirror.git
View
@@ -31,10 +31,14 @@
(.Write message "jqconsole-output"))
prompt-text))
+(defn register-shortcuts [jqconsole shortcut-map]
+ (doseq [[key callback] shortcut-map]
+ (.RegisterShortcut jqconsole key callback)))
+
(defn console
"Create and initialize the REPL console, with a shortcut-map that
maps keys to callback functions."
- [console-selector shortcut-map]
+ [console-selector]
(repl/init)
(let [jqconsole
(.jqconsole (js/$ console-selector)
@@ -47,7 +51,4 @@
(set! *rtn* #(.Write jqconsole % "jqconsole-output"))
(set! *err* #(.Write jqconsole % "jqconsole-message-error"))
(set! *print-fn* #(*out* %1))
- ;; key binding
- (doseq [[key callback] shortcut-map]
- (.RegisterShortcut jqconsole key callback))
(start-prompt jqconsole)))
View
@@ -0,0 +1,86 @@
+(ns webedit
+ (:require [cljs.repl :as repl]))
+
+
+;; file storage
+
+(defn store-item
+ "Writes text at key in localStorage."
+ [key text]
+ (-> js/window .-localStorage (.setItem key text)))
+
+(defn load-item
+ "Reads text at key in localStorage."
+ [key]
+ (-> js/window .-localStorage (.getItem key)))
+
+;; editor keys
+
+(defn- map->js [m]
+ (let [out (js-obj)]
+ (doseq [[k v] m]
+ (aset out (name k) v))
+ out))
+
+(defn str-contains? [s x]
+ (not= (.indexOf s x) -1))
+
+(defn mac? []
+ (str-contains? (str (.-platform js/navigator)) "Mac"))
+
+(def command-prefix (if (mac?) "Cmd" "Ctrl"))
+
+(defn register-shortcuts [editor key-map]
+ (let [js-key-map
+ (->> (for [[k callback] key-map]
+ [(str command-prefix "-" k) callback])
+ (into {})
+ map->js)]
+ (.addKeyMap editor js-key-map)))
+
+;; show/hide editor
+
+(def editor-visible (atom false))
+
+(def editor-visible-key "__editor_visible")
+
+(defn update-editor-visibility [show?]
+ (let [container (js/$ "#editor-container")]
+ (if show?
+ (.slideDown container 100)
+ (.slideUp container 100))))
+
+(defn store-editor-visibility [show?]
+ (store-item editor-visible-key (str show?)))
+
+(defn update-link [show?]
+ (.html (js/$ "#toggle-editor")
+ (str (if show? "Hide" "Show")
+ " file editor")))
+
+(defn add-updating-watch [reference fun]
+ (add-watch reference fun (fn [_ _ _ value] (fun value))))
+
+(defn setup-editor-toggling []
+ (add-updating-watch editor-visible update-editor-visibility)
+ (add-updating-watch editor-visible store-editor-visibility)
+ (add-updating-watch editor-visible update-link)
+ (.click (js/$ "#toggle-editor") #(swap! editor-visible not))
+ (reset! editor-visible (= "true" (load-item editor-visible-key))))
+
+;; main editor function
+
+(defn editor []
+ (let [eval-cmd (str command-prefix "-E")]
+ (setup-editor-toggling)
+ (.html (js/$ "#tiny-note")
+ (str "Press " eval-cmd
+ " to evaluate file in REPL."))
+ (doto
+ (.fromTextArea js/CodeMirror
+ (.getElementById js/document "editor")
+ (map->js {:mode "clojure"
+ :lineNumbers true
+ :matchBrackets true}))
+ (.setValue (or (load-item "scratch")
+ ";; Develop your clojurescript program here")))))
View
@@ -0,0 +1,26 @@
+(ns webrepl
+ (:require [cljs.repl :as repl]
+ [webconsole :as webconsole]
+ [webedit :as webedit]))
+
+(defn evaluate-file
+ "Evaluates the editor's file in the REPL console."
+ [editor console]
+ (let [text (.getValue editor)
+ prompt-text (webconsole/cancel-input console "Evaluating file...\n")]
+ (repl/eval-print text)
+ (webedit/store-item "scratch" text)
+ (webconsole/start-prompt console prompt-text)))
+
+(defn start-app []
+ (let [editor (webedit/editor)
+ console (webconsole/console "#console")]
+ (webconsole/register-shortcuts console
+ {"E" #(this-as this-console
+ (evaluate-file editor this-console))})
+ (webedit/register-shortcuts editor
+ {"E" #(evaluate-file % console)})))
+
+(.ready (js/jQuery js/document) start-app)
+
+
View
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+cd $(dirname ${0})
+
+echo "Building webrepl2.js"
+time ../bin/cljsc ../src/cljs/webrepl2.cljs > webrepl2.js
View
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+cd $(dirname ${0})
+
+echo "Building webrepl3.js"
+time ../bin/cljsc ../src/cljs/webrepl3.cljs > webrepl3.js
View
@@ -0,0 +1,221 @@
+<!--
+Copyright (c) 2013 Joel Martin
+Copyright (c) 2012 Fogus, Jen Myers and Relevance Inc.
+All rights reserved. The use and distribution terms for this software
+are covered by the Eclipse Public License 1.0
+(http://opensource.org/licenses/eclipse-1.0.php) which can be found in
+the file COPYING the root of this distribution. By using this
+software in any fashion, you are agreeing to be bound by the terms of
+this license. You must not remove this notice, or any other, from
+this software.
+-->
+
+<html>
+<head>
+ <!--<script type="text/javascript" src="javascript/jquery-1.4.2.min.js"></script>-->
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
+ <script type="text/javascript" src="vendor/jq-console/jqconsole.min.js"></script>
+ <script src="vendor/codemirror/lib/codemirror.js"></script>
+ <link rel="stylesheet" href="vendor/codemirror/lib/codemirror.css">
+ <script src="vendor/codemirror/mode/clojure/clojure.js"></script>
+ <script src="vendor/codemirror/addon/edit/matchbrackets.js"></script>
+ <script type="text/javascript" src="out/goog/base.js"></script>
+ <script type="text/javascript" src="webrepl3.js"></script>
+ <script type="text/javascript">
+ goog.require('webrepl');
+ </script>
+ <link rel="stylesheet" type="text/css" href="stylesheets/base.css" />
+ <link rel="stylesheet" type="text/css" href="stylesheets/layout.css" />
+ <link rel="stylesheet" type="text/css" href="stylesheets/skeleton.css" />
+ <link rel="stylesheet" type="text/css" href="stylesheets/himera.css" />
+ <style type="text/css" media="screen">
+ </style>
+ <title>ClojureScript-in-ClojureScript REPL</title>
+</head>
+<body>
+
+ <div class="container">
+ <h1 id="title"><a href="http://www.clojurescript.net"/>ClojureScript.net</a></h1>
+
+ <h2>ClojureScript Web REPL</h2>
+
+ <div id="editor-container" class="sixteen columns">
+ <div id="tiny-note" class="tiny-note"></div>
+ <textarea class="editor" id="editor">;; Develop your clojurescript program here.
+;; Ctrl+E/Cmd+E evaluates file in the REPL.</textarea>
+ </div>
+
+ <div id="console-container" class="sixteen columns">
+ <div class="console" id="console"></div>
+ </div>
+ <div class="eight columns">
+ <h3><span style="cursor: pointer" class="doc-link" id="toggle-editor">Show file editor</a></h3>
+ </div>
+ <div class="eight columns">
+ <div class="source">
+ <a href="http://github.com/kanaka/clojurescript">View source on Github <img src="images/github-icon.png" /></a></p>
+ </div><!-- /source -->
+ </div>
+
+ <div class="rule sixteen columns"></div>
+
+ <div class="sixteen columns">
+ <h3>ClojureScript at a glance - <a href="http://appletree.or.kr/quick_reference_cards/Others/ClojureScript%20Cheat%20Sheet.pdf" class="doc-link">PDF</a> | <a href="http://clojuredocs.org/" class="doc-link">Searchable docs</a> | <a href="synonym.html" class="doc-link">Translations from JavaScript</a></h3>
+ </div>
+
+ <div class="cheat-box-container eight columns">
+ <div class="cheat-box">
+ <h4>Datatypes</h4>
+ <table>
+ <tr class="row-one">
+ <td class="row-label">Map</td>
+ <td>{:key1 :val1, :key2 :val2}</td>
+ </tr>
+ <tr>
+ <td class="row-label">Vectors</td>
+ <td>[1 2 3 4 :a :b :c 1 2]</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">Sets</td>
+ <td>#{:a :b :c 1 2 3}</td>
+ </tr>
+ <tr>
+ <td class="row-label">Scalars</td>
+ <td>a-symbol, :a-keyword, "a string"</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">Arrays</td>
+ <td>(array 1 2 3)</td>
+ </tr>
+ </table>
+ </div><!-- /cheat-box -->
+ <div class="cheat-box">
+ <h4>Functions</h4>
+ <table>
+ <tr class="row-one">
+ <td class="row-label">Calling</td>
+ <td>(<span class="ebnf">&lt;function&gt;</span>
+ <span class="ebnf">&lt;args*&gt;</span>)</td>
+ </tr>
+ <tr>
+ <td class="row-label">Defining named functions</td>
+ <td>(defn <span class="ebnf">&lt;name&gt;</span>
+ [<span class="ebnf">&lt;args*&gt;</span>]
+ <span class="ebnf">|constraints|</span>
+ <span class="ebnf">&lt;actions*&gt;</span>)</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">Anonymous function</td>
+ <td>(fn <span class="ebnf">|name|</span>
+ [<span class="ebnf">&lt;args*&gt;</span>]
+ <span class="ebnf">|constraints|</span>
+ <span class="ebnf">&lt;actions*&gt;</span>)</td>
+ </tr>
+ <tr>
+ <td class="row-label">Anonymous inline function</td>
+ <td>#(<span class="ebnf">&lt;action&gt;</span>
+ <span class="ebnf">|%,%2,%3, or %&|</span>)</td>
+ </tr>
+ </table>
+ </div><!-- /cheat-box -->
+ <div class="cheat-box">
+ <h4>Useful Macros</h4>
+ <table>
+ <tr class="row-one">
+ <td class="row-label">Conditionals</td>
+ <td>if if-let cond condp and or when when-let</td>
+ </tr>
+ <tr>
+ <td class="row-label">Nesting, chaining, and Interop</td>
+ <td>-> ->> doto .. .</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">Defining things</td>
+ <td>def defn fn let binding defmulti defmethod deftype defrecord reify this-as</td>
+ </tr>
+ </table>
+ </div>
+ </div><!-- /cheat-box-container -->
+
+ <div class="cheat-box-container eight columns">
+ <div class="cheat-box">
+ <h4>Useful Functions</h4>
+ <table>
+ <tr class="row-one">
+ <td class="row-label">Math</td>
+ <td>+ - * / quot rem mod inc dec max min</td>
+ </tr>
+ <tr>
+ <td class="row-label">Comparison</td>
+ <td>= == not= < > <= >=</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">Predicates</td>
+ <td>nil? identical? zero? pos? neg? even? odd? true? false?</td>
+ </tr>
+ <tr>
+ <td class="row-label">Data processing</td>
+ <td>map reduce filter partition split-at split-with</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">Data create</td>
+ <td>vector vec hash-map set for list list*</td>
+ </tr>
+ <tr>
+ <td class="row-label">Data inspection</td>
+ <td>first rest get get-in keys vals count get nth contains? find</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">Data manipulation</td>
+ <td>seq into conj cons assoc assoc-in dissoc zipmap merge merge-with select-keys update-in</td>
+ </tr>
+ <tr>
+ <td class="row-label">Arrays</td>
+ <td>first rest get get-in keys vals count get nth contains? find</td></td>
+ </tr>
+ </table>
+ </div><!-- /cheat-box -->
+ <div class="cheat-box">
+ <h4>JavaScript Interop</h4>
+ <table>
+ <tr class="row-one">
+ <td class="row-label">Method call</td>
+ <td>(.the-method target-object args...)</td>
+ </tr>
+ <tr>
+ <td class="row-label">Property access</td>
+ <td>(.-property target-object -property)</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">Property setting</td>
+ <td>(set! (.-title js/document) "Hi!")</td>
+ </tr>
+ <tr>
+ <td class="row-label">Direct JavaScript</td>
+ <td>(js/alert "Hello Cleveland!")</td>
+ </tr>
+ <tr class="row-one">
+ <td class="row-label">External library use</td>
+ <td>(.text (js/jQuery "#title") "ClojureScript Rocks!")</td>
+ </tr>
+ </table>
+ </div>
+ </div><!-- /cheat-box-container -->
+
+ <div class="rule sixteen columns"></div>
+
+ <div class="column footer-logo">
+ <div>Cljs-in-Cljs &copy; 2013 Joel Martin</div>
+ <div>Himera design &copy; 2012-2013 <a ref="http://www.fogus.me">Fogus</a>, <a href="http://jenmyers.net/">Jen Myers</a> and <a href="http://www.thinkrelevance.com">Relevance Inc.</a></div>
+ </div>
+
+ <ul class="footer-links">
+ <li><a href="http://clojure.org/">Clojure.org</a></li>
+ <li><a href="https://github.com/kanaka/clojurescript">Cljs-in-Cljs</a></li>
+ <li><a href="https://github.com/clojure/clojurescript">ClojureScript</a></li>
+ <li><a href="http://clojuredocs.org/">Clojure Docs</a></li>
+ </ul>
+
+ </div><!-- / container -->
+</body>
+</html>
Oops, something went wrong.

0 comments on commit f0cbeb9

Please sign in to comment.