diff --git a/build.sc b/build.sc index d974012..709b030 100644 --- a/build.sc +++ b/build.sc @@ -7,7 +7,7 @@ import com.github.lolgab.mill.mima._ val dottyVersions = sys.props.get("dottyVersion").toList -val scalaVersions = "2.12.13" :: "2.13.4" :: "2.11.12" :: "3.0.0" :: dottyVersions +val scalaVersions = "2.12.13" :: "2.13.4" :: "2.11.12" :: "3.0.2" :: dottyVersions val scala2Versions = scalaVersions.filter(_.startsWith("2.")) val scalaJSVersions = for { @@ -64,39 +64,6 @@ trait PPrintMainModule extends CrossScalaModule { ) ) ) - def generatedSources = T{ - val dir = T.ctx().dest - val file = dir/"pprint"/"TPrintGen.scala" - - val typeGen = for(i <- 2 to 22) yield { - val ts = (1 to i).map("T" + _).mkString(", ") - val tsBounded = (1 to i).map("T" + _ + ": Type").mkString(", ") - val tsGet = (1 to i).map("get[T" + _ + "](cfg)").mkString(" + \", \" + ") - s""" - implicit def F${i}TPrint[$tsBounded, R: Type]: Type[($ts) => R] = make[($ts) => R](cfg => - "(" + $tsGet + ") => " + get[R](cfg) - ) - implicit def T${i}TPrint[$tsBounded]: Type[($ts)] = make[($ts)](cfg => - "(" + $tsGet + ")" - ) - """ - } - val output = s""" - package pprint - trait TPrintGen[Type[_], Cfg]{ - def make[T](f: Cfg => String): Type[T] - def get[T: Type](cfg: Cfg): String - implicit def F0TPrint[R: Type]: Type[() => R] = make[() => R](cfg => "() => " + get[R](cfg)) - implicit def F1TPrint[T1: Type, R: Type]: Type[T1 => R] = { - make[T1 => R](cfg => get[T1](cfg) + " => " + get[R](cfg)) - } - ${typeGen.mkString("\n")} - } - """.stripMargin - os.write(file, output, createFolders = true) - Seq(PathRef(file)) - } - } diff --git a/pprint/src-2/TPrintImpl.scala b/pprint/src-2/TPrintImpl.scala index c64b2fe..020bdfb 100644 --- a/pprint/src-2/TPrintImpl.scala +++ b/pprint/src-2/TPrintImpl.scala @@ -7,27 +7,45 @@ trait TPrintLowPri{ implicit def default[T]: TPrint[T] = macro TPrintLowPri.typePrintImpl[T] } object TPrintLowPri{ + sealed trait WrapType + object WrapType{ + case object NoWrap extends WrapType + case object Infix extends WrapType + case object Tuple extends WrapType + } def typePrintImpl[T: c.WeakTypeTag](c: Context): c.Expr[TPrint[T]] = { // Used to provide "empty string" values in quasiquotes + import c.universe._ - val s = "" val tpe = weakTypeOf[T] + val rendered = typePrintImplRec(c)(tpe, rightMost = true).render + val res = c.Expr[TPrint[T]]( + q"_root_.pprint.TPrint.recolor(_root_.fansi.Str($rendered))" + ) + res + } + + val functionTypes = Range.inclusive(0, 22).map(i => s"scala.Function$i").toSet + val tupleTypes = Range.inclusive(0, 22).map(i => s"scala.Tuple$i").toSet + + def typePrintImplRec[T](c: Context)(tpe: c.Type, rightMost: Boolean): fansi.Str = { + typePrintImplRec0(c)(tpe, rightMost)._1 + } + def typePrintImplRec0[T](c: Context)(tpe: c.Type, rightMost: Boolean): (fansi.Str, WrapType) = { + import c.universe._ def printSymString(s: Symbol) = if (s.name.decodedName.toString.startsWith("_$")) "_" else s.name.decodedName.toString.stripSuffix(".type") - def literalColor(s: Tree) = { - q"$cfgSym.typeColor($s).render" - } - def printSym(s: Symbol): Tree = { - literalColor(q"${printSymString(s)}") - } + def literalColor(s: fansi.Str): fansi.Str = fansi.Color.Green(s) + def printSym(s: Symbol): fansi.Str = literalColor(printSymString(s)) - def printSymFull(s: Symbol): Tree = { - if (lookup(s)) printSym(s) - else q"""${printSymFull(s.owner)} + "." + ${printSym(s)}""" + def printSymFull(s: Symbol): fansi.Str = { + if (lookup(s)) printSym(s) + else printSymFull(s.owner) ++ "." ++ printSym(s) } + /** * Looks up a symbol in the enclosing scope and returns * whether it exists in scope by the same name @@ -55,64 +73,43 @@ object TPrintLowPri{ )} } - def prefixFor(pre: Type, sym: Symbol): Tree = { + def prefixFor(pre: Type, sym: Symbol): fansi.Str = { // Depending on what the prefix is, you may use `#`, `.` // or even need to wrap the prefix in parentheses val sep = pre match{ - case x if x.toString.endsWith(".type") => q""" ${rec0(pre)} + "." """ - case x: TypeRef => q""" ${literalColor(implicitRec(pre))} + "#" """ - case x: SingleType => q""" ${literalColor(rec0(pre))} + "." """ - case x: ThisType => q""" ${literalColor(rec0(pre))} + "." """ - case x => q""" "(" + ${implicitRec(pre)} + ")#" """ + case x if x.toString.endsWith(".type") => typePrintImplRec(c)(pre, false) ++ "." + case x: TypeRef => literalColor(typePrintImplRec(c)(pre, true)) ++ "#" + case x: SingleType => literalColor(typePrintImplRec(c)(pre, false)) ++ "." + case x: ThisType => literalColor(typePrintImplRec(c)(pre, false)) ++ "." + case x => fansi.Str("(") ++ typePrintImplRec(c)(pre, true) ++ ")#" } - val prefix = if (!lookup(sym)) sep else q"$s" - q"$prefix + ${printSym(sym)}" + val prefix = if (!lookup(sym)) sep else fansi.Str("") + prefix ++ printSym(sym) } - def printArgSyms(args: List[Symbol]): Tree = { - def added = args.map{x => - val TypeBounds(lo, hi) = x.info - q""" ${printSym(x)} + ${printBounds(lo, hi)}""" - }.reduceLeft[Tree]((l, r) => q"""$l + ", " + $r""") - if (args == Nil) q"$s" else q""" "[" + $added + "]" """ - } - def printArgs(args: List[Type]): Tree = { - def added = args.map(implicitRec(_)) - .reduceLeft[Tree]((l, r) => q"""$l + ", " + $r""") + def printArgSyms(args: List[Symbol]): fansi.Str = { + def added = args + .map{x => + val TypeBounds(lo, hi) = x.info + printSym(x) ++ printBounds(lo, hi) + } + .reduceLeft[fansi.Str]((l, r) => l ++ ", " ++ r) - if (args == Nil) q"$s" else q""" "[" + $added + "]" """ + if (args == Nil) fansi.Str("") else fansi.Str("[") ++ added ++ "]" } + def printArgs(args: List[Type]): fansi.Str = { + def added = args.map(typePrintImplRec(c)(_, true)) + .reduceLeft[fansi.Str]((l, r) => l ++ ", " ++ r) - - def implicitRec(tpe: Type) = { - val byName = (tpe: Type) match{ - case t: TypeRef if t.toString.startsWith("=> ") => Some(t.args(0)) - case _ => None - } - - try { - // Make sure the type isn't higher-kinded or some other weird - // thing, and actually can fit inside the square brackets - - byName match{ - case Some(t) => - c.typecheck(q"null.asInstanceOf[$tpe]") - q""" "=> " + _root_.pprint.TPrint.implicitly[$t].render($cfgSym) """ - case _ => - c.typecheck(q"null.asInstanceOf[$tpe]") - q""" _root_.pprint.TPrint.implicitly[$tpe].render($cfgSym) """ - } - - }catch{case e: TypecheckException => - rec0(tpe) - } + if (args == Nil) fansi.Str("") else fansi.Str("[") ++ added ++ "]" } + def printBounds(lo: Type, hi: Type) = { - val loTree = if (lo =:= typeOf[Nothing]) q"$s" else q""" " >: " + ${implicitRec(lo)} """ - val hiTree = if (hi =:= typeOf[Any]) q"$s" else q""" " <: " + ${implicitRec(hi)} """ - q"$loTree + $hiTree" + val loTree = if (lo =:= typeOf[Nothing]) fansi.Str("") else fansi.Str(" >: ") ++ typePrintImplRec(c)(lo, true) + val hiTree = if (hi =:= typeOf[Any]) fansi.Str("") else fansi.Str(" <: ") ++ typePrintImplRec(c)(hi, true) + loTree ++ hiTree } def showRefinement(quantified: List[Symbol]) = { @@ -122,10 +119,10 @@ object TPrintLowPri{ case PolyType(typeParams, resultType) => val paramTree = printArgSyms(t.asInstanceOf[TypeSymbol].typeParams) val resultBounds = - if (resultType =:= typeOf[Any]) q"$s" - else q""" " <: " + ${implicitRec(resultType)} """ + if (resultType =:= typeOf[Any]) fansi.Str("") + else fansi.Str(" <: ") ++ typePrintImplRec(c)(resultType, true) - Some(q""" $paramTree + $resultBounds""") + Some(paramTree ++ resultBounds) case TypeBounds(lo, hi) if t.toString.contains("$") && lo =:= typeOf[Nothing] && hi =:= typeOf[Any] => None @@ -141,67 +138,91 @@ object TPrintLowPri{ defs ) - q""" - "val " + ${literalColor(q"${t.name.toString.stripSuffix(".type")}")} + - ": " + ${implicitRec(filtered)} - """ + fansi.Str("val ") ++ literalColor(t.name.toString.stripSuffix(".type")) ++ + ": " ++ typePrintImplRec(c)(filtered, true) }else { - q""" "type " + ${printSym(t)} + $suffix """ + fansi.Str("type ") ++ printSym(t) ++ suffix } } if (stmts.length == 0) None - else Some(stmts.reduceLeft((l, r) => q""" $l + "; " + $r """)) + else Some(stmts.reduceLeft((l, r) => l + "; " + r)) } - /** - * Decide how to pretty-print, based on the type. - * - * This is recursive, but we only rarely use direct recursion: more - * often, we'll use `implicitRec`, which goes through the normal - * implicit search channel and can thus - */ - def rec0(tpe: Type, end: Boolean = false): Tree = tpe match { + + tpe match { case TypeBounds(lo, hi) => val res = printBounds(lo, hi) - q""" "_" + $res """ + (fansi.Str("_") ++ res, WrapType.NoWrap) case ThisType(sym) => - q"${printSymFull(sym)} + ${if(sym.isPackage || sym.isModuleClass) "" else ".this.type"}" + (printSymFull(sym) + (if(sym.isPackage || sym.isModuleClass) "" else ".this.type"), WrapType.NoWrap) - case SingleType(NoPrefix, sym) => q"${printSym(sym)} + ${if (end) ".type" else ""}" - case SingleType(pre, sym) => q"${prefixFor(pre, sym)} + ${if (end) ".type" else ""}" + case SingleType(NoPrefix, sym) => (printSym(sym) ++ (if (rightMost) ".type" else ""), WrapType.NoWrap) + case SingleType(pre, sym) => (prefixFor(pre, sym) ++ (if (rightMost) ".type" else ""), WrapType.NoWrap) // Special-case operator two-parameter types as infix case TypeRef(pre, sym, List(left, right)) if lookup(sym) && sym.name.encodedName.toString != sym.name.decodedName.toString => - q"""${implicitRec(left)} + " " + ${printSym(sym)} + " " +${implicitRec(right)}""" + ( + typePrintImplRec(c)(left, true) ++ " " ++ printSym(sym) ++ " " ++ typePrintImplRec(c)(right, true), + WrapType.Infix + ) - case TypeRef(NoPrefix, sym, args) => q"${printSym(sym)} + ${printArgs(args)}" - case TypeRef(pre, sym, args) => q"${prefixFor(pre, sym)} + ${printArgs(args)}" - case et @ ExistentialType(quantified, underlying) => - showRefinement(quantified) match{ - case None => implicitRec(underlying) - case Some(block) => q"""${implicitRec(underlying)} + " forSome { " + $block + " }" """ + case TypeRef(pre, sym, args) if functionTypes.contains(sym.fullName) => + args match{ + case Seq(r) => (fansi.Str("() => ") ++ typePrintImplRec(c)(r, true), WrapType.Infix) + + case many => + val (left, leftWrap) = typePrintImplRec0(c)(many.head, true) + + if (many.size == 2 && leftWrap == WrapType.NoWrap){ + (left ++ " => " ++ typePrintImplRec(c)(many(1), true), WrapType.Infix) + }else ( + fansi.Str("(") ++ + fansi.Str.join( + (left +: many.init.tail.map(typePrintImplRec(c)(_, true))) + .flatMap(Seq(_, fansi.Str(", "))).init:_* + ) ++ + ") => " ++ typePrintImplRec(c)(many.last, true), + WrapType.Infix + ) } + case TypeRef(pre, sym, args) if tupleTypes.contains(sym.fullName) => + ( + fansi.Str("(") ++ + fansi.Str.join(args.map(typePrintImplRec(c)(_, true)).flatMap(Seq(_, fansi.Str(", "))).init:_*) ++ + ")", + WrapType.Tuple + ) + + case TypeRef(NoPrefix, sym, args) => (printSym(sym) ++ printArgs(args), WrapType.NoWrap) + case TypeRef(pre, sym, args) => + if (sym.fullName == "scala.") (fansi.Str("=> ") ++ typePrintImplRec(c)(args(0), true), WrapType.Infix) + else (prefixFor(pre, sym) ++ printArgs(args), WrapType.NoWrap) + case et @ ExistentialType(quantified, underlying) => + ( + showRefinement(quantified) match{ + case None => typePrintImplRec(c)(underlying, true) + case Some(block) => typePrintImplRec(c)(underlying, true) ++ " forSome { " ++ block ++ " }" + }, + WrapType.NoWrap + ) case AnnotatedType(annots, tp) => - val mapped = annots.map(x => q""" " @" + ${implicitRec(x.tpe)}""") - .reduceLeft((x, y) => q"$x + $y") - q"${implicitRec(tp)} + $mapped" + val mapped = annots.map(x => " @" + typePrintImplRec(c)(x.tpe, true)) + .reduceLeft((x, y) => x + y) + + ( + typePrintImplRec(c)(tp, true) + mapped, + WrapType.NoWrap + ) case RefinedType(parents, defs) => val pre = - if (parents.forall(_ =:= typeOf[AnyRef])) q""" "" """ - else parents.map(implicitRec(_)).reduceLeft[Tree]((l, r) => q"""$l + " with " + $r""") - q"$pre + ${ - if (defs.isEmpty) "" else "{" + defs.mkString(";") + "}" - }" + if (parents.forall(_ =:= typeOf[AnyRef])) "" + else parents + .map(typePrintImplRec(c)(_, true)) + .reduceLeft[fansi.Str]((l, r) => l ++ " with " ++ r) + (pre + (if (defs.isEmpty) "" else "{" ++ defs.mkString(";") ++ "}"), WrapType.NoWrap) case ConstantType(value) => - q"$value.toString" + (value.toString, WrapType.NoWrap) } - lazy val cfgSym = c.freshName[TermName](TermName("cfg")) - val res = c.Expr[TPrint[T]](q"""_root_.pprint.TPrint.lambda{ - ($cfgSym: _root_.pprint.TPrintColors) => - ${rec0(tpe, end = true)} - }""") - // println("RES " + res) - res } } diff --git a/pprint/src-3/TPrintImpl.scala b/pprint/src-3/TPrintImpl.scala index 62879b2..0ef84cf 100644 --- a/pprint/src-3/TPrintImpl.scala +++ b/pprint/src-3/TPrintImpl.scala @@ -7,71 +7,67 @@ trait TPrintLowPri{ object TPrintLowPri{ import scala.quoted._ - import sourcecode.Text.generate - - extension (expr: Expr[String]) { - def +(other: Expr[String])(using Quotes): Expr[String] = - '{ $expr + $other } + sealed trait WrapType + object WrapType{ + case object NoWrap extends WrapType + case object Infix extends WrapType + case object Tuple extends WrapType } - extension (exprs: List[Expr[String]]) { - def mkStringExpr(sep: String)(using Quotes): Expr[String] = - exprs match { - case expr :: Nil => - expr - case _ => - exprs.reduceLeft { (l, r) => l + Expr(sep) + r } - } - } + val functionTypes = Range.inclusive(0, 22).map(i => s"scala.Function$i").toSet + val tupleTypes = Range.inclusive(0, 22).map(i => s"scala.Tuple$i").toSet def typePrintImpl[T](using Quotes, Type[T]): Expr[TPrint[T]] = { import quotes.reflect._ import util._ - def literalColor(cfg: Expr[TPrintColors], s: Expr[fansi.Str]) = { - '{ $cfg.typeColor($s).render } + def literalColor(s: fansi.Str): fansi.Str = { + fansi.Color.Green(s) } - def printSymString(cfg: Expr[TPrintColors], s: String) = + def printSymString(s: String) = if (s.toString.startsWith("_$")) "_" else s.toString.stripSuffix(".type") - def printBounds(cfg: Expr[TPrintColors])(lo: TypeRepr, hi: TypeRepr) = { + def printBounds(lo: TypeRepr, hi: TypeRepr): fansi.Str = { val loTree = - if (lo =:= TypeRepr.of[Nothing]) None else Some(Expr(" >: ") + rec0(cfg)(lo) ) + if (lo =:= TypeRepr.of[Nothing]) None else Some(fansi.Str(" >: ") ++ rec(lo) ) val hiTree = - if (hi =:= TypeRepr.of[Any]) None else Some(Expr(" <: ") + rec0(cfg)(hi) ) - val underscore = Expr("_") - loTree.orElse(hiTree).map(underscore + _).getOrElse(underscore) + if (hi =:= TypeRepr.of[Any]) None else Some(fansi.Str(" <: ") ++ rec(hi) ) + val underscore = fansi.Str("_") + loTree.orElse(hiTree).map(underscore ++ _).getOrElse(underscore) } - def printSym(cfg: Expr[TPrintColors], s: String): Expr[String] = { - val expr = Expr(s) - literalColor(cfg, '{ fansi.Str($expr) }) - } + def printSym(s: String): fansi.Str = literalColor(fansi.Str(s)) //TODO: We don't currently use this method - def prefixFor(cfg: Expr[TPrintColors])(pre: TypeTree, sym: String): Expr[String] = { + def prefixFor(pre: TypeTree, sym: String): fansi.Str = { // Depending on what the prefix is, you may use `#`, `.` // or even need to wrap the prefix in parentheses val sep = pre match { case x if x.toString.endsWith(".type") => - rec0(cfg)(pre.tpe) + Expr(".") + rec(pre.tpe) ++ "." } - sep + printSym(cfg, sym) + sep ++ printSym(sym) } - def printArgs(cfg: Expr[TPrintColors])(args: List[TypeRepr]): Expr[String] = { - val added = args.map { - case TypeBounds(lo, hi) => - printBounds(cfg)(lo, hi) - case tpe: TypeRepr => - rec0(cfg)(tpe, false) - }.mkStringExpr(", ") - Expr("[") + added + Expr("]") + def printArgs(args: List[TypeRepr]): fansi.Str = { + fansi.Str("[") ++ printArgs0(args) ++ "]" + } + + def printArgs0(args: List[TypeRepr]): fansi.Str = { + val added = fansi.Str.join( + args.map { + case TypeBounds(lo, hi) => + printBounds(lo, hi) + case tpe: TypeRepr => + rec(tpe, false) + }.flatMap(Seq(_, fansi.Str(", "))).dropRight(1):_* + ) + added } @@ -85,32 +81,49 @@ object TPrintLowPri{ } } - def rec0(cfg: Expr[TPrintColors])(tpe: TypeRepr, end: Boolean = false): Expr[String] = tpe match { + def rec(tpe: TypeRepr, end: Boolean = false): fansi.Str = rec0(tpe)._1 + def rec0(tpe: TypeRepr, end: Boolean = false): (fansi.Str, WrapType) = tpe match { case TypeRef(NoPrefix(), sym) => - printSym(cfg, sym) + (printSym(sym), WrapType.NoWrap) // TODO: Add prefix handling back in once it works! case TypeRef(_, sym) => - printSym(cfg, sym) + (printSym(sym), WrapType.NoWrap) case AppliedType(tpe, args) => - printSym(cfg, tpe.typeSymbol.name) + printArgs(cfg)(args) + if (functionTypes.contains(tpe.typeSymbol.fullName)) { + ( + if(args.size == 1 ) fansi.Str("() => ") ++ rec(args.last) + else{ + val (left, wrap) = rec0(args(0)) + if(args.size == 2 && wrap == WrapType.NoWrap){ + left ++ fansi.Str(" => ") ++ rec(args.last) + } + else fansi.Str("(") ++ printArgs0(args.dropRight(1)) ++ fansi.Str(") => ") ++ rec(args.last) + + }, + WrapType.Infix + ) + + } else if (tupleTypes.contains(tpe.typeSymbol.fullName)) + (fansi.Str("(") ++ printArgs0(args) ++ fansi.Str(")"), WrapType.Tuple) + else (printSym(tpe.typeSymbol.name) ++ printArgs(args), WrapType.NoWrap) case RefinedType(tpe, refinements) => - val pre = rec0(cfg)(tpe) - lazy val defs = refinements.collect { - case (name, tpe: TypeRepr) => - Expr("type " + name + " = ") + rec0(cfg)(tpe) - case (name, TypeBounds(lo, hi)) => - Expr("type " + name) + printBounds(cfg)(lo, hi) + rec0(cfg)(tpe) - }.mkStringExpr("; ") - pre + (if(refinements.isEmpty) '{ "" } else Expr("{") + defs + Expr("}")) + val pre = rec(tpe) + lazy val defs = fansi.Str.join( + refinements.collect { + case (name, tpe: TypeRepr) => + fansi.Str("type " + name + " = ") ++ rec(tpe) + case (name, TypeBounds(lo, hi)) => + fansi.Str("type " + name) ++ printBounds(lo, hi) ++ rec(tpe) + }.flatMap(Seq(_, fansi.Str("; "))).dropRight(1):_* + ) + (pre ++ (if(refinements.isEmpty) fansi.Str("") else fansi.Str("{") ++ defs ++ "}"), WrapType.NoWrap) case AnnotatedType(parent, annot) => - rec0(cfg)(parent, end) + (rec(parent, end), WrapType.NoWrap) case _ => - Expr(Type.show[T]) - } - '{ - new TPrint[T] { - final def render(implicit cfg: TPrintColors): String = ${ rec0('cfg)(TypeRepr.of[T]) } - } + (fansi.Str(Type.show[T]), WrapType.NoWrap) } + val value: fansi.Str = rec(TypeRepr.of[T]) + + '{TPrint.recolor(fansi.Str(${Expr(value.render)}))} } } diff --git a/pprint/src/pprint/TPrint.scala b/pprint/src/pprint/TPrint.scala index 3b4968a..80f6d60 100644 --- a/pprint/src/pprint/TPrint.scala +++ b/pprint/src/pprint/TPrint.scala @@ -9,21 +9,27 @@ package pprint * - Prefixed Types are printed un-qualified, according to * what's currently in scope */ -trait TPrint[T]{ - def render(implicit cfg: TPrintColors): String +trait TPrint[T] { + def render(implicit tpc: TPrintColors): fansi.Str + } -object TPrint extends TPrintGen[TPrint, TPrintColors] with TPrintLowPri{ - def literal[T](s: String) = new TPrint[T]{ - def render(implicit cfg: TPrintColors) = cfg.typeColor(s).toString - } - def lambda[T](f: TPrintColors => String) = new TPrint[T]{ - def render(implicit cfg: TPrintColors) = f(cfg) +object TPrint extends TPrintLowPri{ + def recolor[T](s: fansi.Str): TPrint[T] = { + new TPrint[T]{ + def render(implicit tpc: TPrintColors) = { + val colors = s.getColors + val updatedColors = colors.map{ + c => if (c == fansi.Color.Green.applyMask) tpc.typeColor.applyMask else 0L + } + fansi.Str.fromArrays(s.getChars, updatedColors) + } + } } - def make[T](f: TPrintColors => String) = TPrint.lambda[T](f) - def get[T](cfg: TPrintColors)(implicit t: TPrint[T]) = t.render(cfg) def implicitly[T](implicit t: TPrint[T]): TPrint[T] = t - implicit val NothingTPrint: TPrint[Nothing] = TPrint.literal("Nothing") + implicit val NothingTPrint: TPrint[Nothing] = + recolor[Nothing](fansi.Color.Green("Nothing")) + } case class TPrintColors(typeColor: fansi.Attrs) diff --git a/pprint/src/pprint/package.scala b/pprint/src/pprint/package.scala index fd56501..47205f5 100644 --- a/pprint/src/pprint/package.scala +++ b/pprint/src/pprint/package.scala @@ -6,6 +6,6 @@ */ package object pprint extends PPrinter{ def tprint[T: TPrint](implicit config: TPrintColors) = { - implicitly[TPrint[T]].render(config) + implicitly[TPrint[T]].render } } diff --git a/pprint/test/src-2/test/pprint/TPrintTests.scala b/pprint/test/src-2/test/pprint/TPrintTests.scala index 70c094e..600e173 100644 --- a/pprint/test/src-2/test/pprint/TPrintTests.scala +++ b/pprint/test/src-2/test/pprint/TPrintTests.scala @@ -1,4 +1,4 @@ -package pprint +package test.pprint import pprint.TPrint import utest._ @@ -11,15 +11,15 @@ object TPrintTests extends TestSuite{ // type X = scala.Int with scala.Predef.String{} val x = "" - test("plain"){ - def checkVal[T](expected: String, expr: => T)(implicit tprint: TPrint[T]) = { - check[T](expected)(tprint) - } + def checkVal[T](expected: String, expr: => T)(implicit tprint: TPrint[T]) = { + check[T](expected)(tprint) + } - def check[T](expected: String*)(implicit tprint: TPrint[T]) = { - val tprinted = tprint.render - assert(expected.contains(tprinted)) - } + def check[T](expected: String*)(implicit tprint: TPrint[T]) = { + val tprinted = tprint.render.render + assert(expected.contains(tprinted)) + } + test("plain"){ test("simple"){ check[X]("X") check[String]("String") @@ -139,25 +139,11 @@ object TPrintTests extends TestSuite{ } test("annotated"){ // Can't use the normal implicit method, because of SI-8079 - assert(TPrint.default[M@deprecated].render == "M @deprecated") + val rendered = TPrint.default[M@deprecated].render + assert(rendered.toString() == "M @deprecated") } class Custom - test("custom"){ - // Maybe we want to add some extra decoration - implicit def customTPrint: TPrint[Custom] = TPrint.lambda(cfg => "+++Custom+++") - check[Custom]("+++Custom+++") - check[List[Custom]]("List[+++Custom+++]") - - // Or make it look like F# - implicit def StreamTPrint[T: TPrint]: TPrint[Stream[T]] = TPrint.lambda( - c => implicitly[TPrint[T]].render(c) + " Stream" - ) - check[Stream[Int]]("Int Stream") - - // Note how it works recursively - check[Stream[Custom]]("+++Custom+++ Stream") - } test("complex"){ class A @@ -166,8 +152,7 @@ object TPrintTests extends TestSuite{ } check[(A with B)#C]("(A with B)#C") check[({type T = Int})#T]("Int") - implicit def customTPrint: TPrint[Custom] = TPrint.lambda(cfg => "+++Custom+++") - check[(Custom with B)#C]("(+++Custom+++ with B)#C") + check[(Custom with B)#C]("(Custom with B)#C") } test("higherKinded"){ @@ -175,9 +160,9 @@ object TPrintTests extends TestSuite{ check[C[List]]("C[List]") } test("byName"){ - check[(=> Int) => Double]("Function1[=> Int, Double]") + check[(=> Int) => Double]("(=> Int) => Double") check[(=> Int, String, => (=> Char) => Float) => Double]( - "Function3[=> Int, String, => Function1[=> Char, Float], Double]" + "(=> Int, String, => (=> Char) => Float) => Double" ) } test("range"){ @@ -190,7 +175,7 @@ object TPrintTests extends TestSuite{ test("colored"){ import pprint.TPrintColors.Colors._ def checkColor[T](expected: String)(implicit tprint: TPrint[T]) = { - val tprinted = tprint.render.replace( + val tprinted = tprint.render.toString.replace( fansi.Color.Green.escape, "<" ).replace( fansi.Color.Reset.escape, ">" @@ -214,6 +199,41 @@ object TPrintTests extends TestSuite{ "}" ) } - } + test("functionGrouping"){ + test("simple")( + check[(Int => Option[Int]) => Int]("(Int => Option[Int]) => Int") + ) + test("lazy")( + check[() => (Int => Option[Int])]("() => Int => Option[Int]") + ) + test("complex")( + check[ + (Int => Option[Int]) => + ((Int => String) => Int) => + (Int => Int) + ]("(Int => Option[Int]) => ((Int => String) => Int) => Int => Int") + ) + } + test("wildcards"){ + case class MyList[T <: String]() + check[MyList[_]]("MyList[_]") + } + test("weirdImplicits"){ + trait Lower { + implicit def monad[M[_],T](i: T): M[T] = ??? + implicit def preventNothing[T](i: T): Nothing = ??? + } + object Higher extends Lower{ + implicit def value[M[_],T](l: M[T]): T = ??? + } + import Higher._ + + check[Int]("Int") + } + test("nothingWithWeirdImport"){ + import scala.reflect.runtime.universe._ + check[Nothing]("Nothing") + } + } } diff --git a/pprint/test/src-3/test/src/pprint/TPrintTests.scala b/pprint/test/src-3/test/src/pprint/TPrintTests.scala index 61e3c60..79e5592 100644 --- a/pprint/test/src-3/test/src/pprint/TPrintTests.scala +++ b/pprint/test/src-3/test/src/pprint/TPrintTests.scala @@ -19,12 +19,12 @@ object TPrintTests extends TestSuite{ def check[T](expected: String*)(implicit tprint: TPrint[T]) = { val tprinted = tprint.render - assert(expected.contains(tprinted)) + assert(expected.contains(tprinted.render)) } test("simple"){ -// check[X]("X") + // check[X]("X") check[String]("String") check[java.lang.String]("String") check[Int]("Int") @@ -39,39 +39,39 @@ object TPrintTests extends TestSuite{ } test("nothing"){ - check[Nothing]("Nothing") + test - check[Nothing]("Nothing") //Inferred nothings behave weirdly, make sure it works! - check("Nothing") - checkVal("Nothing", throw new Exception()) - checkVal("Some[Nothing]", Some(???)) + test - check("Nothing") + test - checkVal("Nothing", throw new Exception()) + test - checkVal("Some[Nothing]", Some(???)) } test("singleton"){ - check[x.type]("x.type") - check[TPrintTests.this.M]("M") - // check[TPrintTests.type]("TPrintTests.type") + check[x.type]("x.type") + check[TPrintTests.this.M]("M") + // check[TPrintTests.type]("TPrintTests.type") } test("java"){ //check[java.util.Set[_]]("java.util.Set[_]") - // check[java.util.Set[_ <: Int]]("java.util.Set[_]") - // check[java.util.Set[_ <: String]]("java.util.Set[_] forSome { type _ <: String }") - // check[java.util.Set[_ <: String]]("java.util.Set[_] forSome { type _ <: String }") -// check[java.util.Set[String]]("java.util.Set[String]") + // check[java.util.Set[_ <: Int]]("java.util.Set[_]") + // check[java.util.Set[_ <: String]]("java.util.Set[_] forSome { type _ <: String }") + // check[java.util.Set[_ <: String]]("java.util.Set[_] forSome { type _ <: String }") + // check[java.util.Set[String]]("java.util.Set[String]") } test("mutable"){ - // check[collection.mutable.Buffer[Int]]("collection.mutable.Buffer[Int]") - import collection.mutable -// TPrint.default[mutable.Buffer[Int]] - //check[mutable.Buffer[Int]]("mutable.Buffer[Int]") - check[Seq[Int]]("Seq[Int]") + // check[collection.mutable.Buffer[Int]]("collection.mutable.Buffer[Int]") + import collection.mutable + // TPrint.default[mutable.Buffer[Int]] + //check[mutable.Buffer[Int]]("mutable.Buffer[Int]") + check[Seq[Int]]("Seq[Int]") - // can't use scala.util.Properties on Scala.JS - //val is213Plus = classOf[Seq[Int]].getName != "scala.collection.Seq" - // check[collection.Seq[Int]](if (is213Plus) "collection.Seq[Int]" else "Seq[Int]") - // check[collection.immutable.Seq[Int]](if (is213Plus) "Seq[Int]" else "collection.immutable.Seq[Int]") + // can't use scala.util.Properties on Scala.JS + //val is213Plus = classOf[Seq[Int]].getName != "scala.collection.Seq" + // check[collection.Seq[Int]](if (is213Plus) "collection.Seq[Int]" else "Seq[Int]") + // check[collection.immutable.Seq[Int]](if (is213Plus) "Seq[Int]" else "collection.immutable.Seq[Int]") } test("compound"){ @@ -84,7 +84,7 @@ object TPrintTests extends TestSuite{ check[(Int, Float) => (String, String)]("(Int, Float) => (String, String)") check[(Int, String)]("(Int, String)") check[(Int, String, (Int, String), Double)]("(Int, String, (Int, String), Double)") - //check[Int {val x: Int}]("Int{val x: Int}") + //check[Int {val x: Int}]("Int{val x: Int}") //check[Int & String]("Int with String") } test("existential") { @@ -92,50 +92,33 @@ object TPrintTests extends TestSuite{ // check[{type T = Int}]("{type T = Int}") check[Map[_, _]]("Map[_, _]") - } + } - // test("thisType"){ - // class T { - // check[T.this.type]("T.this.type") - // } - // new T() - // } - // test("annotated"){ - // // Can't use the normal implicit method, because of SI-8079 - // assert(TPrint.default[M@deprecated].render == "M @deprecated") - // } + // test("thisType"){ + // class T { + // check[T.this.type]("T.this.type") + // } + // new T() + // } + // test("annotated"){ + // // Can't use the normal implicit method, because of SI-8079 + // assert(TPrint.default[M@deprecated].render == "M @deprecated") + // } class Custom - test("custom"){ - // Maybe we want to add some extra decoration - implicit def customTPrint: TPrint[Custom] = TPrint.lambda(cfg => "+++Custom+++") - check[Custom]("+++Custom+++") - - //TODO: double check implicit - //check[List[Custom]]("List[+++Custom+++]") - - // Or make it look like F# - implicit def StreamTPrint[T: TPrint]: TPrint[Stream[T]] = TPrint.lambda( - c => implicitly[TPrint[T]].render(c) + " Stream" - ) - check[Stream[Int]]("Int Stream") - - // Note how it works recursively - check[Stream[Custom]]("+++Custom+++ Stream") - } - // test("complex"){ - // class A - // class B{ - // class C - // } - // check[(A with B)#C]("(A with B)#C") - // check[({type T = Int})#T]("Int") - // implicit def customTPrint: TPrint[Custom] = TPrint.lambda(cfg => "+++Custom+++") - // check[(Custom with B)#C]("(+++Custom+++ with B)#C") - - // } + // test("complex"){ + // class A + // class B{ + // class C + // } + // check[(A with B)#C]("(A with B)#C") + // check[({type T = Int})#T]("Int") + // implicit def customTPrint: TPrint[Custom] = TPrint.lambda(cfg => "+++Custom+++") + // check[(Custom with B)#C]("(+++Custom+++ with B)#C") + + // } test("higherKinded"){ //TODO: No idea why this breaks @@ -148,39 +131,39 @@ object TPrintTests extends TestSuite{ // "Function3[=> Int, String, => Function1[=> Char, Float], Double]" // ) // } - // test("range"){ - // check[Range]("Range") - // checkVal("Range.Inclusive", 0 to 10) - // checkVal("Range", 0 until 10) - // check[Range.Inclusive]("Range.Inclusive") - // } - // } - // test("colored"){ - // import pprint.TPrintColors.Colors._ - // def checkColor[T](expected: String)(implicit tprint: TPrint[T]) = { - // val tprinted = tprint.render.replace( - // fansi.Color.Green.escape, "<" - // ).replace( - // fansi.Color.Reset.escape, ">" - // ) - // assert(tprinted == expected) - // } - - // test - checkColor[String]("") - // test - checkColor[Map[Int, String]]("[, ]") - // test - checkColor[collection.mutable.Seq[Int]]("..[]") - // // Not going to bother coloring these for now, since they're quite uncommon - // // test - checkColor[{type T = Int; val x: String; def y: Char; var z: Double}]( - // // "{type = ; val : ; def : ; var : }" - // // ) - // // test - checkColor[Map[K, V] forSome { - // // type K <: Int; val x: Float; type V >: (String, Float with Double) - // // }]( - // // "[, ] forSome { " + - // // "type <: ; val : ; " + - // // "type >: (, with ) " + - // // "}" - // // ) + // test("range"){ + // check[Range]("Range") + // checkVal("Range.Inclusive", 0 to 10) + // checkVal("Range", 0 until 10) + // check[Range.Inclusive]("Range.Inclusive") + // } + // } + // test("colored"){ + // import pprint.TPrintColors.Colors._ + // def checkColor[T](expected: String)(implicit tprint: TPrint[T]) = { + // val tprinted = tprint.render.replace( + // fansi.Color.Green.escape, "<" + // ).replace( + // fansi.Color.Reset.escape, ">" + // ) + // assert(tprinted == expected) + // } + + // test - checkColor[String]("") + // test - checkColor[Map[Int, String]]("[, ]") + // test - checkColor[collection.mutable.Seq[Int]]("..[]") + // // Not going to bother coloring these for now, since they're quite uncommon + // // test - checkColor[{type T = Int; val x: String; def y: Char; var z: Double}]( + // // "{type = ; val : ; def : ; var : }" + // // ) + // // test - checkColor[Map[K, V] forSome { + // // type K <: Int; val x: Float; type V >: (String, Float with Double) + // // }]( + // // "[, ] forSome { " + + // // "type <: ; val : ; " + + // // "type >: (, with ) " + + // // "}" + // // ) } }