summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rwxr-xr-xbook/src/main/resources/css/side-menu.css32
-rw-r--r--book/src/main/scala/book/Book.scala1
-rw-r--r--book/src/main/scala/book/BookData.scala25
-rw-r--r--book/src/main/scala/book/Main.scala8
-rw-r--r--book/src/main/scala/book/Utils.scala7
-rw-r--r--book/src/main/scalatex/book/Intro.scalatex11
-rw-r--r--book/src/main/scalatex/book/handson/CanvasApp.scalatex2
-rw-r--r--book/src/main/scalatex/book/handson/ClientServer.scalatex27
-rw-r--r--book/src/main/scalatex/book/handson/CommandLine.scalatex11
-rw-r--r--book/src/main/scalatex/book/handson/GettingStarted.scalatex32
-rw-r--r--book/src/main/scalatex/book/handson/PublishingModules.scalatex2
-rw-r--r--book/src/main/scalatex/book/handson/WebPage.scalatex8
-rw-r--r--book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex17
-rw-r--r--book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex1
-rw-r--r--book/src/main/scalatex/book/indepth/DesignSpace.scalatex2
-rw-r--r--book/src/main/scalatex/book/indepth/JavaAPIs.scalatex4
-rw-r--r--book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex2
-rw-r--r--build.sbt112
-rw-r--r--examples/crossBuilds/clientserver/Procfile1
-rw-r--r--examples/crossBuilds/clientserver/build.sbt2
-rw-r--r--examples/crossBuilds/clientserver/project/build.properties1
-rw-r--r--examples/crossBuilds/clientserver/project/build.sbt1
-rw-r--r--examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala2
-rw-r--r--examples/crossBuilds/clientserver/server/src/main/scala/simple/Server.scala5
-rw-r--r--examples/demos/build.sbt4
-rw-r--r--examples/demos/src/main/scala/advanced/Async.scala8
-rw-r--r--examples/demos/src/main/scala/scrollmenu/Controller.scala90
-rw-r--r--examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala117
-rw-r--r--examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala170
-rw-r--r--project/build.properties2
-rw-r--r--project/build.sbt6
-rw-r--r--readme.md16
-rw-r--r--scalatex/api/src/main/scala/scalatex/package.scala (renamed from scalatexApi/src/main/scala/scalatex/package.scala)0
-rw-r--r--scalatex/api/src/main/scala/scalatex/stages/Compiler.scala (renamed from scalatexApi/src/main/scala/scalatex/stages/Compiler.scala)1
-rw-r--r--scalatex/api/src/main/scala/scalatex/stages/Omg/scala.scala8
-rw-r--r--scalatex/api/src/main/scala/scalatex/stages/Parser.scala (renamed from scalatexApi/src/main/scala/scalatex/stages/Parser.scala)10
-rw-r--r--scalatex/api/src/main/scala/scalatex/stages/Trim.scala (renamed from scalatexApi/src/main/scala/scalatex/stages/Trim.scala)0
-rw-r--r--scalatex/api/src/test/scala/scalatex/BasicTests.scala (renamed from scalatexApi/src/test/scala/scalatex/BasicTests.scala)0
-rw-r--r--scalatex/api/src/test/scala/scalatex/ErrorTests.scala (renamed from scalatexApi/src/test/scala/scalatex/ErrorTests.scala)0
-rw-r--r--scalatex/api/src/test/scala/scalatex/ParserTests.scala (renamed from scalatexApi/src/test/scala/scalatex/ParserTests.scala)2
-rw-r--r--scalatex/api/src/test/scala/scalatex/TestUtil.scala (renamed from scalatexApi/src/test/scala/scalatex/TestUtil.scala)0
-rw-r--r--scalatex/build.sbt44
-rwxr-xr-xscalatex/compilerPlugin/src/main/resources/scalac-plugin.xml4
-rwxr-xr-xscalatex/compilerPlugin/src/main/scala/scalatex/CompilerPlugin.scala (renamed from scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala)47
-rw-r--r--scalatex/project/build.properties1
-rw-r--r--scalatex/project/build.sbt1
-rw-r--r--scalatex/scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala416
-rw-r--r--scalatex/scalaParser/src/main/scala/scalaParser/syntax/Basic.scala (renamed from scalatexApi/src/main/scala/scalaparser/syntax/Basic.scala)2
-rw-r--r--scalatex/scalaParser/src/main/scala/scalaParser/syntax/Identifiers.scala (renamed from scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala)2
-rw-r--r--scalatex/scalaParser/src/main/scala/scalaParser/syntax/Literals.scala (renamed from scalatexApi/src/main/scala/scalaparser/syntax/Literals.scala)29
-rw-r--r--scalatex/scalaParser/src/test/scala/scalaParser/SyntaxTest.scala (renamed from scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala)119
-rw-r--r--scalatex/scalatexSbtPlugin/src/main/scala/SbtPlugin.scala16
-rw-r--r--scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala412
-rw-r--r--scalatexApi/src/test/scala/scalatex/AdvancedTests.scala120
-rwxr-xr-xscalatexPlugin/src/main/resources/scalac-plugin.xml4
56 files changed, 1068 insertions, 901 deletions
diff --git a/.gitignore b/.gitignore
index 0e25b59..4a67d33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,4 @@ target/
output/
.DS_STORE
*.iml
-.idea/
+.idea
diff --git a/book/src/main/resources/css/side-menu.css b/book/src/main/resources/css/side-menu.css
index b8e36c6..6439a28 100755
--- a/book/src/main/resources/css/side-menu.css
+++ b/book/src/main/resources/css/side-menu.css
@@ -302,26 +302,32 @@ code{
.header-link{
opacity: 0.05;
}
-.collapsed.menu-item-list li.hide > ul{
- opacity: 1;
- overflow: hidden;
- height: 0px;
+#menu .collapsed .menu-item-list .hide ul li > a{
+ padding-top: 0px;
+ padding-bottom: 0px;
}
-.collapsed.menu-item-list li > ul{
- opacity: 1;
+#menu .collapsed.menu-item-list li.hide > ul{
overflow: hidden;
}
+#menu .expanded.menu-item-list .hide li > a:not(:hover){
+ background: none;
+ color: #999;
+}
-#menu .collapsed.menu-item-list li.selected > a{
+#menu .menu-item-list li.selected{
border-left: 2px solid white;
}
+#menu .menu-item-list .hide li.selected{
+ border-left: none;
+}
-.menu-item-list li > a{
- -webkit-transition: background-color 0.2s ease-out;
- -moz-transition: background-color 0.2s ease-out;
- -ms-transition: background-color 0.2s ease-out;
- -o-transition: background-color 0.2s ease-out;
- transition: background-color 0.2s ease-out;
+.menu-item-list ul, .menu-item-list a{
+ -webkit-transition: max-height 0.2s ease;
+ -moz-transition: max-height 0.2s ease;
+ -ms-transition: max-height 0.2s ease;
+ -o-transition: max-height 0.2s ease;
+ transition: max-height 0.2s ease;
+ overflow: hidden
}
/*Override stuff from pure =/ it's not doing what I want*/
diff --git a/book/src/main/scala/book/Book.scala b/book/src/main/scala/book/Book.scala
index 09be8ee..71c0d65 100644
--- a/book/src/main/scala/book/Book.scala
+++ b/book/src/main/scala/book/Book.scala
@@ -25,6 +25,7 @@ object Book {
"META-INF/resources/webjars/font-awesome/4.2.0/fonts/fontawesome-webfont.woff",
"css/side-menu.css",
"example-opt.js",
+// "example-opt.js.map",
"webpage/weather.js",
"favicon.svg",
"favicon.png"
diff --git a/book/src/main/scala/book/BookData.scala b/book/src/main/scala/book/BookData.scala
index d927b5f..ff1f23a 100644
--- a/book/src/main/scala/book/BookData.scala
+++ b/book/src/main/scala/book/BookData.scala
@@ -1,9 +1,14 @@
package book
+import java.io.File
+
import acyclic.file
import scalatags.Text.TypedTag
import scalatags.Text.all._
object BookData {
+ val cloneRoot = System.getProperty("clone.root") + "/"
+
+
lazy val javaAPIs = {
import java.io.File
def recursiveListFiles(f: File): Array[File] = {
@@ -12,17 +17,17 @@ object BookData {
}
val roots = Seq(
- "output/scala-js/javalanglib/src/main/scala",
- "output/scala-js/javalib/src/main/scala"
+ "scala-js/javalanglib/src/main/scala",
+ "scala-js/javalib/src/main/scala"
)
for{
root <- roots
- file <- recursiveListFiles(new File(root))
+ file <- recursiveListFiles(new File(cloneRoot + root))
if file != null
if file.isFile
} yield{
val path = file.getPath
- .drop(root.length + 1)
+ .drop(cloneRoot.length + root.length + 1)
.dropRight(".scala".length)
val filename = path.replace('/', '.')
val docpath = s"https://docs.oracle.com/javase/7/docs/api/$path.html"
@@ -34,12 +39,22 @@ object BookData {
val tagId = "example"+counter
counter += 1
Seq(
- t(id:=tagId, display.block),
+ t(id:=tagId, display.block, overflow.scroll),
script(s"$main(document.getElementById('$tagId'))")
)
}
+
def split = div(cls:="pure-g")
def more = div(cls:="pure-u-1 pure-u-md-13-24")
def less = div(cls:="pure-u-1 pure-u-md-11-24")
def half = div(cls:="pure-u-1 pure-u-md-1-2")
+
+
+ val hl = new Highlighter(
+ Seq(
+ s"$cloneRoot/scala-js" -> "https://github.com/scala-js/scala-js",
+ s"$cloneRoot/workbench-example-app" -> "https://github.com/lihaoyi/workbench-example-app",
+ "" -> "https://github.com/lihaoyi/hands-on-scala-js"
+ )
+ )
}
diff --git a/book/src/main/scala/book/Main.scala b/book/src/main/scala/book/Main.scala
index cb8ef99..4b862d0 100644
--- a/book/src/main/scala/book/Main.scala
+++ b/book/src/main/scala/book/Main.scala
@@ -19,15 +19,15 @@ object Main {
def main(args: Array[String]): Unit = {
println("Writing Book")
-
- write(Book.site, "output/index.html")
+ val outputRoot = System.getProperty("output.root") + "/"
+ write(Book.site, s"$outputRoot/index.html")
val jsFiles = Book.autoResources.filter(_.endsWith(".js")).toSet
val cssFiles = Book.autoResources.filter(_.endsWith(".css")).toSet
val miscFiles = Book.autoResources -- cssFiles -- jsFiles
for(res <- Book.manualResources ++ miscFiles) {
- copy(getClass.getResourceAsStream("/" + res), "output/" + res)
+ copy(getClass.getResourceAsStream("/" + res), outputRoot + res)
}
for((resources, dest) <- Seq(jsFiles -> "scripts.js", cssFiles -> "styles.css")) {
@@ -35,7 +35,7 @@ object Main {
io.Source.fromInputStream(getClass.getResourceAsStream("/"+res)).mkString
}
- write(blobs.mkString("\n"), "output/"+dest)
+ write(blobs.mkString("\n"), outputRoot + dest)
}
val allNames = {
diff --git a/book/src/main/scala/book/Utils.scala b/book/src/main/scala/book/Utils.scala
index 8c5c920..2c861d0 100644
--- a/book/src/main/scala/book/Utils.scala
+++ b/book/src/main/scala/book/Utils.scala
@@ -123,7 +123,7 @@ object lnk{
}
}
-object hl{
+class Highlighter(mappings: Seq[(String, String)]){
def highlight(snippet: Seq[String], lang: String) = {
val string = snippet.mkString
val lines = string.split("\n", -1)
@@ -147,11 +147,6 @@ object hl{
def diff(code: String*) = highlight(code, "diff")
def html(code: String*) = highlight(code, "xml")
- val mappings = Seq(
- "output/scala-js" -> "https://github.com/scala-js/scala-js",
- "output/workbench-example-app" -> "https://github.com/lihaoyi/workbench-example-app",
- "" -> "https://github.com/lihaoyi/hands-on-scala-js"
- )
def ref(filepath: String, start: String = "", end: String = "\n") = {
val lang = filepath.split('.').last match {
diff --git a/book/src/main/scalatex/book/Intro.scalatex b/book/src/main/scalatex/book/Intro.scalatex
index 87c8895..5438cfb 100644
--- a/book/src/main/scalatex/book/Intro.scalatex
+++ b/book/src/main/scalatex/book/Intro.scalatex
@@ -35,7 +35,7 @@
@li
Typo-safety due to its compiler which catches many silly errors before the code is run
@li
- In-editor support for autocomplete, error-highlghting, refactors, and intelligent navigation
+ In-editor support for autocomplete, error-highlighting, refactors, and intelligent navigation
@li
Moderate sized compiled executables, in the 100-400kb range
@li
@@ -59,7 +59,7 @@
However, Javascript is not an easy language to work in at scale: when your code-base extends to thousands, tens or hundreds of thousands of lines of code. The un-typed nature of the language, which is fine for small applications, becomes an issue when you are mainly working with code that you did not write.
@p
- In a large code-base, finding out what methods or properties a variable has is often a long chase through dozens of files to see how it ended up being passed to the current function. Refactorings, which are OK when you can just test the code see if it works, become dangerous when your code base is large enough that "just test all the code" would take hours. Language-warts which are slightly annoying in small programs become a minefield in large ones: it's only a matter of time before you hit one, often in code you did-not/cannot test, resulting in breakages in production.
+ In a large code-base, finding out what methods or properties a variable has is often a long chase through dozens of files to see how it ended up being passed to the current function. Refactorings, which are OK when you can just test the code to see if it works, become dangerous when your code base is large enough that "just test all the code" would take hours. Language-warts which are slightly annoying in small programs become a minefield in large ones: it's only a matter of time before you hit one, often in code you did-not/cannot test, resulting in breakages in production.
@p
Apart from the inherent danger of the language, Javascript has another major problem: the language has left many things unspecified, yet at the same time provides the ability to emulate these things in a variety of ways. This means that rather than having a single way of e.g. defining a class and instantiating an object, there is a decade-long debate between a dozen different and equally-bad, hand-crafted alternatives. Large code-bases use third-party libraries, and most are guaranteed (purely due to how stastistics work) to do these basic things differently from your own code, making understanding these disparate code-bases (e.g. when something goes wrong) very difficult.
@@ -95,7 +95,7 @@
Desktop application security is non-existent. Install one rogue application and it can take over your computer, steal your credit card number, use your email for sending spam, and all sorts of other nasty things. Removing these for-good sometimes involves re-installing your entire operating system. Hence people are much more wary about only installing desktop software from people they "trust".
@p
- In many ways, mobile App platforms like Android and iOS have closed the gap between "native" and "web" applications. Installing a new App may take 30 seconds, you can often deep-link to certain pages within an App, and Apps a much tighter security model than desktop software does. Nevertheless, 30 seconds is still much longer than the 0.5 seconds it takes to open a web page, deep-linking in apps is not very prevalent, and the security model still often leaves space for rogue Apps to misbehave and steal data.
+ In many ways, mobile App platforms like Android and iOS have closed the gap between "native" and "web" applications. Installing a new App may take 30 seconds, you can often deep-link to certain pages within an App, and Apps have a much tighter security model than desktop software does. Nevertheless, 30 seconds is still much longer than the 0.5 seconds it takes to open a web page, deep-linking in apps is not very prevalent, and the security model still often leaves space for rogue Apps to misbehave and steal data.
@hr
@p
@@ -151,7 +151,7 @@
Not having to resort to awkward Ajax-calls or pre-computation to avoid duplicating logic between the client and server
@p
- Shared code doesn't just mean sharing pre-made libraries between the client and server. You can easily @sect.ref("Cross Publishing Libraries", "publish your own libraries") that can be used on both Scala-JVM and Scala.js. This means that as a library author, you can at once target two completely different platforms, and (with some work) take advantage of the intricacies of each platform to optimize your library for each one. Take Scalatags as an example: as the first client-server Scala.js-ScalaJVM shared libraries, it enjoys a roughly event split of downloads from people using it on both platforms:
+ Shared code doesn't just mean sharing pre-made libraries between the client and server. You can easily @sect.ref("Cross Publishing Libraries", "publish your own libraries") that can be used on both Scala-JVM and Scala.js. This means that as a library author, you can at once target two completely different platforms, and (with some work) take advantage of the intricacies of each platform to optimize your library for each one. Take Scalatags as an example: as the first client-server Scala.js-ScalaJVM shared libraries, it enjoys a roughly even split of downloads from people using it on both platforms:
@img(src:="images/Scalatags Downloads.png", width:="100%")
@@ -180,6 +180,3 @@
@p
Scala.js provides all these things, and much more. If you're interested enough to want to make use of Scala.js, read on!
-
-
-
diff --git a/book/src/main/scalatex/book/handson/CanvasApp.scalatex b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
index e68adb7..230ae67 100644
--- a/book/src/main/scalatex/book/handson/CanvasApp.scalatex
+++ b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
@@ -160,7 +160,7 @@
@p
We've by now written a good chunk of Scala.js code, and perhaps debugged some mysterious errors, and tried some new things. One thing you've probably noticed is the efficiency of the process: you make a change in your editor, the browser reloads itself, and life goes on. There is a compile cycle, but after a few runs the compiler warms up and the compilation cycle drops to less than a second.
@p
- Apart from the compilation/reload speed, you've probably noticed the benefit of tooling around Scala.js. Unlike Javascript editors, your existin Scala IDEs like @lnk.misc.IntelliJ or @lnk.misc.Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlghting, jump-to-definition, and a myriad other modern conveniences that are missing when working in dynamically-typed languages are present when working in Scala.js. This makes the code much less mysterious: you're no longer trying to guess what methods a value has, or what a method returns: it's all laid out in front of you in plain sight.
+ Apart from the compilation/reload speed, you've probably noticed the benefit of tooling around Scala.js. Unlike Javascript editors, your existin Scala IDEs like @lnk.misc.IntelliJ or @lnk.misc.Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlighting, jump-to-definition, and a myriad other modern conveniences that are missing when working in dynamically-typed languages are present when working in Scala.js. This makes the code much less mysterious: you're no longer trying to guess what methods a value has, or what a method returns: it's all laid out in front of you in plain sight.
@sect{Full Scala}
@p
diff --git a/book/src/main/scalatex/book/handson/ClientServer.scalatex b/book/src/main/scalatex/book/handson/ClientServer.scalatex
index 0a41dae..50afe76 100644
--- a/book/src/main/scalatex/book/handson/ClientServer.scalatex
+++ b/book/src/main/scalatex/book/handson/ClientServer.scalatex
@@ -1,3 +1,11 @@
+@import BookData._
+
+@def lazyload(id: String) = script(raw("""
+ document.addEventListener("load", function(){
+ document.getElementById(id).src = 'https://hands-on-scala-js.herokuapp.com/'
+ })
+"""))
+
@p
Historically, sharing code across client & server has been a holy-grail for web development. There are many things which have made it hard in the past:
@@ -38,7 +46,8 @@
@hl.ref("examples/crossBuilds/clientserver/build.sbt")
@p
- We have two projects: @code{client} and @code{server}, one of which is a Scala.js project (indicated by the presence of @hl.scala{scalaJSSettings}). Both projects share a number of settings: the presence of the @code{shared/} folder, which shared code can live in (similar to what we saw in @sect.ref{Cross Publishing Libraries}) and the settings to add @lnk.github.Scalatags and @lnk.github.uPickle to the build. Note that those two dependencies use the triple @code{%%%} instead of the double @code{%%} to declare: this means that for each dependency, we will pull in the Scala-JVM or Scala.js version depending on whether it's being used in a Scala.js project.
+ We have two projects: @code{client} and @code{server}, one of which is a Scala.js project (indicated by the presence of @hl.scala{scalaJSSettings}). Both projects share a number of settings: the presence of the @code{shared/} folder, which shared code can live in (similar to what we saw in @sect.ref{Cross Publishing Libraries}) and the settings to add @lnk.github.Scalatags and @lnk.github.uPickle to the build. Note that those two dependencies use the triple @code{%%%} instead of the double @code{%%} to declare: this means that for each dependency, we will pull in the Scala-JVM or Scala.js version depending on whether it's being used in a Scala.js project. Note also the @hl.scala{packageArchetype.java_application} setting, which isn't strictly necessary depending on what you want to do with the application, but this example needs it as part of the deployment to Heroku.
+
@p
The @code{client} subproject is uneventful, with a dependency on the by-now-familiar @code{scalajs-dom} library. The @code{server} project, on the other hand, is interesting: it contains the dependencies required for us to set up out Spray server, and one additional thing: we add the output of @code{fastOptJS} from the client to the @code{resources} on the server. This will allow the @code{server} to serve the compiled-javascript from our @code{client} project from its resources.
@@ -73,6 +82,13 @@
@p
Now, if we go to the browser at @code{localhost:8080}, we should see our web-page!
+ @iframe(id:="heroku1", width:="100%", height:="350px", "frameBorder".attr:="0")
+ @lazyload("heroku1")
+
+
+ @p
+ This is a real, live example running on a @lnk("Heroku server", "https://hands-on-scala-js.herokuapp.com/"). Feel free to poke around and explore the filesystem on the server, just to convince yourself that this actually works and is not just a mock up.
+
@sect{Client-Server Reflections}
@p
By now you've already set up your first client-server application. However, it might not be immediately clear what we've done and why it's interesting! Here are some points to consider.
@@ -206,7 +222,14 @@
While @hl.scala{Ajax.post} returned a @hl.scala{Future[dom.XMLHttpRequest]} and left us to call @hl.scala{upickle.read} and deserialize the data ourselves, @hl.scala{Ajaxer[Api].list(...).call()} now returns a @hl.scala{Future[Seq[FileData]]}! Thus we don't need to worry about making a mistake in the deserialization logic when we write it by hand.
@p
- Other than that, nothing much has changed. If you've done this correctly, the web application will look and behave exactly as it did earlier! So why did we do this in the first place?
+ Other than that, nothing much has changed. If you've done this correctly, the web application will look and behave exactly as it did earlier!
+
+ @iframe(id:="heroku2", width:="100%", height:="350px", "frameBorder".attr:="0")
+ @lazyload("heroku2")
+
+
+ @p
+ So why did we do this in the first place?
@sect{Why Autowire?}
@p
diff --git a/book/src/main/scalatex/book/handson/CommandLine.scalatex b/book/src/main/scalatex/book/handson/CommandLine.scalatex
index 0663378..ea845da 100644
--- a/book/src/main/scalatex/book/handson/CommandLine.scalatex
+++ b/book/src/main/scalatex/book/handson/CommandLine.scalatex
@@ -1,3 +1,4 @@
+@import BookData._
@p
We've by this point done a bunch of work in the browser: we've made a small game that runs in the web browser on the HTML5 canvas, and we've made a number of small web-apps that interact with HTML and 3rd party web-services. However, there's a whole other side to the Scala.js ecosystem: the command line interace, or CLI.
@@ -105,7 +106,7 @@
object RunMe extends scala.scalajs.js.JSApp{
def main(): Unit = {
println("Hello World!")
- println("In Scala.js, 1/0 is ${1/0}!")
+ println("In Scala.js, (1.0).toString is ${(1.0).toString}!")
}
}
@@ -114,10 +115,10 @@
@hl.bash
Hello World!
- In Scala.js, 1/0 is 0!
+ In Scala.js, (1.0).toString is 1!
@p
- This exhibits the weirdness of integer divide-by-zero in Scala.js, which is one of the few ways in which @sect.ref("Deviations from Scala-JVM", "Scala.js deviates from Scala-JVM"). This also shows us we're really running on Scala.js: on Scala-JVM, integer divide-by-zero throws an exception rather than returning zero!
+ This exhibits the weirdness of @hl.scala{Double.toString} in Scala.js, which is one of the few ways in which @sect.ref("Deviations from Scala-JVM", "Scala.js deviates from Scala-JVM"). This also shows us we're really running on Scala.js: on Scala-JVM, @hl.scala{(1.0).toString} returns @hl.scala{"1.0"} rather than @hl.scala{"1"}!
@p
One thing you may be wondering is: when you run a Scala.js program in the terminal, how does it execute the output Javascript? What about the DOM? and Ajax calls? Can it access the filesystem? The answer to all these questions is "it depends": it turns out there are multiple ways you can run Scala.js from the command-line:
@@ -128,7 +129,7 @@
@li
@b{Node.js} using @code{sbt fastOptStage::run} or @code{sbt fullOptStage::run}, having installed Node.js separately
@li
- @b{PhantomJS} using @code{sbt fastOptStage::run} or @code{sbt fullOptStage::run}, having installed PhantomJS separately, and turned on @hl.scala{requiresDOM := true} in SBT
+ @b{PhantomJS} using @code{sbt fastOptStage::run} or @code{sbt fullOptStage::run}, having installed PhantomJS separately, and turned on @hl.scala{jsDependencies += RuntimeDOM} in SBT
@p
Typically, the best way to get started is using Rhino and @code{sbt run}, since it's setup-free, and setting up Node.js or PhantomJS later as necessary. The next two sections elaborate on the differences between these ways of running your code. Check out the later sections on @sect.ref{Headless Runtimes} and @sect.ref{Run Configurations} to learn more about the other settings and why you would want to use them.
@@ -151,7 +152,7 @@
@li
@lnk.misc.Nodejs, a relatively new Javascript runtime based on Google's V8 Javascript engine, Node.js lets you run your Scala.js application from the command line much faster than in Rhino, with performance that matches that of modern browsers. However, you need to separately @lnk("install Node.js", "http://nodejs.org/download/") in order to use it. Like Rhino, it comes with a bare-minimal runtime environment, with no DOM or browser-related functionality. You need to run @code{sbt fastOptStage::run} to run using Node.js.
@li
- @lnk.misc.PhantomJS is a headless Webkit browser. This means that unlike Node.js or Rhino, PhantomJS provides you with a full DOM and all its APIs to use in your tests, if you wish to e.g. test interactions with the HTML of the web page. On the other hand, it is somewhat slower than Node.js, though still much faster than Rhino. Like Node.js, it needs to be installed separately. You need to run You need to run @code{sbt fastOptStage::run}, as well as setting the @hl.scala{requiresDOM := true} flag in your SBT configuration, to use PhantomJS.
+ @lnk.misc.PhantomJS is a headless Webkit browser. This means that unlike Node.js or Rhino, PhantomJS provides you with a full DOM and all its APIs to use in your tests, if you wish to e.g. test interactions with the HTML of the web page. On the other hand, it is somewhat slower than Node.js, though still much faster than Rhino. Like Node.js, it needs to be installed separately. You need to run You need to run @code{sbt fastOptStage::run}, as well as setting the @hl.scala{jsDependencies += RuntimeDOM} flag in your SBT configuration, to use PhantomJS.
@p
These are your three options to run your Scala.js code via the command-line. Generally, it's easiest to get started with Rhino since it's the default and requires no setup, though you may find it worthwhile to setup Node or Phantom if you need additional speed or DOM-integration in your runs.
diff --git a/book/src/main/scalatex/book/handson/GettingStarted.scalatex b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
index 0da9cf6..d2486c2 100644
--- a/book/src/main/scalatex/book/handson/GettingStarted.scalatex
+++ b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
@@ -1,10 +1,11 @@
+@import BookData._
@p
To get started with Scala.js, you will need to prepare a few things:
@ul
@li
- @lnk("sbt", "http://www.scala-sbt.org/"): SBT is the most common build-tool in the Scala community, and is what we will use for building our Scala.js application. Their home page has a link to download and install it.
+ @lnk("sbt", "http://www.scala-sbt.org/"): SBT is the most common build-tool in the Scala community, and is what we will use for building our Scala.js application. Their home page has a link to download and install it. (If you are already using Typesafe Activator, that is effectively sbt.)
@li
An editor for Scala: @lnk("IntelliJ Scala", "http://blog.jetbrains.com/scala/") and @lnk("Eclipse ScalaIDE", "http://scala-ide.org/") are the most popular choices and work on all platforms, though there are others.
@li
@@ -18,7 +19,7 @@
If you've worked with Scala before, you probably already have most of these installed. Otherwise, take a moment to download them before we get to work.
@p
- The quickest way to get started with Scala.js is to @code{git clone} @lnk("workbench-example-app", "https://github.com/lihaoyi/workbench-example-app"), go into the repository root, and run @code{~fastOptJS}
+ The quickest way to get started with Scala.js is to @code{git clone} @lnk("workbench-example-app", "https://github.com/lihaoyi/workbench-example-app"), go into the repository root, and run @code{sbt ~fastOptJS}
@hl.bash
git clone https://github.com/lihaoyi/workbench-example-app
@@ -81,7 +82,7 @@
+ ctx.fillStyle = "white"
@p
- Because we started ran @code{sbt ~fastOptJS} with the @code{~} prefix earlier, it should pick up the change and automatically recompile. The example project is set up to automatically refresh the page when recompilation is complete.
+ Because we started @code{sbt ~fastOptJS} with the @code{~} prefix earlier, it should pick up the change and automatically recompile. The example project is set up to automatically refresh the page when recompilation is complete.
@img(src:="images/Hello World White.png", maxWidth:="100%")
@@ -98,17 +99,17 @@
@p
We've downloaded, compiled, ran, and made changes to our first Scala.js application. Let's now take a closer look at the code that we just ran:
- @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala")
+ @hl.ref(cloneRoot + "workbench-example-app/src/main/scala/example/ScalaJSExample.scala")
@p
It's a good chunk of code, though not a huge amount. To someone who didn't know about Scala.js, they would just think it's normal Scala, albeit with this unusual @hl.scala{dom} library and a few weird annotations. Let's pick it apart starting from the top:
- @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "case class Point", "@JSExport")
+ @hl.ref(cloneRoot + "workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "case class Point", "@JSExport")
@p
Here we are defining a @hl.scala{Point} case class which represents a X/Y position, with some basic operators defined on it. This is done mostly for convenience later on, when we want to manipulate these two-dimensional points. Scala.js is Scala, and supports the entirety of the Scala language. @hl.scala{Point} here behaves identically as it would if you had run Scala on the JVM.
- @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "@JSExport", "val ctx")
+ @hl.ref(cloneRoot + "workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "@JSExport", "val ctx")
@p
This @hl.scala("@JSExport") annotation is used to tell Scala.js that you want this method to be visible and callable from Javascript. By default, Scala.js does @sect.ref("Fast Optimization", "dead code elimination") and removes any methods or classes which are not used. This is done to keep the compiled executables a reasonable size, since most projects use only a small fraction of e.g. the standard library. @hl.scala("@JSExport") is used to tell Scala.js that the @hl.scala{ScalaJSExample} object and its @hl.scala{def main} method are entry points to the program. Even if they aren't called anywhere internally, they are called externally by Javascript that the Scala.js compiler is not aware of, and should not be removed. In this case, we are going to call this method from Javascript to start the Scala.js program.
@@ -116,18 +117,15 @@
@p
Apart from this annotation, @hl.scala{ScalaJSExample} is just a normal Scala @hl.scala{object}, and behaves like one in every way. Note that the main-method in this case takes a @lnk.dom.HTMLCanvasElement: your exported methods can have any signature, with arbitrary arity or types for parameters or the return value. This is in contrast to the main method on the JVM which always takes an @hl.scala{Array[String]} and returns @hl.scala{Unit}. In fact, there's nothing special about this method at all! It's like any other exported method, we just happen to attribute it the "main" entry point. It is entirely possible to define multiple exported classes and methods, and build a "library" using Scala.js of methods that are intended for external Javascript to use.
- @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "val ctx", "var count")
+ @hl.ref(cloneRoot + "workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "val ctx", "var count")
@p
Here we are retrieving a handle to the canvas we will draw on using @hl.scala{document.getElementById}, and from it we can get a @lnk.dom.CanvasRenderingContext2D which we actually use to draw on it.
@p
- @hl.scala{document.getElementById} is the exact same API that's used in normal Javascript, as documented @lnk("here", "https://developer.mozilla.org/en-US/docs/Web/API/document.getElementById"). In fact, the entire @hl.scala{org.scalajs.dom} namespace (imported at the top of the file) comprises statically typed facades for the javascript APIs provided by the browser.
-
- @p
We need to perform the @hl.scala{asInstanceOf} call because depending on what you pass to @hl.scala{getElementById} and @hl.scala{getContext}, you could be returned elements and contexts of different types. Hence we need to tell the compiler explicitly that we're expecting a @lnk.dom.HTMLCanvasElement and @lnk.dom.CanvasRenderingContext2D back from these methods for the strings we passed in.
- @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "def run", "dom.setInterval")
+ @hl.ref(cloneRoot + "workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "def run", "dom.setInterval")
@p
This is the part of the Scala.js program which does the real work. It runs 10 iterations of a @lnk("small algorithm", "http://en.wikipedia.org/wiki/Sierpinski_triangle#Chaos_game") that generates a Sierpinski Triangle point-by-point. The steps, as described by the linked article, are roughly:
@@ -145,7 +143,7 @@
@p
In this example, the triangle is hard-coded to be 255 pixels high by 255 pixels wide, and some math is done to pick a color for each dot which will give the triangle a pretty gradient.
- @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "dom.setInterval")
+ @hl.ref(cloneRoot + "workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "dom.setInterval")
@p
Now this is the call that actually does the useful work. All this method does is call @hl.scala{dom.setInterval}, which tells the browser to run the @hl.scala{run} method every 50 milliseconds. As mentioned earlier, the @hl.scala{dom.*} methods are simply facades to their native Javascript equivalents, and @hl.scala{dom.setInterval} is @lnk("no different", "https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers.setInterval"). Note how you can pass a Scala lambda to @hl.scala{setInterval} to have it called by the browser, where in Javascript you'd need to pass a Javascript @hl.javascript{function(){...}}
@@ -156,7 +154,7 @@
We've already taken a look at the application code for a simple, self-contained Scala.js application, but this application is not @i{entirely} self contained. It's wrapped in a small SBT project that sets up the necessary dependencies and infrastructure for this application to work.
@sect{project/build.sbt}
- @hl.ref("output/workbench-example-app/project/build.sbt")
+ @hl.ref(cloneRoot + "workbench-example-app/project/build.sbt")
@p
This is the list of SBT plugins used by this small example application. There are two of them: the Scala.js plugin (which contains the Scala.js compiler and other things, e.g. tasks such as @code{fastOptJS}) and the @lnk("Workbench", "https://github.com/lihaoyi/workbench") plugin, which is used to provide the auto-reload-on-change behavior and the forwarding of SBT logspam to the browser console.
@@ -166,7 +164,7 @@
@sect{build.sbt}
- @hl.ref("output/workbench-example-app/build.sbt")
+ @hl.ref(cloneRoot + "workbench-example-app/build.sbt")
@p
The @code{build.sbt} project file for this application is similarly unremarkable: It includes the settings for the two SBT plugins we saw earlier, as well as boilerplate @hl.scala{name}/@hl.scala{version}/@hl.scala{scalaVersion} values common to all projects.
@@ -178,7 +176,7 @@
Lastly, we have two Workbench related settings: @hl.scala{bootSnippet} basically tells Workbench how to restart your application when a new compilation run finishes, and @hl.scala{updateBrowsers} actually tells it to perform this application-restarting.
@sect{src/main/resources/index-dev.html}
- @hl.ref("output/workbench-example-app/src/main/resources/index-dev.html")
+ @hl.ref(cloneRoot + "workbench-example-app/src/main/resources/index-dev.html")
@p
This is the HTML page which our toy app lives in, and the same page that we have so far been using to view the app in the browser. To anyone who has used HTML, most of it is probably familiar. Things of note are the @hl.html{<script>} tags: @hl.scala{"../example-fastopt.js"} Is the executable blob spat out by the compiler, which we need to include in the HTML page for anything to happen. This is where the results of your compiled Scala code appear. @hl.scala{"workbench.js"} is the client for the Workbench plugin that connects to SBT, reloads the browser and forwards logspam to the browser console.
@@ -186,6 +184,10 @@
@p
The @hl.scala{ScalaJSExample().main()} call is what kicks off the Scala.js application and starts its execution. Scala.js follows Scala semantics in that @hl.scala{object}s are evaluated lazily, with no top-level code allowed. This is in contrast to Javascript, where you can include top-level statements and object-literals in your code which execute immediately. In Scala.js, nothing happens when @code{../example-fastopt.js} is imported! We have to call the main-method first. In this case, we're passing the canvas object (attained using @hl.javascript{getElementById}) to it so it knows where to do its thing.
+
+ @p
+ @hl.scala{document.getElementById} is the exact same API that's used in normal Javascript, as documented @lnk("here", "https://developer.mozilla.org/en-US/docs/Web/API/document.getElementById"). In fact, the entire @hl.scala{org.scalajs.dom} namespace (imported at the top of the file) comprises statically typed facades for the javascript APIs provided by the browser.
+
@p
Lastly, only @hl.scala("@JSExport")ed objects and methods can be called from Javascript. Also, although this example only exports the @hl.scala{main} method which is called once, there is nothing stopping you from exporting any number of objects and methods and calling them whenever you need to. In this way, you can easily make a Scala.js "library" which is available to external Javascript as an API.
diff --git a/book/src/main/scalatex/book/handson/PublishingModules.scalatex b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
index 9e742e2..b5a0786 100644
--- a/book/src/main/scalatex/book/handson/PublishingModules.scalatex
+++ b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
@@ -109,7 +109,7 @@
Publish it! Both @code{sbt publishLocal} and @code{sbt publishSigned} work on this module, for publishing either locally, Maven Central via Sonatype, or Bintray. Running the command bare should be sufficient to publish both the @code{js} or @code{jvm} projects, or you can also specify which one e.g. @code{jvm/publishLocal} to publish only one subproject.
@p
- This @code{jvm} project works identically to any other Scala-JVM project, and the @code{js} project works identically to the Command Line API described earlier. Thus you can do things like @code{fastOptStage::run} to run the code on Node.js, setting @hl.scala{requiresDOM := true}, run @code{fullOptStage::run} to run the code with full, aggressive optimizations. And of course, things that work in both Scala.js and Scala-JVM can be run on both, basic commands such as @code{run} or @code{test}.
+ This @code{jvm} project works identically to any other Scala-JVM project, and the @code{js} project works identically to the Command Line API described earlier. Thus you can do things like @code{fastOptStage::run} to run the code on Node.js, setting @hl.scala{jsDependencies += RuntimeDOM}, run @code{fullOptStage::run} to run the code with full, aggressive optimizations. And of course, things that work in both Scala.js and Scala-JVM can be run on both, basic commands such as @code{run} or @code{test}.
@p
You can also run tests using this code, if you have a testing library set up. The next section will go into detail as to how to set that up.
diff --git a/book/src/main/scalatex/book/handson/WebPage.scalatex b/book/src/main/scalatex/book/handson/WebPage.scalatex
index 1056460..118dd0c 100644
--- a/book/src/main/scalatex/book/handson/WebPage.scalatex
+++ b/book/src/main/scalatex/book/handson/WebPage.scalatex
@@ -136,7 +136,7 @@
@hl.ref("examples/demos/src/main/scala/webpage/Weather0.scala", "val xhr")
@less
- @BookData.example(div, "Weather0().main")
+ @BookData.example(div(height:="400px"), "Weather0().main")
@p
The above snippet of code uses the raw Javascript Ajax API in order to make a request to @lnk("openweathermap.org", "http://openweathermap.org/"), to get the weather data for the city of Singapore as a JSON blob. The part of the API that we'll be using is documented @lnk("here", "http://openweathermap.org/current"), and if you're interested you can read all about the various options that they provide. For now, we're unceremoniously dumping it in a @hl.scala{pre} so you can see the raw response data.
@@ -184,7 +184,7 @@
@hl.ref("examples/demos/src/main/scala/webpage/Weather1.scala", "val url")
@less
- @BookData.example(div(height:="100%", overflow:="scroll"), "Weather1().main")
+ @BookData.example(div(height:="400px", overflow:="scroll"), "Weather1().main")
@p
A single call to @hl.scala{Ajax.get(...)}, with the URL, and we receive a @hl.scala{scala.concurrent.Future} that we can use to get access to the result when ready. Here we're just using it's @hl.scala{onSuccess}, but we could use it in a for-comprehension, with @lnk("Scala Async", "https://github.com/scala/async"), or however else we can use normal @hl.scala{Future}s
@@ -201,7 +201,7 @@
@hl.ref("examples/demos/src/main/scala/webpage/Weather2.scala", "Ajax.get")
@less
- @BookData.example(div(height:="100%", overflow:="scroll"), "Weather1().main")
+ @BookData.example(div(height:="400px"), "Weather2().main")
@p
We do this by taking @hl.scala{xhr.responseText} and putting it through both @hl.scala{JSON.parse} and @hl.scala{JSON.stringify}, passing in a @hl.scala{space} argument to tell @hl.scala{JSON.stringify} to spread it out nicely.
@@ -214,7 +214,7 @@
@hl.ref("examples/demos/src/main/scala/webpage/Weather3.scala", "Ajax.get")
@less
- @BookData.example(div(height:="100%", overflow:="scroll"), "Weather3().main")
+ @BookData.example(div(height:="400px", overflow:="scroll"), "Weather3().main")
@p
First we parse the incoming response, extract a bunch of values from it, and then stick it in a Scalatags fragment for us to see. Note how we can use the names of the attributes e.g. @hl.scala{json.name} even though @hl.scala{name} is a dynamic property which you can't be sure exists: this is because @hl.scala{json} is of type @hl.scala{js.Dynamic}, which allows us to refer to arbitrary parameters and methods on the underlying object without type-checking.
diff --git a/book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex b/book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex
index 171b2d3..98dccc2 100644
--- a/book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex
+++ b/book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex
@@ -76,7 +76,7 @@
Scalatags requires that anything you want to embed in a Scalatags fragment be implicitly convertible to @hl.scala{Frag}; here we are providing one for any Scala.Rx @hl.scala{Rx[T]}s, as long as the @hl.scala{T} provided is itself convertible to a @hl.scala{Frag}. We call @hl.scala{r().render} to extract the "current" value of the @hl.scala{Rx}, and then set up an @hl.scala{Obs} that watches the @hl.scala{Rx}, replacing the previous value with the current one every time its value changes.
@p
- Now that the set-up is out of the way, let's consider a simple HTML widhet that lets you enter text in a @hl.html{<textarea>}, and keeps track of the number of words, characters, and counts how long each word is.
+ Now that the set-up is out of the way, let's consider a simple HTML widget that lets you enter text in a @hl.html{<textarea>}, and keeps track of the number of words, characters, and counts how long each word is.
@split
@more
@@ -177,13 +177,10 @@
@p
Overall, these helper functions do nothing special, btu we're defining them first to avoid having to copy-&-paste code throughout the subsequent examples. Now that we've defined all the relevant scaffolding, let's walk through a few ways that we can implement the all-important @hl.scala{handle} method.
- @def scrollDiv = div(
- height:="200px",
- overflow:="scroll"
- )
+ @def exampleDiv = div(height:="200px")
@sect{Direct Use of XMLHttpRequest}
- @example(scrollDiv, "Futures().main0")
+ @example(exampleDiv, "Futures().main0")
@hl.ref("examples/demos/src/main/scala/advanced/Futures.scala", "def handle0", "main")
@p
@@ -195,7 +192,7 @@
This solution is basically equivalent to the initial code given in the @sect.ref{Raw Javascript} section of @sect.ref{Interactive Web Pages}, with the additional code necessary for aggregation. As described in @sect.ref{dom.extensions}, we can make use of the @hl.scala{Ajax} object to make it slightly tidier.
@sect{Using dom.extensions.Ajax}
- @example(scrollDiv, "Futures().main1")
+ @example(exampleDiv, "Futures().main1")
@hl.ref("examples/demos/src/main/scala/advanced/Futures.scala", "def handle1", "main")
@p
@@ -204,7 +201,7 @@
However, we still have the messiness inherent in the result aggregation: we don't actually want to perform our action (writing to the @hl.scala{output} div) when one @hl.scala{Future} is complete, but only when @i{all} the @hl.scala{Future}s are complete. Thus we still need to do some amount of manual book-keeping in the @hl.scala{results} buffer.
@sect{Future Combinators}
- @example(scrollDiv, "Futures().main2")
+ @example(exampleDiv, "Futures().main2")
@hl.ref("examples/demos/src/main/scala/advanced/Futures.scala", "def handle2", "main")
@p
@@ -287,14 +284,14 @@
@example(canvas, "Async().main")
@p
- We have an @hl.scala{async} block, which contains a while loop. Each round around the loop, we wait for the @hl.scala{mousedown} channel to start the path, waiting for either @hl.scala{mousedown} or @hl.scala{mousedown} (which continues the path or ends it respectively), fill the shape, and then wait for another @hl.scala{mousedown} before clearing the canvas and going again.
+ We have an @hl.scala{async} block, which contains a while loop. Each round around the loop, we wait for the @hl.scala{mousedown} channel to start the path, waiting for either @hl.scala{mousemove} or @hl.scala{mouseup} (which continues the path or ends it respectively), fill the shape, and then wait for another @hl.scala{mousedown} before clearing the canvas and going again.
@p
Hopefully you'd agree that this code is much simpler to read and understand than the previous version. In particular, the control-flow of the code goes from top to bottom in a "natural" fashion, rather than jumping around ad-hoc like in the previous callback-based design.
@p
You may be wondering what these @hl.scala{Channel} things are, and where they are coming from. Although these are not provided by Scala, they are pretty straightforward to define ourselves:
- @hl.ref("examples/demos/src/main/scala/advanced/Async.scala", "case class Channel")
+ @hl.ref("examples/demos/src/main/scala/advanced/Async.scala", "class Channel")
@p
The point of @hl.scala{Channel} is to allow us to turn event-callbacks (like those provided by the DOM's @hl.scala{onmouseXXX} properties) into some kind of event-stream, that we can listen to asynchronously (via @hl.scala{apply} that returns a @hl.scala{Future}) or merge via @hl.scala{|}. This is a minimal implementation for what we need now, but it would be easy to provide more functionality (filter, map, etc.) as necessary.
diff --git a/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex b/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
index 1718f5e..cb55bf9 100644
--- a/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
+++ b/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
@@ -1,3 +1,4 @@
+@import BookData._
@p
Scala.js is implemented as a compiler plugin in the Scala compiler. Despite this, the overall process looks very different from that of a normal Scala application. This is because Scala.js optimizes for the size of the compiled executable, which is something that Scala-JVM does not usually do.
diff --git a/book/src/main/scalatex/book/indepth/DesignSpace.scalatex b/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
index a1e807f..f167ea6 100644
--- a/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
+++ b/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
@@ -1,3 +1,5 @@
+@import BookData._
+
@p
Scala.js is a relatively large project, and is the result of both an enormous amount of hard work as well as a number of decisions that craft what it's like to program in Scala.js today. Many of these decisions result in marked differences from the behavior of the same code running on the JVM. This chapter explores the reasoning and rationale behind these decisions.
diff --git a/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex b/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
index 0ad886d..685fdfd 100644
--- a/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
+++ b/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
@@ -1,3 +1,5 @@
+@import BookData._
+
@p
Below is a list of classes from the Java Standard Library that are available from Scala.js. In general, much of @hl.scala{java.lang}, and parts of @hl.scala{java.io}, @hl.scala{java.util} and @hl.scala{java.net} have been ported over. This means that all these classes are available for use in Scala.js applications despite being part of the Java standard library.
@p
@@ -37,7 +39,7 @@
@p
And other similar APIs will either need to be rewritten to not-use them. For example, @hl.scala{AtomicXXXs} can be written without threading/unsafe APIs because Javascript is single-threaded, making the implementation for e.g. an @hl.scala{AtomicBoolean} pretty trivial:
- @hl.ref("output/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala")
+ @hl.ref(cloneRoot + "/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala")
@p
Others can't be ported at all (e.g. @code{java.io.File}) simply because the API capabilities they provide (blocking reads & writes to files) do not exist in the Javascript runtime.
diff --git a/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex b/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
index ebfd1db..96f3929 100644
--- a/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
+++ b/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
@@ -1,6 +1,6 @@
@import BookData._
@p
- Although Scala.js tries very hard to maintain compatibility with Scala-JVM, there are some parts where the two platforms differs. This can be roughly grouped into two things: differences in the libraries available,and differences in the language itself. This chapter will cover both of these facets.
+ Although Scala.js tries very hard to maintain compatibility with Scala-JVM, there are some parts where the two platforms differs. This can be roughly grouped into two things: differences in the libraries available, and differences in the language itself. This chapter will cover both of these facets.
@sect{Language Differences}
@sect{Floats are Doubles}
diff --git a/build.sbt b/build.sbt
index 895c2b9..43d679c 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,72 +1,98 @@
+import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode
+import org.eclipse.jgit.api.Git
+import org.eclipse.jgit.merge.MergeStrategy
+import org.eclipse.jgit.transport.{UsernamePasswordCredentialsProvider, RefSpec}
+
import scala.scalajs.sbtplugin.ScalaJSPlugin._
import ScalaJSKeys._
+val cloneRepos = taskKey[Unit]("Clone stuff from github")
-lazy val scalatexApi = project.in(file("scalatexApi"))
- .settings(
+val sharedSettings = Seq(
scalaVersion := "2.11.4",
- libraryDependencies ++= Seq(
- "com.lihaoyi" %% "utest" % "0.2.4",
- "com.scalatags" %% "scalatags" % "0.4.2",
- "org.scala-lang" % "scala-reflect" % scalaVersion.value,
- "com.lihaoyi" %% "acyclic" % "0.1.2" % "provided",
- "org.parboiled" %% "parboiled" % "2.0.1"
- ),
+ libraryDependencies += "com.lihaoyi" %% "acyclic" % "0.1.2" % "provided",
addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2"),
- testFrameworks += new TestFramework("utest.runner.JvmFramework")
+ autoCompilerPlugins := true
)
-lazy val scalatexPlugin = Project(
- id = "scalatexPlugin",
- base = file("scalatexPlugin"),
- dependencies = Seq(scalatexApi)
-) settings (
- scalaVersion := "2.11.4",
- libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value,
- publishArtifact in Compile := false
-)
lazy val book = Project(
id = "book",
- base = file("book"),
- dependencies = Seq(scalatexApi)
-).settings(
- scalaVersion := "2.11.4",
+ base = file("book")
+).settings(sharedSettings ++ inConfig(Compile)(scalatex.SbtPlugin.settings):_*).settings(
libraryDependencies ++= Seq(
"org.webjars" % "highlightjs" % "8.2-1",
"org.webjars" % "pure" % "0.5.0",
"org.webjars" % "font-awesome" % "4.2.0",
- "org.webjars" % "react" % "0.11.1",
- "org.scala-lang" % "scala-reflect" % scalaVersion.value,
- "org.scala-lang" % "scala-compiler" % scalaVersion.value,
- "org.eclipse.jgit" % "org.eclipse.jgit" % "3.5.1.201410131835-r",
+ "com.scalatags" %% "scalatags" % "0.4.2",
"com.lihaoyi" %%% "upickle" % "0.2.5"
),
(resources in Compile) += {
(fullOptJS in (demos, Compile)).value
(artifactPath in (demos, Compile, fullOptJS)).value
},
+
(unmanagedResourceDirectories in Compile) ++=
(unmanagedResourceDirectories in (demos, Compile)).value,
- scalacOptions in Compile ++= {
- val jar = (Keys.`package` in (scalatexPlugin, Compile)).value
- val addPlugin = "-Xplugin:" + jar.getAbsolutePath
- // add plugin timestamp to compiler options to trigger recompile of
- // main after editing the plugin. (Otherwise a 'clean' is needed.)
- val dummy = "-Jdummy=" + jar.lastModified
- val options = "-P:scalatex-options:" + sourceDirectory.value / "scalatex"
- Seq(addPlugin, dummy)
- },
- watchSources ++= {
- ((sourceDirectory in Compile).value / "scalatex" ** "*.scalatex").get
+
+ cloneRepos := {
+ val localPath = target.value / "clones"
+ if (!localPath.isDirectory){
+ val paths = Seq(
+ "scala-js" -> "scala-js",
+ "lihaoyi" -> "workbench-example-app"
+ )
+ localPath.delete()
+ for ((user, repo) <- paths){
+ println(s"Cloning $repo...")
+ Git.cloneRepository()
+ .setURI(s"https://github.com/$user/$repo")
+ .setDirectory(localPath / repo)
+ .call()
+ }
+ println("Done Cloning")
+ }else{
+ println("Already Cloned")
+ }
},
- (watchSources in Test) ++= {
- ((sourceDirectory in Test).value / "scalatex" ** "*.scalatex").get
+ (run in Compile) <<= (run in Compile).dependsOn(cloneRepos),
+ initialize := {
+ System.setProperty("clone.root", target.value.getAbsolutePath + "/clones")
+ System.setProperty("output.root", target.value.getAbsolutePath + "/output")
},
- libraryDependencies += "com.lihaoyi" %% "acyclic" % "0.1.2" % "provided",
- autoCompilerPlugins := true,
- addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2")
+ publish := {
+ // If you want to publish the heroku app, push the contents of
+ // examples/crossBuilds/clientserver to https://git.heroku.com/hands-on-scala-js.git
+ // May aswell do it manually because it's a slow process and the
+ // code doesn't change much
+
+ val outputRoot = target.value.getAbsolutePath + "/output"
+ val repo = Git.init().setDirectory(new File(outputRoot)).call()
+ val remoteUrl = "https://github.com/lihaoyi/hands-on-scala-js"
+
+ val creds = new UsernamePasswordCredentialsProvider(
+ System.console.readLine("username>"),
+ System.console.readPassword("password>")
+ )
+ repo.add()
+ .addFilepattern(".")
+ .call()
+
+ repo.commit()
+ .setAll(true)
+ .setMessage(".")
+ .call()
+
+ repo.push()
+ .setRemote(remoteUrl)
+ .setCredentialsProvider(creds)
+ .setRefSpecs(new RefSpec("master:gh-pages"))
+ .setForce(true)
+ .call()
+
+ streams.value.log("Pushing to Github Pages complete!")
+ }
)
lazy val demos = project.in(file("examples/demos"))
diff --git a/examples/crossBuilds/clientserver/Procfile b/examples/crossBuilds/clientserver/Procfile
new file mode 100644
index 0000000..59ff8a8
--- /dev/null
+++ b/examples/crossBuilds/clientserver/Procfile
@@ -0,0 +1 @@
+web: server/target/universal/stage/bin/server
diff --git a/examples/crossBuilds/clientserver/build.sbt b/examples/crossBuilds/clientserver/build.sbt
index e5b70ff..3bba3af 100644
--- a/examples/crossBuilds/clientserver/build.sbt
+++ b/examples/crossBuilds/clientserver/build.sbt
@@ -1,3 +1,4 @@
+import NativePackagerKeys._
import utest.jsrunner.JsCrossBuild
import scala.scalajs.sbtplugin.ScalaJSPlugin._
import ScalaJSKeys._
@@ -22,6 +23,7 @@ lazy val client = project.in(file("client"))
lazy val server = project.in(file("server"))
.settings(sharedSettings:_*)
+ .settings(packageArchetype.java_application:_*)
.settings(
libraryDependencies ++= Seq(
"io.spray" %% "spray-can" % "1.3.2",
diff --git a/examples/crossBuilds/clientserver/project/build.properties b/examples/crossBuilds/clientserver/project/build.properties
new file mode 100644
index 0000000..748703f
--- /dev/null
+++ b/examples/crossBuilds/clientserver/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.7
diff --git a/examples/crossBuilds/clientserver/project/build.sbt b/examples/crossBuilds/clientserver/project/build.sbt
index 7c60a91..c24e2b0 100644
--- a/examples/crossBuilds/clientserver/project/build.sbt
+++ b/examples/crossBuilds/clientserver/project/build.sbt
@@ -3,3 +3,4 @@ addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5")
addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.4")
+addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.7.4")
diff --git a/examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala b/examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala
index ce6617c..d657290 100644
--- a/examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala
+++ b/examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala
@@ -10,7 +10,7 @@ object Page{
script(src:="/client-fastopt.js"),
link(
rel:="stylesheet",
- href:="http://yui.yahooapis.com/pure/0.5.0/pure-min.css"
+ href:="https://cdnjs.cloudflare.com/ajax/libs/pure/0.5.0/pure-min.css"
)
),
body(
diff --git a/examples/crossBuilds/clientserver/server/src/main/scala/simple/Server.scala b/examples/crossBuilds/clientserver/server/src/main/scala/simple/Server.scala
index 4d1ece2..e9038b5 100644
--- a/examples/crossBuilds/clientserver/server/src/main/scala/simple/Server.scala
+++ b/examples/crossBuilds/clientserver/server/src/main/scala/simple/Server.scala
@@ -4,10 +4,13 @@ import akka.actor.ActorSystem
import spray.http.{HttpEntity, MediaTypes}
import spray.routing.SimpleRoutingApp
+import scala.util.Properties
+
object Server extends SimpleRoutingApp{
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem()
- startServer("localhost", port = 8080){
+ val port = Properties.envOrElse("PORT", "8080").toInt
+ startServer("0.0.0.0", port = port){
get{
pathSingleSlash{
complete{
diff --git a/examples/demos/build.sbt b/examples/demos/build.sbt
index 5b55829..aa801b0 100644
--- a/examples/demos/build.sbt
+++ b/examples/demos/build.sbt
@@ -1,4 +1,6 @@
-import scala.scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys.jsDependencies
+import scala.scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys._
+
+(emitSourceMaps in fullOptJS) := false
scalaJSSettings
diff --git a/examples/demos/src/main/scala/advanced/Async.scala b/examples/demos/src/main/scala/advanced/Async.scala
index 481e80e..c4d7366 100644
--- a/examples/demos/src/main/scala/advanced/Async.scala
+++ b/examples/demos/src/main/scala/advanced/Async.scala
@@ -29,11 +29,11 @@ object Async {
type ME = dom.MouseEvent
val mousemove =
- Channel[ME](canvas.onmousemove = _)
+ new Channel[ME](canvas.onmousemove = _)
val mouseup =
- Channel[ME](canvas.onmouseup = _)
+ new Channel[ME](canvas.onmouseup = _)
val mousedown =
- Channel[ME](canvas.onmousedown = _)
+ new Channel[ME](canvas.onmousedown = _)
async{
while(true){
@@ -99,7 +99,7 @@ object Async {
}
}
-case class Channel[T](init: (T => Unit) => Unit){
+class Channel[T](init: (T => Unit) => Unit){
init(update)
private[this] var value: Promise[T] = null
def apply(): Future[T] = {
diff --git a/examples/demos/src/main/scala/scrollmenu/Controller.scala b/examples/demos/src/main/scala/scrollmenu/Controller.scala
index 4480445..fe35be8 100644
--- a/examples/demos/src/main/scala/scrollmenu/Controller.scala
+++ b/examples/demos/src/main/scala/scrollmenu/Controller.scala
@@ -28,53 +28,53 @@ object Controller{
val snippets = dom.document.getElementsByClassName("highlight-me")
snippets.foreach(js.Dynamic.global.hljs.highlightBlock(_))
- def rest() = {
- val scrollSpy = new ScrollSpy(structure, main)
- val list = ul(cls := "menu-item-list collapsed")(
- scrollSpy.domTrees.map(_.value.frag)
+
+ val scrollSpy = new ScrollSpy(structure, main)
+ val list = ul(cls := "menu-item-list collapsed")(
+ scrollSpy.domTrees.value.frag
+ ).render
+
+ def updateScroll() = scrollSpy()
+ val expandIcon = i(cls := "fa fa-caret-down").render
+ val expandLink =
+ a(
+ expandIcon,
+ href := "javascript:",
+ marginLeft := "0px",
+ paddingLeft := "15px",
+ paddingRight := "15px",
+ position.absolute,
+ top := "0px",
+ right := "0px",
+ cls := "pure-menu-selected",
+ onclick := { (e: dom.Event) =>
+ expandIcon.classList.toggle("fa-caret-down")
+ expandIcon.classList.toggle("fa-caret-up")
+ list.classList.toggle("collapsed")
+ list.classList.toggle("expanded")
+ scrollSpy.toggleOpen()
+// updateScroll()
+ }
+ ).render
+
+
+ menu.appendChild(
+ div(cls := "pure-menu pure-menu-open")(
+ a(cls := "pure-menu-heading")(
+ "Contents", expandLink
+ ),
+ list
).render
+ )
- def updateScroll() = scrollSpy()
- val expandIcon = i(cls := "fa fa-caret-down").render
- val expandLink =
- a(
- expandIcon,
- href := "javascript:",
- marginLeft := "0px",
- paddingLeft := "15px",
- paddingRight := "15px",
- position.absolute,
- top := "0px",
- right := "0px",
- cls := "pure-menu-selected",
- onclick := { (e: dom.Event) =>
- expandIcon.classList.toggle("fa-caret-down")
- expandIcon.classList.toggle("fa-caret-up")
- list.classList.toggle("collapsed")
- scrollSpy.clean = !scrollSpy.clean
- updateScroll()
- }
- ).render
-
-
- menu.appendChild(
- div(cls := "pure-menu pure-menu-open")(
- a(cls := "pure-menu-heading")(
- "Contents", expandLink
- ),
- list
- ).render
- )
-
- menuLink.onclick = (e: dom.MouseEvent) => {
- layout.classList.toggle("active")
- menu.classList.toggle("active")
- menuLink.classList.toggle("active")
- }
-
- main.onscroll = (e: dom.UIEvent) => updateScroll()
- updateScroll()
+ menuLink.onclick = (e: dom.MouseEvent) => {
+ layout.classList.toggle("active")
+ menu.classList.toggle("active")
+ menuLink.classList.toggle("active")
}
- dom.setTimeout(rest _, 10)
+
+ main.onscroll = (e: dom.UIEvent) => updateScroll()
+ updateScroll()
+
}
}
diff --git a/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala b/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala
deleted file mode 100644
index 9ce9a2b..0000000
--- a/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala
+++ /dev/null
@@ -1,117 +0,0 @@
-package scrollmenu
-
-import org.scalajs.dom
-
-import scala.scalajs.js
-import scalatags.JsDom.all._
-
-case class Tree[T](value: T, children: Vector[Tree[T]])
-
-case class MenuNode(frag: dom.HTMLElement, id: String, start: Int, end: Int)
-
-/**
- * High performance scrollspy to work keep the left menu bar in sync.
- * Lots of sketchy imperative code in order to maximize performance.
- */
-class ScrollSpy(structure: Tree[String],
- main: dom.HTMLElement,
- var clean: Boolean = false){
- val (headers, domTrees) = {
- var i = 0
- def recurse(t: Tree[String], depth: Int): Tree[MenuNode] = {
- val curr =
- li(
- a(
- t.value,
- href:="#"+Controller.munge(t.value),
- cls:="menu-item"
- )
- )
- val originalI = i
- i += 1
- val children = t.children.map(recurse(_, depth + 1))
- Tree(
- MenuNode(
- curr(ul(paddingLeft := "15px",children.map(_.value.frag))).render,
- Controller.munge(t.value),
- originalI,
- if (children.length > 0) children.map(_.value.end).max else originalI + 1
- ),
- children
- )
- }
- def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = {
- if (el == parent) 0
- else el.offsetTop + offset(el.offsetParent.asInstanceOf[dom.HTMLElement], parent)
- }
- val headers = {
- val menuItems = {
- def rec(current: Tree[String]): Seq[String] = {
- current.value +: current.children.flatMap(rec)
- }
- rec(structure).tail
- }
- menuItems.map(Controller.munge)
- .map(dom.document.getElementById)
- .map(offset(_, main))
- .toVector
- }
- val domTrees = structure.children.map(recurse(_, 0))
- (headers, domTrees)
- }
-
-
- private[this] var scrolling = -1
- def apply() = {
- dom.clearTimeout(scrolling)
- scrolling = dom.setTimeout(() => start(), 200)
- }
- private[this] def start() = {
-// scrolling = false
- def scroll(el: dom.Element) = {
- val rect = el.getBoundingClientRect()
- if (rect.top <= 0)
- el.scrollIntoView(true)
- else if (rect.top > dom.innerHeight)
- el.scrollIntoView(false)
- }
- def walkTree(tree: Tree[MenuNode]): Boolean = {
- val Tree(MenuNode(menuItem, itemId, start, end), children) = tree
- val before = headers(start) <= main.scrollTop
- val after = (end >= headers.length) || headers(end) > main.scrollTop
-
- val win = before && after
-
- if (win){
- menuItem.classList.remove("hide")
- var winFound = false
-
- for(c <- tree.children){
- val newWinFound = walkTree(c)
- if (!winFound) c.value.frag.classList.add("selected")
- else c.value.frag.classList.remove("selected")
- winFound = winFound | newWinFound
- }
- if (!winFound) {
- // This means it's the leaf element, because it won but there
- // aren't any children which won, so it must be the actual leaf
- tree.children.foreach(_.value.frag.classList.remove("selected"))
- if (dom.location.hash != itemId)
- dom.history.pushState(null, null, "#"+itemId)
- scroll(menuItem.children(0))
-
- }
- menuItem.children(0).classList.add("pure-menu-selected")
- }else{
- if(clean) tree.children.map(walkTree)
- menuItem.children(0).classList.remove("pure-menu-selected")
- menuItem.classList.add("hide")
- menuItem.classList.remove("selected")
- }
- win
- }
- domTrees.map(walkTree)
- }
-
-
-} \ No newline at end of file
diff --git a/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala b/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala
new file mode 100644
index 0000000..736f9b3
--- /dev/null
+++ b/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala
@@ -0,0 +1,170 @@
+package scrollmenu
+
+import org.scalajs.dom
+import org.scalajs.dom.extensions._
+import scala.scalajs.js
+import scalatags.JsDom.all._
+
+case class Tree[T](value: T, children: Vector[Tree[T]])
+
+case class MenuNode(frag: dom.HTMLElement, id: String, start: Int, end: Int)
+
+/**
+ * High performance scrollspy to work keep the left menu bar in sync.
+ * Lots of sketchy imperative code in order to maximize performance.
+ */
+class ScrollSpy(structure: Tree[String],
+ main: dom.HTMLElement){
+ lazy val domTrees = {
+ var i = -1
+ def recurse(t: Tree[String], depth: Int): Tree[MenuNode] = {
+ val curr =
+ li(
+ a(
+ t.value,
+ display := (if (i == -1) "none" else "block"),
+ href:="#"+Controller.munge(t.value),
+ cls:="menu-item"
+ )
+ )
+ val originalI = i
+ i += 1
+ val children = t.children.map(recurse(_, depth + 1))
+ Tree(
+ MenuNode(
+ curr(ul(paddingLeft := "15px",children.map(_.value.frag))).render,
+ Controller.munge(t.value),
+ originalI,
+ if (children.length > 0) children.map(_.value.end).max else originalI + 1
+ ),
+ children
+ )
+ }
+
+ val domTrees = recurse(structure, 0)
+ domTrees
+ }
+ def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = {
+ if (el == parent) 0
+ else el.offsetTop + offset(el.offsetParent.asInstanceOf[dom.HTMLElement], parent)
+ }
+ lazy val headers = {
+ val menuItems = {
+ def rec(current: Tree[String]): Seq[String] = {
+ current.value +: current.children.flatMap(rec)
+ }
+ rec(structure).tail
+ }
+
+ js.Array(
+ menuItems.map(name => dom.document.getElementById(Controller.munge(name)))
+ .map((el) => () => offset(el, main)):_*
+ )
+ }
+
+ var open = false
+ def toggleOpen() = {
+ open = !open
+ if (open){
+ def rec(tree: Tree[MenuNode])(f: MenuNode => Unit): Unit = {
+ f(tree.value)
+ tree.children.foreach(rec(_)(f))
+ }
+ rec(domTrees)(setFullHeight)
+ }else{
+ start(force = true)
+ }
+ }
+
+ def setFullHeight(mn: MenuNode) = {
+ mn.frag
+ .children(1)
+ .asInstanceOf[dom.HTMLElement]
+ .style
+ .maxHeight = (mn.end - mn.start + 1) * 44 + "px"
+ }
+ private[this] var scrolling = false
+ private[this] var scrollTop = -1
+ def apply(): Unit = {
+ if (!scrolling) {
+ scrolling = true
+ scrollTop = main.scrollTop
+ dom.setTimeout({() =>
+ scrolling = false
+ if (scrollTop == main.scrollTop) start()
+ else apply()
+ },
+ 75
+ )
+ }
+ }
+ private[this] var previousWin: MenuNode = null
+ private[this] def start(force: Boolean = false) = {
+
+ def scroll(el: dom.Element) = {
+ val rect = el.getBoundingClientRect()
+ if (rect.top <= 0)
+ el.scrollIntoView(true)
+ else if (rect.top > dom.innerHeight)
+ el.scrollIntoView(false)
+ }
+ val scrollTop = main.scrollTop
+ def walkIndex(tree: Tree[MenuNode]): List[Tree[MenuNode]] = {
+ val t @ Tree(m, children) = tree
+ val win = if(m.start == -1) true
+ else {
+ val before = headers(m.start)() <= scrollTop
+ val after = (m.end >= headers.length) || headers(m.end)() > scrollTop
+ before && after
+ }
+ val childIndexes = children.map(walkIndex)
+ val childWin = childIndexes.indexWhere(_ != null)
+ if (childWin != -1) t :: childIndexes(childWin)
+ else if (win) List(t)
+ else null
+ }
+
+ val winPath = walkIndex(domTrees)
+ val winItem = winPath.last.value
+ def walkTree(indices: List[Tree[MenuNode]]): Int = indices match {
+ case Nil => 0
+ case (Tree(mn, children) :: rest) =>
+
+ mn.frag.classList.remove("hide")
+ mn.frag.classList.remove("selected")
+
+ mn.frag.children(0).classList.add("pure-menu-selected")
+ for {
+ child <- children
+ if !indices.headOption.exists(_.value.frag == child.value.frag)
+ } walkHide(child)
+
+ val size = walkTree(rest) + children.length
+ mn.frag.children(1).asInstanceOf[dom.HTMLElement].style.maxHeight =
+ if (!open) size * 44 + "px" else "none"
+ size
+ }
+
+ def walkHide(tree: Tree[MenuNode]): Unit = {
+ val frag = tree.value.frag
+
+ frag.children(0).classList.remove("pure-menu-selected")
+ frag.classList.add("hide")
+
+ frag.children(1).asInstanceOf[dom.HTMLElement].style.maxHeight =
+ if (!open) "0px" else "none"
+
+ if (tree.value.start < winItem.start) frag.classList.add("selected")
+ else frag.classList.remove("selected")
+ tree.children.foreach(walkHide)
+ }
+
+ if (winItem != previousWin || force){
+ scroll(winItem.frag.children(0))
+ dom.history.replaceState(null, null, "#" + winItem.id)
+ previousWin = winItem
+// println(winPath.map(_.value.id))
+ walkTree(winPath)
+ }
+ }
+} \ No newline at end of file
diff --git a/project/build.properties b/project/build.properties
index be6c454..748703f 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=0.13.5
+sbt.version=0.13.7
diff --git a/project/build.sbt b/project/build.sbt
index 1fbce95..489e892 100644
--- a/project/build.sbt
+++ b/project/build.sbt
@@ -1,3 +1,9 @@
addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5")
addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.4")
+
+addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.7.4")
+
+libraryDependencies += "org.eclipse.jgit" % "org.eclipse.jgit" % "3.5.1.201410131835-r"
+
+addSbtPlugin("com.lihaoyi" % "scalatex-sbt-plugin" % "0.1.0")
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..0646b4c
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,16 @@
+Hands-on Scala.js
+=================
+
+Source code for [lihaoyi.github.io/hands-on-scala-js](http://lihaoyi.github.io/hands-on-scala-js)
+
+To build,
+
+```
+sbt book/run
+```
+
+And look in
+
+```
+book/target/output/index.html
+```
diff --git a/scalatexApi/src/main/scala/scalatex/package.scala b/scalatex/api/src/main/scala/scalatex/package.scala
index 1f13e63..1f13e63 100644
--- a/scalatexApi/src/main/scala/scalatex/package.scala
+++ b/scalatex/api/src/main/scala/scalatex/package.scala
diff --git a/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala b/scalatex/api/src/main/scala/scalatex/stages/Compiler.scala
index 4f11b04..3df8da7 100644
--- a/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala
+++ b/scalatex/api/src/main/scala/scalatex/stages/Compiler.scala
@@ -48,7 +48,6 @@ object Compiler{
incPos(q"$curr(..${compileBlock(parts, offset1)})", offset1)
case (curr, Ast.Header(header, block, offset1)) =>
incPos(q"$curr(${compileHeader(header, block, offset1)})", offset1)
-
}
out
diff --git a/scalatex/api/src/main/scala/scalatex/stages/Omg/scala.scala b/scalatex/api/src/main/scala/scalatex/stages/Omg/scala.scala
new file mode 100644
index 0000000..81fa1f9
--- /dev/null
+++ b/scalatex/api/src/main/scala/scalatex/stages/Omg/scala.scala
@@ -0,0 +1,8 @@
+package scalatex.stages.Omg
+
+/**
+ * Created by haoyi on 12/3/14.
+ */
+class scala {
+
+}
diff --git a/scalatexApi/src/main/scala/scalatex/stages/Parser.scala b/scalatex/api/src/main/scala/scalatex/stages/Parser.scala
index 403cb72..0b87d97 100644
--- a/scalatexApi/src/main/scala/scalatex/stages/Parser.scala
+++ b/scalatex/api/src/main/scala/scalatex/stages/Parser.scala
@@ -2,7 +2,7 @@ package scalatex
package stages
import acyclic.file
import org.parboiled2._
-import scalaparser.ScalaSyntax
+import scalaParser.ScalaSyntax
/**
* Parses the input text into a roughly-structured AST. This AST
@@ -15,7 +15,7 @@ object Parser extends ((String, Int) => Ast.Block){
new Parser(input, offset).Body.run().get
}
}
-class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends ScalaSyntax(input) {
+class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends scalaParser.ScalaSyntax(input) {
def offsetCursor = offset + cursor
val txt = input.sliceString(0, input.length)
val indentTable = txt.split('\n').map{ s =>
@@ -41,7 +41,7 @@ class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends Scala
"@" ~ capture(Identifiers.Id | BlockExpr2 | ('(' ~ optional(Exprs) ~ ')'))
}
def Header = rule {
- "@" ~ capture(Def(false) | Import(false))
+ "@" ~ capture(Def | Import)
}
def HeaderBlock: Rule1[Ast.Header] = rule{
@@ -65,7 +65,7 @@ class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends Scala
test(cursorNextIndent() > indent) ~
runSubParser(new Parser(_, cursorNextIndent(), cursor).Body)
}
- def IfHead = rule{ "@" ~ capture("if" ~ "(" ~ Expr() ~ ")") }
+ def IfHead = rule{ "@" ~ capture("if" ~ "(" ~ Expr ~ ")") }
def IfElse1 = rule{
push(offsetCursor) ~ IfHead ~ BraceBlock ~ optional("else" ~ (BraceBlock | IndentBlock))
}
@@ -104,7 +104,7 @@ class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends Scala
def TypeArgs2 = rule { '[' ~ Ws ~ Types ~ ']' }
def ArgumentExprs2 = rule {
'(' ~ Ws ~
- (optional(Exprs ~ ',' ~ Ws) ~ PostfixExpr() ~ ':' ~ Ws ~ '_' ~ Ws ~ '*' ~ Ws | optional(Exprs) ) ~
+ (optional(Exprs ~ ',' ~ Ws) ~ PostfixExpr ~ ':' ~ Ws ~ '_' ~ Ws ~ '*' ~ Ws | optional(Exprs) ) ~
')'
}
def BlockExpr2: Rule0 = rule { '{' ~ Ws ~ (CaseClauses | Block) ~ Ws ~ '}' }
diff --git a/scalatexApi/src/main/scala/scalatex/stages/Trim.scala b/scalatex/api/src/main/scala/scalatex/stages/Trim.scala
index 8993734..8993734 100644
--- a/scalatexApi/src/main/scala/scalatex/stages/Trim.scala
+++ b/scalatex/api/src/main/scala/scalatex/stages/Trim.scala
diff --git a/scalatexApi/src/test/scala/scalatex/BasicTests.scala b/scalatex/api/src/test/scala/scalatex/BasicTests.scala
index 4bc362c..4bc362c 100644
--- a/scalatexApi/src/test/scala/scalatex/BasicTests.scala
+++ b/scalatex/api/src/test/scala/scalatex/BasicTests.scala
diff --git a/scalatexApi/src/test/scala/scalatex/ErrorTests.scala b/scalatex/api/src/test/scala/scalatex/ErrorTests.scala
index d8cd4f5..d8cd4f5 100644
--- a/scalatexApi/src/test/scala/scalatex/ErrorTests.scala
+++ b/scalatex/api/src/test/scala/scalatex/ErrorTests.scala
diff --git a/scalatexApi/src/test/scala/scalatex/ParserTests.scala b/scalatex/api/src/test/scala/scalatex/ParserTests.scala
index a4b11b6..9a4ee63 100644
--- a/scalatexApi/src/test/scala/scalatex/ParserTests.scala
+++ b/scalatex/api/src/test/scala/scalatex/ParserTests.scala
@@ -2,7 +2,7 @@ package scalatex
import org.parboiled2._
-import scalaparser.ScalaSyntax
+import scalaParser.ScalaSyntax
import scalatex.stages.{Trim, Parser, Ast}
import scalatex.stages.Ast.Block.{IfElse, For, Text}
diff --git a/scalatexApi/src/test/scala/scalatex/TestUtil.scala b/scalatex/api/src/test/scala/scalatex/TestUtil.scala
index 5a72677..5a72677 100644
--- a/scalatexApi/src/test/scala/scalatex/TestUtil.scala
+++ b/scalatex/api/src/test/scala/scalatex/TestUtil.scala
diff --git a/scalatex/build.sbt b/scalatex/build.sbt
new file mode 100644
index 0000000..4769bad
--- /dev/null
+++ b/scalatex/build.sbt
@@ -0,0 +1,44 @@
+val sharedSettings = Seq(
+ version := "0.1.0",
+ organization := "com.lihaoyi",
+ crossScalaVersions:= Seq("2.10.4", "2.11.2"),
+ scalaVersion := "2.11.4",
+ libraryDependencies += "com.lihaoyi" %% "acyclic" % "0.1.2" % "provided",
+ addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2"),
+ autoCompilerPlugins := true
+)
+
+lazy val scalaParser = project.settings(sharedSettings:_*)
+ .settings(
+ name := "scala-parser-lite",
+ libraryDependencies ++= Seq(
+ "com.lihaoyi" %% "utest" % "0.2.4",
+ "org.parboiled" %% "parboiled" % "2.0.1"
+ ),
+ testFrameworks += new TestFramework("utest.runner.JvmFramework")
+ )
+lazy val api = project.settings(sharedSettings:_*)
+ .dependsOn(scalaParser)
+ .settings(
+ name := "scalatex-api",
+ libraryDependencies ++= Seq(
+ "com.lihaoyi" %% "utest" % "0.2.4",
+ "com.scalatags" %% "scalatags" % "0.4.2",
+ "org.scala-lang" % "scala-reflect" % scalaVersion.value,
+ "org.parboiled" %% "parboiled" % "2.0.1"
+ ),
+ testFrameworks += new TestFramework("utest.runner.JvmFramework")
+ )
+
+lazy val scalatexSbtPlugin = project.settings(sharedSettings:_*)
+ .settings(
+ name := "scalatex-sbt-plugin",
+ scalaVersion := "2.10.4",
+ sbtPlugin := true
+)
+lazy val compilerPlugin = project.settings(sharedSettings:_*)
+ .dependsOn(api)
+ .settings(
+ name := "scalatex-compiler-plugin",
+ libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value
+) \ No newline at end of file
diff --git a/scalatex/compilerPlugin/src/main/resources/scalac-plugin.xml b/scalatex/compilerPlugin/src/main/resources/scalac-plugin.xml
new file mode 100755
index 0000000..a446f13
--- /dev/null
+++ b/scalatex/compilerPlugin/src/main/resources/scalac-plugin.xml
@@ -0,0 +1,4 @@
+<plugin>
+ <name>demo-plugin</name>
+ <classname>scalatex.CompilerPlugin</classname>
+</plugin> \ No newline at end of file
diff --git a/scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala b/scalatex/compilerPlugin/src/main/scala/scalatex/CompilerPlugin.scala
index 6686258..e122de5 100755
--- a/scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala
+++ b/scalatex/compilerPlugin/src/main/scala/scalatex/CompilerPlugin.scala
@@ -7,40 +7,52 @@ import scala.reflect.io.VirtualFile
import scala.tools.nsc.{ Global, Phase }
import scala.tools.nsc.plugins.{ Plugin, PluginComponent }
-class ScalaTexPlugin(val global: Global) extends Plugin {
+class CompilerPlugin(val global: Global) extends Plugin {
import global._
- println(options)
- val name = "scalatex-plugin"
- val description = "Enforces coding standards"
+
+ override def init(options: List[String], error: String => Unit): Boolean = true
+
+ val name = "scalatex"
+ val description = "Compiles scalatex files into Scala compilation units"
val components = List[PluginComponent](DemoComponent)
private object DemoComponent extends PluginComponent {
- val global = ScalaTexPlugin.this.global
+
+ val global = CompilerPlugin.this.global
import global._
override val runsAfter = List("parser")
override val runsBefore = List("namer")
val phaseName = "Demo"
- val scalatexRoot = "book/src/main/scalatex"
+
override def newPhase(prev: Phase) = new GlobalPhase(prev) {
+ val splitOptions = options.map(o => o.splitAt(o.indexOf(":")+1))
+ val scalatexRoots = splitOptions.collect{case ("root:", p) => p}
override def run() = {
- def recursiveListFiles(f: java.io.File): Array[java.io.File] = {
- val (dirs, files) = f.listFiles().partition(_.isDirectory)
- files ++ dirs.flatMap(recursiveListFiles)
+ def recursiveListFiles(f: java.io.File): Iterator[java.io.File] = {
+ val (dirs, files) =
+ Option(f.listFiles())
+ .toSeq
+ .flatten
+ .partition(_.isDirectory)
+ files.iterator ++ dirs.iterator.flatMap(recursiveListFiles)
}
- for (file <- recursiveListFiles(new java.io.File(scalatexRoot))) {
- val txt = io.Source.fromFile(file).mkString
- val fakeJfile = new java.io.File(file.getName)
- val virtualFile = new VirtualFile(file.getName) {
+ for {
+ scalatexRoot <- scalatexRoots
+ file <- recursiveListFiles(new java.io.File(scalatexRoot))
+ } {
+ val name = file.getCanonicalPath
+ val fakeJfile = new java.io.File(name)
+ val txt = io.Source.fromFile(name).mkString
+ val virtualFile = new VirtualFile(name) {
override def file = fakeJfile
}
val sourceFile = new BatchSourceFile(virtualFile, txt)
val unit = new CompilationUnit(sourceFile)
- val name = file.getName
- val objectName = name.slice(name.lastIndexOf('/'), name.lastIndexOf('.'))
+ val objectName = name.slice(name.lastIndexOf('/')+1, name.lastIndexOf('.'))
val pkgName =
Paths.get(scalatexRoot)
- .relativize(file.getParentFile.toPath)
+ .relativize(fakeJfile.getParentFile.toPath)
.toString
.split("/")
.map(s => s"package $s")
@@ -49,8 +61,9 @@ class ScalaTexPlugin(val global: Global) extends Plugin {
val shim = s"""
$pkgName
import scalatags.Text.all._
+
object $objectName{
- def apply() = scalatex.twf("${file.getPath}")
+ def apply() = scalatex.twf("${name}")
}
"""
unit.body = global.newUnitParser(shim).parse()
diff --git a/scalatex/project/build.properties b/scalatex/project/build.properties
new file mode 100644
index 0000000..748703f
--- /dev/null
+++ b/scalatex/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.7
diff --git a/scalatex/project/build.sbt b/scalatex/project/build.sbt
new file mode 100644
index 0000000..f1db6ad
--- /dev/null
+++ b/scalatex/project/build.sbt
@@ -0,0 +1 @@
+addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.4")
diff --git a/scalatex/scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala b/scalatex/scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala
new file mode 100644
index 0000000..de9f039
--- /dev/null
+++ b/scalatex/scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala
@@ -0,0 +1,416 @@
+package scalaParser
+import acyclic.file
+import language.implicitConversions
+import syntax._
+import org.parboiled2._
+
+/**
+ * Parser for Scala syntax.
+ *
+ * The `G` parameter that gets passed in to each rule stands for
+ * "Greedy", and determines whether or not that rule is to consume
+ * newlines after the last terminal in that rule. We need to pass it
+ * everywhere so it can go all the way to the last terminal deep
+ * inside the parse tree, which can then decide whether or not to
+ * consume whitespace.
+ *
+ * The vast majority of terminals will consume newlines; only rules
+ * which occur in {} blocks won't have their terminals consume newlines,
+ * and only the *last* terminal in the rule will be affected.
+ * That's why the parser does terminals-consume-newlines-by-default,
+ * and leaves it up to the dev to thread the `G` variable where-ever
+ * we want the opposite behavior.
+ */
+class ScalaSyntax(val input: ParserInput) extends Parser with Basic with Identifiers with Literals {
+ // Aliases for common things. These things are used in almost every parser
+ // in the file, so it makes sense to keep them short.
+ type B = Boolean
+ val t = true
+ type R0 = Rule0
+ /**
+ * Parses all whitespace, excluding newlines. This is only
+ * really useful in e.g. {} blocks, where we want to avoid
+ * capturing newlines so semicolon-inference would work
+ */
+ def WS = rule { zeroOrMore(Basic.WhitespaceChar | Literals.Comment) }
+
+ /**
+ * Parses whitespace, including newlines.
+ * This is the default for most things
+ */
+ def WL = rule{ zeroOrMore(Basic.WhitespaceChar | Literals.Comment | Basic.Newline) }
+
+
+
+ /**
+ * By default, all strings and characters greedily
+ * capture all whitespace immediately after the token.
+ */
+ implicit private[this] def wspStr(s: String): R0 = rule { WL ~ str(s) }
+ implicit private[this] def wspChar(s: Char): R0 = rule { WL ~ ch(s) }
+
+ /**
+ * Most keywords don't just require the correct characters to match,
+ * they have to ensure that subsequent characters *don't* match in
+ * order for it to be a keyword. This enforces that rule for key-words
+ * (W) and key-operators (O) which have different non-match criteria.
+ */
+ object K {
+ def W(s: String) = rule {
+ WL ~ Key.W(s)
+ }
+
+ def O(s: String) = rule {
+ WL ~ Key.O(s)
+ }
+ }
+
+
+ def pos = cursor -> cursorChar
+
+ /**
+ * helper printing function
+ */
+ def pr(s: String) = rule { run(println(s"LOGGING $cursor: $s")) }
+
+ def Id = rule { WL ~ Identifiers.Id }
+ def VarId = rule { WL ~ Identifiers.VarId }
+ def Literal = rule { WL ~ Literals.Literal }
+ def Semi = rule { WS ~ Basic.Semi }
+ def Semis = rule { oneOrMore(Semi) }
+ def Newline = rule { WL ~ Basic.Newline }
+
+ def QualId = rule { WL ~ oneOrMore(Id).separatedBy('.') }
+ def Ids = rule { oneOrMore(Id) separatedBy ',' }
+
+ def Path: R0 = rule {
+ zeroOrMore(Id ~ '.') ~ K.W("this") ~ zeroOrMore(Id).separatedBy('.') |
+ StableId
+ }
+ def StableId: R0 = rule {
+ zeroOrMore(Id ~ '.') ~ (K.W("this") | K.W("super") ~ optional(ClassQualifier)) ~ '.' ~ oneOrMore(Id).separatedBy('.') |
+ Id ~ zeroOrMore(WL ~ '.' ~ WL ~ Id)
+ }
+
+ def ClassQualifier = rule { '[' ~ Id ~ ']' }
+
+ def Type: R0 = rule {
+ FunctionArgTypes ~ K.O("=>") ~ Type | InfixType ~ optional(WL ~ ExistentialClause)
+ }
+ def FunctionArgTypes = rule {
+ InfixType | '(' ~ optional(oneOrMore(ParamType) separatedBy ',') ~ ')'
+ }
+
+ def ExistentialClause = rule { "forSome" ~ '{' ~ oneOrMore(ExistentialDcl).separatedBy(Semi) }
+ def ExistentialDcl = rule { K.W("type") ~ TypeDcl | K.W("val") ~ ValDcl }
+
+ def InfixType = rule {
+ CompoundType ~ zeroOrMore(WL ~ Id ~ optional(Newline) ~ CompoundType)
+ }
+ def CompoundType = rule {
+ oneOrMore(AnnotType).separatedBy(WL ~ K.W("with")) ~ optional(Refinement)
+ }
+ def AnnotType = rule {
+ SimpleType ~ zeroOrMore(WL ~ Annotation)
+ }
+ def SimpleType: R0 = rule {
+ BasicType ~
+ optional(WL ~ '#' ~ Id) ~
+ optional(WL ~ TypeArgs)
+ }
+ def BasicType: R0 = rule {
+ '(' ~ Types ~ ')' |
+ Path ~ '.' ~ K.W("type") |
+ StableId
+ }
+ def TypeArgs = rule { '[' ~ Types ~ "]" }
+ def Types = rule { oneOrMore(Type).separatedBy(',') }
+ def Refinement = rule {
+ optional(Newline) ~ '{' ~ oneOrMore(RefineStat).separatedBy(Semi) ~ "}"
+ }
+ def RefineStat = rule { "type" ~ TypeDef | Dcl | MATCH }
+ def TypePat = rule { CompoundType }
+ def Ascription = rule {
+ ":" ~ ("_" ~ "*" | InfixType | oneOrMore(Annotation))
+ }
+
+ def ParamType = rule { K.O("=>") ~ Type | Type ~ "*" | Type }
+
+ def Expr: R0 = rule {
+ (Bindings | optional(K.W("implicit")) ~ Id | "_") ~ K.O("=>") ~ Expr |
+ Expr1
+ }
+ def Expr1: R0 = rule {
+ IfCFlow |
+ WhileCFlow |
+ TryCFlow |
+ DoWhileCFlow |
+ ForCFlow |
+ K.W("throw") ~ Expr |
+ K.W("return") ~ optional(Expr) |
+ SimpleExpr ~ K.O("=") ~ Expr |
+ PostfixExpr ~ optional("match" ~ '{' ~ CaseClauses ~ "}" | Ascription)
+
+ }
+ def IfCFlow = rule { "if" ~ '(' ~ Expr ~ ')' ~ zeroOrMore(Newline) ~ Expr ~ optional(optional(Semi) ~ K.W("else") ~ Expr) }
+ def WhileCFlow = rule { "while" ~ '(' ~ Expr ~ ')' ~ zeroOrMore(Newline) ~ Expr }
+ def TryCFlow = rule {
+ K.W("try") ~ Expr ~
+ optional(WL ~ K.W("catch") ~ Expr) ~
+ optional(WL ~ K.W("finally") ~ Expr)
+ }
+
+ def DoWhileCFlow = rule { K.W("do") ~ Expr ~ optional(Semi) ~ "while" ~ '(' ~ Expr ~ ")" }
+ def ForCFlow = rule {
+ "for" ~
+ ('(' ~ Enumerators ~ ')' | '{' ~ Enumerators ~ '}') ~
+ zeroOrMore(Newline) ~
+ optional(K.W("yield")) ~
+ Expr }
+ def NotNewline: R0 = rule{ &( WS ~ noneOf("\n") )}
+ def PostfixExpr: R0 = rule { InfixExpr ~ optional(NotNewline ~ Id ~ optional(Newline)) }
+ def InfixExpr: R0 = rule {
+ PrefixExpr ~
+ zeroOrMore(
+ NotNewline ~
+ Id ~
+ optional(Newline) ~
+ PrefixExpr
+ )
+ }
+ def PrefixExpr = rule { optional(WL ~ anyOf("-+~!")) ~ SimpleExpr }
+
+ def SimpleExpr: R0 = rule {
+ SimpleExpr1 ~
+ zeroOrMore(WL ~ ('.' ~ Id | TypeArgs | ArgumentExprs)) ~
+ optional(WL ~ "_")
+ }
+
+ def SimpleExpr1 = rule{
+ K.W("new") ~ (ClassTemplate | TemplateBody) |
+ BlockExpr |
+ Literal |
+ Path |
+ K.W("_") |
+ '(' ~ optional(Exprs) ~ ")"
+ }
+
+
+
+ def Exprs: R0 = rule { oneOrMore(Expr).separatedBy(',') }
+ def ArgumentExprs: R0 = rule {
+ '(' ~ optional(Exprs ~ optional(K.O(":") ~ K.W("_") ~ '*')) ~ ")" |
+ optional(Newline) ~ BlockExpr
+ }
+
+ def BlockExpr: R0 = rule { '{' ~ (CaseClauses | Block) ~ "}" }
+ def BlockEnd: R0 = rule{ optional(Semis) ~ &("}" | "case") }
+ def Block: R0 = rule {
+ optional(Semis) ~
+ (
+ BlockStats ~ optional(Semis ~ ResultExpr) ~ BlockEnd |
+ ResultExpr ~ BlockEnd |
+ MATCH ~ BlockEnd
+ )
+ }
+ def BlockStats: R0 = rule{
+ oneOrMore(BlockStat).separatedBy(Semis)
+ }
+ def BlockStat: R0 = rule {
+ Import |
+ zeroOrMore(Annotation) ~ (optional(K.W("implicit") | K.W("lazy")) ~ Def | zeroOrMore(LocalModifier) ~ TmplDef) |
+ Expr1
+ }
+ def ResultExpr: R0 = rule {
+ (Bindings | optional(K.W("implicit")) ~ Id | "_") ~ K.W("=>") ~ Block | Expr1
+ }
+ def Enumerators: R0 = rule { Generator ~ zeroOrMore(Semi ~ Enumerator) ~ WL }
+ def Enumerator: R0 = rule { Generator | Guard | Pattern1 ~ K.O("=") ~ Expr }
+ def Generator: R0 = rule { Pattern1 ~ K.O("<-") ~ Expr ~ optional(WL ~ Guard) }
+ def CaseClauses: R0 = rule { oneOrMore(CaseClause) }
+ def CaseClause: R0 = rule { K.W("case") ~ Pattern ~ optional(Guard) ~ K.O("=>") ~ Block }
+ def Guard: R0 = rule { K.W("if") ~ PostfixExpr }
+ def Pattern: R0 = rule {
+ oneOrMore(Pattern1).separatedBy('|')
+ }
+ def Pattern1: R0 = rule {
+ K.W("_") ~ K.O(":") ~ TypePat | VarId ~ K.O(":") ~ TypePat | Pattern2
+ }
+ def Pattern2: R0 = rule {
+ VarId ~ "@" ~ Pattern3 | Pattern3 | VarId
+ }
+ def Pattern3: R0 = rule {
+ SimplePattern ~ zeroOrMore(Id ~ SimplePattern)
+ }
+ def SimplePattern: R0 = rule {
+ K.W("_") |
+ Literal |
+ '(' ~ optional(Patterns) ~ ')' |
+ (
+ StableId ~
+ optional(
+ '(' ~
+ (optional(Patterns ~ ',') ~ optional(VarId ~ '@') ~ K.W("_") ~ '*' | optional(Patterns)) ~
+ ')'
+ )
+ ) |
+ VarId
+ }
+ def Patterns: R0 = rule { K.W("_") ~ '*' | oneOrMore(Pattern).separatedBy(',') }
+
+ def TypeParamClause: R0 = rule { '[' ~ oneOrMore(VariantTypeParam).separatedBy(',') ~ ']' }
+ def FunTypeParamClause: R0 = rule { '[' ~ oneOrMore(TypeParam).separatedBy(',') ~ ']' }
+ def VariantTypeParam: R0 = rule { zeroOrMore(Annotation) ~ optional(anyOf("+-")) ~ TypeParam }
+ def TypeParam: R0 = rule {
+ (Id | K.W("_")) ~
+ optional(TypeParamClause) ~
+ optional(K.O(">:") ~ Type) ~
+ optional(K.O("<:") ~ Type) ~
+ zeroOrMore(K.O("<%") ~ Type) ~
+ zeroOrMore(K.O(":") ~ Type)
+ }
+ def ParamClauses: R0 = rule { zeroOrMore(ParamClause) ~ optional(optional(Newline) ~ '(' ~ K.W("implicit") ~ Params ~ ')') }
+ def ParamClause: R0 = rule { optional(Newline) ~ '(' ~ optional(Params) ~ ')' }
+ def Params: R0 = rule { zeroOrMore(Param).separatedBy(',') }
+ def Param: R0 = rule { zeroOrMore(Annotation) ~ Id ~ optional(K.O(":") ~ ParamType) ~ optional(K.O("=") ~ Expr) }
+ def ClassParamClauses: R0 = rule { zeroOrMore(ClassParamClause) ~ optional(optional(Newline) ~ '(' ~ K.W("implicit") ~ ClassParam ~ ")") }
+ def ClassParamClause: R0 = rule { optional(Newline) ~ '(' ~ optional(ClassParams) ~ ")" }
+ def ClassParams: R0 = rule { oneOrMore(ClassParam).separatedBy(',') }
+ def ClassParam: R0 = rule { zeroOrMore(Annotation) ~ optional(zeroOrMore(Modifier) ~ (K.W("val") | K.W("var"))) ~ Id ~ K.O(":") ~ ParamType ~ optional(K.O("=") ~ Expr) }
+
+ def Bindings: R0 = rule { '(' ~ zeroOrMore(Binding).separatedBy(',') ~ ')' }
+ def Binding: R0 = rule { (Id | K.W("_")) ~ optional(K.O(":") ~ Type) }
+
+ def Modifier: R0 = rule { LocalModifier | AccessModifier | K.W("override") }
+ def LocalModifier: R0 = rule { K.W("abstract") | K.W("final") | K.W("sealed") | K.W("implicit") | K.W("lazy") }
+ def AccessModifier: R0 = rule { (K.W("private") | K.W("protected")) ~ optional(AccessQualifier) }
+ def AccessQualifier: R0 = rule { '[' ~ (K.W("this") | Id) ~ ']' }
+
+ def Annotation: R0 = rule { '@' ~ SimpleType ~ zeroOrMore(WL ~ ArgumentExprs) }
+ def ConstrAnnotation: R0 = rule { '@' ~ SimpleType ~ ArgumentExprs }
+
+ def TemplateBody: R0 = rule {
+ '{' ~
+ optional(SelfType) ~
+ zeroOrMore(TemplateStat).separatedBy(Semis) ~
+ '}'
+ }
+ def TemplateStat: R0 = rule {
+ Import |
+ zeroOrMore(Annotation ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ (Def | Dcl) |
+ Expr
+ }
+
+ def SelfType: R0 = rule { K.W("this") ~ K.O(":") ~ Type ~ K.O("=>") | Id ~ optional(K.O(":") ~ Type) ~ K.O("=>") }
+
+ def Import: R0 = rule { K.W("import") ~ oneOrMore(ImportExpr).separatedBy(',') }
+
+ def ImportExpr: R0 = rule {
+ StableId ~ optional('.' ~ ("_" | ImportSelectors))
+ }
+ def ImportSelectors: R0 = rule { '{' ~ zeroOrMore(ImportSelector ~ ',') ~ (ImportSelector | K.W("_")) ~ "}" }
+ def ImportSelector: R0 = rule { Id ~ optional(K.O("=>") ~ (Id | K.W("_"))) }
+
+ def Dcl: R0 = rule {
+ K.W("val") ~ ValDcl |
+ K.W("var") ~ VarDcl |
+ K.W("def") ~ FunDcl |
+ K.W("type") ~ zeroOrMore(Newline) ~ TypeDcl
+ }
+ def ValDcl: R0 = rule { Ids ~ K.O(":") ~ Type }
+ def VarDcl: R0 = rule { Ids ~ K.O(":") ~ Type }
+ def FunDcl: R0 = rule { FunSig ~ optional(WL ~ K.O(":") ~ Type) }
+ def FunSig: R0 = rule { Id ~ optional(FunTypeParamClause) ~ ParamClauses }
+ def TypeDcl: R0 = rule {
+ Id ~
+ optional(WL ~ TypeParamClause) ~
+ optional(WL ~ K.O(">:") ~ Type) ~
+ optional(WL ~ K.O("<:") ~ Type)
+ }
+
+ def PatVarDef: R0 = rule { K.W("val") ~ PatDef | K.W("var") ~ VarDef }
+ def Def: R0 = rule { K.W("def") ~ FunDef | K.W("type") ~ zeroOrMore(Newline) ~ TypeDef | PatVarDef | TmplDef }
+ def PatDef: R0 = rule { oneOrMore(Pattern2).separatedBy(',') ~ optional(K.O(":") ~ Type) ~ K.O("=") ~ Expr }
+ def VarDef: R0 = rule { Ids ~ K.O(":") ~ Type ~ K.O("=") ~ K.W("_") | PatDef }
+ def FunDef: R0 = rule {
+ K.W("this") ~ ParamClause ~ ParamClauses ~ (K.O("=") ~ ConstrExpr | optional(Newline) ~ ConstrBlock) |
+ FunSig ~
+ (
+ optional(K.O(":") ~ Type) ~ K.O("=") ~ optional(K.W("macro")) ~ Expr |
+ optional(Newline) ~ '{' ~ Block ~ "}"
+ )
+ }
+ def TypeDef: R0 = rule { Id ~ optional(TypeParamClause) ~ K.O("=") ~ Type }
+
+ def TmplDef: R0 = rule {
+ K.W("trait") ~ TraitDef |
+ optional(K.W("case")) ~ (K.W("class") ~ ClassDef |
+ K.W("object") ~ ObjectDef)
+ }
+ def ClassDef: R0 = rule {
+ Id ~
+ optional(TypeParamClause) ~
+ zeroOrMore(ConstrAnnotation) ~
+ optional(AccessModifier) ~
+ ClassParamClauses ~
+ ClassTemplateOpt
+ }
+ def TraitDef: R0 = rule { Id ~ optional(TypeParamClause) ~ TraitTemplateOpt }
+ def ObjectDef: R0 = rule { Id ~ ClassTemplateOpt }
+ def ClassTemplateOpt: R0 = rule {
+ WL ~ K.W("extends") ~ ClassTemplate |
+ optional(WL ~ optional(K.W("extends")) ~ TemplateBody)
+ }
+ def TraitTemplateOpt: R0 = rule { K.W("extends") ~ TraitTemplate | optional(optional(K.W("extends")) ~ TemplateBody) }
+ def ClassTemplate: R0 = rule {
+ optional(EarlyDefs) ~
+ ClassParents ~
+ optional(WL ~ TemplateBody)
+ }
+
+ def TraitTemplate: R0 = rule {
+ optional(EarlyDefs) ~ TraitParents ~ optional(TemplateBody)
+ }
+ def ClassParents: R0 = rule {
+ Constr ~ zeroOrMore(WL ~ K.W("with") ~ AnnotType)
+ }
+ def TraitParents: R0 = rule {
+ AnnotType ~ zeroOrMore(WL ~ K.W("with") ~ AnnotType)
+ }
+ def Constr: R0 = rule {
+ AnnotType ~ zeroOrMore(WL ~ ArgumentExprs)
+ }
+ def EarlyDefs: R0 = rule {
+ '{' ~ optional(oneOrMore(EarlyDef).separatedBy(Semis)) ~ '}' ~ K.W("with")
+ }
+ def EarlyDef: R0 = rule {
+ zeroOrMore(Annotation ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ PatVarDef
+ }
+ def ConstrExpr: R0 = rule { ConstrBlock | SelfInvocation }
+ def ConstrBlock: R0 = rule { '{' ~ SelfInvocation ~ zeroOrMore(Semis ~ BlockStat) ~ '}' }
+ def SelfInvocation: R0 = rule { K.W("this") ~ oneOrMore(ArgumentExprs) }
+
+ def TopStatSeq: R0 = rule { oneOrMore(TopStat).separatedBy(Semis) }
+ def TopStat: R0 = rule {
+ Packaging |
+ PackageObject |
+ Import |
+ zeroOrMore(Annotation ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ TmplDef
+ }
+ def Packaging: R0 = rule { K.W("package") ~ QualId ~ '{' ~ TopStatSeq ~ '}' }
+ def PackageObject: R0 = rule { K.W("package") ~ K.W("object") ~ ObjectDef }
+ def TopPackageSeq: R0 = rule{
+ oneOrMore(K.W("package") ~ QualId).separatedBy(Semis)
+ }
+ def CompilationUnit: Rule1[String] = rule {
+ capture(
+ pr("CompulationUnit 0") ~
+ optional(Semis) ~
+ pr("CompulationUnit 1") ~
+ (TopPackageSeq ~ optional(Semis ~ TopStatSeq) | TopStatSeq) ~
+ optional(Semis) ~
+ WL
+
+ )
+ }
+}
diff --git a/scalatexApi/src/main/scala/scalaparser/syntax/Basic.scala b/scalatex/scalaParser/src/main/scala/scalaParser/syntax/Basic.scala
index 157b0bb..8d3232a 100644
--- a/scalatexApi/src/main/scala/scalaparser/syntax/Basic.scala
+++ b/scalatex/scalaParser/src/main/scala/scalaParser/syntax/Basic.scala
@@ -1,4 +1,4 @@
-package scalaparser
+package scalaParser
package syntax
import acyclic.file
import org.parboiled2._
diff --git a/scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala b/scalatex/scalaParser/src/main/scala/scalaParser/syntax/Identifiers.scala
index d10c7ea..4bc972f 100644
--- a/scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala
+++ b/scalatex/scalaParser/src/main/scala/scalaParser/syntax/Identifiers.scala
@@ -1,4 +1,4 @@
-package scalaparser
+package scalaParser
package syntax
import acyclic.file
import org.parboiled2._
diff --git a/scalatexApi/src/main/scala/scalaparser/syntax/Literals.scala b/scalatex/scalaParser/src/main/scala/scalaParser/syntax/Literals.scala
index 3222254..9fd9d5b 100644
--- a/scalatexApi/src/main/scala/scalaparser/syntax/Literals.scala
+++ b/scalatex/scalaParser/src/main/scala/scalaParser/syntax/Literals.scala
@@ -1,4 +1,4 @@
-package scalaparser
+package scalaParser
package syntax
import acyclic.file
import org.parboiled2._
@@ -7,17 +7,18 @@ trait Literals { self: Parser with Basic with Identifiers =>
object Literals{
import Basic._
def FloatingPointLiteral = rule {
- capture(
+
+ "." ~ oneOrMore(Digit) ~ optional(ExponentPart) ~ optional(FloatType) |
+ oneOrMore(Digit) ~ (
"." ~ oneOrMore(Digit) ~ optional(ExponentPart) ~ optional(FloatType) |
- oneOrMore(Digit) ~ (
- "." ~ oneOrMore(Digit) ~ optional(ExponentPart) ~ optional(FloatType) |
- ExponentPart ~ optional(FloatType) |
- optional(ExponentPart) ~ FloatType))
+ ExponentPart ~ optional(FloatType) |
+ optional(ExponentPart) ~ FloatType
+ )
}
- def IntegerLiteral = rule { capture((DecimalNumeral | HexNumeral) ~ optional(anyOf("Ll"))) }
+ def IntegerLiteral = rule { (DecimalNumeral | HexNumeral) ~ optional(anyOf("Ll")) }
- def BooleanLiteral = rule { capture(Key.W("true") | Key.W("false")) }
+ def BooleanLiteral = rule { Key.W("true") | Key.W("false") }
def MultilineComment: Rule0 = rule { "/*" ~ zeroOrMore(MultilineComment | !"*/" ~ ANY) ~ "*/" }
def Comment: Rule0 = rule {
@@ -26,26 +27,26 @@ trait Literals { self: Parser with Basic with Identifiers =>
}
def Literal = rule {
- (capture(optional("-")) ~ (FloatingPointLiteral | IntegerLiteral) ~> ((sign: String, number) => sign + number)) |
+ (optional("-") ~ (FloatingPointLiteral | IntegerLiteral)) |
BooleanLiteral |
CharacterLiteral |
StringLiteral |
SymbolLiteral |
- capture(Key.W("null") ~ !(Basic.Letter | Basic.Digit))
+ (Key.W("null") ~ !(Basic.Letter | Basic.Digit))
}
def EscapedChars = rule { '\\' ~ anyOf("rnt\\\"") }
// Note that symbols can take on the same values as keywords!
- def SymbolLiteral = rule { ''' ~ capture(Identifiers.PlainId | Identifiers.Keywords) }
+ def SymbolLiteral = rule { ''' ~ (Identifiers.PlainId | Identifiers.Keywords) }
- def CharacterLiteral = rule { ''' ~ capture(UnicodeExcape | EscapedChars | !'\\' ~ CharPredicate.from(isPrintableChar)) ~ ''' }
+ def CharacterLiteral = rule { ''' ~ (UnicodeExcape | EscapedChars | !'\\' ~ CharPredicate.from(isPrintableChar)) ~ ''' }
def MultiLineChars = rule { zeroOrMore(optional('"') ~ optional('"') ~ noneOf("\"")) }
def StringLiteral = rule {
- (optional(Identifiers.Id) ~ "\"\"\"" ~ capture(MultiLineChars) ~ capture("\"\"\"" ~ zeroOrMore('"')) ~> ((multilineChars: String, quotes) => multilineChars + quotes.dropRight(3))) |
- (optional(Identifiers.Id) ~ '"' ~ capture(zeroOrMore("\\\"" | noneOf("\n\""))) ~ '"')
+ (optional(Identifiers.Id) ~ "\"\"\"" ~ MultiLineChars ~ ("\"\"\"" ~ zeroOrMore('"'))) |
+ (optional(Identifiers.Id) ~ '"' ~ zeroOrMore("\\\"" | noneOf("\n\"")) ~ '"')
}
def isPrintableChar(c: Char): Boolean = {
diff --git a/scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala b/scalatex/scalaParser/src/test/scala/scalaParser/SyntaxTest.scala
index 9559154..fe5fc2c 100644
--- a/scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala
+++ b/scalatex/scalaParser/src/test/scala/scalaParser/SyntaxTest.scala
@@ -1,4 +1,4 @@
-package scalaparser
+package scalaParser
import org.parboiled2.ParseError
import utest._
@@ -9,11 +9,12 @@ import scala.util.{Failure, Success}
object SyntaxTest extends TestSuite{
def check[T](input: String) = {
+ println("Checking...")
new ScalaSyntax(input).CompilationUnit.run() match{
case Failure(f: ParseError) =>
println(f.position)
println(f.formatExpectedAsString)
-// println(f.formatTraces)
+ println(f.formatTraces)
throw new Exception(f.position + "\t" + f.formatTraces)
case Success(parsed) =>
assert(parsed == input)
@@ -27,14 +28,16 @@ object SyntaxTest extends TestSuite{
)
* - check(
- """
- |package torimatomeru
+ """package torimatomeru
|
- |import org.parboiled2.ParseError
- |import utest._
- |import utest.framework.Test
+ |package lols
+ """.stripMargin
+ )
+ * - check(
+ """package torimatomeru
+ |import a
+ |import b
""".stripMargin
-
)
* - check(
"""
@@ -70,16 +73,9 @@ object SyntaxTest extends TestSuite{
* - check(
"""
|object SyntaxTest extends TestSuite{
- | def check[T](input: String) = {
- | new ScalaSyntax(input).CompilationUnit.run() match{
- | case Failure(f: ParseError) =>
- | println(f.position)
- | println(f.formatExpectedAsString)
- | println(f.formatTraces)
- | throw new Exception(f.position + "\t" + f.formatTraces)
- | case Success(parsed) =>
- | assert(parsed == input)
- | }
+ | {
+ | println
+ | throw 1
| }
|}
""".stripMargin
@@ -220,6 +216,15 @@ object SyntaxTest extends TestSuite{
""".stripMargin
)
* - check(
+ """
+ |object L{
+ | a b c
+ | d = 1
+ |}
+ """.stripMargin
+ )
+
+ * - check(
"""/* __ *\
|** ________ ___ / / ___ __ ____ Scala.js CLI **
|** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
@@ -360,17 +365,59 @@ object SyntaxTest extends TestSuite{
|}
""".stripMargin
)
+ * - check(
+ """
+ |trait Basic {
+ | b match {
+ | case C => true; case _ => false
+ | }
+ |}
+ """.stripMargin
+ )
+ * - check(
+ """trait Basic {
+ | !a.b
+ |}
+ """.stripMargin
+ )
+ * - check(
+ """
+ |class Parser {
+ | {() => }
+ |}
+ |
+ """.stripMargin
+ )
+ * - check(
+ """
+ |
+ |
+ |
+ |package omg
+ |;
+ |
+ |;
+ |
+ |;
+ |class Parser
+ |;
+ |
+ |;
+ |
+ |;
+ """.stripMargin
+ )
}
def checkFile(path: String) = check(io.Source.fromFile(path).mkString)
'file{
+ * - checkFile("test.txt")
+ * - checkFile("scalaParser/src/main/scala/scalaParser/syntax/Basic.scala")
+ * - checkFile("scalaParser/src/main/scala/scalaParser/syntax/Identifiers.scala")
+ * - checkFile("scalaParser/src/main/scala/scalaParser/syntax/Literals.scala")
+ * - checkFile("scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala")
- * - checkFile("scalatexApi/src/main/scala/scalaparser/syntax/Basic.scala")
- * - checkFile("scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala")
- * - checkFile("scalatexApi/src/main/scala/scalaparser/syntax/Literals.scala")
- * - checkFile("scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala")
-
- * - checkFile("scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala")
+ * - checkFile("scalaParser/src/test/scala/scalaParser/SyntaxTest.scala")
* - checkFile("scalatexApi/src/main/scala/scalatex/stages/Compiler.scala")
@@ -386,16 +433,16 @@ object SyntaxTest extends TestSuite{
* - checkFile("scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala")
}
- 'omg{
- val root = new java.io.File("../scala-js/")
- def listFiles(s: java.io.File): Iterator[String] = {
- val (dirs, files) = s.listFiles().toIterator.partition(_.isDirectory)
- files.map(_.getPath) ++ dirs.flatMap(listFiles)
- }
- for(f <- listFiles(root).filter(_.endsWith(".scala"))){
- println("CHECKING " + f)
- checkFile(f)
- }
- }
+// 'omg{
+// val root = new java.io.File("../scala-js/")
+// def listFiles(s: java.io.File): Iterator[String] = {
+// val (dirs, files) = s.listFiles().toIterator.partition(_.isDirectory)
+// files.map(_.getPath) ++ dirs.flatMap(listFiles)
+// }
+// for(f <- listFiles(root).filter(_.endsWith(".scala"))){
+// println("CHECKING " + f)
+// checkFile(f)
+// }
+// }
}
-}
+} \ No newline at end of file
diff --git a/scalatex/scalatexSbtPlugin/src/main/scala/SbtPlugin.scala b/scalatex/scalatexSbtPlugin/src/main/scala/SbtPlugin.scala
new file mode 100644
index 0000000..f09dc17
--- /dev/null
+++ b/scalatex/scalatexSbtPlugin/src/main/scala/SbtPlugin.scala
@@ -0,0 +1,16 @@
+package scalatex
+
+import sbt.Keys._
+import sbt._
+object SbtPlugin extends sbt.Plugin{
+ val scalatexDirectory = taskKey[sbt.File]("Clone stuff from github")
+ override val settings = Seq(
+ scalatexDirectory := sourceDirectory.value / "scalatex",
+ scalacOptions += {
+ "-P:scalatex:root:" + scalatexDirectory.value.getCanonicalPath
+ },
+ watchSources += scalatexDirectory.value,
+ addCompilerPlugin("com.lihaoyi" %% "scalatex-compiler-plugin" % "0.1.0"),
+ libraryDependencies += "com.lihaoyi" %% "scalatex-api" % "0.1.0"
+ )
+}
diff --git a/scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala b/scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala
deleted file mode 100644
index 83e0b90..0000000
--- a/scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala
+++ /dev/null
@@ -1,412 +0,0 @@
-package scalaparser
-import acyclic.file
-import language.implicitConversions
-import syntax._
-import org.parboiled2._
-
-/**
- * Parser for Scala syntax.
- *
- * The `G` parameter that gets passed in to each rule stands for
- * "Greedy", and determines whether or not that rule is to consume
- * newlines after the last terminal in that rule. We need to pass it
- * everywhere so it can go all the way to the last terminal deep
- * inside the parse tree, which can then decide whether or not to
- * consume whitespace.
- *
- * The vast majority of terminals will consume newlines; only rules
- * which occur in {} blocks won't have their terminals consume newlines,
- * and only the *last* terminal in the rule will be affected.
- * That's why the parser does terminals-consume-newlines-by-default,
- * and leaves it up to the dev to thread the `G` variable where-ever
- * we want the opposite behavior.
- */
-class ScalaSyntax(val input: ParserInput) extends Parser with Basic with Identifiers with Literals {
- // Aliases for common things. These things are used in almost every parser
- // in the file, so it makes sense to keep them short.
- type B = Boolean
- val t = true
- type R0 = Rule0
- /**
- * Parses all whitespace, excluding newlines. This is only
- * really useful in e.g. {} blocks, where we want to avoid
- * capturing newlines so semicolon-inference would work
- */
- def WS = rule { zeroOrMore(Basic.WhitespaceChar | Literals.Comment) }
-
- /**
- * Parses whitespace, including newlines.
- * This is the default for most things
- */
- def WL = rule{ zeroOrMore(Basic.WhitespaceChar | Literals.Comment | Basic.Newline) }
-
-
- /**
- * Whitespace which captures or doesn't-capture
- * newlines depending on the G that gets passed in
- */
- def W(G: B = t) =
- if (G) WL
- else WS
-
- /**
- * By default, all strings and characters greedily
- * capture all whitespace immediately after the token.
- */
- implicit private[this] def wspStr(s: String): R0 = rule { str(s) ~ WL }
- implicit private[this] def wspChar(s: Char): R0 = rule { ch(s) ~ WL }
-
- /**
- * Most keywords don't just require the correct characters to match,
- * they have to ensure that subsequent characters *don't* match in
- * order for it to be a keyword. This enforces that rule for key-words
- * (W) and key-operators (O) which have different non-match criteria.
- */
- object K {
- def W(s: String) = rule {
- Key.W(s) ~ WL
- }
-
- def O(s: String) = rule {
- Key.O(s) ~ WL
- }
- }
-
- /**
- * Occasionally, you want to decide whether or not to
- * capture newlines based on the context, so use this
- * and pass in G manually.
- */
- def StrW(s: String, G: B): R0 = rule { str(s) ~ W(G) }
-
- def pos = cursor -> cursorChar
-
- /**
- * helper printing function
- */
- def pr(s: String) = rule { run(println(s"LOGGING $cursor: $s")) }
-
- def Id(G: B = t) = rule { Identifiers.Id ~ W(G) }
- def VarId(G: B = t) = rule { Identifiers.VarId ~ W(G) }
- def Literal(G: B = t) = rule { Literals.Literal ~ W(G) }
- def Semi = rule { Basic.Semi ~ WL }
- def Newline = rule { Basic.Newline ~ WL }
-
- def QualId(G: B = t) = rule { oneOrMore(Id(false)) separatedBy '.' ~ W(G) }
- def Ids = rule { oneOrMore(Id()) separatedBy ',' }
-
- def Path(G: B = t): R0 = rule {
- zeroOrMore(Id(G) ~ '.') ~ K.W("this") ~ zeroOrMore(Id(G)).separatedBy('.') |
- StableId(G)
- }
- def StableId(G: B = t): R0 = rule {
- zeroOrMore(Id() ~ '.') ~ (K.W("this") | K.W("super") ~ optional(ClassQualifier)) ~ '.' ~ oneOrMore(Id(G)).separatedBy('.') |
- Id(false) ~ zeroOrMore(WL ~ '.' ~ WL ~ Id(false)) ~ W(G)
- }
-
- def ClassQualifier = rule { '[' ~ Id() ~ ']' }
-
- def Type(G: B = t): R0 = rule {
- FunctionArgTypes ~ K.O("=>") ~ Type(G) | InfixType(false) ~ optional(WL ~ ExistentialClause) ~ W(G)
- }
- def FunctionArgTypes = rule {
- InfixType() | '(' ~ optional(oneOrMore(ParamType) separatedBy ',') ~ ')'
- }
-
- def ExistentialClause = rule { "forSome" ~ '{' ~ oneOrMore(ExistentialDcl(false)).separatedBy(Semi) }
- def ExistentialDcl(G: B = t) = rule { K.W("type") ~ TypeDcl(G) | K.W("val") ~ ValDcl(G) }
-
- def InfixType(G: B = t) = rule {
- CompoundType(false) ~ zeroOrMore(WL ~ Id() ~ optional(Newline) ~ CompoundType(false)) ~ W(G)
- }
- def CompoundType(G: B = t) = rule {
- oneOrMore(AnnotType(false)).separatedBy(WL ~ K.W("with")) ~ optional(Refinement(false)) ~ W(G)
- }
- def AnnotType(G: B = t) = rule {
- SimpleType(false) ~ zeroOrMore(WL ~ Annotation(false)) ~ W(G)
- }
- def SimpleType(G: B = t): R0 = rule {
- BasicType(false) ~
- optional(WL ~ '#' ~ Id(false)) ~
- optional(WL ~ TypeArgs(false)) ~
- W(G)
- }
- def BasicType(G: B = t): R0 = rule {
- '(' ~ Types ~ ')' |
- Path() ~ '.' ~ K.W("type") |
- StableId(G)
- }
- def TypeArgs(G: B = t) = rule { '[' ~ Types ~ StrW("]", G) }
- def Types = rule { oneOrMore(Type()).separatedBy(',') }
- def Refinement(G: B = t) = rule {
- optional(Newline) ~ '{' ~ oneOrMore(RefineStat).separatedBy(Semi) ~ StrW("}", G)
- }
- def RefineStat = rule { "type" ~ TypeDef(false) | Dcl(false) | MATCH }
- def TypePat = rule { CompoundType() }
- def Ascription(G: B = t) = rule {
- ":" ~ ("_" ~ StrW("*", G) | InfixType(G) | oneOrMore(Annotation(G)))
- }
-
- def ParamType = rule { K.O("=>") ~ Type() | Type() ~ "*" | Type() }
-
- def Expr(G: B = t): R0 = rule { (Bindings | optional(K.W("implicit")) ~ Id() | "_") ~ K.O("=>") ~ Expr(G) | Expr1(G) }
- def Expr1(G: B = t): R0 = rule {
- IfCFlow(G) |
- WhileCFlow(G) |
- TryCFlow(G) |
- DoWhileCFlow(G) |
- ForCFlow(G) |
- K.W("throw") ~ Expr(G) |
- K.W("return") ~ optional(Expr(G)) |
- SimpleExpr() ~ K.O("=") ~ Expr(G) |
- PostfixExpr(false) ~ optional("match" ~ '{' ~ CaseClauses ~ StrW("}", false) | Ascription(false)) ~ W(G)
- }
- def IfCFlow(G: B = t) = rule { "if" ~ '(' ~ Expr() ~ ')' ~ zeroOrMore(Newline) ~ Expr(G) ~ optional(optional(Semi) ~ K.W("else") ~ Expr(G)) }
- def WhileCFlow(G: B = t) = rule { "while" ~ '(' ~ Expr() ~ ')' ~ zeroOrMore(Newline) ~ Expr(G) }
- def TryCFlow(G: B = t) = rule {
- K.W("try") ~ Expr(false) ~
- optional(WL ~ K.W("catch") ~ Expr(false)) ~
- optional(WL ~ K.W("finally") ~ Expr(false)) ~
- W(G)
- }
-
- def DoWhileCFlow(G: B = t) = rule { K.W("do") ~ Expr() ~ optional(Semi) ~ "while" ~ '(' ~ Expr() ~ StrW(")", G) }
- def ForCFlow(G: B = t) = rule {
- "for" ~
- ('(' ~ Enumerators ~ ')' | '{' ~ Enumerators ~ '}') ~
- zeroOrMore(Newline) ~
- optional(K.W("yield")) ~
- Expr(G) }
- def PostfixExpr(G: B = t): R0 = rule { InfixExpr(G) ~ optional(Id() ~ optional(Newline)) }
- def InfixExpr(G: B = t): R0 = rule { PrefixExpr(G) ~ zeroOrMore(Id() ~ optional(Newline) ~ PrefixExpr(G)) }
- def PrefixExpr(G: B = t) = rule { optional(anyOf("-+~!")) ~ SimpleExpr(G) }
-
- def SimpleExpr(G: B = t): R0 = rule {
- SimpleExpr1(false) ~
- zeroOrMore(WL ~ ('.' ~ Id(false) | TypeArgs(false) | ArgumentExprs(false))) ~
- optional(WL ~ StrW("_", false)) ~
- W(G)
- }
-
- def SimpleExpr1(G: B = t) = rule{
- K.W("new") ~ (ClassTemplate(G) | TemplateBody(G)) |
- BlockExpr(G) |
- Literal(G) ~ drop[String] |
- Path(G) |
- K.W("_") |
- '(' ~ optional(Exprs) ~ StrW(")", G)
- }
-
-
-
- def Exprs: R0 = rule { oneOrMore(Expr()).separatedBy(',') }
- def ArgumentExprs(G: B = t): R0 = rule {
- '(' ~ optional(Exprs ~ optional(K.O(":") ~ K.W("_") ~ '*')) ~ StrW(")", G) |
- optional(Newline) ~ BlockExpr(G)
- }
-
- def BlockExpr(G: B = t): R0 = rule { '{' ~ (CaseClauses | Block) ~ StrW("}", G) }
- def Block: R0 = rule {
- zeroOrMore(BlockStat ~ Semi) ~ optional(ResultExpr())
- }
-
- def BlockStat: R0 = rule {
- Semi |
- Import(false) |
- zeroOrMore(Annotation(false)) ~ (optional(K.W("implicit") | K.W("lazy")) ~ Def(false) | zeroOrMore(LocalModifier) ~ TmplDef(false)) |
- Expr1(false)
- }
- def ResultExpr(G: B = t): R0 = rule { (Bindings | optional(K.W("implicit")) ~ Id() | "_") ~ K.W("=>") ~ Block | Expr1(t) }
- def Enumerators: R0 = rule { Generator(false) ~ zeroOrMore(Semi ~ Enumerator(false)) ~ WL }
- def Enumerator(G: B = t): R0 = rule { Generator(G) | Guard(G) | Pattern1 ~ K.O("=") ~ Expr(G) }
- def Generator(G: B = t): R0 = rule { Pattern1 ~ K.O("<-") ~ Expr(false) ~ optional(WL ~ Guard(false)) ~ W(G) }
- def CaseClauses: R0 = rule { oneOrMore(CaseClause) }
- def CaseClause: R0 = rule { K.W("case") ~ Pattern ~ optional(Guard(true)) ~ K.O("=>") ~ Block }
- def Guard(G: B = t): R0 = rule { K.W("if") ~ PostfixExpr(G) }
- def Pattern: R0 = rule {
- oneOrMore(Pattern1).separatedBy('|')
- }
- def Pattern1: R0 = rule {
- K.W("_") ~ K.O(":") ~ TypePat | VarId() ~ K.O(":") ~ TypePat | Pattern2
- }
- def Pattern2: R0 = rule {
- VarId() ~ "@" ~ Pattern3 | Pattern3 | VarId()
- }
- def Pattern3: R0 = rule {
- SimplePattern ~ zeroOrMore(Id() ~ SimplePattern)
- }
- def SimplePattern: R0 = rule {
- K.W("_") |
- Literal() ~ drop[String] |
- '(' ~ optional(Patterns) ~ ')' |
- (
- StableId() ~
- optional(
- '(' ~
- (optional(Patterns ~ ',') ~ optional(VarId() ~ '@') ~ K.W("_") ~ '*' | optional(Patterns)) ~
- ')'
- )
- ) |
- VarId()
- }
- def Patterns: R0 = rule { K.W("_") ~ '*' | oneOrMore(Pattern).separatedBy(',') }
-
- def TypeParamClause: R0 = rule { '[' ~ oneOrMore(VariantTypeParam).separatedBy(',') ~ ']' }
- def FunTypeParamClause: R0 = rule { '[' ~ oneOrMore(TypeParam).separatedBy(',') ~ ']' }
- def VariantTypeParam: R0 = rule { zeroOrMore(Annotation()) ~ optional(anyOf("+-")) ~ TypeParam }
- def TypeParam: R0 = rule {
- (Id() | K.W("_")) ~
- optional(TypeParamClause) ~
- optional(K.O(">:") ~ Type()) ~
- optional(K.O("<:") ~ Type()) ~
- zeroOrMore(K.O("<%") ~ Type()) ~
- zeroOrMore(K.O(":") ~ Type())
- }
- def ParamClauses: R0 = rule { zeroOrMore(ParamClause) ~ optional(optional(Newline) ~ '(' ~ K.W("implicit") ~ Params ~ ')') }
- def ParamClause: R0 = rule { optional(Newline) ~ '(' ~ optional(Params) ~ ')' }
- def Params: R0 = rule { zeroOrMore(Param).separatedBy(',') }
- def Param: R0 = rule { zeroOrMore(Annotation()) ~ Id() ~ optional(K.O(":") ~ ParamType) ~ optional(K.O("=") ~ Expr()) }
- def ClassParamClauses(G: B = t): R0 = rule { zeroOrMore(ClassParamClause(G)) ~ optional(optional(Newline) ~ '(' ~ K.W("implicit") ~ ClassParam ~ StrW(")", G)) }
- def ClassParamClause(G: B = t): R0 = rule { optional(Newline) ~ '(' ~ optional(ClassParams) ~ StrW(")", G) }
- def ClassParams: R0 = rule { oneOrMore(ClassParam).separatedBy(',') }
- def ClassParam: R0 = rule { zeroOrMore(Annotation()) ~ optional(zeroOrMore(Modifier) ~ (K.W("val") | K.W("var"))) ~ Id() ~ K.O(":") ~ ParamType ~ optional(K.O("=") ~ Expr()) }
-
- def Bindings: R0 = rule { '(' ~ oneOrMore(Binding).separatedBy(',') ~ ')' }
- def Binding: R0 = rule { (Id() | K.W("_")) ~ optional(K.O(":") ~ Type()) }
-
- def Modifier: R0 = rule { LocalModifier | AccessModifier | K.W("override") }
- def LocalModifier: R0 = rule { K.W("abstract") | K.W("final") | K.W("sealed") | K.W("implicit") | K.W("lazy") }
- def AccessModifier: R0 = rule { (K.W("private") | K.W("protected")) ~ optional(AccessQualifier) }
- def AccessQualifier: R0 = rule { '[' ~ (K.W("this") | Id()) ~ ']' }
-
- def Annotation(G: B = t): R0 = rule { '@' ~ SimpleType(false) ~ zeroOrMore(WL ~ ArgumentExprs(false)) ~ W(G) }
- def ConstrAnnotation: R0 = rule { '@' ~ SimpleType() ~ ArgumentExprs() }
-
- def TemplateBody(G: B = t): R0 = rule {
- WL ~
- '{' ~
- optional(SelfType) ~
- TemplateStat ~
- zeroOrMore(Semi ~ TemplateStat) ~
- WL ~
- StrW("}", G)
- }
- def TemplateStat: R0 = rule {
- Import(false) |
- zeroOrMore(Annotation() ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ (Def(false) | Dcl(false)) |
- Expr(false) |
- MATCH
- }
-
- def SelfType: R0 = rule { K.W("this") ~ K.O(":") ~ Type() ~ K.O("=>") | Id() ~ optional(K.O(":") ~ Type()) ~ K.O("=>") }
-
- def Import(G: B = t): R0 = rule { K.W("import") ~ oneOrMore(ImportExpr(G)).separatedBy(',') }
-
- def ImportExpr(G: B = t): R0 = rule { StableId(G) ~ optional('.' ~ (StrW("_", G) | ImportSelectors(G))) }
- def ImportSelectors(G: B = t): R0 = rule { '{' ~ zeroOrMore(ImportSelector ~ ',') ~ (ImportSelector | K.W("_")) ~ StrW("}", G) }
- def ImportSelector: R0 = rule { Id() ~ optional(K.O("=>") ~ (Id() | K.W("_"))) }
-
- def Dcl(G: B = t): R0 = rule {
- K.W("val") ~ ValDcl(G) |
- K.W("var") ~ VarDcl(G) |
- K.W("def") ~ FunDcl(G) |
- K.W("type") ~ zeroOrMore(Newline) ~ TypeDcl(G)
- }
- def ValDcl(G: B = t): R0 = rule { Ids ~ K.O(":") ~ Type(G) }
- def VarDcl(G: B = t): R0 = rule { Ids ~ K.O(":") ~ Type(G) }
- def FunDcl(G: B = t): R0 = rule { FunSig(false) ~ optional(WL ~ K.O(":") ~ Type(G)) }
- def FunSig(G: B = t): R0 = rule { Id() ~ optional(FunTypeParamClause) ~ ParamClauses }
- def TypeDcl(G: B = t): R0 = rule {
- Id(false) ~
- optional(WL ~ TypeParamClause) ~
- optional(WL ~ K.O(">:") ~ Type(false)) ~
- optional(WL ~ K.O("<:") ~ Type(false)) ~
- W(G)
- }
-
- def PatVarDef(G: B = t): R0 = rule { K.W("val") ~ PatDef(G) | K.W("var") ~ VarDef(G) }
- def Def(G: B = t): R0 = rule { K.W("def") ~ FunDef(G) | K.W("type") ~ zeroOrMore(Newline) ~ TypeDef(G) | PatVarDef(G) | TmplDef(G) }
- def PatDef(G: B = t): R0 = rule { oneOrMore(Pattern2).separatedBy(',') ~ optional(K.O(":") ~ Type()) ~ K.O("=") ~ Expr(G) }
- def VarDef(G: B = t): R0 = rule { Ids ~ K.O(":") ~ Type() ~ K.O("=") ~ K.W("_") | PatDef(G) }
- def FunDef(G: B = t): R0 = rule {
- K.W("this") ~ ParamClause ~ ParamClauses ~ (K.O("=") ~ ConstrExpr | optional(Newline) ~ ConstrBlock) |
- FunSig() ~
- (
- optional(K.O(":") ~ Type()) ~ K.O("=") ~ optional(K.W("macro")) ~ Expr(G) |
- optional(Newline) ~ '{' ~ Block ~ StrW("}", G)
- )
- }
- def TypeDef(G: B = t): R0 = rule { Id() ~ optional(TypeParamClause) ~ K.O("=") ~ Type(G) }
-
- def TmplDef(G: B = t): R0 = rule {
- K.W("trait") ~ TraitDef(G) |
- optional(K.W("case")) ~ (K.W("class") ~ ClassDef(G) |
- K.W("object") ~ ObjectDef(G))
- }
- def ClassDef(G: B = t): R0 = rule {
- Id() ~
- optional(TypeParamClause) ~
- zeroOrMore(ConstrAnnotation) ~
- optional(AccessModifier) ~
- ClassParamClauses(false) ~
- ClassTemplateOpt(false) ~
- W(G)
-
- }
- def TraitDef(G: B = t): R0 = rule { Id() ~ optional(TypeParamClause) ~ TraitTemplateOpt(G) }
- def ObjectDef(G: B = t): R0 = rule { Id() ~ ClassTemplateOpt(G) }
- def ClassTemplateOpt(G: B = t): R0 = rule {
- WL ~ K.W("extends") ~ ClassTemplate(G) |
- optional(WL ~ optional(K.W("extends")) ~ TemplateBody(G))
- }
- def TraitTemplateOpt(G: B = t): R0 = rule { K.W("extends") ~ TraitTemplate(G) | optional(optional(K.W("extends")) ~ TemplateBody(G)) }
- def ClassTemplate(G: B = t): R0 = rule {
- optional(EarlyDefs) ~
- ClassParents(false) ~
- optional(WL ~ TemplateBody(false)) ~
- W(G)
- }
-
- def TraitTemplate(G: B = t): R0 = rule {
- optional(EarlyDefs) ~ TraitParents(false) ~ optional(TemplateBody(false)) ~ W(G)
- }
- def ClassParents(G: B = t): R0 = rule {
- Constr(false) ~ zeroOrMore(WL ~ K.W("with") ~ AnnotType(G)) ~ W(G)
- }
- def TraitParents(G: B = t): R0 = rule {
- AnnotType(false) ~ zeroOrMore(WL ~ K.W("with") ~ AnnotType(false)) ~ W(G)
- }
- def Constr(G: B = t): R0 = rule {
- AnnotType(false) ~ zeroOrMore(WL ~ ArgumentExprs(false)) ~
- W(G)
- }
- def EarlyDefs: R0 = rule {
- '{' ~ optional(oneOrMore(EarlyDef).separatedBy(Semi)) ~ '}' ~ K.W("with")
- }
- def EarlyDef: R0 = rule {
- zeroOrMore(Annotation() ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ PatVarDef(false)
- }
- def ConstrExpr: R0 = rule { ConstrBlock | SelfInvocation }
- def ConstrBlock: R0 = rule { '{' ~ SelfInvocation ~ zeroOrMore(Semi ~ BlockStat) ~ '}' }
- def SelfInvocation: R0 = rule { K.W("this") ~ oneOrMore(ArgumentExprs()) }
-
- def TopStatSeq: R0 = rule { zeroOrMore(TopStat).separatedBy(Semi) }
- def TopStat: R0 = rule {
- Packaging |
- PackageObject(false) |
- Import(false) |
- zeroOrMore(Annotation(false) ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ TmplDef(false) |
- MATCH
- }
- def Packaging: R0 = rule { K.W("package") ~ QualId() ~ '{' ~ TopStatSeq ~ '}' }
- def PackageObject(G: B = t): R0 = rule { K.W("package") ~ K.W("object") ~ ObjectDef(G) }
- def CompilationUnit: Rule1[String] = rule {
- capture(
- WL ~
- zeroOrMore(Semi) ~
- zeroOrMore(K.W("package") ~ QualId(false)).separatedBy(Semi) ~
- TopStatSeq ~
- EOI
- )
- }
-}
diff --git a/scalatexApi/src/test/scala/scalatex/AdvancedTests.scala b/scalatexApi/src/test/scala/scalatex/AdvancedTests.scala
deleted file mode 100644
index cbca40a..0000000
--- a/scalatexApi/src/test/scala/scalatex/AdvancedTests.scala
+++ /dev/null
@@ -1,120 +0,0 @@
-//package scalatex
-//
-//import utest._
-//import scalatex.stages._
-//import scalatags.Text.all._
-//
-//
-///**
-//* Created by haoyi on 7/14/14.
-//*/
-//object AdvancedTests extends TestSuite{
-// import TestUtil._
-//
-// val tests = TestSuite{
-// 'localDef{
-// check(
-// tw("""
-// @lol(n: Int) = @{
-// "omg" * n
-// }
-//
-// @lol(2)
-// """),
-// "omgomg"
-// )
-// }
-// 'innerTemplate{
-// check(
-// tw("""
-// @lol(f: Int) =
-// omg @f
-//
-// @lol(1)
-// @lol(2: Int)
-// @lol(3 + 1)
-// """),
-// tw("""
-// @lol(f: Int) ={
-// omg @f
-// }
-// @lol(1)
-// @lol(2: Int)
-// @lol(3 + 1)
-// """),
-// tw("""
-// @lol(f: Int) = {
-// omg @f
-// }
-// @lol(1)
-// @lol(2: Int)
-// @lol(3 + 1)
-// """),
-// """
-// omg1omg2omg4
-// """
-// )
-// }
-// 'innerInnerTemplate{
-// check(
-// tw("""
-// @lol(f: Int) =
-// @wtf(g: Int) =
-// wtf @g
-//
-// @wtf(1 + 2 + 3)
-// @wtf(f)
-//
-// @lol(1)
-// @lol(2: Int)
-// @lol(3 + 1)
-// """),
-// tw("""
-// @lol(f: Int) = {
-// @wtf(g: Int) = {
-// wtf @g
-// }
-// @wtf(1 + 2 + 3)
-// @wtf(f)
-// }
-// @lol(1)
-// @lol(2: Int)
-// @lol(3 + 1)
-// """),
-// tw("""
-// @lol(f: Int) = {
-// @wtf(g: Int) =
-// wtf @g
-//
-// @wtf(1 + 2 + 3)
-// @wtf(f)
-// }
-// @lol(1)
-// @lol(2: Int)
-// @lol(3 + 1)
-// """),
-// tw("""
-// @lol(f: Int) =
-// @wtf(g: Int) = {
-// wtf @g
-// }
-// @wtf(1 + 2 + 3)
-// @wtf(f)
-//
-// @lol(1)
-// @lol(2: Int)
-// @lol(3 + 1)
-// """),
-// """
-// wtf6
-// wtf1
-// wtf6
-// wtf2
-// wtf6
-// wtf4
-// """
-// )
-// }
-//
-// }
-//}
diff --git a/scalatexPlugin/src/main/resources/scalac-plugin.xml b/scalatexPlugin/src/main/resources/scalac-plugin.xml
deleted file mode 100755
index d4062fa..0000000
--- a/scalatexPlugin/src/main/resources/scalac-plugin.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<plugin>
- <name>demo-plugin</name>
- <classname>scalatex.ScalaTexPlugin</classname>
-</plugin> \ No newline at end of file