Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 32 additions & 20 deletions compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -730,21 +730,24 @@ package """ :+ packageName :+ """
getFunctionMapping(signature, returnType, ScalaCompat(None))

private[compiler] def getFunctionMapping(
signature: String,
signature: String, // can also contain type params like [A, B, C](...)
returnType: String,
sc: ScalaCompat
): (String, String, String) = {
): (String, String, String) = { // renderCall, f, templateType

val params: List[List[Term.Param]] =
val (tparams, params): (List[Type.Param], List[List[Term.Param]]) =
try {
val dialect = Dialect.current.withAllowGivenUsing(true)
val input = Input.String(s"object FT { def signature$signature }")
val obj = implicitly[Parse[Stat]].apply(input, dialect).get.asInstanceOf[Defn.Object]
val templ = obj.templ
val defdef = templ.body.stats.head.asInstanceOf[Decl.Def]
defdef.paramClauseGroups.headOption.map(_.paramClauses.map(_.values)).getOrElse(Nil)
(
defdef.paramClauseGroups.headOption.map(_.tparamClause.values).getOrElse(Nil),
defdef.paramClauseGroups.headOption.map(_.paramClauses.map(_.values)).getOrElse(Nil)
)
} catch {
case e: ParseException => Nil
case e: ParseException => (Nil, Nil)
}

def filterType(p: Term.Param) =
Expand Down Expand Up @@ -782,36 +785,45 @@ package """ :+ packageName :+ """
}.mkString
}

val renderCall = "def render%s: %s = apply%s".format(
val typeParams = if (tparams.isEmpty) "" else tparams.mkString("[", ",", "]")

val renderCall = "def render%s%s: %s = apply%s%s".format(
typeParams,
"(" + params.flatten
.map {
case p @ ByNameParam(_, paramType) => p.name.toString + ":" + paramType
case p => p.name.toString + ":" + filterType(p)
}
.mkString(",") + ")",
returnType,
typeParams,
applyArgs
)

val f = "def f:%s = %s => apply%s".format(
val f = "def f%s:%s = %s => apply%s%s".format(
typeParams,
functionType,
params.map(group => "(" + group.map(_.name.toString).mkString(",") + ")").mkString(" => "),
typeParams,
applyArgs
)

val templateType = sc.valueOrEmptyIfScala3Exceeding22Params(
params.flatten.size,
"_root_.play.twirl.api.Template%s[%s%s]".format(
params.flatten.size,
params.flatten
.map {
case ByNameParam(_, paramType) => paramType
case p => filterType(p)
}
.mkString(","),
(if (params.flatten.isEmpty) "" else ",") + returnType
)
)
val templateType =
if (tparams.isEmpty)
sc.valueOrEmptyIfScala3Exceeding22Params(
params.flatten.size,
"_root_.play.twirl.api.Template%s[%s%s]".format(
params.flatten.size,
params.flatten
.map {
case ByNameParam(_, paramType) => paramType
case p => filterType(p)
}
.mkString(","),
(if (params.flatten.isEmpty) "" else ",") + returnType
)
)
else "" // If type params -> no TemplateN trait. Too many possibilities. TODO: Generate TemplateN on the fly?

(renderCall, f, templateType)
}
Expand Down
28 changes: 26 additions & 2 deletions parser/src/main/scala/play/twirl/parser/TwirlParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1254,8 +1254,8 @@ class TwirlParser(val shouldParseInclusiveDot: Boolean) {
* Parse the template arguments, if they exist
*/
private def maybeTemplateArgs(): Option[PosString] = {
if (check("@(")) {
input.regress(1)
val reset = input.offset()
def parseArgs(): Option[PosString] = {
val p = input.offset()
val args = templateArgs()
if (args != null) {
Expand All @@ -1265,6 +1265,30 @@ class TwirlParser(val shouldParseInclusiveDot: Boolean) {
} else {
None
}
}
if (check("@(")) {
input.regress(1)
parseArgs()
} else if (check("@[")) {
input.regress(1)
Option(squareBrackets()) match {
case Some(value) if value.replaceAll("\\s", "") == "[]" =>
input.regressTo(reset) // don't consume @
error(s"identifier expected but ']' found", reset) // TODO: really reset hier?
None
case Some(types) =>
parseArgs() match {
case Some(value) => Some(position(PosString(types + value.str), reset))
case None =>
val result = Some(position(PosString(types + "()"), reset))
check("\n")
result
}
case None =>
input.regressTo(reset) // don't consume @
error(s"Type parameter(s) expected", reset) // TODO: really reset hier?
None
}
} else None
}

Expand Down
Loading