summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi@dropbox.com>2014-11-01 10:15:13 -0700
committerLi Haoyi <haoyi@dropbox.com>2014-11-01 10:15:13 -0700
commitc0b06c5e3f3caa60736794143f278c2af5cbe9fb (patch)
treef66c244943702c46daee94aaa1b98a07d6542830
parentaae594fd3c8397abca4cd4e55f538d41b172b4e3 (diff)
downloadhands-on-scala-js-c0b06c5e3f3caa60736794143f278c2af5cbe9fb.tar.gz
hands-on-scala-js-c0b06c5e3f3caa60736794143f278c2af5cbe9fb.tar.bz2
hands-on-scala-js-c0b06c5e3f3caa60736794143f278c2af5cbe9fb.zip
scalatex tests pass
-rw-r--r--book/src/main/scalatex/book/WebPage.scalatex8
-rw-r--r--scalatexApi/src/main/scala/scalatex/package.scala42
-rw-r--r--scalatexApi/src/main/scala/scalatex/stages/Compiler.scala1
-rw-r--r--scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala16
-rw-r--r--scalatexApi/src/test/scala/scalatex/BasicTests.scala47
-rw-r--r--scalatexApi/src/test/scala/scalatex/ErrorTests.scala132
6 files changed, 138 insertions, 108 deletions
diff --git a/book/src/main/scalatex/book/WebPage.scalatex b/book/src/main/scalatex/book/WebPage.scalatex
index aa45699..c27981e 100644
--- a/book/src/main/scalatex/book/WebPage.scalatex
+++ b/book/src/main/scalatex/book/WebPage.scalatex
@@ -1,3 +1,5 @@
+
+
@p
Most web applications aren't neat little games which live on a single canvas: they are large, structured HTML pages, which involve displaying data (whether from the user or from the web) in multiple ways, while allowing the user to make changes to the data that can be saved back to whatever remote web-service/database it came from.
@@ -17,6 +19,7 @@
@div(id:="div1")
@script("HelloWorld0().main(document.getElementById('div1'))")
+
@p
This approach works, as the above example shows, but has a couple of disadvantages:
@@ -125,8 +128,5 @@
@p
Now that you've gotten a quick overview of the kinds of things you can do with Scalatags, let's move on to the next section of our hands-on tutorial...
-@sect{Using Web Services}
-
- @p
- One half of the web application faces forwards towards the user, managing and rendering HTML or Canvas for the user to view and interact with. Another half faces backwards, talking to various web-services or databases which turn the application from a standalone-widget into part of a greater whole. We've already seen how to make the front half, let's now talk about working with the back half.
+@sect{Using Web Services}
diff --git a/scalatexApi/src/main/scala/scalatex/package.scala b/scalatexApi/src/main/scala/scalatex/package.scala
index 5732411..d86976e 100644
--- a/scalatexApi/src/main/scala/scalatex/package.scala
+++ b/scalatexApi/src/main/scala/scalatex/package.scala
@@ -15,11 +15,13 @@ package object scalatex {
def twf(filename: String): Frag = macro Internals.applyMacroFile
object Internals {
+ def twRuntimeErrors(expr: String): Frag = macro applyMacroRuntimeErrors
def twDebug(expr: String): Frag = macro applyMacroDebug
- def applyMacro(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, false)
+ def applyMacro(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, false, false)
+ def applyMacroDebug(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, false, true)
- def applyMacroDebug(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, true)
+ def applyMacroRuntimeErrors(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, true, false)
def applyMacroFile(c: Context)(filename: c.Expr[String]): c.Expr[Frag] = {
import c.universe._
@@ -34,34 +36,46 @@ package object scalatex {
txt.toCharArray
)
- compileThing(c)(txt, sourceFile, 0, false)
+ compileThing(c)(txt, sourceFile, 0, false, false)
}
case class DebugFailure(msg: String, pos: String) extends Exception(msg)
- private[this] def applyMacroFull(c: Context)(expr: c.Expr[String], runtimeErrors: Boolean): c.Expr[Frag] = {
+ private[this] def applyMacroFull(c: Context)
+ (expr: c.Expr[String],
+ runtimeErrors: Boolean,
+ debug: Boolean)
+ : c.Expr[Frag] = {
import c.universe._
val s = expr.tree
- .asInstanceOf[Literal]
- .value
- .value
- .asInstanceOf[String]
+ .asInstanceOf[Literal]
+ .value
+ .value
+ .asInstanceOf[String]
val stringStart =
expr.tree
.pos
.lineContent
.drop(expr.tree.pos.column)
.take(2)
+ val indented = s |> stages.IndentHandler
+ if (debug) println(indented)
compileThing(c)(
- s |> stages.IndentHandler,
+ indented,
expr.tree.pos.source,
expr.tree.pos.point + (if (stringStart == "\"\"") 1 else -1),
- runtimeErrors
+ runtimeErrors,
+ debug
)
}
}
- def compileThing(c: Context)(s: String, source: SourceFile, point: Int, runtimeErrors: Boolean) = {
+ def compileThing(c: Context)
+ (s: String,
+ source: SourceFile,
+ point: Int,
+ runtimeErrors: Boolean,
+ debug: Boolean) = {
import c.universe._
def compile(s: String): c.Tree = {
val realPos = new OffsetPosition(source, point).asInstanceOf[c.universe.Position]
@@ -71,13 +85,15 @@ package object scalatex {
import c.Position
try {
- c.Expr(c.typeCheck(compile(s)))
+ val compiled = compile(s)
+ if (debug) println(compiled)
+ c.Expr[Frag](c.typeCheck(compiled))
} catch {
case e@TypecheckException(pos: Position, msg) =>
if (!runtimeErrors) c.abort(pos, msg)
else {
val posMsg = pos.lineContent + "\n" + (" " * pos.column) + "^"
- c.Expr( q"""throw twist.Internals.DebugFailure($msg, $posMsg)""")
+ c.Expr( q"""throw scalatex.Internals.DebugFailure($msg, $posMsg)""")
}
}
diff --git a/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala b/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala
index 85bb0f0..fcbe08b 100644
--- a/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala
+++ b/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala
@@ -75,6 +75,7 @@ object Compiler{
q"if($cond){ Seq[$fragType](..$b1): $fragType } else { Seq[$fragType](..$b2): $fragType }"
case xx @ WN.ScalaExp(WN.Simple(first, _) +: rest, offset) =>
+
val firstTree = c.parse(first)
firstTree.foreach{x =>
diff --git a/scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala b/scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala
index 180d134..697ccc3 100644
--- a/scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala
+++ b/scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala
@@ -49,8 +49,15 @@ object IndentHandler extends (String => String){
// println(spaces, current)
val declRemainder = successRemainder(Parser.parse(current.trim, _.templateDeclaration()))
-
- val exprRemainder = successRemainder(Parser.parse(current.trim, _.expression())).filter(_ == current.trim)
+// println("::::::" + current.trim)
+// println(successRemainder(Parser.parse(current.trim, _.expression())))
+ val exprRemainder = successRemainder(Parser.parse(current.trim, _.expression())).filter {
+ // Either it takes up the entire line or the line ends with a =>, in which case
+ // we assume the guy wanted to pass a lambda
+ x =>
+// println(x)
+ x == current.trim || current.trim.endsWith("=>")
+ }
/**
@@ -81,14 +88,13 @@ object IndentHandler extends (String => String){
val newFirst =
if (!before.startsWith("@else")) before
else before.dropRight("@else".length)+ "} else"
-
+ println(before, after, delta)
if (delta > 0 && noBraceLine(after)) newFirst + "{" + after
else if (delta <= 0 && noBraceLine(after) && after.trim != "") newFirst + "{" + after + "}" * (1 - elseCorrection)
else current
-
-
}
+
val closing = "}" * (-delta - elseCorrection)
val newStack =
diff --git a/scalatexApi/src/test/scala/scalatex/BasicTests.scala b/scalatexApi/src/test/scala/scalatex/BasicTests.scala
index 0488ef6..f432e51 100644
--- a/scalatexApi/src/test/scala/scalatex/BasicTests.scala
+++ b/scalatexApi/src/test/scala/scalatex/BasicTests.scala
@@ -74,7 +74,6 @@ object BasicTests extends TestSuite{
'attributes{
check(
tw("""
- @div(id:="my-id") omg
@div(id:="my-id"){ omg }
@div(id:="my-id")
omg
@@ -82,7 +81,6 @@ object BasicTests extends TestSuite{
"""
<divid="my-id">omg</div>
<divid="my-id">omg</div>
- <divid="my-id">omg</div>
"""
)
}
@@ -134,11 +132,12 @@ object BasicTests extends TestSuite{
check(
tw("""
- @h1 Hello World
- @h2 hello
- @world
+ @h1
+ Hello World
+ @h2
+ hello @world
@h3
- Cow
+ Cow
"""),
"""
<h1>HelloWorld</h1>
@@ -157,9 +156,9 @@ object BasicTests extends TestSuite{
@div Cow
"""),
"""
- <h1><span></span><a></a>HelloWorld</h1>
- <h2><span></span><a></a>hello<b>world</b></h2>
- <h3><i></i><div>Cow</div></h3>
+ <h1></h1><span></span><a></a>HelloWorld
+ <h2></h2><span></span><a></a>hello<b></b>world
+ <h3></h3><i></i><div></div>Cow
"""
)
}
@@ -213,7 +212,7 @@ object BasicTests extends TestSuite{
<div>
Hello
<div>
- <h1>WORLD<b>!!!</b>lol</h1>
+ <h1></h1>WORLD<b>!!!</b>lol
<p><h2>Header2</h2></p>
</div>
</div>
@@ -227,13 +226,15 @@ object BasicTests extends TestSuite{
tw("""
@ul
@things.map { x =>
- @li @x
+ @li
+ @x
}
"""),
- tw("""
+ Internals.twDebug("""
@ul
- @things.map x =>
- @li @x
+ @things.map x =>
+ @li
+ @x
"""),
"""
@@ -319,7 +320,8 @@ object BasicTests extends TestSuite{
tw("""
@if(false)
Hello
- @else lols
+ @else
+ lols
@p
"""),
"lols<p></p>"
@@ -330,7 +332,8 @@ object BasicTests extends TestSuite{
@div
@if(true)
Hello
- @else lols
+ @else
+ lols
"""),
"<div>Hello</div>"
)
@@ -338,15 +341,19 @@ object BasicTests extends TestSuite{
* - check(
tw("""
@div
- @if(true) Hello
- @else lols
+ @if(true)
+ Hello
+ @else
+ lols
"""),
"<div>Hello</div>"
)
* - check(
tw("""
- @if(false) Hello
- @else lols
+ @if(false)
+ Hello
+ @else
+ lols
"""),
"lols"
)
diff --git a/scalatexApi/src/test/scala/scalatex/ErrorTests.scala b/scalatexApi/src/test/scala/scalatex/ErrorTests.scala
index a122dd2..f9e9308 100644
--- a/scalatexApi/src/test/scala/scalatex/ErrorTests.scala
+++ b/scalatexApi/src/test/scala/scalatex/ErrorTests.scala
@@ -3,7 +3,7 @@ package scalatex
import utest._
import scalatex.stages._
import scalatags.Text.all._
-import scalatex.Internals.{DebugFailure, twDebug}
+import scalatex.Internals.{DebugFailure, twRuntimeErrors}
/**
* Created by haoyi on 7/14/14.
@@ -31,109 +31,109 @@ object ErrorTests extends TestSuite{
'simple - check(
- twDebug("omg @notInScope lol"),
+ twRuntimeErrors("omg @notInScope lol"),
"""not found: value notInScope""",
"""
- twDebug("omg @notInScope lol"),
- ^
+ twRuntimeErrors("omg @notInScope lol"),
+ ^
"""
)
'chained{
'properties {
* - check(
- twDebug("omg @math.lol lol"),
+ twRuntimeErrors("omg @math.lol lol"),
"""object lol is not a member of package math""",
"""
- twDebug("omg @math.lol lol"),
- ^
+ twRuntimeErrors("omg @math.lol lol"),
+ ^
"""
)
* - check(
- twDebug("omg @math.E.lol lol"),
+ twRuntimeErrors("omg @math.E.lol lol"),
"""value lol is not a member of Double""",
"""
- twDebug("omg @math.E.lol lol"),
- ^
+ twRuntimeErrors("omg @math.E.lol lol"),
+ ^
"""
)
* - check(
- twDebug("omg @_root_.scala.math.lol lol"),
+ twRuntimeErrors("omg @_root_.scala.math.lol lol"),
"""object lol is not a member of package math""",
"""
- twDebug("omg @_root_.scala.math.lol lol"),
- ^
+ twRuntimeErrors("omg @_root_.scala.math.lol lol"),
+ ^
"""
)
* - check(
- twDebug("omg @_root_.scala.gg.lol lol"),
+ twRuntimeErrors("omg @_root_.scala.gg.lol lol"),
"""object gg is not a member of package scala""",
"""
- twDebug("omg @_root_.scala.gg.lol lol"),
- ^
+ twRuntimeErrors("omg @_root_.scala.gg.lol lol"),
+ ^
"""
)
* - check(
- twDebug("omg @_root_.ggnore.math.lol lol"),
+ twRuntimeErrors("omg @_root_.ggnore.math.lol lol"),
"""object ggnore is not a member of package <root>""",
"""
- twDebug("omg @_root_.ggnore.math.lol lol"),
- ^
+ twRuntimeErrors("omg @_root_.ggnore.math.lol lol"),
+ ^
"""
)
}
'calls{
* - check(
- twDebug("@scala.QQ.abs(-10).tdo(10).sum.z"),
+ twRuntimeErrors("@scala.QQ.abs(-10).tdo(10).sum.z"),
"""object QQ is not a member of package scala""",
"""
- twDebug("@scala.QQ.abs(-10).tdo(10).sum.z"),
- ^
+ twRuntimeErrors("@scala.QQ.abs(-10).tdo(10).sum.z"),
+ ^
"""
)
* - check(
- twDebug("@scala.math.abs(-10).tdo(10).sum.z"),
+ twRuntimeErrors("@scala.math.abs(-10).tdo(10).sum.z"),
"value tdo is not a member of Int",
"""
- twDebug("@scala.math.abs(-10).tdo(10).sum.z"),
- ^
+ twRuntimeErrors("@scala.math.abs(-10).tdo(10).sum.z"),
+ ^
"""
)
* - check(
- twDebug("@scala.math.abs(-10).to(10).sum.z"),
+ twRuntimeErrors("@scala.math.abs(-10).to(10).sum.z"),
"value z is not a member of Int",
"""
- twDebug("@scala.math.abs(-10).to(10).sum.z"),
- ^
+ twRuntimeErrors("@scala.math.abs(-10).to(10).sum.z"),
+ ^
"""
)
* - check(
- twDebug("@scala.math.abs(-10).to(10).sum.z()"),
+ twRuntimeErrors("@scala.math.abs(-10).to(10).sum.z()"),
"value z is not a member of Int",
"""
- twDebug("@scala.math.abs(-10).to(10).sum.z()"),
- ^
+ twRuntimeErrors("@scala.math.abs(-10).to(10).sum.z()"),
+ ^
"""
)
* - check(
- twDebug("@scala.math.abs(-10).cow.sum.z"),
+ twRuntimeErrors("@scala.math.abs(-10).cow.sum.z"),
"value cow is not a member of Int",
"""
- twDebug("@scala.math.abs(-10).cow.sum.z"),
- ^
+ twRuntimeErrors("@scala.math.abs(-10).cow.sum.z"),
+ ^
"""
)
* - check(
- twDebug("@scala.smath.abs.cow.sum.z"),
+ twRuntimeErrors("@scala.smath.abs.cow.sum.z"),
"object smath is not a member of package scala",
"""
- twDebug("@scala.smath.abs.cow.sum.z"),
- ^
+ twRuntimeErrors("@scala.smath.abs.cow.sum.z"),
+ ^
"""
)
* - check(
- twDebug("""
+ twRuntimeErrors("""
I am cow hear me moo
@scala.math.abs(-10).tdo(10).sum.z
I weigh twice as much as you
@@ -147,20 +147,20 @@ object ErrorTests extends TestSuite{
}
'callContents{
* - check(
- twDebug("@scala.math.abs((1, 2).wtf)"),
+ twRuntimeErrors("@scala.math.abs((1, 2).wtf)"),
"value wtf is not a member of (Int, Int)",
"""
- twDebug("@scala.math.abs((1, 2).wtf)"),
- ^
+ twRuntimeErrors("@scala.math.abs((1, 2).wtf)"),
+ ^
"""
)
* - check(
- twDebug("@scala.math.abs((1, 2).swap._1.toString().map(_.toString.wtf))"),
+ twRuntimeErrors("@scala.math.abs((1, 2).swap._1.toString().map(_.toString.wtf))"),
"value wtf is not a member of String",
"""
- twDebug("@scala.math.abs((1, 2).swap._1.toString().map(_.toString.wtf))"),
- ^
+ twRuntimeErrors("@scala.math.abs((1, 2).swap._1.toString().map(_.toString.wtf))"),
+ ^
"""
)
}
@@ -168,33 +168,33 @@ object ErrorTests extends TestSuite{
'ifElse{
'oneLine {
* - check(
- twDebug("@if(math > 10){ 1 }else{ 2 }"),
+ twRuntimeErrors("@if(math > 10){ 1 }else{ 2 }"),
"object > is not a member of package math",
"""
- twDebug("@if(math > 10){ 1 }else{ 2 }"),
- ^
+ twRuntimeErrors("@if(math > 10){ 1 }else{ 2 }"),
+ ^
"""
)
* - check(
- twDebug("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"),
+ twRuntimeErrors("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"),
"Unspecified value parameter y",
"""
- twDebug("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"),
- ^
+ twRuntimeErrors("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"),
+ ^
"""
)
* - check(
- twDebug("@if(true){ * 10 }else{ @math.sin(3, 4, 5) }"),
+ twRuntimeErrors("@if(true){ * 10 }else{ @math.sin(3, 4, 5) }"),
"too many arguments for method sin: (x: Double)Double",
"""
- twDebug("@if(true){ * 10 }else{ @math.sin(3, 4, 5) }"),
- ^
+ twRuntimeErrors("@if(true){ * 10 }else{ @math.sin(3, 4, 5) }"),
+ ^
"""
)
}
'multiLine{
* - check(
- twDebug("""
+ twRuntimeErrors("""
Ho Ho Ho
@if(math != 10)
@@ -210,7 +210,7 @@ object ErrorTests extends TestSuite{
"""
)
* - check(
- twDebug("""
+ twRuntimeErrors("""
Ho Ho Ho
@if(4 != 10)
@@ -226,7 +226,7 @@ object ErrorTests extends TestSuite{
"""
)
* - check(
- twDebug("""
+ twRuntimeErrors("""
Ho Ho Ho
@if(12 != 10)
@@ -246,26 +246,26 @@ object ErrorTests extends TestSuite{
'forLoop{
'oneLine{
'header - check(
- twDebug("omg @for(x <- (0 + 1 + 2) omglolol (10 + 11 + 2)){ hello }"),
+ twRuntimeErrors("omg @for(x <- (0 + 1 + 2) omglolol (10 + 11 + 2)){ hello }"),
"""value omglolol is not a member of Int""",
"""
- twDebug("omg @for(x <- (0 + 1 + 2) omglolol (10 + 11 + 2)){ hello }"),
- ^
+ twRuntimeErrors("omg @for(x <- (0 + 1 + 2) omglolol (10 + 11 + 2)){ hello }"),
+ ^
"""
)
'body - check(
- twDebug("omg @for(x <- 0 until 10){ @((x, 2) + (1, 2)) }"),
+ twRuntimeErrors("omg @for(x <- 0 until 10){ @((x, 2) + (1, 2)) }"),
"""too many arguments for method +""",
"""
- twDebug("omg @for(x <- 0 until 10){ @((x, 2) + (1, 2)) }"),
- ^
+ twRuntimeErrors("omg @for(x <- 0 until 10){ @((x, 2) + (1, 2)) }"),
+ ^
"""
)
}
'multiLine{
'body - check(
- twDebug("""
+ twRuntimeErrors("""
omg
@for(x <- 0 until 10)
I am cow hear me moo
@@ -281,7 +281,7 @@ object ErrorTests extends TestSuite{
}
'multiLine{
'missingVar - check(
- twDebug("""
+ twRuntimeErrors("""
omg @notInScope lol
"""),
"""not found: value notInScope""",
@@ -291,7 +291,7 @@ object ErrorTests extends TestSuite{
"""
)
'wrongType - check(
- twDebug("""
+ twRuntimeErrors("""
omg @{() => ()} lol
"""),
"""type mismatch""",
@@ -302,7 +302,7 @@ object ErrorTests extends TestSuite{
)
'bigExpression - check(
- twDebug("""
+ twRuntimeErrors("""
@{
val x = 1 + 2
val y = new Object()