diff --git a/visualisers/hmvis/package-lock.json b/visualisers/hmvis/package-lock.json index 8fd5084..4b71123 100644 --- a/visualisers/hmvis/package-lock.json +++ b/visualisers/hmvis/package-lock.json @@ -7,6 +7,7 @@ "dependencies": { "ace-builds": "^1.32.7", "react": "16.13.0", + "react-ace": "^10.1.0", "react-dom": "16.13.0" }, "devDependencies": { @@ -308,6 +309,11 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -473,6 +479,16 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -731,6 +747,22 @@ "node": ">=0.10.0" } }, + "node_modules/react-ace": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-10.1.0.tgz", + "integrity": "sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==", + "dependencies": { + "ace-builds": "^1.4.14", + "diff-match-patch": "^1.0.5", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "16.13.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.0.tgz", @@ -1314,6 +1346,11 @@ "minimalistic-assert": "^1.0.0" } }, + "diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -1458,6 +1495,16 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -1691,6 +1738,18 @@ "prop-types": "^15.6.2" } }, + "react-ace": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-10.1.0.tgz", + "integrity": "sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==", + "requires": { + "ace-builds": "^1.4.14", + "diff-match-patch": "^1.0.5", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "prop-types": "^15.7.2" + } + }, "react-dom": { "version": "16.13.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.0.tgz", diff --git a/visualisers/hmvis/package.json b/visualisers/hmvis/package.json index 5bfc0a8..ebde054 100644 --- a/visualisers/hmvis/package.json +++ b/visualisers/hmvis/package.json @@ -5,6 +5,7 @@ "dependencies": { "ace-builds": "^1.32.7", "react": "16.13.0", + "react-ace": "^10.1.0", "react-dom": "16.13.0" } } diff --git a/visualisers/hmvis/public/css/main.css b/visualisers/hmvis/public/css/main.css index 82ed759..d6ce1c6 100644 --- a/visualisers/hmvis/public/css/main.css +++ b/visualisers/hmvis/public/css/main.css @@ -1,26 +1,45 @@ +@import "solarized.css"; + +html, body +{ height: 100% +} + body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - color: green; overflow: hidden; } -#editor { - margin: 0; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; +.editor-container +{ position: relative +; height: 80vh } -#type-check { +#editor +{ width: 100%; +; height: 100% +; position: relative +} + +#type-check-button { position: fixed; top: 0; left: 50%; z-index: 2; + /* margin: 0 auto; */ transform: translateX(-50%); } +#type-check-output +{ background: green +; width: 100% +; height: 100% +} + +.main-view-container +{ columns: 2 auto; + +} + .split { height: 100%; width: 50%; @@ -40,14 +59,36 @@ body { } .annotation-wrapper +{ display: inline-flex +; flex-direction: column +/* ; border-style: solid */ +/* ; border-width: 0 0 0.45em 0 */ +} + +.typed-wrapper { display: inline-block -; padding-bottom: 1em -; border-style: solid -; border-color: green -; border-width: 0 0 4px 0 } .annotation-wrapper .annotation -{ position: fixed +{ position: relative +; bottom: 0 +; min-height: 0.50em } +.annotation-text +{ display: none +} + +.annotation.hovering > .annotation-text +{ display: inline-block +} + +.code-wrapper +{ display: inline-block +} + +/* .typed-wrapper.hovering > .code-wrapper */ +/* { border-width: 0.2em */ +/* ; border-style: solid */ +/* } */ + diff --git a/visualisers/hmvis/public/css/solarized.css b/visualisers/hmvis/public/css/solarized.css new file mode 100644 index 0000000..2ba66d1 --- /dev/null +++ b/visualisers/hmvis/public/css/solarized.css @@ -0,0 +1,303 @@ +@import url(http://fonts.googleapis.com/css?family=PT+Sans); +@import url(http://fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700); +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section, +summary { + display: block; +} +audio, +canvas, +video { + display: inline-block; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden] { + display: none; +} +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +a:focus { + outline: thin dotted; +} +a:active, +a:hover { + outline: 0; +} +h1 { + font-size: 2em; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +mark { + background: #ff0; + color: #000; +} +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} +pre { + white-space: pre-wrap; + word-wrap: break-word; +} +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 0; +} +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} +legend { + border: 0; + padding: 0; +} +button, +input, +select, +textarea { + font-family: inherit; + font-size: 100%; + margin: 0; +} +button, +input { + line-height: normal; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +input[disabled] { + cursor: default; +} +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + padding: 0; +} +input[type="search"] { + -webkit-appearance: textfield; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} +textarea { + overflow: auto; + vertical-align: top; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +html { + font-family: 'PT Sans', sans-serif; +} +pre, +code { + font-family: 'Inconsolata', sans-serif; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: 'PT Sans Narrow', sans-serif; + font-weight: 700; +} +html { + background-color: #eee8d5; + color: #657b83; + margin: 1em; +} +body { + background-color: #fdf6e3; + margin: 0 auto; + max-width: 23cm; + border: 1pt solid #93a1a1; + padding: 1em; +} +code { + background-color: #eee8d5; + padding: 2px; +} +a { + color: #b58900; +} +a:visited { + color: #cb4b16; +} +a:hover { + color: #cb4b16; +} +h1 { + color: #d33682; +} +h2, +h3, +h4, +h5, +h6 { + color: #859900; +} +pre { + background-color: #fdf6e3; + color: #657b83; + border: 1pt solid #93a1a1; + padding: 1em; + box-shadow: 5pt 5pt 8pt #eee8d5; +} +pre code { + background-color: #fdf6e3; +} +h1 { + font-size: 2.8em; +} +h2 { + font-size: 2.4em; +} +h3 { + font-size: 1.8em; +} +h4 { + font-size: 1.4em; +} +h5 { + font-size: 1.3em; +} +h6 { + font-size: 1.15em; +} +.tag { + background-color: #eee8d5; + color: #d33682; + padding: 0 0.2em; +} +.todo, +.next, +.done { + color: #fdf6e3; + background-color: #dc322f; + padding: 0 0.2em; +} +.tag { + -webkit-border-radius: 0.35em; + -moz-border-radius: 0.35em; + border-radius: 0.35em; +} +.TODO { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + background-color: #2aa198; +} +.NEXT { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + background-color: #268bd2; +} +.ACTIVE { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + background-color: #268bd2; +} +.DONE { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + background-color: #859900; +} +.WAITING { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + background-color: #cb4b16; +} +.HOLD { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + background-color: #d33682; +} +.NOTE { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + background-color: #d33682; +} +.CANCELLED { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + background-color: #859900; +} + diff --git a/visualisers/hmvis/public/index.html b/visualisers/hmvis/public/index.html index 90797a6..35d59fe 100644 --- a/visualisers/hmvis/public/index.html +++ b/visualisers/hmvis/public/index.html @@ -12,18 +12,10 @@ - -
-
id = \x -> x
-twice f x = f (f x)
-flip f x y = f y x
-
+
+
+
-
-
- - - diff --git a/visualisers/hmvis/src/hmvis/annotated.cljs b/visualisers/hmvis/src/hmvis/annotated.cljs index a13efe1..9611ac0 100644 --- a/visualisers/hmvis/src/hmvis/annotated.cljs +++ b/visualisers/hmvis/src/hmvis/annotated.cljs @@ -24,62 +24,81 @@ (defn formatln [fs & rest] (apply cl-format true (str fs "~%") rest)) -(defn Annotation [text visible?] - (if visible? - [:div {:class "annotation"} - text] - nil)) - (def nesting-rainbow (cycle ["red" "orange" "yellow" "green" "blue" "purple"])) -(defn Typed [t child] +(defn text-colour-by-background [colour] + (match colour + "yellow" "black" + _ "white")) + +(defn Annotation [colour text hovering?] + [:div {:class (if @hovering? + "annotation hovering" + "annotation") + :on-mouse-enter #(reset! hovering? true) + :on-mouse-leave #(reset! hovering? false) + :style {:background colour + :color (text-colour-by-background colour)}} + [:div {:class "annotation-text"} + text]]) + +(defn Typed [colour t child] (let [hovering? (r/atom false)] (fn [] - [:div {:class "annotation-wrapper" - :on-mouse-enter #(reset! hovering? true) - :on-mouse-leave #(reset! hovering? false)} - child - [Annotation t @hovering?]]))) + [:div {:class "annotation-wrapper"} + [:div {:class (if @hovering? + "typed-wrapper hovering" + "typed-wrapper") + } + [:div {:class "code-wrapper"} child]] + [Annotation colour t hovering?]]))) (declare Expr) -(defn LambdaExpr [binds body] +(defn LambdaExpr [colours binds body] [:<> [:code (hsep "λ" (apply hsep binds) "-> ")] - [Expr 0 body]]) + [Expr colours 0 body]]) (defn VarExpr [var-id] [:code var-id]) -(defn AppExpr [f x] - [:<> [Expr ppr/app-prec f] +(defn AppExpr [colours f x] + [:<> [Expr colours ppr/app-prec f] " " - [Expr ppr/app-prec1 x]]) + [Expr colours ppr/app-prec1 x]]) -(defn Expr [p {e :e t :type}] +(defn Expr [[c & colours] p {e :e t :type}] (match e {:InL {:tag "LamF" :contents [bs body & _]}} (maybe-parens (< ppr/app-prec1 p) - [Typed t [LambdaExpr bs body]]) + [Typed c t [LambdaExpr colours bs body]]) {:InL {:tag "VarF" :contents var-id}} - [Typed t [VarExpr var-id]] + [Typed c t [VarExpr var-id]] {:InL {:tag "AppF" :contents [f x]}} (maybe-parens (< ppr/app-prec p) - [Typed t [AppExpr f x]]) + [Typed c t [AppExpr colours f x]]) :else [:code ""])) +(def rainbow-cycle (cycle ["red" + "orange" + "yellow" + "green" + "blue" + "violet"])) + (defn render-decl [{name :name body :body}] [:code {:key name :display "block"} - (str name " = ") [Expr 0 body] #_ (render-expr body) + (str name " = ") [Expr rainbow-cycle 0 body] #_ (render-expr body) [:br]]) -(defn type-checker [] +(defn TypeChecker [] [:div (map render-decl (or @tc-input []))]) -(defn init [] - (rdom/render [type-checker] - (js/document.querySelector "#output"))) +; (defn init [] +; (rdom/render [type-checker] +; (js/document.querySelector "#output"))) diff --git a/visualisers/hmvis/src/main.cljs b/visualisers/hmvis/src/main.cljs index 0a4b5d3..647003e 100644 --- a/visualisers/hmvis/src/main.cljs +++ b/visualisers/hmvis/src/main.cljs @@ -1,16 +1,21 @@ (ns main (:require [clojure.spec.alpha :as s] + ["react-ace$default" :as AceEditor] + ["ace-builds/src-noconflict/mode-haskell"] + ["ace-builds/src-noconflict/theme-solarized_light"] + ["ace-builds/src-noconflict/keybinding-vim"] [wscljs.client :as ws] [wscljs.format :as fmt] [cljs.core.match :refer-macros [match]] [hmvis.annotated :as annotated] + [reagent.core :as r] [reagent.dom :as rdom])) -(def *editor - (doto (js/ace.edit "editor") - (.setTheme "ace/theme/solarized_light") - (.setKeyboardHandler "ace/keyboard/vim") - (.setOption "mode" "ace/mode/haskell"))) +; (def *editor +; (doto (js/ace.edit "editor") +; (.setTheme "ace/theme/solarized_light") +; (.setKeyboardHandler "ace/keyboard/vim") +; (.setOption "mode" "ace/mode/haskell"))) (def *output (.querySelector js/document "#output")) @@ -42,20 +47,55 @@ (defn send [msg] (ws/send *socket msg fmt/json)) -(defn init-type-check-button [] - (let [b (.querySelector js/document "#type-check")] - (.addEventListener b "click" - #(send {:command "annotate" - :source (.getValue *editor)})))) +; (defn init-type-check-button [] +; (let [b (.querySelector js/document "#type-check")] +; (.addEventListener b "click" +; #(send {:command "annotate" +; :source (.getValue *editor)})))) + +(defonce *editor nil) + +(defn TypeCheckButton [] + [:button {:id "type-check-button" + :on-click #(send {:command "annotate" + :source (.getValue *editor)})} + "type-check"]) + +(defn Editor [] + [:div {:class "editor-container"} + [(r/adapt-react-class AceEditor) + {:mode "haskell" + :theme "solarized_light" + :keyboardHandler "vim" + :defaultValue (str "id = \\x -> x\n" + "flip f x y = f y x\n") + :style {:width "100%" + :height "100%"} + :on-load (fn [editor] + (set! *editor editor) + (set! (.. editor -container -style -resize) "both") + (js/document.addEventListener + "mouseup" + #(.resize editor))) + :name "editor"} ]]) + +(defn Main [] + [:<> + [:div {:class "main-view-container"} + [TypeCheckButton] + [Editor] + [annotated/TypeChecker] + #_ [:div {:id "type-check-output"} + "doge soge quoge"]] + #_ [annotated/TypeChecker]]) ;; start is called by init and after code reloading finishes (defn ^:dev/after-load start [] - ; (rdom/render [type-checker] (js/document.getElementById "output")) - (annotated/init) + (rdom/render [Main] + (js/document.getElementById "mount")) (js/console.log "start")) (defn init [] - (init-type-check-button) ;; init is called ONCE when the page loads ;; this is called in the index.html and must be exported ;; so it is available even in :advanced release builds