├── Plugin
├── .gitignore
├── project
│ ├── build.properties
│ └── plugins.sbt
├── README.md
├── build.sbt
└── src
│ └── main
│ └── scala
│ └── com.scalakata
│ └── ScalaKata.scala
├── .gitignore
├── Backend
├── src
│ ├── test
│ │ ├── resources
│ │ │ ├── a
│ │ │ │ └── 1.scala
│ │ │ └── assets
│ │ │ │ ├── b
│ │ │ │ └── main.css
│ │ │ │ └── index.html
│ │ └── scala
│ │ │ └── com.scalakata.backend
│ │ │ └── RestSpec.scala
│ └── main
│ │ └── scala
│ │ └── com.scalakata.backend
│ │ ├── Server.scala
│ │ ├── ScalaKataActor.scala
│ │ └── Json.scala
├── project
│ ├── build.properties
│ └── plugins.sbt
└── build.sbt
├── Eval
├── project
│ ├── build.properties
│ ├── plugins.sbt
│ └── Build.scala
├── classPathtest
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── com.example.test
│ │ └── Testing.scala
├── macro
│ └── src
│ │ ├── test
│ │ └── scala
│ │ │ └── com.scalakata.eval
│ │ │ └── InstrumentationSpec.scala
│ │ └── main
│ │ └── scala
│ │ └── com.scalakata.eval
│ │ ├── list
│ │ ├── package.scala
│ │ └── Annotation.scala
└── compile
│ └── src
│ ├── main
│ └── scala
│ │ └── com.scalakata.eval
│ │ ├── Security.scala
│ │ ├── Model.scala
│ │ ├── Eval.scala
│ │ └── Compiler.scala
│ └── test
│ └── scala
│ └── com.scalakata.eval
│ └── CompilerSpec.scala
├── Frontend
├── project
│ ├── build.properties
│ └── plugins.sbt
├── publish.sh
├── .gitignore
├── web
│ ├── assets
│ │ └── favicon.ico
│ ├── app.js
│ ├── services
│ │ ├── katas.js
│ │ ├── scalaEval.js
│ │ ├── wrap.js
│ │ ├── webcam.js
│ │ ├── autocomplete.js
│ │ ├── errorsRenderer.js
│ │ └── insightRenderer.js
│ ├── index.html
│ └── controllers
│ │ └── code.js
├── styles
│ ├── main.less
│ ├── codemirror.less
│ ├── theme.less
│ ├── solarized.less
│ └── layout.less
├── video
│ ├── kinect.sh
│ └── record.sh
├── README.md
├── plugins
│ ├── install.js
│ └── run.js
├── build.sbt
├── cert.pem
├── key.pem
├── bower.json
├── package.json
├── LICENSE
└── gulpfile.js
├── Examples
├── README.md
├── To.png
├── From.png
├── Desugaring.png
├── Instructor.png
└── TypesafeDocumentation.png
├── README.md
├── travis
├── bintray.template
└── bintray.sh
├── tb.scala
├── publish.sh
└── .travis.yml
/Plugin/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | Issues/
3 |
--------------------------------------------------------------------------------
/Backend/src/test/resources/a/1.scala:
--------------------------------------------------------------------------------
1 | scala
2 |
--------------------------------------------------------------------------------
/Backend/src/test/resources/assets/b/main.css:
--------------------------------------------------------------------------------
1 | css
2 |
--------------------------------------------------------------------------------
/Plugin/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 0.13.8
--------------------------------------------------------------------------------
/Backend/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 0.13.8
2 |
--------------------------------------------------------------------------------
/Backend/src/test/resources/assets/index.html:
--------------------------------------------------------------------------------
1 | index
2 |
--------------------------------------------------------------------------------
/Eval/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 0.13.5
2 |
--------------------------------------------------------------------------------
/Frontend/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 0.13.8
2 |
--------------------------------------------------------------------------------
/Frontend/publish.sh:
--------------------------------------------------------------------------------
1 | rm -rf out/
2 | mkdir out
3 | sbt publish
4 |
--------------------------------------------------------------------------------
/Examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | See https://github.com/ScalaKata
4 |
--------------------------------------------------------------------------------
/Plugin/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.1.2")
--------------------------------------------------------------------------------
/Frontend/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.1.2")
--------------------------------------------------------------------------------
/Examples/To.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasseGuillaume/ScalaKata/HEAD/Examples/To.png
--------------------------------------------------------------------------------
/Examples/From.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasseGuillaume/ScalaKata/HEAD/Examples/From.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [Moved to MasseGuillaume/ScalaKata2](https://github.com/MasseGuillaume/ScalaKata2)
2 |
--------------------------------------------------------------------------------
/Examples/Desugaring.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasseGuillaume/ScalaKata/HEAD/Examples/Desugaring.png
--------------------------------------------------------------------------------
/Examples/Instructor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasseGuillaume/ScalaKata/HEAD/Examples/Instructor.png
--------------------------------------------------------------------------------
/travis/bintray.template:
--------------------------------------------------------------------------------
1 | realm = Bintray API Realm
2 | host = api.bintray.com
3 | user = $BT_USER
4 | password = $BT_KEY
--------------------------------------------------------------------------------
/Frontend/.gitignore:
--------------------------------------------------------------------------------
1 | web/SourceCodePro
2 | out/
3 | node_modules/
4 | bower_components/
5 | packages/
6 | dist/
7 | tmp/
8 |
--------------------------------------------------------------------------------
/Frontend/web/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasseGuillaume/ScalaKata/HEAD/Frontend/web/assets/favicon.ico
--------------------------------------------------------------------------------
/Examples/TypesafeDocumentation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasseGuillaume/ScalaKata/HEAD/Examples/TypesafeDocumentation.png
--------------------------------------------------------------------------------
/travis/bintray.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | mkdir -p ~/.bintray
3 | eval "echo \"$(< ./travis/bintray.template)\"" > ~/.bintray/.credentials
--------------------------------------------------------------------------------
/Frontend/styles/main.less:
--------------------------------------------------------------------------------
1 | @import 'solarized.less';
2 | @import 'codemirror.less';
3 | @import 'layout.less';
4 | @import 'theme.less';
--------------------------------------------------------------------------------
/Backend/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2")
2 |
3 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.1.2")
--------------------------------------------------------------------------------
/Eval/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.3.2")
2 |
3 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.1.2")
--------------------------------------------------------------------------------
/Eval/classPathtest/src/main/scala/com.example.test/Testing.scala:
--------------------------------------------------------------------------------
1 | package com.example.test
2 |
3 | object Testing {
4 | val onetwothree = 123
5 | }
--------------------------------------------------------------------------------
/Frontend/web/app.js:
--------------------------------------------------------------------------------
1 | window.app = angular.module('ScalaKata', ['ui.codemirror', 'ngRoute']);
2 |
3 | app.constant('LANGUAGE', 'scala').
4 | constant('VERSION', '0.12.0');
5 |
--------------------------------------------------------------------------------
/Frontend/web/services/katas.js:
--------------------------------------------------------------------------------
1 | app.factory("katas",["$http", function($http) {
2 | return function(path){
3 | return $http.get("/kata/scala" + path + ".scala");
4 | };
5 | }]);
6 |
--------------------------------------------------------------------------------
/tb.scala:
--------------------------------------------------------------------------------
1 | import scala.reflect.runtime.{currentMirror ⇒ cm}
2 | import scala.reflect.runtime.universe._
3 | import scala.tools.reflect.ToolBox
4 | val tb = cm.mkToolBox()
5 |
6 | tb.parse("1+1")
7 |
--------------------------------------------------------------------------------
/Frontend/video/kinect.sh:
--------------------------------------------------------------------------------
1 | sudo modprobe v4l2loopback
2 |
3 | gst-launch -v v4l2src device=/dev/video2 ! \
4 | ffmpegcolorspace ! \
5 | video/x-raw-rgb ! \
6 | ffmpegcolorspace ! \
7 | video/x-raw-yuv,format=\(fourcc\)YUY2 ! \
8 | v4l2sink device=/dev/video1
9 |
--------------------------------------------------------------------------------
/Plugin/README.md:
--------------------------------------------------------------------------------
1 | ## Publish Docker Image
2 |
3 | seq(kataDockerSettings: _*)
4 |
5 | ```
6 | sbt kata:docker // << error here
7 | cd target/docker
8 | sudo docker build -t="masseguillaume/scalakata:0.8.0" .
9 | sudo docker push masseguillaume/scalakata:0.8.0
10 | ```
11 |
--------------------------------------------------------------------------------
/Frontend/README.md:
--------------------------------------------------------------------------------
1 | # ScalaKata
2 |
3 | install node (http://nodejs.org/download/)
4 | install bower & gulp (```sudo npm install -g bower gulp```)
5 |
6 | install Adobe Font Development Kit for OpenType
7 | (http://download.macromedia.com/pub/developer/opentype/FDK-25-LINUX.zip)
8 |
--------------------------------------------------------------------------------
/Frontend/plugins/install.js:
--------------------------------------------------------------------------------
1 | var run = require('./run.js'),
2 | es = require('event-stream');
3 |
4 | exports.bower = function(){
5 | return es.map(function(){
6 | return run("bower", ["install"]);
7 | });
8 | }
9 | exports.npm = function(){
10 | return es.map(function(){
11 | return run("npm", ["install"]);
12 | });
13 | }
--------------------------------------------------------------------------------
/Frontend/video/record.sh:
--------------------------------------------------------------------------------
1 | rm -f output.mp4 &&
2 | play -n synth pl G2 pl B2 pl D3 pl G3 pl D4 pl G4 \
3 | delay 0 .05 .1 .15 .2 .25 remix - fade 0 4 .1 norm -1 && \
4 | ffmpeg \
5 | -s 1366x768 -f x11grab -i :0.0 \
6 | -f alsa -i pulse \
7 | -vcodec libx264 -b 3000k \
8 | -acodec aac -strict experimental \
9 | -af lowpass=f=5000 \
10 | -s hd720 -ab 320k -r 25 -g 25 -threads 0 output.mp4 &&
11 | mplayer output.mp4
12 |
--------------------------------------------------------------------------------
/Frontend/web/services/scalaEval.js:
--------------------------------------------------------------------------------
1 | app.factory("scalaEval",["$http", function($http) {
2 | return {
3 | "insight": function(code){
4 | return $http.post("/eval", {"code": code});
5 | },
6 | "autocomplete": function(code, position){
7 | return $http.post("/completion", {"code": code, "position": position});
8 | },
9 | "typeAt": function(code, position){
10 | return $http.post("/typeAt", {"code": code, "position": position});
11 | }
12 | };
13 | }]);
14 |
--------------------------------------------------------------------------------
/Eval/macro/src/test/scala/com.scalakata.eval/InstrumentationSpec.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata.eval
2 |
3 | import scala.collection.immutable.Queue
4 |
5 | import org.specs2._
6 |
7 | class InstrumentationSpecs extends Specification { def is = s2"""
8 | imports $imports
9 | """
10 | def imports = {
11 | @ScalaKata object P {
12 | import scala.collection.mutable.Stack
13 | case class VV(a: Stack[Int])
14 | VV(Stack(1))
15 | }
16 | P.eval$() must not be empty
17 | }
18 | }
--------------------------------------------------------------------------------
/publish.sh:
--------------------------------------------------------------------------------
1 | # I know this is so stupid
2 |
3 | cd Frontend && \
4 | ./publish.sh && \
5 | cd .. && \
6 | cd Eval && \
7 | sbt publish && \
8 | cd .. && \
9 | cd Backend && \
10 | sbt publish && \
11 | cd .. && \
12 | cd Plugin && \
13 | sbt publish
14 |
15 | # cd Frontend && \
16 | # sbt publish && \
17 | # cd .. && \
18 | # cd Eval && \
19 | # sbt publish && \
20 | # cd .. && \
21 | # cd Backend && \
22 | # sbt publish && \
23 | # cd .. && \
24 | # cd Plugin && \
25 | # sbt publish
26 |
--------------------------------------------------------------------------------
/Frontend/plugins/run.js:
--------------------------------------------------------------------------------
1 | var spawn = require('child_process').spawn,
2 | gutil = require('gulp-util');
3 |
4 | module.exports = function (command, args){
5 | var child = spawn(command, args, {cwd: process.cwd()});
6 |
7 | child.stdout.setEncoding('utf8');
8 | child.stdout.on('data', function (data) {
9 | gutil.log(data);
10 | });
11 |
12 | child.stderr.setEncoding('utf8');
13 | child.stderr.on('data', function (data) {
14 | gutil.log(gutil.colors.red(data));
15 | gutil.beep();
16 | });
17 |
18 | child.on('close', function(code) {
19 | gutil.log("Done with exit code", code);
20 | });
21 | }
--------------------------------------------------------------------------------
/Plugin/build.sbt:
--------------------------------------------------------------------------------
1 | import bintray.Keys._
2 |
3 | sbtPlugin := true
4 |
5 | name := "plugin"
6 |
7 | organization := "com.scalakata"
8 |
9 | version := "0.12.0"
10 |
11 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2")
12 |
13 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "0.5.2")
14 |
15 | licenses := Seq("MIT" -> url("http://www.opensource.org/licenses/mit-license.html"))
16 |
17 | seq(bintraySettings:_*)
18 |
19 | repository in bintray := "sbt-plugins"
20 |
21 | bintrayOrganization in bintray := None
22 |
23 | scalaVersion := "2.10.4"
24 |
25 | scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
26 |
27 | homepage := Some(url("http://scalakata.com"))
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: scala
2 | before_script: ./publish/bintray.sh
3 | script: sbt ++${TRAVIS_SCALA_VERSION} test publishTravis
4 |
5 | scala:
6 | - 2.11.6
7 | jdk:
8 | - openjdk7
9 | - oraclejdk7
10 | - oraclejdk8
11 | notifications:
12 | email:
13 | - masgui@gmail.com
14 | env:
15 | global:
16 | - secure: "HsBKxCc1SydicZr8GtI2oifOSvGdkVhPiIpkpbV1OKLvtNhbWLb+6fAJfQGF8y7FwUFHRw79tlcVqC9bazOEl6q+Nn86UuhS1Xn3ijtQ+wYSt+Xbvxk/Dmok90xUsAFuHircl386rQQn1eLbB00Ock7q7EZj6ZT1tdaZNgsxUaU="
17 | - secure: "IRxUaifbCvhCZywU/Oo1AlXjaRhfATqX4+P3/SiuZl57ps7kjYfcXx7kcPWLTkPV5pkr/rJw9Aqgj1PKl83DdZ8AKP3FcIc51ul8AMk8sqm03fClBYwMKZ5wAcMNgJhnNcBKFrjU6Rsfni/mvCVKlOQRrfCpscSEmblKEZ/hbow="
--------------------------------------------------------------------------------
/Frontend/build.sbt:
--------------------------------------------------------------------------------
1 | name := "frontend"
2 |
3 | organization := "com.scalakata"
4 |
5 | version := "0.12.0"
6 |
7 | autoScalaLibrary := false
8 |
9 | scalaVersion := "2.11.6"
10 |
11 | resourceDirectory in Compile := {
12 | baseDirectory.value / "out"
13 | }
14 |
15 | resourceGenerators in Compile += Def.task {
16 | "gulp build" ! streams.value.log
17 | ((resourceDirectory in Compile).value ***).get
18 | /*Seq.empty[java.io.File]*/
19 | }.taskValue
20 |
21 | publishArtifact in (Compile, packageDoc) := false
22 |
23 | publishArtifact in (Compile, packageSrc) := false
24 |
25 | crossPaths := false
26 |
27 | licenses := Seq("MIT" -> url("http://www.opensource.org/licenses/mit-license.html"))
28 |
29 | seq(bintraySettings:_*)
30 |
--------------------------------------------------------------------------------
/Frontend/cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICDTCCAXYCCQCrZvZIoAqNDzANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJj
3 | YTELMAkGA1UECAwCcWMxETAPBgNVBAcMCE1vbnRyZWFsMRwwGgYDVQQKDBNEZWZh
4 | dWx0IENvbXBhbnkgTHRkMB4XDTE0MDkwMTIxMDQyMFoXDTQyMDExNjIxMDQyMFow
5 | SzELMAkGA1UEBhMCY2ExCzAJBgNVBAgMAnFjMREwDwYDVQQHDAhNb250cmVhbDEc
6 | MBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOB
7 | jQAwgYkCgYEA5ElXk+PrM5MWQo5l+3TeL7RMQCBUsQapqiwQTwljtv9gkIwYBXkp
8 | GScBl2PlYdvzlQJNerz58whzrysa0rznxgHbtSHKJgF1PURfPtHLNrBZWrc86+9u
9 | e4SwIvsVQ8jsVDqtM/OdMSL9v37eqRo3KXF/z/pUs6/SL9We49M5LHcCAwEAATAN
10 | BgkqhkiG9w0BAQUFAAOBgQClvan5abSP1IP8og0zJqj6ssqJjIjpUQQUFhgBvWTU
11 | lJFwLN3ukb8ci7JGwyRfegf4Lz7GaPXFLDz3QB618he+Itp+da/NWTXGPIUSSzRy
12 | +QftA1ELCSVhgww2/mBfmxzUkdhdsCyD9qd9ARLjRmcXnsZ/vJgI//9HxoEaLDQv
13 | xg==
14 | -----END CERTIFICATE-----
15 |
--------------------------------------------------------------------------------
/Eval/compile/src/main/scala/com.scalakata.eval/Security.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata.eval
2 |
3 | import java.security._
4 | import java.io._
5 |
6 | object Security {
7 | val read1 = new java.io.FilePermission("../-", "read")
8 | val read2 = new java.io.FilePermission("../.", "read")
9 | val other = new java.net.NetPermission("specifyStreamHandler")
10 |
11 | class SecurityPolicy extends Policy {
12 | override def implies(domain: ProtectionDomain, permission: Permission) = {
13 | // not in eval
14 | Thread.currentThread().getStackTrace().find(_.getFileName == "(inline)").isEmpty ||
15 | read1.implies(permission) ||
16 | read2.implies(permission) ||
17 | other.implies(permission)
18 | }
19 | }
20 |
21 | def start: Unit = {
22 | Policy.setPolicy(new SecurityPolicy)
23 | System.setSecurityManager(new SecurityManager)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Frontend/key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXgIBAAKBgQDkSVeT4+szkxZCjmX7dN4vtExAIFSxBqmqLBBPCWO2/2CQjBgF
3 | eSkZJwGXY+Vh2/OVAk16vPnzCHOvKxrSvOfGAdu1IcomAXU9RF8+0cs2sFlatzzr
4 | 7257hLAi+xVDyOxUOq0z850xIv2/ft6pGjcpcX/P+lSzr9Iv1Z7j0zksdwIDAQAB
5 | AoGBALLay2SxAwtrR9tpWXlDPPi/F6Z+LhxsDe2njDeAMcGkH3HgcMmI/awZJWQI
6 | /iRHgXXTUgTBnkrM6mXpLaDCJAtBGe4bzPduarygz+o+VDanVwNs2ycORBqNoSnb
7 | 3CSxSWGcbCeAz4NmiDhN8qTqOse77xVkZMaA/uOKEV6muTZhAkEA/X6b9BkkdRj3
8 | kIYp/JMs85f34Xjvis0ymD3vXAg32lbrHOUXoXngAodhUwQtP4YnaS8IIdSWlBhN
9 | BW/H/pFtqQJBAOaK85jUnktdbHv0Y2M920Wlj8R976RxN8ON2vgOoRwDgxz5o8ee
10 | LO4HMNHdhVU2rUzvj62xEdJWouaTW/ei3R8CQDzU2DnwmqYbErW07Hh3HQ9rxYlv
11 | CxTk/yI3oqwUMMsNlqg9dblDVPVmZX2VgAJBgE2mgq4TpPrvBfSrCXRWk9kCQQDQ
12 | 5QD671ULZvigjH3t86OWxQCzA4W8FBZ6LDRH5kGXd5s7CvybKLj/aSaUUsu4vdVj
13 | zAH9WV+Tnor2sHl5tmnzAkEAhl0cHvqgKWNuhpQ+8yzmqpNsQDlbqZCFSvXrP06+
14 | VVs+yWhOCF4/oqTZJhvL4UnxVotj46SGplcQ9RM4cFzPng==
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/Frontend/web/services/wrap.js:
--------------------------------------------------------------------------------
1 | app.factory("wrap", function() {
2 | return function (code, insight){
3 | var import_ = "import com.scalakata.eval._",
4 | macroBegin = "@ScalaKata object $Playground {",
5 | macroEnd = "}",
6 | presentationBegin = "object $Playground {",
7 | presentationEnd = "}",
8 | nl = "\n",
9 | full = insight ?
10 | [import_, macroBegin, code, macroEnd].join(nl)
11 | : [import_, presentationBegin, code, presentationEnd].join(nl)
12 |
13 | return {
14 | codeOffset: ([import_, presentationBegin].join(nl).length),
15 | fixRange: function(range) {
16 | return insight ?
17 | range - ([import_, macroBegin].join(nl).length)
18 | : range - ([import_, presentationBegin].join(nl).length)
19 | },
20 | fixLine: function(line) {
21 | return line - 2
22 | },
23 | full: full
24 | }
25 | };
26 | });
27 |
--------------------------------------------------------------------------------
/Frontend/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ScalaKata",
3 | "version": "0.12.0",
4 | "homepage": "",
5 | "authors": [
6 | "Guillaume Massé"
7 | ],
8 | "description": "ScalaKata",
9 | "dependencies": {
10 | "lodash": "~2.4.1",
11 | "angular": "~1.2.22",
12 | "angular-route": "~1.2.22",
13 | "jquery": "~2.1.0",
14 | "marked": "~0.3.2",
15 | "d3": "~3.4.12",
16 | "octicons": "~2.1.2",
17 | "fontawesome": "~4.1.0",
18 | "getusermedia": "git://github.com/MasseGuillaume/getUserMedia.js.git#1bd2c73b1dbd7b5cec3c1f337e8d9398d10483d7",
19 | "screenfull": "~1.2.1",
20 | "codemirror": "~4.12.0",
21 | "angular-ui-codemirror": "~0.1.6",
22 | "zeroclipboard": "~2.1.6"
23 | },
24 | "keywords": [],
25 | "license": "MIT",
26 | "ignore": [
27 | "**/.*",
28 | "node_modules",
29 | "bower_components",
30 | "app/bower_components",
31 | "test",
32 | "tests"
33 | ],
34 | "resolutions": {
35 | "angular": "1.2.28",
36 | "codemirror": "~4.12.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Backend/build.sbt:
--------------------------------------------------------------------------------
1 | name := "backend"
2 |
3 | organization := "com.scalakata"
4 |
5 | version := "0.12.0"
6 |
7 | scalaVersion := "2.11.6"
8 |
9 | Revolver.settings
10 |
11 | resolvers ++= Seq(
12 | "spray repo" at "http://repo.spray.io",
13 | "typesafe releases" at "http://repo.typesafe.com/typesafe/releases",
14 | "masseguillaume" at "http://dl.bintray.com/content/masseguillaume/maven"
15 | )
16 |
17 | libraryDependencies ++= Seq(
18 | "com.scalakata" %% "eval" % version.value,
19 | "com.scalakata" % "frontend" % "0.10.0",
20 | "io.spray" %% "spray-can" % "1.3.1",
21 | "io.spray" %% "spray-routing" % "1.3.1",
22 | "io.spray" %% "spray-testkit" % "1.3.1" % "test",
23 | "org.specs2" %% s"specs2" % "2.3.12" % "test",
24 | "com.typesafe.akka" %% "akka-actor" % "2.3.3",
25 | "com.typesafe.play" %% "play-json" % "2.4.0-M1"
26 | )
27 |
28 | licenses := Seq("MIT" -> url("http://www.opensource.org/licenses/mit-license.html"))
29 |
30 | seq(bintraySettings:_*)
31 |
32 | scalacOptions ++= Seq("-Yrangepos", "-unchecked", "-deprecation", "-feature")
33 |
--------------------------------------------------------------------------------
/Frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ScalaKata",
3 | "version": "0.12.0",
4 | "dependencies": {},
5 | "description": "ScalaKata",
6 | "repository": "https://github.com/MasseGuillaume/ScalaMirror",
7 | "devDependencies": {
8 | "gulp": "~3.6.0",
9 | "gulp-plumber": "^0.6.6",
10 | "gulp-concat": "~2.1.7",
11 | "express": "~3.5.0",
12 | "event-stream": "~3.0.18",
13 | "gulp-less": "~1.2.3",
14 | "connect-livereload": "~0.3.2",
15 | "gulp-cached": "~0.0.3",
16 | "gulp-livereload": "~2.1.1",
17 | "gulp-minify-css": "~0.3.0",
18 | "gulp-uglify": "~0.2.1",
19 | "gulp-util": "~2.2.14",
20 | "gulp-filter": "~1.0.0",
21 | "gulp-autoprefixer": "0.0.6",
22 | "gulp-rename": "~1.1.0",
23 | "gulp-spawn": "~0.3.0",
24 | "gulp-usemin": "~0.3.1",
25 | "gulp-uglify": "~0.3.1",
26 | "gulp-minify-html": "~0.1.4",
27 | "gulp-minify-css": "~0.3.5",
28 | "gulp-rev": "~0.4.1",
29 | "request": "~2.36.0",
30 | "tiny-lr": "0.1.1"
31 | },
32 | "engines": {
33 | "node": "0.10.x"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Eval/compile/src/main/scala/com.scalakata.eval/Model.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata.eval
2 |
3 | sealed trait Severity
4 | final case object Info extends Severity
5 | final case object Warning extends Severity
6 | final case object Error extends Severity
7 |
8 | case class CompilationInfo(
9 | message: String,
10 | start: Int,
11 | end: Int
12 | )
13 |
14 | case class RuntimeError(
15 | message: String,
16 | line: Int
17 | )
18 |
19 | case class EvalRequest(
20 | code: String
21 | )
22 |
23 | case class EvalResponse(
24 | insight: OrderedRender,
25 | infos: Map[Severity, List[CompilationInfo]],
26 | timeout: Boolean,
27 | runtimeError: Option[RuntimeError]
28 | )
29 |
30 | object EvalResponse {
31 | def empty = EvalResponse(Nil, Map.empty, false, None)
32 | }
33 |
34 | case class TypeAtRequest(
35 | code: String,
36 | position: Int
37 | )
38 |
39 | case class TypeAtResponse(
40 | tpe: String
41 | )
42 |
43 | case class CompletionRequest(
44 | code: String,
45 | position: Int
46 | )
47 |
48 | case class CompletionResponse(
49 | name: String,
50 | signature: String
51 | )
52 |
--------------------------------------------------------------------------------
/Frontend/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Guillaume Massé
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Frontend/styles/codemirror.less:
--------------------------------------------------------------------------------
1 | .code .CodeMirror {
2 | background: transparent;
3 | }
4 |
5 | .CodeMirror-gutters {
6 | width: 30px;
7 | }
8 |
9 | .CodeMirror-linewidget {
10 | overflow: hidden;
11 | }
12 |
13 | .CodeMirror, .CodeMirror-scroll {
14 | height: 100%;
15 | }
16 |
17 | .CodeMirror-lines {
18 | padding: 16px 0;
19 | }
20 |
21 | .CodeMirror-code pre {
22 | padding: 0 16px;
23 | line-height: 21px;
24 | }
25 |
26 | .CodeMirror {
27 | font-size: 18px;
28 | }
29 |
30 | .CodeMirror-hints {
31 | resize: horizontal;
32 | .autocomplete {
33 | width: calc("80%");
34 | }
35 | z-index: 10;
36 | }
37 |
38 | .CodeMirror-hint {
39 | max-width: none;
40 | resize: none;
41 |
42 | .autocomplete-result-name{
43 | }
44 | .autocomplete-result-signature{
45 | opacity:0.5;
46 | }
47 | }
48 |
49 | .CodeMirror-dialog.CodeMirror-dialog-top {
50 | border: none;
51 | margin-left: 56px; // gutter size
52 | line-height: 21px;
53 |
54 | padding-left: 2px;
55 | padding-right: 0;
56 | span {
57 | display: none;
58 | }
59 | input {
60 | font-size: 18px;
61 | padding: 0;
62 | font-family: "Source Code Pro Regular";
63 | width: calc(~"100% - "150px) !important;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Eval/macro/src/main/scala/com.scalakata.eval/list:
--------------------------------------------------------------------------------
1 | q"" EmptyTree
2 | q"$value" Literal
3 | q"name" Ident
4 | q"$expr.$tname" Select
5 | q"$tpname.super[$tpname].$tname" Select
6 | q"$tpname.this" This
7 | q"$expr(...$exprss)" Apply
8 | q"$expr[..$tpts]" TypeApply
9 |
10 | q"$expr(..$exprs) = $expr" Tree
11 |
12 | q"throw $expr" Throw
13 |
14 | q"(..$exprs)" Tree
15 | q"{ ..$stats }" Block
16 | q"if ($expr) $expr else $expr" If
17 | q"$expr match { case ..$cases }" Match
18 | q"try $expr catch { case ..$cases } finally $expr" Try
19 | q"(..$params) ⇒ $expr" Function
20 |
21 | q"while ($expr) $expr" LabelDef
22 | q"do $expr while ($expr)" LabelDef
23 | q"for (..$enums) $expr" Tree
24 | q"for (..$enums) yield $expr" Tree
25 | q"new { ..$earlydefns } with ..$parents { $self ⇒ ..$stats }" Tree
26 |
27 | Definitions
28 |
29 | q"$mods val $pat = $expr"
30 | q"$mods var $tname: $tpt = $expr"
31 |
32 | q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr"
33 | q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self ⇒ ..$stats }"
34 | q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self ⇒ ..$stats }"
35 | q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self ⇒ ..$body }"
36 |
37 |
38 | q"package $ref { ..$topstats }" !!! Not supported
39 | q"package object $tname extends { ..$earlydefns } with ..$parents { $self ⇒ ..$stats }" !!! Not suported
40 |
--------------------------------------------------------------------------------
/Backend/src/main/scala/com.scalakata.backend/Server.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata.backend
2 |
3 | import com.scalakata.eval._
4 |
5 | import akka.actor.{ActorSystem, Props}
6 | import akka.io.IO
7 | import akka.util.Timeout
8 | import spray.can.Http
9 |
10 | import scala.concurrent.duration._
11 | import com.typesafe.config.{ ConfigValueFactory, ConfigFactory, Config }
12 |
13 |
14 | object Boot {
15 | def main(args: Array[String]) = {
16 | val (readyPort :: artifacts :: host :: port ::
17 | production :: security :: timeoutS :: scalacOptions) = args.to[List]
18 |
19 | val timeout = Duration(timeoutS)
20 |
21 | val config: Config = ConfigFactory.parseString(s"""
22 | spray {
23 | can.server {
24 | idle-timeout = ${timeout.toSeconds + 5}s
25 | request-timeout = ${timeout.toSeconds + 2}s
26 | }
27 | }
28 | """)
29 |
30 | implicit val system = ActorSystem("scalakata-playground", config)
31 |
32 | val service = system.actorOf(Props(
33 | classOf[ScalaKataActor], artifacts, scalacOptions, security.toBoolean, timeout
34 | ), "scalakata-service")
35 |
36 |
37 | import akka.pattern.ask
38 | implicit val bindingTimeout = Timeout(5.seconds)
39 | import system.dispatcher
40 | (IO(Http) ? Http.Bind(service, host, port.toInt)) onSuccess {
41 | case _: Http.Bound ⇒ {
42 | if(!production.toBoolean) {
43 | val ready = new java.net.Socket(host, readyPort.toInt)
44 | ready.sendUrgentData(0)
45 | ready.close()
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Backend/src/test/scala/com.scalakata.backend/RestSpec.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata
2 | package backend
3 |
4 | import org.specs2._
5 | import spray.testkit.Specs2RouteTest
6 | import spray.http.StatusCodes._
7 | import spray.http._
8 |
9 | class RestSpec extends Specification with Specs2RouteTest with ScalaKata { def is = s2"""
10 | $assets must work
11 | $assetRoute must handle querystrings
12 | $resourceRoute must redirect resources to index
13 | $index must work
14 | $echo must allow sandboxed xss
15 | """
16 | def actorRefFactory = system
17 | val artifacts: String = ""
18 | val code: String = ""
19 | val codePrelude: String = ""
20 | val scalacOptions: Seq[String] = Seq()
21 | val security = false
22 |
23 | def assets = {
24 | Get("/assets/b/main.css") ~> route ~> check {
25 | responseAs[String] must contain("css")
26 | }
27 | }
28 |
29 | def assetRoute = {
30 | Get("/assets/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML&delayStartupUntil=configured&dummy=.js") ~> route ~> check {
31 | status mustEqual OK
32 | }
33 | }
34 |
35 | def resourceRoute = {
36 | Get("/a/1.scala") ~> route ~> check {
37 | responseAs[String] must contain("index")
38 | }
39 | }
40 |
41 | def index = {
42 | Get("/") ~> route ~> check {
43 | responseAs[String] must contain("index")
44 | }
45 | }
46 |
47 | def echo = {
48 | Post("/echo", FormData(Seq("code"->"code"))) ~> route ~> check {
49 | headers must contain(HttpHeaders.RawHeader("X-XSS-Protection", "0"))
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Frontend/web/services/webcam.js:
--------------------------------------------------------------------------------
1 | app.factory("webcam", ["$q", function($q) {
2 | // mapping: cameraId -> webcam | background
3 | return function(mapping){
4 | var newMapping = $q.defer();
5 |
6 | MediaStreamTrack.getSources(function(sources){
7 | var camIds,
8 | requests,
9 | options = {
10 | audio: false,
11 | video: true,
12 | el: "_",
13 | extern: null,
14 | append: true,
15 | width: 0,
16 | height: 0,
17 | mode: "callback",
18 | onCapture: function () {
19 | window.webcam.save(); // ???
20 | }
21 | };
22 |
23 | camIds =
24 | _.chain(sources).filter(function(e){
25 | return e.kind === 'video';
26 | }).map(function(e){
27 | return e.id;
28 | }).value();
29 |
30 | if(!angular.isDefined(mapping)){
31 | mapping = mapping || _.zipObject(camIds, ["webcam", "background"]);
32 | }
33 | newMapping.resolve(mapping);
34 |
35 | requests = _.map(mapping, function(element, camId){
36 | var ops = angular.copy(options);
37 | ops.video = {
38 | mandatory: {
39 | sourceId: camId
40 | }
41 | };
42 | ops.el = element;
43 |
44 | return ops;
45 | });
46 |
47 | _.forEach(requests, function(op){
48 | getUserMedia(op, function(stream){
49 | var video = op.videoEl;
50 |
51 | var vendorURL = window.URL || window.webkitURL;
52 | video.src = vendorURL ? vendorURL.createObjectURL(stream) : stream;
53 |
54 | video.onerror = function () {
55 | stream.stop();
56 | streamError();
57 | };
58 |
59 | }, function(){
60 | console.log("error");
61 | });
62 | });
63 | });
64 |
65 | return newMapping.promise;
66 | };
67 | }]);
68 |
--------------------------------------------------------------------------------
/Eval/project/Build.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Keys._
3 | import sbtbuildinfo.Plugin._
4 |
5 | object Settings {
6 | lazy val default =
7 | Project.defaultSettings ++
8 | bintray.Plugin.bintraySettings ++
9 | Seq(
10 | organization := "com.scalakata",
11 | scalaVersion := "2.11.6",
12 | version := "0.12.0",
13 | licenses := Seq("MIT" -> url("http://www.opensource.org/licenses/mit-license.html")),
14 | scalacOptions ++= Seq("-Yrangepos", "-unchecked", "-deprecation", "-feature"),
15 | libraryDependencies ++= Seq(
16 | "org.scala-lang" % "scala-compiler" % scalaVersion.value,
17 | "org.scala-lang" % "scala-reflect" % scalaVersion.value,
18 | "org.specs2" %% "specs2-core" % "3.1" % "test"
19 | ),
20 | resolvers += Resolver.sonatypeRepo("releases"),
21 | addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0-M5" cross CrossVersion.full)
22 | )
23 | }
24 |
25 | object EvalBuild extends Build {
26 | import Settings._
27 |
28 | lazy val macro = Project(
29 | id = "macro",
30 | base = file("macro"),
31 | settings = default ++ Seq(
32 | name := "macro"
33 | )
34 | )
35 |
36 | lazy val classPathtest = Project(
37 | id = "classpathTest",
38 | base = file("classPathtest"),
39 | settings = default ++ Seq(
40 | /* dont */ publish := { },
41 | /* dont */ publishLocal := { }
42 | )
43 | )
44 |
45 | lazy val compile = Project(
46 | id = "compile",
47 | base = file("compile"),
48 | settings = default ++ buildInfoSettings ++ Seq(
49 | name := "eval",
50 | sourceGenerators in Test <+= buildInfo,
51 | buildInfoKeys := Seq[BuildInfoKey](
52 | BuildInfoKey.map((fullClasspath in classPathtest in Compile)){ case (k, v) ⇒ k -> v.map(_.data) },
53 | BuildInfoKey.map((exportedProducts in Runtime in macro)){ case (k, v) ⇒ k -> v.map(_.data) },
54 | (scalacOptions in Compile)
55 | ),
56 | buildInfoPackage := "com.scalakata.eval.sbt",
57 | parallelExecution in Test := false
58 | )
59 | ) dependsOn(macro)
60 | }
61 |
--------------------------------------------------------------------------------
/Frontend/styles/theme.less:
--------------------------------------------------------------------------------
1 | .CodeMirror {
2 | ::-webkit-scrollbar {
3 | width: auto;
4 | height: auto;
5 | }
6 | ::-webkit-scrollbar-thumb {
7 | border-radius: 5px;
8 | }
9 |
10 | ::-webkit-scrollbar-thumb {
11 | box-shadow: 0 0 1px grey inset;
12 | }
13 | }
14 | .cm-s-solarized {
15 | .solarized-pallette;
16 |
17 | &.cm-s-dark {
18 | .menu, .clip {
19 | background-color: @base02;
20 | color: @base01;
21 | i:hover, &.active {
22 | color: @base1;
23 | }
24 | }
25 |
26 | .CodeMirror-dialog {
27 | background-color: @base02;
28 | color: @base01;
29 | }
30 |
31 | ::-webkit-scrollbar-corner {
32 | background: @base03;
33 | }
34 |
35 | ::-webkit-scrollbar-thumb {
36 | background: @base02;
37 | box-shadow: 0 0 1px black inset;
38 | }
39 |
40 | .insight {
41 | background-color: @base02;
42 |
43 | &.block .insight {
44 | background: @base03;
45 | }
46 |
47 | hr {
48 | border-color: @base03;
49 | }
50 | }
51 | }
52 |
53 | &.cm-s-light {
54 | .menu, .clip {
55 | background-color: @base2;
56 | color: @base1;
57 | i:hover, &.active {
58 | color: @base01;
59 | }
60 | }
61 |
62 | ::-webkit-scrollbar-corner {
63 | background: @base3;
64 | }
65 |
66 | ::-webkit-scrollbar-thumb {
67 | box-shadow: 0 0 1px grey inset;
68 | }
69 |
70 | .insight {
71 | background-color: @base2;
72 |
73 | &.block .insight {
74 | background: @base3;
75 | }
76 |
77 | hr {
78 | border-color: @base3;
79 | }
80 | }
81 | }
82 | }
83 | .cm-s-mdn-like {
84 | ::-webkit-scrollbar-thumb {
85 | background: #f8f8f8;
86 | box-shadow: 0 0 1px grey inset;
87 | }
88 |
89 | .menu, .clip {
90 | border: 1px solid #ddd;
91 | background-color: #f8f8f8;
92 | color: rgba(0,83,159,0.65);
93 | i:hover, &.active {
94 | color: black;
95 | }
96 | }
97 |
98 | .insight {
99 | background-color: #f8f8f8;
100 | border: 1px solid #ddd;
101 | }
102 | }
103 |
104 | .ui-splitbar {
105 | background: rgba(125,125,125,0.2);
106 | }
107 |
108 | .menu, .clip {
109 | color: grey;
110 | i:hover, &.active {
111 | color: green;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Backend/src/main/scala/com.scalakata.backend/ScalaKataActor.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata
2 | package backend
3 |
4 | import eval._
5 |
6 | import akka.actor._
7 | import spray.routing.HttpService
8 | import spray.http._
9 | import spray.util._
10 |
11 | import scala.concurrent.duration._
12 |
13 | class ScalaKataActor(
14 | override val artifacts: String,
15 | override val scalacOptions: Seq[String],
16 | override val security: Boolean,
17 | override val timeout: Duration) extends Actor with ScalaKata {
18 |
19 | def actorRefFactory = context
20 | def receive = runRoute(route)
21 | }
22 |
23 | trait ScalaKata extends HttpService {
24 | val artifacts: String
25 | val scalacOptions: Seq[String]
26 | val security: Boolean
27 | val timeout: Duration
28 |
29 | implicit def executionContext = actorRefFactory.dispatcher
30 |
31 | import Request._
32 | import Response._
33 |
34 | lazy val compiler = new Compiler(artifacts, scalacOptions, security, timeout)
35 |
36 | val redirectCodebrew = hostName { hn ⇒
37 | if(hn == "codebrew.io") redirect("www.scalakata.com", StatusCodes.PermanentRedirect)
38 | else getFromResource("assets/index.html")
39 | }
40 |
41 | val route = {
42 | path("eval") {
43 | post {
44 | entity(as[EvalRequest]) { request ⇒
45 | val EvalRequest(code) = request
46 | complete(compiler.insight(code))
47 | }
48 | }
49 | } ~
50 | path("completion") {
51 | post {
52 | entity(as[CompletionRequest]) { request ⇒
53 | val CompletionRequest(code, pos) = request
54 | complete(compiler.autocomplete(code, pos))
55 | }
56 | }
57 | } ~
58 | path("typeAt") {
59 | post {
60 | entity(as[TypeAtRequest]) { request ⇒
61 | val TypeAtRequest(code, pos) = request
62 | complete(compiler.typeAt(code, pos, pos))
63 | }
64 | }
65 | } ~
66 | path("echo") {
67 | post {
68 | formFields('code){ code ⇒
69 | respondWithHeader(HttpHeaders.RawHeader("X-XSS-Protection", "0")) {
70 | complete(HttpEntity(ContentType(MediaTypes.`text/html`, HttpCharsets.`UTF-8`),
71 | HttpData(code)))
72 | }
73 | }
74 | }
75 | } ~
76 | pathSingleSlash {
77 | redirectCodebrew
78 | } ~
79 | path("assets" / Rest) { path ⇒
80 | getFromResource(s"assets/$path")
81 | } ~
82 | path("kata" / "scala" / Rest) { path ⇒
83 | getFromResource(s"scala/$path")
84 | } ~
85 | path(Rest) { path ⇒
86 | redirectCodebrew
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Frontend/web/services/autocomplete.js:
--------------------------------------------------------------------------------
1 | app.run(["scalaEval", function(scalaEval){
2 | // remove select line
3 | delete CodeMirror.keyMap.sublime["Ctrl-L"];
4 |
5 | function hint(cm, sf, cf, single){
6 | var wrap = CodeMirror.hack.wrap(
7 | CodeMirror.hack.code.getDoc().getValue(),
8 | false
9 | );
10 |
11 | sf(
12 | wrap.full,
13 | cm.getDoc().indexFromPos(cm.getCursor()) + 1 + wrap.codeOffset
14 | ).then(function(r){
15 | var data = r.data;
16 |
17 | CodeMirror.showHint(cm, function(cm, options){
18 | var i;
19 | var cur= cm.getCursor();
20 | var curTo = {"ch" : cur.ch, "line" : cur.line};
21 | var curFrom = {"ch" : cur.ch, "line" : cur.line};
22 |
23 | var currentLine = cm.getDoc().getValue().split("\n")[cur.line];
24 |
25 | function delimiter(c){
26 | return /^[a-zA-Z0-9\_]$/.test(c);
27 | }
28 |
29 | for (i = cur.ch-1; i >= 0 && delimiter(currentLine[i]); i--){
30 | curFrom.ch = i;
31 | }
32 | for (i = cur.ch; i < currentLine.length && delimiter(currentLine[i]); i++){
33 | curTo.ch = i+1;
34 | }
35 |
36 | var term = currentLine.substr(curFrom.ch, curTo.ch - curFrom.ch);
37 |
38 | options.completeSingle = single;
39 |
40 | if(single){
41 | return {from: curFrom, to: curTo, list: cf(data, term)};
42 | } else {
43 | curFrom.ch = Math.Infinity;
44 | curTo.ch = Math.Infinity;
45 | return {from: curFrom, to: curTo, list: cf(data)};
46 | }
47 |
48 | });
49 | });
50 | }
51 |
52 | CodeMirror.commands.typeAt = function(cm) {
53 | hint(cm, scalaEval.typeAt, function(data){
54 | return [
55 | {
56 | className: "typeAt",
57 | text: " // " + data.tpe,
58 | render: function(el, _, _1){
59 | var elem = document.createElement("pre");
60 | elem.innerText = data.tpe;
61 | el.appendChild(elem);
62 | }
63 | }
64 | ];
65 | }, false);
66 | }
67 | CodeMirror.commands.autocomplete = function(cm) {
68 | hint(cm, scalaEval.autocomplete, function(data, term){
69 | return data.filter(function(c){
70 | return c.name.toLowerCase().indexOf(term.toLowerCase()) != -1;
71 | }).map(function(c){
72 | return {
73 | className: "autocomplete",
74 | text: c.name,
75 | completion: c,
76 | alignWithWord: true,
77 | render: function(el, _, _1){
78 | el.innerHTML = "" + c.name + " " + c.signature +"";
79 | }
80 | }
81 | });
82 | }, true);
83 | };
84 | CodeMirror.commands.autocompleteDot = function (cm){
85 | cm.replaceSelection(".");
86 | cm.execCommand("autocomplete");
87 | };
88 | }]);
89 |
--------------------------------------------------------------------------------
/Frontend/web/services/errorsRenderer.js:
--------------------------------------------------------------------------------
1 | app.factory('errorsRenderer', function() {
2 | var insightWidget = [];
3 | var errorMessages = [];
4 | var errorUnderlines = [];
5 |
6 | function errorUnderline(cmCode, wrap, severity, value) {
7 | var start;
8 | function render(from, to){
9 | return cmCode.markText(from, to, {className: severity} );
10 | }
11 | if(angular.isDefined(value.start) && angular.isDefined(value.end) && value.start != value.end) {
12 | return render(
13 | wrap.fixRange(value.start),
14 | wrap.fixRange(value.end)
15 | )
16 | } else {
17 | var l = angular.isDefined(value.line) ?
18 | wrap.fixLine(value.line)
19 | : cmCode.getDoc().posFromIndex(wrap.fixRange(value.start)).line;
20 |
21 | return render(
22 | {line: l, ch: 0},
23 | {line: l, ch: Infinity}
24 | );
25 | }
26 | }
27 |
28 | function errorMessage(cmCode, wrap, severity, value){
29 | var offset;
30 | function render(line){
31 | var msg = document.createElement("div"),
32 | icon = msg.appendChild(document.createElement("i"));
33 |
34 | icon.className = "fa ";
35 | if(severity == "error") {
36 | icon.className += "fa-times-circle";
37 | } else if(severity == "warning") {
38 | icon.className += "fa-exclamation-triangle";
39 | } else if(severity == "info") {
40 | icon.className += "fa-info-circle";
41 | }
42 | msg.appendChild(document.createTextNode(value.message));
43 | msg.className = "error-message";
44 |
45 | return cmCode.addLineWidget(line, msg);
46 | }
47 |
48 | if(angular.isDefined(value.start)) {
49 | offset = value.start;
50 |
51 | console.log(wrap.fixRange(offset).line);
52 |
53 | if(value.start == -1) offset = Infinity;
54 | return render(cmCode.getDoc().posFromIndex(wrap.fixRange(offset)).line);
55 |
56 | } else {
57 | if (value.line !== -1) {
58 | return render(
59 | render(wrap.fixLine(value.line) -1)
60 | );
61 | } else {
62 | return render(Infinity);
63 | }
64 | }
65 | }
66 |
67 | function clearFun(){
68 | // clear line errors
69 | errorMessages.forEach(function (value){
70 | value.clear();
71 | });
72 | errorMessages = [];
73 |
74 | errorUnderlines.forEach(function (value){
75 | value.clear();
76 | });
77 | errorUnderlines = [];
78 | }
79 |
80 | return {
81 | clear: clearFun,
82 | render: function(cmCode, wrap, infos, runtimeError){
83 | clearFun();
84 | ["error", "warning", "info"].forEach(function(severity){
85 | if (infos[severity]){
86 | infos[severity].forEach(function(value) {
87 | errorMessages.push(errorMessage(cmCode, wrap, severity, value));
88 | errorUnderlines.push(errorUnderline(cmCode, wrap, severity, value));
89 | });
90 | }
91 | });
92 | if(angular.isDefined(runtimeError)) {
93 | var value = runtimeError;
94 | var severity = "runtime-error";
95 |
96 | errorMessages.push(errorMessage(cmCode, cmPrelude, wrap, severity, value));
97 | errorUnderlines.push(errorUnderline(cmCode, cmPrelude, wrap, severity, value));
98 | }
99 | }
100 | }
101 | });
102 |
--------------------------------------------------------------------------------
/Eval/macro/src/main/scala/com.scalakata.eval/package.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata
2 |
3 | import scala.language.experimental.macros
4 | import collection.immutable.Queue
5 |
6 | package object eval {
7 | type Range = (Int, Int)
8 | type OrderedRender = List[(Range, List[Render])]
9 |
10 | sealed trait Render
11 |
12 | sealed trait Expression extends Render
13 | case class EString(v: String) extends Expression
14 | case class Other(repr: String) extends Expression
15 |
16 | case class Block(childs: OrderedRender ) extends Render
17 | case class Steps(simplifications: List[Expression]) extends Render
18 |
19 | case class Markdown(a: String) extends Render {
20 | override def toString = a
21 | def stripMargin = Markdown(a.stripMargin)
22 | }
23 |
24 | case class Markdown2(a: String) extends Render {
25 | override def toString = a
26 | def stripMargin = Markdown(a.stripMargin)
27 | }
28 |
29 | case class Html(a: String, height: Int = 500) extends Render {
30 | override def toString = a
31 | def stripMargin = Html(a.stripMargin)
32 | }
33 |
34 | case class Html2(a: String) extends Render {
35 | override def toString = a
36 | def stripMargin = Html2(a.stripMargin)
37 | }
38 |
39 | def isNotUnit(a: Any) = {
40 | a match {
41 | case _: Unit ⇒ false
42 | case _ ⇒ true
43 | }
44 | }
45 | def render[A >: Null](a: A): Render = {
46 | a match {
47 | case null ⇒ Other("null")
48 | case ar: Array[_] ⇒ Other(ar.deep.toString)
49 | case v: String ⇒ EString(v)
50 | case _: scala.xml.Elem ⇒ Html(a.toString)
51 | case md: Markdown ⇒ md
52 | case md2: Markdown2 ⇒ md2
53 | case h: Html ⇒ h
54 | case h2: Html2 ⇒ h2
55 | case other ⇒ Other(other.toString)
56 | }
57 | }
58 |
59 | def htmlFile(clazz: Class[_], filename: String, size: Int = 500): Html =
60 | Option(clazz.getClassLoader.getResourceAsStream(filename)).map{ res ⇒
61 | Html(io.Source.fromInputStream(res).mkString, size)
62 | }.getOrElse(Html(s"
$filename not found
", size))
63 |
64 | implicit class MarkdownHelper(val sc: StringContext) extends AnyVal {
65 | def markdown(args: Any*): Markdown = {
66 | Markdown(sc.s(args: _*))
67 | }
68 | def md(args: Any*) = markdown(args: _*)
69 | def markdownR(args: Any*): Markdown = {
70 | Markdown(sc.raw(args: _*))
71 | }
72 | def mdR(args: Any*) = markdownR(args: _*)
73 | }
74 | implicit class MarkdownHelper2(val sc: StringContext) extends AnyVal {
75 | def markdown2(args: Any*): Markdown2 = {
76 | Markdown2(sc.s(args: _*))
77 | }
78 | def md2(args: Any*) = markdown2(args: _*)
79 | def markdownR2(args: Any*): Markdown2 = {
80 | Markdown2(sc.raw(args: _*))
81 | }
82 | def mdR2(args: Any*) = markdownR2(args: _*)
83 | }
84 | implicit class HtmlHelper(val sc: StringContext) extends AnyVal {
85 | def html(args: Any*): Html = {
86 | Html(sc.s(args: _*))
87 | }
88 | def htmlR(args: Any*): Html = {
89 | Html(sc.raw(args: _*))
90 | }
91 | }
92 | implicit class HtmlHelper2(val sc: StringContext) extends AnyVal {
93 | def html2(args: Any*): Html2 = {
94 | Html2(sc.s(args: _*))
95 | }
96 | def htmlR2(args: Any*): Html2 = {
97 | Html2(sc.raw(args: _*))
98 | }
99 | }
100 |
101 | def desugar[T](code: T): Unit = ??? // via annotation macro
102 |
103 | def desugar2[T](code: T): Unit = macro ScalaKataMacro.desugar2_impl[T]
104 |
105 | def trace: Any ⇒ Unit = macro ScalaKataMacro.trace_implf
106 | def print: Any ⇒ Unit = macro ScalaKataMacro.trace_implf
107 | def println: Any ⇒ Unit = macro ScalaKataMacro.trace_implf
108 | def oprintln: Any ⇒ Unit = (a) ⇒ scala.Predef.println(a)
109 | }
110 |
--------------------------------------------------------------------------------
/Frontend/styles/solarized.less:
--------------------------------------------------------------------------------
1 | /*
2 | Solarized theme for code-mirror: http://ethanschoonover.com/solarized
3 | Solarized color pallette: http://ethanschoonover.com/solarized/img/solarized-palette.png
4 | less version: gui
5 | */
6 |
7 | .solarized-pallette {
8 | @base00: #657b83;
9 | @base01: #586e75;
10 | @base02: #073642;
11 | @base03: #002b36;
12 |
13 | @base0: #839496;
14 | @base1: #93a1a1;
15 | @base2: #eee8d5;
16 | @base3: #fdf6e3;
17 |
18 | @yellow: #b58900;
19 | @orange: #cb4b16;
20 | @red: #dc322f;
21 | @magenta: #d33682;
22 | @violet: #6c71c4;
23 | @blue: #268bd2;
24 | @cyan: #2aa198;
25 | @green: #859900;
26 | }
27 |
28 | .cm-s-solarized {
29 |
30 | .solarized-pallette;
31 |
32 | color-profile: sRGB;
33 | rendering-intent: auto;
34 |
35 | .CodeMirror-widget { text-shadow: none; }
36 |
37 | .cm-keyword { color: @orange }
38 | .cm-atom { color: @magenta; }
39 | .cm-number { color: @magenta; }
40 | .cm-def { color: @cyan; }
41 | .cm-variable { color: @blue; }
42 | .cm-variable-2 { color: @yellow; }
43 | .cm-variable-3 { color: @violet; }
44 | .cm-property { color: @cyan; }
45 | .cm-operator {color: @violet;}
46 | .cm-comment { color: @base01 }
47 | .cm-string { color: @green; }
48 | .cm-string-2 { color: @yellow; }
49 | .cm-meta { color: @green; }
50 | .cm-error, .cm-invalidchar {
51 | color: @base01;
52 | border-bottom: 1px dotted @red;
53 | }
54 | .cm-qualifier { color: @yellow; }
55 | .cm-builtin { color: @magenta; }
56 | .cm-bracket { color: @orange; }
57 | .CodeMirror-matchingbracket { color: @green; }
58 | .CodeMirror-nonmatchingbracket { color: @red; }
59 | .cm-tag { color: @base1; }
60 | .cm-attribute { color: @cyan; }
61 | .cm-header { color: @base01; }
62 | .cm-quote { color: @base1; }
63 | .cm-hr {
64 | color: transparent;
65 | border-top: 1px solid @base01;
66 | display: block;
67 | }
68 | .cm-link { color: @base1; cursor: pointer; }
69 | .cm-special { color: @violet; }
70 | .cm-em {
71 | color: #999;
72 | text-decoration: underline;
73 | text-decoration-style: dotted;
74 | }
75 | .cm-strong { color: #eee; }
76 | .cm-tab:before { color: @base01; }
77 |
78 | .cm-matchhighlight {
79 | border-bottom-style: solid;
80 | border-bottom-width: 2px;
81 | }
82 |
83 | .CodeMirror-gutters {
84 | background-color: @base02;
85 | border-right-color: transparent;
86 | }
87 |
88 | a:visited {
89 | color: @violet;
90 | }
91 |
92 | &.cm-s-dark {
93 | color: @base0;
94 | background-color: @base03;
95 | .cm-matchhighlight {
96 | border-bottom-color: @base02;
97 | }
98 | .CodeMirror-gutters {
99 | background-color: @base02;
100 | }
101 | .CodeMirror-gutter-filler {
102 | background-color: @base02;
103 | }
104 | .CodeMirror-selected, .cm-searching {
105 | background: rgba(255, 255, 255, 0.10);
106 | }
107 | .CodeMirror-activeline-background {
108 | background-color: @base02;
109 | }
110 | .CodeMirror-scrollbar-filler {
111 | background-color: @base03;
112 | }
113 | }
114 |
115 | &.cm-s-light {
116 | background-color: @base3;
117 | color: @base00;
118 |
119 | .cm-matchhighlight {
120 | border-bottom-color: @base2;
121 | }
122 |
123 | .CodeMirror-gutters {
124 | background-color: @base2;
125 | }
126 |
127 | .CodeMirror-gutter-filler {
128 | background-color: @base2;
129 | }
130 |
131 | .CodeMirror-selected, .cm-searching {
132 | background: rgba(0, 0, 0, 0.10);
133 | }
134 |
135 | .CodeMirror-activeline-background {
136 | background-color: @base2;
137 | }
138 |
139 | .CodeMirror-scrollbar-filler {
140 | background-color: @base3;
141 | }
142 | }
143 |
144 | .CodeMirror-gutters {
145 | padding: 0 15px 0 10px;
146 | }
147 |
148 | .CodeMirror-linenumber {
149 | color: @base00;
150 | }
151 |
152 | .CodeMirror-gutter .CodeMirror-gutter-text {
153 | color: @base01;
154 | }
155 |
156 | .CodeMirror-lines .CodeMirror-cursor {
157 | border-left: 1px solid @base01;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/Backend/src/main/scala/com.scalakata.backend/Json.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata
2 | package backend
3 |
4 | import eval._
5 |
6 | import play.api.libs.json._
7 | import play.api.libs.json.Json._
8 | import play.api.libs.functional.syntax._
9 |
10 | import spray.http._
11 | import spray.http.ContentTypes._
12 | import spray.http.ContentTypeRange._
13 |
14 | import spray.httpx.marshalling.Marshaller
15 | import spray.httpx.unmarshalling.Unmarshaller
16 |
17 | case class Code(prelude: String, code: String)
18 |
19 | object Request {
20 | val json = ContentTypeRange(MediaTypes.`application/json`, HttpCharsets.`UTF-8`)
21 |
22 | implicit private val evalrequest = Json.reads[EvalRequest]
23 | implicit val EvalRequestUnmarshaller =
24 | Unmarshaller.delegate[String, EvalRequest](json) {
25 | data ⇒ fromJson[EvalRequest](Json.parse(data)).get
26 | }
27 |
28 | implicit private val completionrequest = Json.reads[CompletionRequest]
29 | implicit val CompletionRequestUnmarshaller =
30 | Unmarshaller.delegate[String, CompletionRequest](json) {
31 | data ⇒ fromJson[CompletionRequest](Json.parse(data)).get
32 | }
33 |
34 | implicit private val typeatrequest = Json.reads[TypeAtRequest]
35 | implicit val TypeAtRequestUnmarshaller =
36 | Unmarshaller.delegate[String, TypeAtRequest](json) {
37 | data ⇒ fromJson[TypeAtRequest](Json.parse(data)).get
38 | }
39 | }
40 |
41 | object Response {
42 | implicit private val code = Json.writes[Code]
43 |
44 | implicit private val orderedRender = new Writes[OrderedRender]{
45 | def writes(s: OrderedRender) = {
46 | val res:Seq[JsValue] =
47 | s.map { case ((rs, re), renders) ⇒
48 | JsArray(Seq(
49 | JsArray(Seq(JsNumber(rs), JsNumber(re))),
50 | JsArray(renders.map(rendertype.writes))
51 | ))
52 | }
53 | JsArray(res)
54 | }
55 | }
56 |
57 |
58 | implicit private val expression = new Writes[List[Expression]] {
59 | def writes(xs: List[Expression]) = JsArray(xs.map(rendertype.writes))
60 | }
61 |
62 | implicit private val rendertype: Writes[Render] = new Writes[Render] {
63 | def writes(s: Render) = {
64 | def wrap(tpe: String, value: String) = wrap2(tpe, JsString(value))
65 | def wrap2(tpe: String, value: JsValue) =
66 | JsObject(Seq("type" -> JsString(tpe), "value" -> value))
67 |
68 | implicit val tuples = new Writes[(String, Int)] {
69 | def writes(t: (String, Int)) = JsArray(Seq(JsString(t._1), JsNumber(t._2)))
70 | }
71 | val res =
72 | s match {
73 | case Html(v, h) ⇒ wrap2("html", toJson((v, h)))
74 | case Html2(v) ⇒ wrap2("html2", toJson(v))
75 | case Markdown(v) ⇒ wrap("markdown", v)
76 | case Markdown2(v) ⇒ wrap("markdown2", v)
77 | case EString(v) ⇒ wrap("string", v)
78 | case Other(v) ⇒ wrap("other", v)
79 | case Block(cs) ⇒ wrap2("block", toJson(cs))
80 | case Steps(ss) ⇒ wrap2("steps", toJson(ss))
81 | }
82 | toJson(res)
83 | }
84 | }
85 |
86 | implicit private val compilationinfo = Json.writes[CompilationInfo]
87 | implicit private val runtimeerror = Json.writes[RuntimeError]
88 | implicit private val compilationinfomap = new Writes[Map[Severity,List[CompilationInfo]]] {
89 | def writes(s: Map[Severity,List[CompilationInfo]]) = {
90 | val a = s.map{ case (s, cis) ⇒
91 | val sev = s match {
92 | case Error ⇒ "error"
93 | case Warning ⇒ "warning"
94 | case Info ⇒ "info"
95 | }
96 | sev -> toJson(cis)
97 | }
98 | toJson(a)
99 | }
100 | }
101 |
102 | implicit private val evalresponse = Json.writes[EvalResponse]
103 | implicit val EvalResponseMarshaller =
104 | Marshaller.of[EvalResponse](`application/json`) { (eval, contentType, ctx) ⇒
105 | ctx.marshalTo(HttpEntity(contentType, toJson(eval).toString))
106 | }
107 |
108 | implicit private val completionresponse = Json.writes[CompletionResponse]
109 | implicit val CompletionResponseMarshaller =
110 | Marshaller.of[List[CompletionResponse]](`application/json`) { (eval, contentType, ctx) ⇒
111 | ctx.marshalTo(HttpEntity(contentType, toJson(eval).toString))
112 | }
113 |
114 | implicit private val typeatresponse = Json.writes[TypeAtResponse]
115 | implicit val typeatresponseMarshaller =
116 | Marshaller.of[Option[TypeAtResponse]](`application/json`) { (eval, contentType, ctx) ⇒
117 | ctx.marshalTo(HttpEntity(contentType, toJson(eval).toString))
118 | }
119 |
120 | implicit val CodeMarshaller =
121 | Marshaller.of[Code](`application/json`) { (eval, contentType, ctx) ⇒
122 | ctx.marshalTo(HttpEntity(contentType, toJson(eval).toString))
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/Eval/compile/src/test/scala/com.scalakata.eval/CompilerSpec.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata.eval
2 |
3 | import sbt.BuildInfo
4 |
5 | import java.io.File
6 |
7 | import org.specs2._
8 |
9 | class CommpilerSpecs extends Specification { def is = s2"""
10 | typeAt $typeAt
11 | autocomplete symbols $autocompleteSymbols
12 | autocomplete types $autocompleteTypes
13 | """
14 | /*
15 |
16 |
17 | nopackage $nopackage
18 | compilation infos $infos
19 | runtimeErrors $runtimeErrors
20 | works $works
21 | support packages $packages
22 | compile classpath $compileClasspath
23 | compileClasspath $compileClasspath
24 | linkage $linkage
25 | doubledef $doubledef
26 | */
27 |
28 | def wrap(code: String) =
29 | s"""|object Playground {
30 | | $code
31 | |}""".stripMargin
32 |
33 | def typeAt = {
34 | val c = compiler
35 | val code = wrap("List(1)")
36 | oprintln(code.slice(28, 29))
37 | c.typeAt(code, 28, 28) ====
38 | Some(TypeAtResponse("List[Int]"))
39 | }
40 |
41 | def autocompleteSymbols = {
42 | val c = compiler
43 | c.autocomplete(" ", 0) must contain(
44 | CompletionResponse(
45 | name = "assert",
46 | signature = "(assertion: Boolean): Unit"
47 | )
48 | )
49 | }
50 |
51 | def autocompleteTypes = {
52 | val c = compiler
53 | val code = wrap("List(1).")
54 | oprintln(code.slice(65, 67)) // [65, 67[
55 | c.autocomplete(code, 29) must contain(
56 | CompletionResponse("map","[B](f: A => B): scala.collection.TraversableOnce[B]")
57 | )
58 | }
59 |
60 |
61 | def doubledef = {
62 | val c = compiler
63 | val code = """|trait K[T] { def f = A }
64 | |case object A extends K[Int]""".stripMargin
65 |
66 | val result = c.insight(wrap(code))
67 | result.runtimeError must be empty
68 | }
69 |
70 |
71 |
72 |
73 |
74 | val artifacts =
75 | (BuildInfo.fullClasspath ++ BuildInfo.runtime_exportedProducts).
76 | map(_.getAbsoluteFile).
77 | mkString(File.pathSeparator)
78 |
79 | import scala.concurrent.duration._
80 |
81 | val scalacOptions = sbt.BuildInfo.scalacOptions.to[Seq]
82 | def compiler = new Compiler(artifacts, scalacOptions, security = false, timeout = 20.seconds)
83 |
84 | def linkage = {
85 | val c = compiler
86 | val inner = """|(new BC).f
87 | |(new CB).f""".stripMargin
88 |
89 | val code = s"""|trait A { def f: String }
90 | |trait B extends A { override def f = "B" }
91 | |trait C extends A { override def f = "C" }
92 | |class BC extends A with B with C
93 | |class CB extends A with C with B
94 | |${wrap(inner)}""".stripMargin
95 | val result = c.insight(code)
96 | result.runtimeError must be empty
97 | }
98 |
99 | def nopackage = {
100 | val c = compiler
101 | val result = c.insight(wrap("1+1"))
102 | result.insight must not be empty
103 | }
104 |
105 | def infos = {
106 | val c = compiler
107 | val result = c.insight(
108 | """|object Extras {
109 | | type V = Toto
110 | | class Meter(val v: Int) extends Anyval {
111 | | def +(o: Meter) = new Meter(v + o.v)
112 | | }
113 | |}""".stripMargin)
114 | result.infos must not be empty
115 | }
116 |
117 | def works = {
118 | val c = compiler
119 | val result = c.insight(wrap("1+1"))
120 | result.insight must not be empty
121 | }
122 |
123 | def packages = {
124 | val c = compiler
125 | val code =
126 | """|val a = List(1,2)
127 | |val b = 2""".stripMargin
128 |
129 | val result = c.insight(
130 | s"""|package intro
131 | |class TEEEEEEEEEEEEEEST()
132 | |${wrap(code)}""".stripMargin
133 | )
134 |
135 | result.insight must not be empty
136 | /*result ==== EvalResponse.empty.copy(insight =
137 | List(Instrumentation("List(1, 2)", RT_Other, 62, 75), Instrumentation("2", RT_Other, 80, 85))
138 | )*/
139 | }
140 |
141 |
142 | def runtimeErrors = {
143 | val c = compiler
144 | val code =
145 | """|0
146 | |1/0""".stripMargin
147 | c.insight(wrap(code)) ==== EvalResponse.empty.copy(
148 | runtimeError = Some(RuntimeError("java.lang.ArithmeticException: / by zero", 2))
149 | )
150 | }
151 |
152 | def compileClasspath = {
153 | val c = compiler
154 | c.insight(wrap("com.example.test.Testing.onetwothree")) ==== EvalResponse.empty.copy(
155 | insight = List(((83, 94), List(Other("123"))))
156 | )
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/Frontend/styles/layout.less:
--------------------------------------------------------------------------------
1 | // trick to hide textareas durring loading
2 | .code textarea {
3 | opacity: 0;
4 | }
5 |
6 | body {
7 | height: 100%;
8 | margin: 0;
9 | overflow: hidden;
10 | }
11 |
12 | .fullscreen(@rules) {
13 | :fullscreen { @rules(); }
14 | :-ms-fullscreen { @rules(); }
15 | :-moz-full-screen { @rules(); }
16 | :-webkit-full-screen { @rules(); }
17 | }
18 |
19 | .zen(@full, @p) {
20 | padding: @p;
21 | height: calc(~"100% - "(2 *@p));
22 | & when(@full) {
23 | width: calc(~"100% - "(2 *@p));
24 | }
25 | }
26 |
27 |
28 | @screen-sm-min: 768px;
29 | @screen-md-min: 992px;
30 | @screen-lg-min: 1200px;
31 | @screen-xs-max: (@screen-sm-min - 1);
32 | @screen-sm-max: (@screen-md-min - 1);
33 | @screen-md-max: (@screen-lg-min - 1);
34 |
35 | // small
36 | @media (max-width: @screen-xs-max) {
37 | .insight {
38 | max-width: @screen-xs-max;
39 | }
40 | }
41 |
42 | // medium
43 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {
44 | .insight {
45 | max-width: @screen-sm-max;
46 | }
47 | }
48 |
49 | // large
50 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
51 | .insight {
52 | max-width: @screen-md-max;
53 | }
54 | }
55 |
56 | // wide
57 | @media (min-width: @screen-lg-min) {
58 | .insight {
59 | max-width: @screen-lg-min - 200px;
60 | }
61 | .code {
62 | .zen(false, 20px);
63 | }
64 | }
65 |
66 | .fullscreen({
67 | .menu {
68 | display: none;
69 | }
70 | .code {
71 | .zen(true, 100px);
72 | }
73 | });
74 |
75 | @media print {
76 | .menu {
77 | display: none;
78 | }
79 | }
80 |
81 | .menu {
82 | position: fixed;
83 | top: 0;
84 | right: 0;
85 | margin: 16px;
86 | border-radius: 5px;
87 | z-index: 20;
88 |
89 | &.unsaved {
90 | .save{ display: block; }
91 | .update, .fork{ display: none; }
92 | }
93 | &.saved {
94 | .save{ display: none; }
95 | .update, .fork{ display: block }
96 | }
97 |
98 | .run, .clear, .loading { display: none; }
99 | &.idle .run { display: block; }
100 | &.running .loading { display: block; }
101 | &.viewing .clear { display: block; }
102 |
103 | i {
104 | display: block;
105 | font-size: 32px;
106 | margin: 10px;
107 | cursor: pointer;
108 | }
109 | }
110 |
111 | .code {
112 | .error {
113 | display: inline-block;
114 | border-bottom: 1px dashed red;
115 | }
116 | .error-message {
117 | line-height: 22px;
118 | color: red;
119 | padding-left: 16px;
120 | i {
121 | margin-right: 5px;
122 | margin-left: 5px;
123 | }
124 | }
125 |
126 | .macroAnnotation {
127 | opacity: 0.3;
128 | }
129 |
130 | pre.insight.inline {
131 | display: inline-block;
132 | }
133 | .insight {
134 | // so we can see the cursor
135 | margin-left: 2px;
136 |
137 | border-radius: 2px;
138 | white-space: normal;
139 | position: relative;
140 | margin-top: 5px;
141 | overflow: auto;
142 |
143 | &.markdown {
144 | h1 { margin: 30px 0; }
145 | p {
146 | margin: 15px;
147 | white-space: pre-line;
148 | }
149 | }
150 |
151 | &.html {
152 | display: block;
153 | width: 100%;
154 | resize: vertical;
155 | border: none;
156 | }
157 |
158 | &.block {
159 | &.CodeMirror {
160 | height: auto;
161 | overflow: hidden;
162 | position: relative;
163 | }
164 | .CodeMirror-cursors {
165 | display: none;
166 | }
167 | .CodeMirror-scroll {
168 | height: auto;
169 | overflow-y: auto;
170 | overflow-x: auto;
171 | }
172 | .insight {
173 | margin-left: 40px;
174 | }
175 | .clip {
176 | position: absolute;
177 | top: 0;
178 | right: 0;
179 | margin: 20px;
180 | font-size: 32px;
181 | cursor: pointer;
182 | z-index: 2;
183 | }
184 | }
185 |
186 |
187 | &.code {
188 | font-style: italic;
189 | opacity: 0.8;
190 | }
191 | &.inline {
192 | height: auto;
193 | border-box: none;
194 | margin-left: 16px;
195 | padding: 10px;
196 | }
197 |
198 | &.fold {
199 | padding: 10px;
200 | margin-top: -22px;
201 | }
202 |
203 | hr {
204 | border-style: solid;
205 | }
206 |
207 | pre {
208 | white-space: pre-wrap;
209 | }
210 |
211 | z-index: 2;
212 | }
213 | }
214 |
215 | .ui-splitbar{
216 | height: 4px !important;
217 | z-index: 11;
218 | }
219 |
220 | video {
221 | pointer-events: none;
222 | opacity: 0.2;
223 | z-index: 3;
224 | }
225 |
226 | #background video {
227 | position: fixed;
228 | top: 0;
229 | left: 0;
230 | width: 100%;
231 | height: auto;
232 | }
233 |
234 | #webcam video {
235 | position: fixed;
236 | bottom: 0;
237 | right: 0;
238 | margin: 20px;
239 | width: 256px;
240 | height: auto;
241 | }
242 |
243 |
244 |
245 |
246 | //
247 |
--------------------------------------------------------------------------------
/Eval/compile/src/main/scala/com.scalakata.eval/Eval.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata.eval
2 |
3 | import scala.tools.nsc.{Global, Settings}
4 | import scala.tools.nsc.reporters.StoreReporter
5 | import scala.tools.nsc.io.{VirtualDirectory, AbstractFile}
6 | import scala.reflect.internal.util.{NoPosition, BatchSourceFile, AbstractFileClassLoader}
7 |
8 | import scala.language.reflectiveCalls
9 |
10 | import java.io.File
11 | import java.net.{URL, URLClassLoader, URLEncoder}
12 |
13 | class Eval(settings: Settings, security: Boolean) {
14 |
15 | if(security) { Security.start }
16 |
17 | private val reporter = new StoreReporter()
18 |
19 | private val artifactLoader = {
20 | val loaderFiles =
21 | settings.classpath.value.split(File.pathSeparator).map(a ⇒ {
22 |
23 | val node = new java.io.File(a)
24 | val endSlashed =
25 | if(node.isDirectory) node.toString + "/"
26 | else node.toString
27 |
28 | val t =
29 | if(sys.props("os.name") == "Window") URLEncoder.encode(endSlashed, "UTF-8")
30 | else endSlashed
31 | new java.net.URI(s"file://$t").toURL
32 | })
33 | new URLClassLoader(loaderFiles, this.getClass.getClassLoader)
34 | }
35 |
36 | private val target = new VirtualDirectory("(memory)", None)
37 | private var classLoader: AbstractFileClassLoader = _
38 |
39 | settings.outputDirs.setSingleOutput(target)
40 | settings.Ymacroexpand.value = settings.MacroExpand.Normal
41 |
42 | private val compiler = new Global(settings, reporter)
43 |
44 | def apply(code: String): EvalResponse = {
45 | compile(code)
46 |
47 | val infos = check()
48 |
49 | if(!infos.contains(Error)) {
50 | // Look for static class with eval$ method that return
51 | // an instrumentation
52 | def findEval: Option[OrderedRender] = {
53 | def removeExt(of: String) = {
54 | of.slice(0, of.lastIndexOf(".class"))
55 | }
56 |
57 | def removeMem(of: String) = {
58 | of.slice("(memory)/".length, of.length)
59 | }
60 |
61 | def recurseFolders(file: AbstractFile): Set[AbstractFile] = {
62 | file.iterator.to[Set].flatMap{ fs ⇒
63 | if(fs.isDirectory)
64 | fs.to[Set] ++
65 | fs.filter(_.isDirectory).flatMap(recurseFolders).to[Set]
66 | else Set(fs)
67 | }
68 | }
69 |
70 | val instrClass =
71 | recurseFolders(target).
72 | map(_.path).
73 | map(((removeExt _) compose (removeMem _))).
74 | map(_.replace('/', '.')).
75 | filterNot(_.endsWith("$class")).
76 | find { n ⇒
77 | classLoader.loadClass(n).getMethods.exists(m ⇒
78 | m.getName == "eval$" &&
79 | m.getReturnType == classOf[OrderedRender]
80 | )
81 | }
82 |
83 | import scala.reflect.runtime.{universe ⇒ ru}
84 | val m = ru.runtimeMirror(classLoader)
85 |
86 | instrClass.map{ c ⇒
87 | m.reflectModule(m.staticModule(c)).
88 | instance.asInstanceOf[{def eval$(): OrderedRender}].eval$()
89 | }
90 | }
91 |
92 | EvalResponse.empty.copy(
93 | insight = findEval.getOrElse(Nil),
94 | infos = infos
95 | )
96 | } else {
97 | EvalResponse.empty.copy(infos = infos)
98 | }
99 | }
100 |
101 | private def check(): Map[Severity, List[CompilationInfo]] = {
102 | val infos =
103 | reporter.infos.map { info ⇒
104 | val (start, end) = info.pos match {
105 | case NoPosition ⇒ (-1, -1)
106 | case _ ⇒ (info.pos.start, info.pos.end)
107 | }
108 | (
109 | info.severity,
110 | start,
111 | end,
112 | info.msg
113 | )
114 | }.to[List]
115 | .filterNot{ case (sev, _, _, msg) ⇒
116 | // annoying
117 | sev == reporter.WARNING &&
118 | msg == ("a pure expression does nothing in statement " +
119 | "position; you may be omitting necessary parentheses")
120 | }.groupBy(_._1)
121 | .mapValues{_.map{case (_,start, end, message) ⇒ (start, end, message)}}
122 |
123 | def convert(infos: Map[reporter.Severity, List[(Int, Int, String)]]): Map[Severity, List[CompilationInfo]] = {
124 | infos.map{ case (k,vs) ⇒
125 | val sev = k match {
126 | case reporter.ERROR ⇒ Error
127 | case reporter.WARNING ⇒ Warning
128 | case reporter.INFO ⇒ Info
129 | }
130 | val info = vs map {case (start, end, message) ⇒
131 | CompilationInfo(message, start, end)
132 | }
133 | (sev, info)
134 | }
135 | }
136 | convert(infos)
137 | }
138 |
139 | private def reset(): Unit = {
140 | target.clear()
141 | reporter.reset()
142 | classLoader = new AbstractFileClassLoader(target, artifactLoader)
143 | }
144 |
145 | private def compile(code: String): Unit = {
146 | reset()
147 | val run = new compiler.Run
148 | val sourceFiles = List(new BatchSourceFile("(inline)", code))
149 |
150 | run.compileSources(sourceFiles)
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/Eval/macro/src/main/scala/com.scalakata.eval/Annotation.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata.eval
2 |
3 | import scala.reflect.macros.whitebox.Context
4 |
5 | import scala.language.experimental.macros
6 | import scala.annotation.StaticAnnotation
7 |
8 | import collection.immutable.Queue
9 | import collection.mutable.{Map ⇒ MMap}
10 |
11 | case class Record[K: Ordering, V](inner: MMap[K, Queue[V]] = MMap.empty[K, Queue[V]]) {
12 | def update(k: K, nv: V): Unit = {
13 | val t =
14 | inner.get(k) match {
15 | case Some(v) ⇒ v :+ nv
16 | case None ⇒ Queue(nv)
17 | }
18 | inner(k) = t
19 | }
20 | def ordered: List[(K, List[V])] = {
21 | inner.toMap.mapValues(_.to[List]).to[List].sortBy(_._1)
22 | }
23 | }
24 |
25 | class Helper[C <: Context](val c: C) {
26 | import c.universe._
27 |
28 | def lift(p: c.universe.Position): (Int, Int) = {
29 | (p.start, p.end)
30 | }
31 | implicit def liftq = Liftable[c.universe.Position] { p ⇒
32 | q"(${p.point}, ${p.end})"
33 | }
34 |
35 | def topInst(tree: Tree, depth: Int = 0)(instr: TermName): Tree = tree match {
36 | case t @ q"desugar{ ..$body }" ⇒ block(lift(t.pos), body, depth, true)(instr)
37 | case ident: Ident ⇒ inst(ident)(instr) // a
38 | case apply: Apply ⇒ inst(apply)(instr) // f(..)
39 |
40 | case bl @ Block(childs, last) if (depth == 0) ⇒ block(lift(bl.pos), childs ::: List(last), depth)(instr)
41 | case select: Select ⇒ inst(select)(instr)
42 | case mat: Match ⇒ inst(mat)(instr)
43 | case tr: Try ⇒ inst(tr)(instr)
44 | case c: ClassDef if (depth == 0) ⇒ q""
45 | case m: ModuleDef if (depth == 0) ⇒ q""
46 | case otherwise ⇒ otherwise
47 | }
48 |
49 | def block(position: (Int, Int), childs: List[Tree], depth: Int, desug: Boolean = false)(instr: TermName): Tree = {
50 | val t = TermName("t$")
51 | val oldinstr = TermName("oldinstr$")
52 |
53 | def des(tree: Tree): Tree = {
54 | val pre = s"""|```scala
55 | |${showCode(tree)}
56 | |```""".stripMargin
57 | q"${instr}(${tree.pos}) = com.scalakata.eval.Markdown2($pre)"
58 | }
59 |
60 | val childs2 = childs.flatMap{ s ⇒
61 | val ins = topInst(s, depth + 1)(instr)
62 | if(desug) List(des(s), ins)
63 | else List(ins)
64 | }
65 |
66 | q"""{
67 | val $oldinstr = $instr
68 | $instr = Record[Range, Render]()
69 | ..$childs2
70 | ${oldinstr}($position) = Block(${instr}.ordered)
71 | $instr = $oldinstr
72 | ()
73 | }"""
74 | }
75 |
76 | def inst2(expr: Tree, posTree: Tree)(instr: TermName) : Tree = {
77 | val t = TermName("t$")
78 | q"""{
79 | val $t = $expr
80 | if(isNotUnit($t)) ${instr}(${posTree.pos}) = render($t)
81 | $t
82 | }"""
83 | }
84 | def inst(expr: Tree)(instr: TermName): Tree = inst2(expr, expr)(instr)
85 |
86 | def trace(instr: TermName) = {
87 | val t = TermName("t$")
88 | c.Expr[Any ⇒ Unit](q"""{
89 | (v: Any) ⇒ {
90 | ${instr}(${c.enclosingPosition}) = render(v)
91 | ()
92 | }
93 | }""")
94 | }
95 | }
96 |
97 | object ScalaKataMacro {
98 | val instrName = "instr$"
99 |
100 | def trace_implf(c: Context): c.Expr[Any ⇒ Unit] = {
101 | val instr = c.universe.TermName(instrName)
102 | new Helper[c.type](c).trace(instr)
103 | }
104 |
105 | def desugar2_impl[T](c: Context)(code: c.Expr[T]): c.Expr[Unit] = {
106 | import c.universe._
107 | val instr = TermName(instrName)
108 |
109 | val q"{ ..$body }" = code.tree
110 |
111 | val pos =
112 | if(body.isEmpty) (code.tree.pos.start, code.tree.pos.end)
113 | else (body.head.pos.start, body.last.pos.end)
114 |
115 | c.Expr[Unit](
116 | new Helper[c.type](c).block(pos, body, 0, true)(instr)
117 | )
118 | }
119 |
120 | def instrumentation(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
121 | import c.universe._
122 |
123 | // we modify a global mutable set of range position and representation
124 | // we can swap this set if we are inside a block
125 | // so we have easy access to it in the trace macro
126 | def instrument(body: Seq[Tree], name: c.TermName, extend: List[Tree]) = {
127 | val instr = TermName(instrName)
128 |
129 | val classAndObjects = body.collect {
130 | case c: ClassDef ⇒ c
131 | case m: ModuleDef ⇒ m
132 | case td: TypeDef ⇒ td
133 | case ips: Import ⇒ ips
134 | }
135 |
136 | val helper = new Helper[c.type](c)
137 | q"""
138 | object $name extends ..$extend {
139 | ..$classAndObjects
140 | private var $instr = Record[Range, Render]()
141 | def ${TermName("eval$")}(): OrderedRender = {
142 | ..${body.map(b ⇒ helper.topInst(b)(instr))}
143 | ${instr}.ordered
144 | }
145 | }
146 | """
147 | }
148 |
149 | c.Expr[Any]{
150 | annottees.map(_.tree).toList match {
151 | case q"object $name extends ..$extend { ..$body }" :: Nil ⇒
152 | instrument(body, name, extend)
153 | }
154 | }
155 | }
156 | }
157 |
158 | class ScalaKata extends StaticAnnotation {
159 | def macroTransform(annottees: Any*) = macro ScalaKataMacro.instrumentation
160 | }
161 |
--------------------------------------------------------------------------------
/Frontend/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Scala Kata
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/Frontend/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | plumber = require('gulp-plumber'),
3 | gulpUtil = require('gulp-util'),
4 | concat = require('gulp-concat'),
5 | express = require('express'),
6 | https = require('https'),
7 | http = require('http'),
8 | fs = require('fs'),
9 | install = require('./plugins/install.js'),
10 | run = require('./plugins/run.js'),
11 | less = require('gulp-less'),
12 | livereload = require('connect-livereload'),
13 | spawn = require("gulp-spawn"),
14 | refresh = require('gulp-livereload'),
15 | gulpFilter = require('gulp-filter'),
16 | rename = require('gulp-rename'),
17 | request = require('request'),
18 | usemin = require('gulp-usemin'),
19 | uglify = require('gulp-uglify'),
20 | minifyHtml = require('gulp-minify-html'),
21 | minifyCss = require('gulp-minify-css'),
22 | gutil = require('gulp-util'),
23 | rev = require('gulp-rev');
24 |
25 | var useHttps = false,
26 | i = 0,
27 | livereloadport = 35729 + i,
28 | serverport = 5443 + i,
29 | apiport = 7331 + i,
30 | certs = {
31 | key: fs.readFileSync('key.pem'),
32 | cert: fs.readFileSync('cert.pem'),
33 | port: livereloadport
34 | },
35 | lrserver = useHttps ?
36 | require('tiny-lr')(certs) :
37 | require('tiny-lr')();
38 |
39 |
40 |
41 | function serveF(assets){
42 | var server = express();
43 |
44 | if(useHttps) {
45 | request.defaults({
46 | strictSSL: false, // allow us to use our self-signed cert for testing
47 | rejectUnauthorized: false
48 | });
49 | }
50 |
51 | server.use(livereload({port: livereloadport}));
52 |
53 | assets.forEach(function(a){
54 | server.use(express.static(a));
55 | });
56 |
57 | // catch all to api
58 | server.use(function(req, res) {
59 | gutil.log(req.originalUrl);
60 | gutil.log(req.url);
61 |
62 | var isApi = [
63 | "eval",
64 | "completion",
65 | "typeAt",
66 | "echo"
67 | ].some(function(v){
68 | return req.originalUrl == "/" + v
69 | }) || req.originalUrl.indexOf(".scala") !== -1;
70 |
71 | if(isApi) {
72 | req.pipe(request("http://localhost:" + apiport + req.originalUrl)).pipe(res);
73 | } else {
74 | if(useHttps) {
75 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
76 | }
77 | req.pipe(request("http://localhost:" + serverport)).pipe(res);
78 | }
79 | });
80 |
81 | if(useHttps) https.createServer(certs, server).listen(serverport);
82 | else http.createServer(server).listen(serverport);
83 |
84 | console.log(serverport, useHttps);
85 |
86 | lrserver.listen(livereloadport);
87 | }
88 |
89 | gulp.task('styles', function() {
90 | return gulp.src('styles/main.less')
91 | .pipe(plumber())
92 | .pipe(less())
93 | .pipe(gulp.dest('tmp/styles'))
94 | .pipe(refresh(lrserver));
95 | });
96 |
97 | gulp.task('html', function(){
98 | gulp.src('web/**/*.html')
99 | .pipe(refresh(lrserver));
100 | });
101 |
102 | // gulp.task('browser', function(){
103 | // var protocol = useHttps ?
104 | // "https" :
105 | // "http";
106 |
107 | // run("open", [protocol + "://localhost:" + serverport]);
108 | // });
109 |
110 | // to develop codemirror
111 | // gulp.task('js2', function(){
112 | // gulp.src('bower_components/codemirror/**/*.js')
113 | // .pipe(refresh(lrserver));
114 | // });
115 |
116 | gulp.task('js', function(){
117 | gulp.src('web/**/*.js')
118 | .pipe(refresh(lrserver));
119 | });
120 |
121 | gulp.task('bower', function(){
122 | gulp.src('bower.json')
123 | .pipe(install.bower())
124 | .pipe(refresh(lrserver));
125 | });
126 |
127 | gulp.task('install', ['bower', 'npm']);
128 |
129 | gulp.task('npm', function(){
130 | gulp.src('package.json')
131 | .pipe(install.npm())
132 | .pipe(refresh(lrserver));
133 | });
134 |
135 | gulp.task('default', function() {
136 | // 'install'
137 | gulp.start('styles', 'serve', 'watch');
138 | });
139 |
140 | gulp.task('serve', function(){
141 | serveF(['web', 'bower_components', 'tmp']);
142 | });
143 |
144 | gulp.task('watch', function() {
145 | // gulp.watch('bower_components/codemirror/**/*.js', ['js2']);
146 | gulp.watch('styles/**/*.less', ['styles']);
147 | gulp.watch('web/**/*.html', ['html']);
148 | gulp.watch('web/**/*.js', ['js']);
149 | gulp.watch('bower.json', ['bower']);
150 | gulp.watch('package.json', ['npm']);
151 | });
152 |
153 | gulp.task('build', ['styles', 'usemin', 'font', 'fav', 'zeroclipboard']);
154 | gulp.task('buildServe', ['build', 'serveDist']);
155 |
156 | gulp.task('serveDist', function(){
157 | serveF(['out']);
158 | });
159 |
160 | gulp.task('font', function(){
161 | gulp.src('bower_components/fontawesome/fonts/fontawesome-webfont.woff')
162 | .pipe(gulp.dest('out/assets/fonts/'));
163 |
164 | gulp.src('bower_components/octicons/octicons/octicons.woff')
165 | .pipe(gulp.dest('out/assets/styles/'));
166 | });
167 |
168 | gulp.task('zeroclipboard', function(){
169 | gulp.src('bower_components/zeroclipboard/dist/ZeroClipboard.swf')
170 | .pipe(gulp.dest('out/assets/scripts'));
171 | });
172 |
173 | gulp.task('fav', function(){
174 | gulp.src('web/assets/favicon.ico')
175 | .pipe(gulp.dest('out/assets/'));
176 | })
177 |
178 | gulp.task('usemin', ['styles'], function() {
179 | var index = gulpFilter(['**/index.html']);
180 |
181 | gulp.src('web/index.html')
182 | .pipe(usemin())
183 | .pipe(gulp.dest('out/'))
184 | .pipe(index)
185 | .pipe(gulp.dest('out/assets/'))
186 |
187 | });
188 |
--------------------------------------------------------------------------------
/Frontend/web/controllers/code.js:
--------------------------------------------------------------------------------
1 | CodeMirror.hack = {};
2 | app.controller('code',["$scope", "$timeout", "LANGUAGE", "VERSION", "scalaEval", "katas", "insightRenderer", "errorsRenderer", "wrap", "webcam",
3 | function code( $scope , $timeout , LANGUAGE , VERSION , scalaEval , katas, insightRenderer , errorsRenderer , wrap , webcam){
4 |
5 | var cmCode,
6 | state = {},
7 | ctrl = CodeMirror.keyMap["default"] == CodeMirror.keyMap.pcDefault ? "Ctrl-" : "Cmd-";
8 |
9 | state.configEditing = false;
10 |
11 | $scope.state = 'idle';
12 |
13 | if(angular.isDefined(window.localStorage['codemirror_' + VERSION])) {
14 | $scope.cmOptions = JSON.parse(window.localStorage['codemirror_' + VERSION]);
15 | } else {
16 |
17 | var keys = {}
18 | keys[ctrl + "Space"] = "autocomplete";
19 | keys['.'] = "autocompleteDot";
20 | keys[ctrl + "Enter"] = "run";
21 | keys[ctrl + ","] = "config";
22 | keys[ctrl + "."] = "typeAt";
23 | keys["F11"] = "fullscreen";
24 |
25 | $scope.cmOptions = {
26 | "_to config codemirror see_": "http://codemirror.net/doc/manual.html#config",
27 | extraKeys: keys,
28 | coverGutterNextToScrollbar: true,
29 | firstLineNumber: 0,
30 | lineNumbers: false,
31 | lineWrapping: true,
32 | tabSize: 2,
33 | theme: 'solarized light',
34 | "_supported_themes": [ "solarized dark", "solarized light", "mdn-like"],
35 | smartIndent: false,
36 | multiLineStrings: true,
37 | matchTags: {bothTags: true},
38 | autoCloseBrackets: true,
39 | styleActiveLine: false,
40 | scrollPastEnd: true,
41 | keyMap: "sublime",
42 | mode: 'text/x-' + LANGUAGE,
43 | highlightSelectionMatches: { showToken: false },
44 | video: false
45 | }
46 | }
47 |
48 | $scope.theme = function(){
49 | return _.map($scope.cmOptions.theme.split(" "), function(v){
50 | return "cm-s-" + v;
51 | }).join(" ");
52 | }
53 |
54 | function clear(){
55 | $timeout(function(){
56 | $scope.state = 'idle';
57 | });
58 | insightRenderer.clear();
59 | errorsRenderer.clear();
60 | }
61 | $scope.clear = clear;
62 |
63 | function setMode(edit){
64 | if(edit) {
65 | state.code = $scope.code;
66 | clear();
67 | $timeout(function(){
68 | $scope.cmOptions.mode = 'application/json';
69 | $scope.code = JSON.stringify($scope.cmOptions, null, '\t');
70 | });
71 | } else {
72 | $scope.cmOptions.onLoad = function(cm_) {
73 | cm_.refresh();
74 |
75 | cmCode = cm_;
76 | CodeMirror.hack.code = cm_;
77 | cmCode.focus();
78 | cmCode.on('changes', function(){
79 | clear();
80 | });
81 | cmCode.on('dblclick', function(){
82 | clear();
83 | });
84 | };
85 |
86 | $scope.cmOptions.mode = 'text/x-' + LANGUAGE;
87 |
88 | window.localStorage['codemirror_' + VERSION] = JSON.stringify($scope.cmOptions);
89 |
90 | this.videoSet = this.videoSet || false;
91 | if($scope.cmOptions.video && !this.videoSet) {
92 | this.videoSet = true;
93 | webcam($scope.cmOptions.videoMapping).then(function(newMapping){
94 | $scope.cmOptions.videoMapping = newMapping;
95 | window.localStorage['codemirror_' + VERSION] = JSON.stringify($scope.cmOptions);
96 | })
97 | }
98 | $scope.code = state.code;
99 | $timeout(function(){
100 | $scope.code = state.code;
101 | });
102 | }
103 | }
104 | function setResource(){
105 | function load(path){
106 | setMode(false, false);
107 | katas(path).then(function(r){
108 | state.code = r.data;
109 | setMode(false, false);
110 | window.history.replaceState({"code": r.data}, null, path);
111 | });
112 | }
113 | if(window.location.pathname !== "/") {
114 | load(window.location.pathname);
115 | } else {
116 | if(angular.isDefined(window.localStorage['code_' + VERSION])){
117 | state.code = window.localStorage['code_' + VERSION];
118 | window.history.replaceState({"code": state.code}, null, "/");
119 | } else {
120 | state.code = "";
121 | window.history.replaceState({"code": state.code}, null, "/");
122 | }
123 | setMode(false, false);
124 | }
125 | }
126 | setResource();
127 |
128 | $scope.toogleEdit = function(){
129 | state.configEditing = !state.configEditing;
130 | setMode(state.configEditing, false);
131 | };
132 |
133 | CodeMirror.hack.wrap = wrap;
134 |
135 | window.onpopstate = function(event) {
136 | if(event.state) {
137 | $scope.code = event.state.code;
138 | $scope.$digest();
139 | run();
140 | }
141 | };
142 |
143 | function run(){
144 | if(state.configEditing) return;
145 | if(!angular.isDefined($scope.code)) return;
146 |
147 | $scope.state = 'running';
148 |
149 | var w = wrap($scope.code, true);
150 | scalaEval.insight(w.full).then(function(r){
151 | var data = r.data;
152 | var code = $scope.code.split("\n");
153 |
154 | $scope.state = 'viewing';
155 |
156 | insightRenderer.render(cmCode, w, $scope.cmOptions, data.insight, setResource, $scope.code);
157 | errorsRenderer.render(cmCode, w, data.infos, data.runtimeError, code);
158 | });
159 | }
160 |
161 | CodeMirror.commands.run = run;
162 | CodeMirror.commands.save = run;
163 | CodeMirror.commands.config = $scope.toogleEdit;
164 |
165 | CodeMirror.commands.fullscreen = function(){
166 | if(screenfull.enabled) {
167 | screenfull.toggle();
168 | }
169 | }
170 |
171 | $scope.$watch('code', function(){
172 | if(state.configEditing) {
173 | try {
174 | $scope.cmOptions = JSON.parse($scope.code);
175 | } catch(e){}
176 | } else {
177 | clear();
178 | window.localStorage['code_' + VERSION] = $scope.code;
179 | }
180 | });
181 |
182 | $scope.run = run;
183 | }]);
184 |
--------------------------------------------------------------------------------
/Eval/compile/src/main/scala/com.scalakata.eval/Compiler.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata.eval
2 |
3 | import java.io.File
4 | import java.math.BigInteger
5 | import java.security.MessageDigest
6 | import java.util.Random
7 | import java.util.concurrent.{TimeoutException, Callable, FutureTask, TimeUnit}
8 |
9 | import scala.util.control.NonFatal
10 | import scala.concurrent.duration._
11 |
12 | import scala.tools.nsc.interactive.Global
13 | import scala.tools.nsc.Settings
14 | import scala.tools.nsc.reporters.StoreReporter
15 | import scala.tools.nsc.io.VirtualDirectory
16 | import scala.reflect.internal.util._
17 | import scala.tools.nsc.interactive.Response
18 |
19 | import scala.concurrent.duration._
20 |
21 | class Compiler(artifacts: String, scalacOptions: Seq[String], security: Boolean, timeout: Duration) {
22 |
23 | def insight(code: String): EvalResponse = {
24 | if (code.isEmpty) EvalResponse.empty
25 | else {
26 | try {
27 | withTimeout{eval(code)}(timeout).getOrElse(
28 | EvalResponse.empty.copy(timeout = true)
29 | )
30 | } catch {
31 | case NonFatal(e) ⇒ {
32 | e.printStackTrace
33 | val pos =
34 | if(e.getCause != null) {
35 | e.getCause.getStackTrace().
36 | find(_.getFileName == "(inline)").
37 | map(_.getLineNumber).
38 | getOrElse(-1) // not virtual, this is a runtime error in ScalaKata code
39 | } else {
40 | e.getStackTrace()(0).getLineNumber
41 | }
42 |
43 | EvalResponse.empty.copy(runtimeError =
44 | Some(RuntimeError(e.getCause.toString, pos))
45 | )
46 | }
47 | }
48 | }
49 | }
50 |
51 | private def reload(code: String): BatchSourceFile = {
52 | val file = new BatchSourceFile("default", code)
53 | withResponse[Unit](r ⇒ compiler.askReload(List(file), r)).get
54 | file
55 | }
56 |
57 | private def withResponse[A](op: Response[A] ⇒ Any): Response[A] = {
58 | val response = new Response[A]
59 | op(response)
60 | response
61 | }
62 |
63 | def autocomplete(code: String, p: Int): List[CompletionResponse] = {
64 | def completion(f: (compiler.Position, compiler.Response[List[compiler.Member]]) ⇒ Unit,
65 | pos: compiler.Position):
66 | List[CompletionResponse] = {
67 |
68 | withResponse[List[compiler.Member]](r ⇒ f(pos, r)).get match {
69 | case Left(members) ⇒ compiler.ask(() ⇒ {
70 | members.map(member ⇒
71 | CompletionResponse(
72 | name = member.sym.decodedName,
73 | signature = member.sym.signatureString
74 | )
75 | )
76 | })
77 | case Right(e) ⇒
78 | e.printStackTrace
79 | Nil
80 | }
81 | }
82 | def typeCompletion(pos: compiler.Position) = {
83 | completion(compiler.askTypeCompletion _, pos)
84 | }
85 |
86 | def scopeCompletion(pos: compiler.Position) = {
87 | completion(compiler.askScopeCompletion _, pos)
88 | }
89 |
90 | // inspired by scala-ide
91 | // https://github.com/scala-ide/scala-ide/blob/4.0.0-m3-luna/org.scala-ide.sdt.core/src/org/scalaide/core/completion/ScalaCompletions.scala#L170
92 | askTypeAt(code, p, p) { (tree, pos) ⇒ tree match {
93 | case compiler.New(name) ⇒ typeCompletion(name.pos)
94 | case compiler.Select(qualifier, _) if qualifier.pos.isDefined && qualifier.pos.isRange ⇒
95 | typeCompletion(qualifier.pos)
96 | case compiler.Import(expr, _) ⇒ typeCompletion(expr.pos)
97 | case compiler.Apply(fun, _) ⇒
98 | fun match {
99 | case compiler.Select(qualifier: compiler.New, _) ⇒ typeCompletion(qualifier.pos)
100 | case compiler.Select(qualifier, _) if qualifier.pos.isDefined && qualifier.pos.isRange ⇒
101 | typeCompletion(qualifier.pos)
102 | case _ ⇒ scopeCompletion(fun.pos)
103 | }
104 | case _ ⇒ scopeCompletion(pos)
105 | }}{
106 | pos ⇒ Some(scopeCompletion(pos))
107 | }.getOrElse(Nil)
108 | }
109 |
110 | def typeAt(code: String, start: Int, end: Int): Option[TypeAtResponse] = {
111 | askTypeAt(code, start, end){(tree, _) ⇒ {
112 | // inspired by ensime
113 | val res =
114 | tree match {
115 | case compiler.Select(qual, name) ⇒ qual
116 | case t: compiler.ImplDef if t.impl != null ⇒ t.impl
117 | case t: compiler.ValOrDefDef if t.tpt != null ⇒ t.tpt
118 | case t: compiler.ValOrDefDef if t.rhs != null ⇒ t.rhs
119 | case t ⇒ t
120 | }
121 | scala.Predef.println(tree)
122 | TypeAtResponse(res.tpe.toString)
123 | }}{Function.const(None)}
124 | }
125 |
126 | private def askTypeAt[A]
127 | (code: String, start: Int, end: Int)
128 | (f: (compiler.Tree, compiler.Position) ⇒ A)
129 | (fb: compiler.Position ⇒ Option[A]): Option[A] = {
130 |
131 | if(code.isEmpty) None
132 | else {
133 | val file = reload(code)
134 | val rpos = compiler.rangePos(file, start, start, end)
135 |
136 | val response = withResponse[compiler.Tree](r ⇒
137 | compiler.askTypeAt(rpos, r)
138 | )
139 |
140 | response.get match {
141 | case Left(tree) ⇒ Some(f(tree, rpos))
142 | case Right(e) ⇒ e.printStackTrace; fb(rpos)
143 | }
144 | }
145 | }
146 |
147 | private val jvmId = java.lang.Math.abs(new Random().nextInt())
148 |
149 | private val reporter = new StoreReporter()
150 | private val settings = new Settings()
151 |
152 | settings.processArguments(scalacOptions.to[List], true)
153 | settings.bootclasspath.value = artifacts
154 | settings.classpath.value = artifacts
155 | settings.Yrangepos.value = true
156 |
157 | private lazy val compiler = new Global(settings, reporter)
158 | private lazy val eval = new Eval(settings.copy, security)
159 |
160 | private def withTimeout[T](f: ⇒ T)(timeout: Duration): Option[T]= {
161 | val task = new FutureTask(new Callable[T]() {
162 | def call = f
163 | })
164 | val thread = new Thread( task )
165 | try {
166 | thread.start()
167 | Some(task.get(timeout.toMillis, TimeUnit.MILLISECONDS))
168 | } catch {
169 | case e: TimeoutException ⇒ None
170 | } finally {
171 | if( thread.isAlive ){
172 | thread.interrupt()
173 | thread.stop()
174 | }
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/Frontend/web/services/insightRenderer.js:
--------------------------------------------------------------------------------
1 | app.factory('insightRenderer', ["$timeout", function($timeout) {
2 | var widgets = [];
3 |
4 | function apply(cmCode, wrap, cmOptions, insight, cmOriginal, blockOffset, updateF, prelude, code){
5 | var nl = "\n", elem, start, end, clearF, joined;
6 |
7 | start = cmCode.getDoc().posFromIndex(wrap.fixRange(insight[0][0]));
8 | start.line -= (blockOffset + 1);
9 |
10 | end = cmCode.getDoc().posFromIndex(wrap.fixRange(insight[0][1]));
11 | end.line -= (blockOffset + 1);
12 |
13 | function captureClick(el){
14 | $("a", el).map(function(i, e){
15 | var href = $(e).attr("href");
16 | if(!angular.isDefined(href)) return;
17 |
18 | function domain(url) {
19 | return url.replace('http://','').replace('https://','').split('/')[0];
20 | };
21 | if(domain(href) === domain(window.location.origin) ||
22 | (href[0] === '/' && href[1] !== '/')) {
23 | $(e).on('click', function(ev){
24 | var path = href.replace(window.location.origin, ""),
25 | state = {"prelude": prelude, "code": code};
26 | ev.preventDefault();
27 | window.history.pushState(state, null, path);
28 | updateF(path);
29 | })
30 | } else {
31 | $(e).attr('target', '_blank');
32 | }
33 | })
34 | }
35 |
36 | function joined(sep){
37 | return _.map(insight[1], function(v){ return v.value; }).join(sep);
38 | }
39 |
40 | function addClass(two, e){
41 | angular.element(e)
42 | .addClass("insight")
43 | .addClass(two);
44 | }
45 |
46 | function fold(e){
47 | addClass("fold", e);
48 | var range = cmCode.markText({ch: 0, line: start.line}, end, {
49 | replacedWith: e
50 | });
51 | clearF = function(){
52 | range.clear();
53 | };
54 | }
55 |
56 | function inline(e){
57 | addClass("inline", e);
58 | var widget = cmCode.addLineWidget(end.line, e);
59 | clearF = function(){ widget.clear() };
60 | }
61 |
62 | function html(){
63 | var time = new Date().getTime(),
64 | code = insight[1][0].value[0],
65 | div = $(''),
66 | form = $(''),
67 | iframe = $('');
68 |
69 | $("")
70 | .attr("name", "code")
71 | .attr("value", code)
72 | .appendTo(form);
73 |
74 | iframe.css("height", insight[1][0].value[1]);
75 |
76 | elem = iframe.contents();
77 | fold(iframe[0]);
78 | $timeout(function(){
79 | form.submit();
80 | iframe.load(function(){
81 | captureClick(iframe.contents());
82 | });
83 | }, 500);
84 | }
85 |
86 | function html2(){
87 | elem = document.createElement("div");
88 | elem.innerHTML = joined("");
89 | }
90 |
91 | function markdown(){
92 | elem = document.createElement("div");
93 | elem.innerHTML = marked.parse(joined(nl), {
94 | ghf: true,
95 | highlight: function (code, lang) {
96 | var elem = document.createElement("div"),
97 | option = angular.copy(cmOptions),
98 | langs = {
99 | "scala": "text/x-scala",
100 | "json": "application/json"
101 | },
102 | mode = langs[lang] || "";
103 |
104 |
105 | CodeMirror.runMode(code, mode, elem, option);
106 | return elem.innerHTML;
107 | }
108 | });
109 |
110 | elem.className = "markdown";
111 | captureClick(elem);
112 | }
113 |
114 | switch (insight[1][0].type) {
115 | case "html":
116 | html();
117 | break;
118 |
119 | case "html2":
120 | html2();
121 | fold(elem);
122 | break;
123 |
124 | case "markdown":
125 | markdown();
126 | fold(elem);
127 | break;
128 |
129 | case "markdown2":
130 | markdown();
131 | inline(elem);
132 | break;
133 |
134 | case "block":
135 | var $element = angular.element(""),
136 | ta = document.createElement("textarea"),
137 | clip = angular.element(""),
138 | cmOptions2 = angular.copy(cmOptions),
139 | content = cmCode.getRange(start, end);
140 | ta.textContent = content;
141 | $element.append(ta);
142 | cmOptions2.readOnly = true;
143 | cmOptions2.lineNumbers = false;
144 | cmOptions2.scrollPastEnd = false;
145 | var cm = CodeMirror.fromTextArea(ta, cmOptions2);
146 |
147 | var client = new ZeroClipboard(clip);
148 |
149 | client.on("ready", function (event){
150 | client.on("copy", function (event){
151 | event.clipboardData.setData("text/plain", content);
152 | clip.addClass("active");
153 | $timeout(function(){
154 | clip.removeClass("active");
155 | }, 400);
156 | });
157 | });
158 |
159 | elem = cm.display.wrapper;
160 | angular.element(elem).addClass("block").append(clip);
161 | $timeout(function(){
162 | cm.refresh();
163 | })
164 |
165 | _.forEach(insight[1][0].value, function(it){
166 | apply(cm, wrap, cmOptions, it, cmOriginal, start.line);
167 | });
168 |
169 | fold(elem);
170 |
171 | break;
172 | case "string":
173 | elem = document.createElement("pre");
174 | elem.innerText = joined(nl);
175 | inline(elem);
176 | break;
177 | case "other":
178 | elem = document.createElement("pre");
179 | elem.className = "code";
180 | CodeMirror.runMode(joined(nl), cmOptions, elem);
181 | inline(elem);
182 | break;
183 | }
184 |
185 | return clearF;
186 | }
187 | function clearFun(){
188 | widgets.forEach(function(w){
189 | w();
190 | });
191 | widgets = [];
192 | }
193 | return {
194 | clear: clearFun,
195 | render: function(cmCode, wrap, cmOptions, insights, updateF, prelude, code){
196 | clearFun();
197 | widgets = _.map(insights, function(insight){
198 | return apply(cmCode, wrap, cmOptions, insight, cmCode, 0, updateF, prelude, code);
199 | });
200 | $timeout(function(){
201 | cmCode.refresh();
202 | });
203 | // focus on cursor
204 | // cmCode.focus();
205 | // cmCode.scrollIntoView(cmCode.getCursor());
206 | // cmCode.setCursor(cmCode.getCursor(), null, { focus: true});
207 | }
208 | }
209 | }]);
210 |
--------------------------------------------------------------------------------
/Plugin/src/main/scala/com.scalakata/ScalaKata.scala:
--------------------------------------------------------------------------------
1 | package com.scalakata
2 |
3 | import java.awt.Desktop
4 |
5 | import sbt._
6 | import Def.Initialize
7 | import Keys._
8 | import Attributed.data
9 |
10 | import java.net.URL
11 | import java.io.File
12 |
13 | import spray.revolver.Actions
14 | import spray.revolver.RevolverPlugin.Revolver
15 |
16 | import sbtdocker._
17 | import sbtdocker.Plugin._
18 | import sbtdocker.Plugin.DockerKeys._
19 |
20 | import scala.concurrent.duration._
21 |
22 | object Scalakata extends Plugin {
23 |
24 | case class StartArgs(
25 | readyPort: Int,
26 | classPath: Seq[File],
27 | host: String,
28 | port: Int,
29 | production: Boolean,
30 | security: Boolean,
31 | timeout: Duration,
32 | scalacOptions: Seq[String]
33 | ) {
34 | def toArgs = Seq(
35 | readyPort.toString,
36 | classPath.
37 | map(_.getAbsoluteFile).
38 | mkString(File.pathSeparator),
39 | host,
40 | port.toString,
41 | production.toString,
42 | security.toString,
43 | timeout.toString
44 | ) ++ scalacOptions
45 | }
46 |
47 | lazy val Kata = config("kata") extend(Runtime)
48 | lazy val Backend = config("backend")
49 |
50 | lazy val openBrowser = TaskKey[Unit]("open-browser", "task to open browser to kata url")
51 | lazy val readyPort = SettingKey[Int]("ready-port", "port to send ready command")
52 | lazy val kataUri = SettingKey[URI]("kata-uri", "uri to scala kata")
53 | lazy val startArgs = TaskKey[StartArgs]("start-args",
54 | "The arguments to be passed to the applications main method when being started")
55 | lazy val startArgs2 = TaskKey[Seq[String]]("start-args2",
56 | "The arguments to be passed to the applications main method when being started")
57 |
58 | lazy val securityManager = SettingKey[Boolean]("security-manager", "turn on jvm security manager")
59 | lazy val production = SettingKey[Boolean]("production", "deployed version")
60 | lazy val timeout = SettingKey[Duration]("timeout", "maximum time to wait for evaluation response")
61 |
62 | lazy val test = Project(
63 | id = "test",
64 | base = file("."),
65 | settings = kataSettings
66 | )
67 | lazy val scalaKataVersion = "0.12.0"
68 | val start = "kstart"
69 |
70 | lazy val kataAutoStart =
71 | onLoad in Global := {
72 | ((s: State) ⇒ { start :: s }) compose (onLoad in Global).value
73 | }
74 |
75 | lazy val kataSettings =
76 | addCommandAlias(start, ";backend:reStart ;backend:openBrowser ;kwatch") ++
77 | addCommandAlias("kwatch", "~ ;backend:copyResources ;kata:compile ;kata:copyResources") ++
78 | addCommandAlias("kstop", "backend:reStop") ++
79 | addCommandAlias("krestart", ";backend:reStop ;backend:reStart") ++
80 | inConfig(Backend)(
81 | Classpaths.ivyBaseSettings ++
82 | Classpaths.jvmBaseSettings ++
83 | Defaults.compileBase ++
84 | Defaults.configTasks ++
85 | Defaults.configSettings ++
86 | Revolver.settings ++
87 | Seq(
88 | production := false,
89 | securityManager := false,
90 | mainClass in Revolver.reStart := Some("com.scalakata.backend.Boot"),
91 | startArgs2 in Revolver.reStart := (startArgs in Revolver.reStart).value.toArgs,
92 | fullClasspath in Revolver.reStart <<= fullClasspath,
93 | Revolver.reStart <<= InputTask(Actions.startArgsParser) { args ⇒
94 | (
95 | streams,
96 | Revolver.reLogTag,
97 | thisProjectRef,
98 | Revolver.reForkOptions,
99 | mainClass in Revolver.reStart,
100 | fullClasspath in Revolver.reStart,
101 | startArgs2 in Revolver.reStart,
102 | args
103 | ).map(Actions.restartApp)
104 | .dependsOn(products in Compile)
105 | },
106 | kataUri := new URI("http://localhost:7331"),
107 | readyPort := 8081,
108 | openBrowser := {
109 | val socket = new java.net.ServerSocket(readyPort.value)
110 | socket.accept()
111 | socket.close()
112 |
113 | sys.props("os.name").toLowerCase match {
114 | case x if x contains "mac" ⇒ s"open ${kataUri.value.toString}".!
115 | case _ ⇒ Desktop.getDesktop.browse(kataUri.value)
116 | }
117 |
118 | ()
119 | },
120 | libraryDependencies ++= Seq(
121 | "com.scalakata" % s"backend_${scalaBinaryVersion.value}" % scalaKataVersion,
122 | "com.scalakata" % s"eval_${scalaBinaryVersion.value}" % scalaKataVersion,
123 | "com.scalakata" % "frontend" % scalaKataVersion
124 | )
125 | )
126 | ) ++
127 | inConfig(Kata)(
128 | Classpaths.ivyBaseSettings ++
129 | Classpaths.jvmBaseSettings ++
130 | Defaults.compileBase ++
131 | Defaults.configTasks ++
132 | Defaults.configSettings ++
133 | Seq(
134 | scalaVersion := "2.11.6",
135 | unmanagedResourceDirectories += sourceDirectory.value,
136 | scalacOptions ++= Seq("-Yrangepos", "-unchecked", "-deprecation", "-feature"),
137 | libraryDependencies ++= Seq(
138 | "com.scalakata" % s"macro_${scalaBinaryVersion.value}" % scalaKataVersion,
139 | "org.scala-lang" % "scala-compiler" % scalaVersion.value,
140 |
141 | compilerPlugin("org.scalamacros" % "paradise" % "2.1.0-M5" cross CrossVersion.full)
142 | // compilerPlugin("org.scalamacros" % s"paradise_${scalaVersion.value}" % "2.1.0-M3")
143 | )
144 | )
145 | ) ++
146 | Seq(
147 | // the backend can serve .scala files
148 | unmanagedResourceDirectories in Backend += (sourceDirectory in Kata).value,
149 | // you have full acces to your project in the kata sandbox
150 | dependencyClasspath in Kata ++= (fullClasspath in Compile).value ++ (fullClasspath in Test).value,
151 | scalaVersion in Backend := (scalaVersion in Kata).value,
152 | timeout in Backend := 20.seconds,
153 | startArgs in (Backend, Revolver.reStart) := StartArgs(
154 | (readyPort in Backend).value,
155 | (fullClasspath in Kata).value.
156 | map(_.data).
157 | map(_.getAbsoluteFile),
158 | (kataUri in Backend).value.getHost,
159 | (kataUri in Backend).value.getPort,
160 | (production in Backend).value,
161 | (securityManager in Backend).value,
162 | (timeout in Backend).value,
163 | (scalacOptions in Kata).value
164 | ),
165 | resolvers ++= Seq(
166 | "spray repo" at "http://repo.spray.io",
167 | "typesafe releases" at "http://repo.typesafe.com/typesafe/releases",
168 | "masseguillaume" at "http://dl.bintray.com/content/masseguillaume/maven",
169 | Resolver.sonatypeRepo("releases")
170 | )
171 | )
172 |
173 | lazy val kataDockerSettings = kataSettings ++ inConfig(Backend)(Seq(
174 | production := true,
175 | securityManager := true,
176 | openBrowser := { }
177 | )) ++ inConfig(Kata)(dockerSettings) ++ Seq(
178 | kataUri in Backend := new URI("http://0.0.0.0:7331"),
179 | imageName in (Kata, docker) := {
180 | ImageName(
181 | namespace = None,
182 | repository = name.value,
183 | tag = Some("v" + version.value)
184 | )
185 | },
186 | dockerfile in (Kata, docker) := {
187 | val Some(main) = (mainClass in (Backend, Revolver.reStart)).value
188 |
189 | val app = "/app"
190 | val libs = s"$app/libs"
191 | val katas = s"$app/katas"
192 | val plugins = s"$app/plugins"
193 |
194 | val classpath = s"$libs/*:$katas/*"
195 |
196 | new Dockerfile {
197 | from("dockerfile/java:oracle-java8")
198 |
199 | val args = {
200 | val t = (startArgs in (Backend, Revolver.reStart)).value
201 | val kataClasspath =
202 | (packageBin in Compile).value +:
203 | (packageBin in Kata).value +:
204 | (managedClasspath in Kata).value.
205 | map(_.data).
206 | map(_.getAbsoluteFile)
207 | t.copy(
208 | // update compiler plugin path
209 | scalacOptions = t.scalacOptions.map{ v ⇒
210 | val pluginArg = "-Xplugin:"
211 | if(v.startsWith(pluginArg)) {
212 | val plugin = file(v.slice(pluginArg.length, v.length))
213 | val target = file(plugins) / plugin.name
214 | stageFile(plugin, target)
215 | pluginArg + target.getAbsolutePath
216 | } else v
217 | },
218 | // update frontend classpath
219 | classPath = kataClasspath.map { v ⇒
220 | val target = file(katas) / v.name
221 | stageFile(v, target)
222 | target
223 | }
224 | )
225 | }
226 |
227 | // backend classpath
228 | (managedClasspath in Backend).value.files.foreach{ dep ⇒
229 | val target = file(libs) / dep.name
230 | stageFile(dep, target)
231 | }
232 | add(libs, libs)
233 |
234 | // frontend classpath
235 | add(katas, katas)
236 | add(plugins, plugins)
237 |
238 | // exposes
239 | expose(args.port)
240 | entryPoint((
241 | Seq("java", "-Xmx1G", "-Xms256M", "-cp", classpath, main) ++ args.toArgs
242 | ):_*)
243 | }
244 | }
245 | )
246 | }
247 |
--------------------------------------------------------------------------------