summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi@dropbox.com>2014-09-24 20:19:19 -0700
committerLi Haoyi <haoyi@dropbox.com>2014-09-24 20:19:19 -0700
commita57376f40b539260f1d13d3278f10e16a4a2fbe1 (patch)
tree847d57c845d06e9f8556930e982eb975d990cd19
parentec99de45b82abdcc614e661afd1b9bb375ecd865 (diff)
downloadworkbench-a57376f40b539260f1d13d3278f10e16a4a2fbe1.tar.gz
workbench-a57376f40b539260f1d13d3278f10e16a4a2fbe1.tar.bz2
workbench-a57376f40b539260f1d13d3278f10e16a4a2fbe1.zip
wip
-rw-r--r--example/build.sbt4
-rw-r--r--example/src/main/scala/example/ScalaJSExample.scala32
-rw-r--r--src/main/scala/workbench/Plugin.scala26
-rw-r--r--src/main/scala/workbench/Server.scala4
4 files changed, 47 insertions, 19 deletions
diff --git a/example/build.sbt b/example/build.sbt
index ceefedc..0bca703 100644
--- a/example/build.sbt
+++ b/example/build.sbt
@@ -21,5 +21,7 @@ libraryDependencies ++= Seq(
bootSnippet := "ScalaJSExample().main();"
-updateBrowsers <<= updateBrowsers.triggeredBy(ScalaJSKeys.fastOptJS in Compile)
+ScalaJSKeys.inliningMode := scala.scalajs.sbtplugin.InliningMode.Off
+
+spliceBrowsers <<= spliceBrowsers.triggeredBy(ScalaJSKeys.fastOptJS in Compile)
diff --git a/example/src/main/scala/example/ScalaJSExample.scala b/example/src/main/scala/example/ScalaJSExample.scala
index 7a41c44..9df7b18 100644
--- a/example/src/main/scala/example/ScalaJSExample.scala
+++ b/example/src/main/scala/example/ScalaJSExample.scala
@@ -7,6 +7,7 @@ case class Point(x: Int, y: Int){
def +(p: Point) = Point(x + p.x, y + p.y)
def /(d: Int) = Point(x / d, y / d)
}
+
// Seems like you need this for sbt ~fastOptJS to work
// mkdir ~/.sbt/0.13/plugins/target/scala-2.10/sbt-0.13/classes
@JSExport
@@ -17,31 +18,30 @@ object ScalaJSExample {
.getContext("2d")
.asInstanceOf[dom.CanvasRenderingContext2D]
- var count = 0
- var p = Point(0, 0)
- val corners = Seq(Point(255, 255), Point(0, 255), Point(128, 0))
+ var p = Point(128, 128)
+ def color = "black"
+ var enemies = List.fill(10)(Point(util.Random.nextInt(255), util.Random.nextInt(255)))
def clear() = {
- ctx.fillStyle = "white"
+ ctx.fillStyle = color
ctx.fillRect(0, 0, 255, 255)
}
- def run = for (i <- 0 until 10){
- if (count % 30000 == 0) clear()
- count += 1
- p = (p + corners(Random.nextInt(3))) / 2
- val height = 512.0 / (255 + p.y)
- val r = (p.x * height).toInt
- val g = ((255-p.x) * height).toInt
- val b = p.y
- ctx.fillStyle = s"rgb($g, $r, $b)"
-
- ctx.fillRect(p.x, p.y, 1, 1)
+ def run = {
+ clear()
+ ctx.fillStyle = "cyan"
+ p = Point(p.x, (p.y + 2) % 255)
+ ctx.fillRect(p.x, p.y, 5, 20)
+ enemies = for (enemy <- enemies) yield {
+ ctx.fillStyle = "red"
+ ctx.fillRect(enemy.x, enemy.y, 10, 10)
+ Point((enemy.x + 1) % 255, (enemy.y + 1) % 255)
+ }
}
@JSExport
def main(): Unit = {
dom.console.log("main")
- dom.setInterval(() => run, 50)
+ dom.setInterval(() => run, 10)
}
}
diff --git a/src/main/scala/workbench/Plugin.scala b/src/main/scala/workbench/Plugin.scala
index 8e122fe..85b8f39 100644
--- a/src/main/scala/workbench/Plugin.scala
+++ b/src/main/scala/workbench/Plugin.scala
@@ -7,7 +7,8 @@ import scala.concurrent.ExecutionContext.Implicits.global
object Plugin extends sbt.Plugin {
val refreshBrowsers = taskKey[Unit]("Sends a message to all connected web pages asking them to refresh the page")
- val updateBrowsers = taskKey[Unit]("partially resets some of the stuff in the browser")
+ val updateBrowsers = taskKey[Unit]("Partially resets some of the stuff in the browser")
+ val spliceBrowsers = taskKey[Unit]("Attempts to do a live update of the code running in the browser while maintaining state")
val localUrl = settingKey[(String, Int)]("localUrl")
private[this] val server = settingKey[Server]("local websocket server")
@@ -65,6 +66,29 @@ object Plugin extends sbt.Plugin {
}
}
},
+ spliceBrowsers := {
+ val changed = updatedJS.value
+ // There is no point in clearing the browser if no js files have changed.
+ if (changed.length > 0) {
+ for{
+ path <- changed
+ if !path.endsWith(".js.js")
+ }{
+
+ streams.value.log.info("workbench: Splicing " + path)
+ val prefix = "http://localhost:12345/"
+ var s = IO.read(new sbt.File(path.drop(prefix.length)))
+
+ s = s.replace("\nvar ScalaJS = ", "\nvar ScalaJS = ScalaJS || ")
+ s = s.replaceAll("\n(ScalaJS\\.c\\.[a-zA-Z_$0-9]+\\.prototype) = ", "/*X*/\n$1 = $1 || ")
+ for(char <- Seq("d", "c", "h", "i", "n", "m")){
+ s = s.replaceAll("\n(ScalaJS\\." + char + "\\.[a-zA-Z_$0-9]+) = ", "\n$1 = $1 || ")
+ }
+ IO.write(new sbt.File(path.drop(prefix.length) + ".js"), s.getBytes)
+ server.value.Wire[Api].run(path + ".js", None).call()
+ }
+ }
+ },
server := new Server(localUrl.value._1, localUrl.value._2, bootSnippet.value),
(onUnload in Global) := { (onUnload in Global).value.compose{ state =>
server.value.kill()
diff --git a/src/main/scala/workbench/Server.scala b/src/main/scala/workbench/Server.scala
index 4d37073..8d05037 100644
--- a/src/main/scala/workbench/Server.scala
+++ b/src/main/scala/workbench/Server.scala
@@ -3,11 +3,12 @@ package com.lihaoyi.workbench
import akka.actor.{ActorRef, Actor, ActorSystem}
import com.typesafe.config.ConfigFactory
import sbt.IO
+import spray.httpx.encoding.Gzip
import spray.routing.SimpleRoutingApp
import akka.actor.ActorDSL._
import upickle.{Reader, Writer, Js}
-import spray.http.{AllOrigins, HttpResponse}
+import spray.http.{HttpEntity, AllOrigins, HttpResponse}
import spray.http.HttpHeaders.`Access-Control-Allow-Origin`
import concurrent.duration._
import scala.concurrent.Future
@@ -94,6 +95,7 @@ class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingA
}
} ~
getFromDirectory(".")
+
} ~
post {
path("notifications") { ctx =>