Skip to content

Instantly share code, notes, and snippets.

@Katrix
Created December 7, 2019 12:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Katrix/7f989ee734ae5107daa42ba1ab2f9a93 to your computer and use it in GitHub Desktop.
Save Katrix/7f989ee734ae5107daa42ba1ab2f9a93 to your computer and use it in GitHub Desktop.
package dottyspongetext
import org.spongepowered.api.text.Text
import org.spongepowered.api.text.translation.{Translatable, Translation}
import org.spongepowered.api.text.selector.Selector
import org.spongepowered.api.scoreboard.Score
import scala.quoted._
import scala.quoted.matching._
import reflect._
//Lot's of stuff in here taken from the f interpolator
inline def (sc: => StringContext) t(args: Any*) <: Text = ${ textOfMacro('sc, 'args) }
private def getPartsExprs(scExpr: Expr[StringContext])(given qctx: QuoteContext): Option[(List[Expr[String]], List[String])] =
def notStatic =
qctx.error("Expected statically known String Context", scExpr)
None
def splitParts(seq: Expr[Seq[String]]) = (seq, seq) match
case (ExprSeq(p1), ConstSeq(p2)) => Some((p1.toList, p2.toList))
case (_, _) => notStatic
scExpr match
case '{ StringContext($parts: _*) } => splitParts(parts)
case '{ new StringContext($parts: _*) } => splitParts(parts)
case _ => notStatic
def getArgsExprs(argsExpr: Expr[Seq[Any]])(given qctx: QuoteContext): Option[List[Expr[Any]]] =
import qctx.tasty.{_, given}
argsExpr.unseal.underlyingArgument match
case Typed(Repeated(args, _), _) =>
Some(args.map(_.seal))
case tree =>
qctx.error("Expected statically known argument list", argsExpr)
None
def checkSizes(format: Int, argument : Int)(given qctx: QuoteContext): Unit =
if (format > argument && !(format == -1 && argument == 0))
if (argument == 0)
qctx.error("too few arguments for interpolated string")
else
qctx.error("too few arguments for interpolated string")
if (format < argument && !(format == -1 && argument == 0))
if (argument == 0)
qctx.error("too many arguments for interpolated string")
else
qctx.error("too many arguments for interpolated string")
if (format == -1)
qctx.error("there are no parts")
private def textOfMacro(scExpr: Expr[StringContext], argsExpr: Expr[Seq[Any]])(given qctx: QuoteContext): Expr[Text] =
import qctx.tasty.{_, given}
val (partsExpr, parts) = getPartsExprs(scExpr) match
case Some(x) => x
case None => return '{Text.EMPTY}
val args = getArgsExprs(argsExpr) match
case Some(args) => args
case None => return '{Text.EMPTY}
checkSizes(parts.size - 1, args.size)
def firstPartEmpty: Boolean = parts.head.isEmpty
def singleArgOf[T: scala.quoted.Type]: Boolean =
parts.forall(_.isEmpty) && parts.size == 2 && firstArgIs[T]
def firstArgIs[T](given tpe: scala.quoted.Type[T]): Boolean =
firstPartEmpty && args.headOption.exists(h => h.unseal.tpe <:< tpe.unseal.tpe)
def castObj(e: Expr[Any]): Expr[AnyRef] =
e.unseal.tpe.seal match
case '[Byte] => '{ $e.asInstanceOf[java.lang.Byte] }
case '[Short] => '{ $e.asInstanceOf[java.lang.Short] }
case '[Int] => '{ $e.asInstanceOf[java.lang.Integer] }
case '[Long] => '{ $e.asInstanceOf[java.lang.Long] }
case '[Float] => '{ $e.asInstanceOf[java.lang.Float] }
case '[Double] => '{ $e.asInstanceOf[java.lang.Double] }
case '[Boolean] => '{ $e.asInstanceOf[java.lang.Boolean] }
case '[Unit] => '{ $e.asInstanceOf[scala.runtime.BoxedUnit] }
case _ => e.cast[AnyRef]
if singleArgOf[Score] then
'{ Text.of(${args.head.cast[Score]}) }
else if singleArgOf[Selector] then
'{ Text.of(${args.head.cast[Selector]}) }
else if firstArgIs[Translatable] then
val objsList = (partsExpr.tail.head +: args.tail.zip(partsExpr.tail.tail).flatMap((a, p) => Seq(a, p))).map(castObj)
val objs = Expr.ofSeq(objsList)
'{ Text.of(${args.head.cast[Translatable]}, $objs: _*) }
else if firstArgIs[Translation] then
val objsList = (partsExpr.tail.head +: args.tail.zip(partsExpr.tail.tail).flatMap((a, p) => Seq(a, p))).map(castObj)
val objs = Expr.ofSeq(objsList)
'{ Text.of(${args.head.cast[Translation]}, $objs: _*) }
else if parts.size == 1 && args.isEmpty then
'{ Text.of(${partsExpr.head}) }
else
val objsList = (partsExpr.head +: args.zip(partsExpr.tail).flatMap((a, p) => Seq(a, p))).map(castObj)
val objs = Expr.ofSeq(objsList)
'{ Text.of($objs: _*) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment