Skip to content

Commit 7030de6

Browse files
committed
feat(scala3): WIP working on scala 3 cross compilation. #390.
1 parent ea8d3c5 commit 7030de6

20 files changed

Lines changed: 1109 additions & 41 deletions

File tree

.travis.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ jobs:
2828
- SCALA_VERSION=2.13.7
2929
- ADOPTOPENJDK=11
3030

31+
- name: "Run tests with Scala 3 and AdoptOpenJDK 11"
32+
script: scripts/test-code.sh
33+
env:
34+
- TRAVIS_SCALA_VERSION=3.1.0
35+
- ADOPTOPENJDK=11
36+
3137
- name: "Run tests with Scala 2.12 and AdoptOpenJDK 8"
3238
script: scripts/test-code.sh
3339
env:
@@ -40,6 +46,12 @@ jobs:
4046
- SCALA_VERSION=2.13.7
4147
- ADOPTOPENJDK=8
4248

49+
- name: "Run tests with Scala 3 and AdoptOpenJDK 8"
50+
script: scripts/test-code.sh
51+
env:
52+
- TRAVIS_SCALA_VERSION=3.1.0
53+
- ADOPTOPENJDK=8
54+
4355
- stage: docs
4456
script: scripts/validate-docs.sh
4557
name: "Validate documentation"

api/shared/src/main/scala/play/twirl/api/BaseScalaTemplate.scala renamed to api/shared/src/main/scala-2.12/play/twirl/api/BaseScalaTemplate.scala

File renamed without changes.

api/shared/src/main/scala/play/twirl/api/TwirlHelperImports.scala renamed to api/shared/src/main/scala-2.12/play/twirl/api/TwirlHelperImports.scala

File renamed without changes.

api/shared/src/main/scala/play/twirl/api/package.scala renamed to api/shared/src/main/scala-2.12/play/twirl/api/package.scala

