Skip to content
Open
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
86 changes: 52 additions & 34 deletions core/src/main/scala/slinky/core/annotations/react.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import scala.reflect.macros.whitebox
import scala.scalajs.js

@compileTimeOnly("Enable macro paradise to expand the @react macro annotation")
class react extends scala.annotation.StaticAnnotation {
class react(expandChildren: Boolean = false) extends scala.annotation.StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ReactMacrosImpl.reactImpl
}

Expand All @@ -30,7 +30,7 @@ object ReactMacrosImpl {
}
}

def createComponentBody(c: whitebox.Context)(cls: c.Tree): (c.Tree, List[c.Tree]) = {
def createComponentBody(c: whitebox.Context)(cls: c.Tree, expandChildren: Boolean): (c.Tree, List[c.Tree]) = {
import c.universe._
val q"..$_ class ${className: Name} extends ..$parents { $self => ..$stats}" = cls // scalafix:ok
val (propsDefinition, applyMethods) = stats.flatMap {
Expand All @@ -47,24 +47,30 @@ object ReactMacrosImpl {
val applyValues = caseClassparamss.map(ps => ps.map(_.name))

val caseClassApply = if (childrenParam.isDefined) {
// from https://groups.google.com/forum/#!topic/scala-user/dUOonrP_5K4
val body = c.typecheck(childrenParam.get.tpt, c.TYPEmode).tpe match {
case TypeRef(_, sym, _) if sym == definitions.RepeatedParamClass =>
val applyValuesChildrenVararg = caseClassparamss.map { ps =>
ps.map { ps =>
if (ps == childrenParam.get) {
q"${ps.name}: _*"
} else q"${ps.name}"
}
childrenParam.get.tpt match {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could make this even more general, based on the comments in #237, by having expandChildren make children curried regardless of the parameter type. That would line up with JSX behavior better too. And since expandChildren is false by default, there's no worry of unexpected behavior.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jedahu thinking about things a bit more, I'm fairly convinced myself that removing the type restriction is the way to go; if you think that makes sense could you go ahead and remove it?

case defn @ tq"Seq[$child]" if expandChildren =>
q"""def apply[..$tparams](...$paramssWithoutChildren)(children: $child*): _root_.slinky.core.KeyAndRefAddingStage[Def] =
this.apply(Props.apply(...$applyValues))"""
case _ =>
// from https://groups.google.com/forum/#!topic/scala-user/dUOonrP_5K4
val body = c.typecheck(childrenParam.get.tpt, c.TYPEmode).tpe match {
case TypeRef(_, sym, _) if sym == definitions.RepeatedParamClass =>
val applyValuesChildrenVararg = caseClassparamss.map { ps =>
ps.map { ps =>
if (ps == childrenParam.get) {
q"${ps.name}: _*"
} else q"${ps.name}"
}
}

q"this.apply(Props.apply[..$tparams](...$applyValuesChildrenVararg))"
case _ =>
q"this.apply(Props.apply[..$tparams](...$applyValues))"
}

q"this.apply(Props.apply[..$tparams](...$applyValuesChildrenVararg))"
case _ =>
q"this.apply(Props.apply[..$tparams](...$applyValues))"
q"""def apply[..$tparams](...$paramssWithoutChildren)(${childrenParam.get}): _root_.slinky.core.KeyAndRefAddingStage[Def] =
$body"""
}

q"""def apply[..$tparams](...$paramssWithoutChildren)(${childrenParam.get}): _root_.slinky.core.KeyAndRefAddingStage[Def] =
$body"""
} else {
q"""def apply[..$tparams](...$paramssWithoutChildren): _root_.slinky.core.KeyAndRefAddingStage[Def] =
this.apply(Props.apply[..$tparams](...$applyValues))"""
Expand Down Expand Up @@ -200,11 +206,17 @@ object ReactMacrosImpl {
def reactImpl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._

val expandChildren = c.prefix.tree match {
case q"new react(true)" => true
case q"new react(expandChildren = true)" => true
case _ => false
}

val outs: List[Tree] = annottees.map(_.tree).toList match {
case Seq(cls @ q"..$_ class $className extends ..$parents { $_ => ..$_}")
if parentsContainsType(c)(parents, typeOf[Component]) ||
parentsContainsType(c)(parents, typeOf[StatelessComponent]) =>
val (newCls, companionStats) = createComponentBody(c)(cls)
val (newCls, companionStats) = createComponentBody(c)(cls, expandChildren)
val parent = tq"${TermName(parents.head.toString)}.Wrapper"
List(newCls, q"object ${TermName(className.decodedName.toString)} extends $parent { ..$companionStats }")

Expand All @@ -214,7 +226,7 @@ object ReactMacrosImpl {
)
if parentsContainsType(c)(parents, typeOf[Component]) ||
parentsContainsType(c)(parents, typeOf[StatelessComponent]) =>
val (newCls, companionStats) = createComponentBody(c)(cls)
val (newCls, companionStats) = createComponentBody(c)(cls, expandChildren)
val parent = tq"${TermName(parents.head.toString)}.Wrapper"
List(
newCls,
Expand Down Expand Up @@ -246,24 +258,30 @@ object ReactMacrosImpl {
val applyValues = caseClassparamss.map(ps => ps.map(_.name))

val caseClassApply = if (childrenParam.isDefined) {
// from https://groups.google.com/forum/#!topic/scala-user/dUOonrP_5K4
val body = c.typecheck(childrenParam.get.tpt, c.TYPEmode).tpe match {
case TypeRef(_, sym, _) if sym == definitions.RepeatedParamClass =>
val applyValuesChildrenVararg = caseClassparamss.map { ps =>
ps.map { ps =>
if (ps == childrenParam.get) {
q"${ps.name}: _*"
} else q"${ps.name}"
}
childrenParam.get.tpt match {
case defn @ tq"Seq[$child]" if expandChildren =>
q"""def apply[..$tparams](...$paramssWithoutChildren)(children: $child*) =
component.apply(Props.apply(...$applyValues))"""
case _ =>
// from https://groups.google.com/forum/#!topic/scala-user/dUOonrP_5K4
val body = c.typecheck(childrenParam.get.tpt, c.TYPEmode).tpe match {
case TypeRef(_, sym, _) if sym == definitions.RepeatedParamClass =>
val applyValuesChildrenVararg = caseClassparamss.map { ps =>
ps.map { ps =>
if (ps == childrenParam.get) {
q"${ps.name}: _*"
} else q"${ps.name}"
}
}

q"component.apply(Props.apply(...$applyValuesChildrenVararg))"
case _ =>
q"component.apply(Props.apply(...$applyValues))"
}

q"component.apply(Props.apply(...$applyValuesChildrenVararg))"
case _ =>
q"component.apply(Props.apply(...$applyValues))"
q"""def apply[..$tparams](...$paramssWithoutChildren)(${childrenParam.get}) =
$body"""
}

q"""def apply[..$tparams](...$paramssWithoutChildren)(${childrenParam.get}) =
$body"""
} else {
if (paramssWithoutChildren.flatten.isEmpty) {
q"def apply() = component.apply(Props.apply())"
Expand Down
Loading