File renamed without changes.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
3+
*/
4+
package play.twirl.api
5+
6+
import java.util.Optional
7+
import scala.collection.immutable
8+
import scala.jdk.CollectionConverters._
9+
import scala.reflect.ClassTag
10+
11+
case class BaseScalaTemplate[T <: Appendable[T], F <: Format[T]](format: F) {
12+
// The overloaded methods are here for speed. The compiled templates
13+
// can take advantage of them for a 12% performance boost
14+
def _display_(x: AnyVal): T = format.escape(x.toString)
15+
def _display_(x: String): T = if (x eq null) format.empty else format.escape(x)
16+
def _display_(x: Unit): T = format.empty
17+
def _display_(x: scala.xml.NodeSeq): T = if (x eq null) format.empty else format.raw(x.toString())
18+
def _display_(x: T): T = if (x eq null) format.empty else x
19+
20+
def _display_(o: Any)(implicit m: ClassTag[T]): T = {
21+
o match {
22+
case escaped if escaped != null && escaped.getClass == m.runtimeClass => escaped.asInstanceOf[T]
23+
case () => format.empty
24+
case None => format.empty
25+
case Some(v) => _display_(v)
26+
case key: Optional[_] =>
27+
(if (key.isPresent) Some(key.get) else None) match {
28+
case None => format.empty
29+
case Some(v) => _display_(v)
30+
case _ => format.empty
31+
}
32+
case xml: scala.xml.NodeSeq => format.raw(xml.toString())
33+
case escapeds: immutable.Seq[_] => format.fill(escapeds.map(_display_))
34+
case escapeds: TraversableOnce[_] => format.fill(escapeds.iterator.map(_display_).toList)
35+
case escapeds: Array[_] => format.fill(escapeds.view.map(_display_).toList)
36+
case escapeds: java.util.List[_] =>
37+
format.fill(escapeds.asScala.map(_display_).toList)
38+
case string: String => format.escape(string)
39+
case v if v != null => format.escape(v.toString)
40+
case _ => format.empty
41+
}
42+
}
43+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
3+
*/
4+
package play.twirl.api
5+
6+
import scala.language.implicitConversions
7+
8+
/**
9+
* Imports for useful Twirl helpers.
10+
*/
11+
object TwirlHelperImports {
12+
13+
/** Allows Java collections to be used as if they were Scala collections. */
14+
implicit def twirlJavaCollectionToScala[T](x: java.lang.Iterable[T]): Iterable[T] = {
15+
import scala.jdk.CollectionConverters._
16+
x.asScala
17+
}
18+
19+
/** Allows inline formatting of java.util.Date */
20+
implicit class TwirlRichDate(date: java.util.Date) {
21+
def format(pattern: String): String = {
22+
new java.text.SimpleDateFormat(pattern).format(date)
23+
}
24+
}
25+
26+
/** Adds a when method to Strings to control when they are rendered. */
27+
implicit class TwirlRichString(string: String) {
28+
def when(predicate: => Boolean): String = {
29+
predicate match {
30+
case true => string
31+
case false => ""
32+
}
33+
}
34+
}
35+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
3+
*/
4+
package play.twirl
5+
6+
import scala.reflect.ClassTag
7+
8+
package object api {
9+
10+
/**
11+
* Brings the template engine as a
12+
* [[http://docs.scala-lang.org/overviews/core/string-interpolation.html string interpolator]].
13+
*
14+
* Basic usage:
15+
*
16+
* {{{
17+
* import play.twirl.api.StringInterpolation
18+
*
19+
* val name = "Martin"
20+
* val htmlFragment: Html = html"&lt;div&gt;Hello \$name&lt;/div&gt;"
21+
* }}}
22+
*
23+
* Three interpolators are available: `html`, `xml` and `js`.
24+
*/
25+
implicit class StringInterpolation(val sc: StringContext) extends AnyVal {
26+
def html(args: Any*): Html = interpolate(args, HtmlFormat)
27+
28+
def xml(args: Any*): Xml = interpolate(args, XmlFormat)
29+
30+
def js(args: Any*): JavaScript = interpolate(args, JavaScriptFormat)
31+
32+
def interpolate[A <: Appendable[A]: ClassTag](args: Seq[Any], format: Format[A]): A = {
33+
StringContext.checkLengths(args, sc.parts)
34+
val array = Array.ofDim[Any](args.size + sc.parts.size)
35+
val strings = sc.parts.iterator
36+
val expressions = args.iterator
37+
array(0) = format.raw(strings.next())
38+
var i = 1
39+
while (strings.hasNext) {
40+
array(i) = expressions.next()
41+
array(i + 1) = format.raw(strings.next())
42+
i += 2
43+
}
44+
new BaseScalaTemplate[A, Format[A]](format)._display_(array)
45+
}
46+
}
47+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
3+
*/
4+
package play.twirl.api
5+
6+
import java.util.Optional
7+
import scala.collection.immutable
8+
import scala.jdk.CollectionConverters._
9+
import scala.reflect.ClassTag
10+
11+
case class BaseScalaTemplate[T <: Appendable[T], F <: Format[T]](format: F) {
12+
// The overloaded methods are here for speed. The compiled templates
13+
// can take advantage of them for a 12% performance boost
14+
def _display_(x: AnyVal): T = format.escape(x.toString)
15+
def _display_(x: String): T = if (x eq null) format.empty else format.escape(x)
16+
def _display_(x: Unit): T = format.empty
17+
def _display_(x: scala.xml.NodeSeq): T = if (x eq null) format.empty else format.raw(x.toString())
18+
def _display_(x: T): T = if (x eq null) format.empty else x
19+
20+
def _display_(o: Any)(implicit m: ClassTag[T]): T = {
21+
o match {
22+
case escaped if escaped != null && escaped.getClass == m.runtimeClass => escaped.asInstanceOf[T]
23+
case () => format.empty
24+
case None => format.empty
25+
case Some(v) => _display_(v)
26+
case key: Optional[_] =>
27+
(if (key.isPresent) Some(key.get) else None) match {
28+
case None => format.empty
29+
case Some(v) => _display_(v)
30+
case null => format.empty
31+
}
32+
case xml: scala.xml.NodeSeq => format.raw(xml.toString())
33+
case escapeds: immutable.Seq[_] => format.fill(escapeds.map(_display_))
34+
case escapeds: TraversableOnce[_] => format.fill(escapeds.iterator.map(_display_).toList)
35+
case escapeds: Array[_] => format.fill(escapeds.view.map(_display_).toList)
36+
case escapeds: java.util.List[_] =>
37+
format.fill(escapeds.asScala.map(_display_).toList)
38+
case string: String => format.escape(string)
39+
case v if v != null => format.escape(v.toString)
40+
case null => format.empty
41+
}
42+
}
43+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
3+
*/
4+
package play.twirl.api
5+
6+
import scala.language.implicitConversions
7+
8+
/**
9+
* Imports for useful Twirl helpers.
10+
*/
11+
object TwirlHelperImports {
12+
13+
/** Allows Java collections to be used as if they were Scala collections. */
14+
implicit def twirlJavaCollectionToScala[T](x: java.lang.Iterable[T]): Iterable[T] = {
15+
import scala.jdk.CollectionConverters._
16+
x.asScala
17+
}
18+
19+
/** Allows inline formatting of java.util.Date */
20+
implicit class TwirlRichDate(date: java.util.Date) {
21+
def format(pattern: String): String = {
22+
new java.text.SimpleDateFormat(pattern).format(date)
23+
}
24+
}
25+
26+
/** Adds a when method to Strings to control when they are rendered. */
27+
implicit class TwirlRichString(string: String) {
28+
def when(predicate: => Boolean): String = {
29+
predicate match {
30+
case true => string
31+
case false => ""
32+
}
33+
}
34+
}
35+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
3+
*/
4+
package play.twirl
5+
6+
import scala.reflect.ClassTag
7+
8+
package object api {
9+
10+
/**
11+
* Brings the template engine as a
12+
* [[http://docs.scala-lang.org/overviews/core/string-interpolation.html string interpolator]].
13+
*
14+
* Basic usage:
15+
*
16+
* {{{
17+
* import play.twirl.api.StringInterpolation
18+
*
19+
* val name = "Martin"
20+
* val htmlFragment: Html = html"&lt;div&gt;Hello \$name&lt;/div&gt;"
21+
* }}}
22+
*
23+
* Three interpolators are available: `html`, `xml` and `js`.
24+
*/
25+
implicit class StringInterpolation(val sc: StringContext) extends AnyVal {
26+
def html(args: Any*): Html = interpolate(args, HtmlFormat)
27+
28+
def xml(args: Any*): Xml = interpolate(args, XmlFormat)
29+
30+
def js(args: Any*): JavaScript = interpolate(args, JavaScriptFormat)
31+
32+
def interpolate[A <: Appendable[A]: ClassTag](args: Seq[Any], format: Format[A]): A = {
33+
StringContext.checkLengths(args, sc.parts)
34+
val array = Array.ofDim[Any](args.size + sc.parts.size)
35+
val strings = sc.parts.iterator
36+
val expressions = args.iterator
37+
array(0) = format.raw(strings.next())
38+
var i = 1
39+
while (strings.hasNext) {
40+
array(i) = expressions.next()
41+
array(i + 1) = format.raw(strings.next())
42+
i += 2
43+
}
44+
new BaseScalaTemplate[A, Format[A]](format)._display_(array)
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)