in scala/tasty-reader/src/TreePrinter.scala [284:396]
private def textOfTemplate(sb: StringBuilder, indent: String, node: Node, definition: Option[Node]): Unit = {
val children = node.children
val primaryConstructor = children.find(it => it.is(DEFDEF) && it.names == Seq("<init>"))
val isInEnum = definition.exists(_.contains(ENUM))
val isInCaseClass = !isInEnum && definition.exists(_.contains(CASE))
def textOf(tpe: Node): String = textOfType(tpe, parens = 1)
val blockChildren = children match {
case Seq(Node3(BLOCK, _, children), _*) => children
case _ => children
}
// Recursive textOf method, common syntactic sugar for FunctionN and TupleN?
val parents = blockChildren.collect { // Rely on name kind?
case node if node.isTypeTree => textOf(node)
case Node3(APPLY, _, Seq(Node3(SELECTin, _, Seq(Node3(NEW, _, Seq(tpe, _*)), _*)), _*)) => textOf(tpe)
case Node3(APPLY, _, Seq(Node3(APPLY, _, Seq(Node3(SELECTin, _, Seq(Node3(NEW, _, Seq(tpe, _*)), _*)), _*)), _*)) => textOf(tpe)
case Node3(APPLY, _, Seq(Node3(TYPEAPPLY, _, Seq(Node3(SELECTin, _, Seq(Node3(NEW, _, Seq(base @ Node1(IDENTtpt), _*)), _*)), arguments*)), _*)) =>
simple(textOfType(base)) + arguments.map(t => simple(textOfType(t))).mkString("[", ", ", "]")
case Node3(APPLY, _, Seq(Node3(TYPEAPPLY, _, Seq(Node3(SELECTin, _, Seq(Node3(NEW, _, Seq(tpe, _*)), _*)), _*)), _*)) => textOf(tpe)
case Node3(APPLY, _, Seq(Node3(APPLY, _, Seq(Node3(TYPEAPPLY, _, Seq(Node3(SELECTin, _, Seq(Node3(NEW, _, Seq(tpe, _*)), _*)), _*)), _*)), _*)) => textOf(tpe)
}.filter(s => s.nonEmpty && s != "_root_.java.lang.Object" && s != "_root_.scala.runtime.EnumValue" &&
!(isInCaseClass && CommonQualifiedNames.isProductOrScalaSerializableCanonical(s)))
.map(simple)
val isInGiven = definition.exists(it => isGivenObject0(it) || isGivenClass0(it))
val isInAnonymousGiven = isInGiven && definition.exists(_.name.startsWith("given_")) // Common method?
val previousLength = sb.length
primaryConstructor.foreach { constructor =>
val sb1 = new StringBuilder() // Reuse?
val hasParameters = node.children.exists(_.is(PARAM))
val hasModifiers = constructor.contains(PRIVATE) || constructor.contains(PROTECTED) || constructor.contains(PRIVATEqualified) || constructor.contains(PROTECTEDqualified)
textOfAnnotationIn(sb1, "", constructor, " ", parens = hasParameters && !hasModifiers)
modifiersIn(sb1, constructor)
val modifiers = if (sb1.nonEmpty) " " + sb1.toString else ""
parametersIn(sb, constructor, Some(node), definition, modifiers = _ ++= modifiers)
}
val hasParameters = sb.length > previousLength
if (isInGiven && (!isInAnonymousGiven || hasParameters)) {
sb ++= ": "
}
if (isInGiven) {
sb ++= (if (parents.nonEmpty) parents.mkString(" with ") else "{}")
sb ++= " with"
} else {
// Enum Enum[+A] { case Case extends Enum[Nothing] } ?
// Enum Enum[-A] { case Case extends Enum[Any] } ?
if (parents.nonEmpty && !(parents.length == 1 && !parents.head.endsWith("]") && (definition.isEmpty || definition.exists(it => it.contains(ENUM) && it.contains(CASE))))) {
sb ++= " extends " + parents.mkString(if (legacySyntax) " with " else ", ")
}
val derived = definition match {
case Some(node) => node.nextSiblings.take(2).toSeq match {
case Seq(Node2(VALDEF, Seq(name1)), cobj @ Node3(TYPEDEF, Seq(name2), _)) if name2 == name1 + "$" && node.name == name1 => cobj.firstChild.children.collect {
case Node3(VALDEF, Seq(name), Seq(Node3(APPLIEDtpt | APPLIEDtype, _, Seq(tc, _*)), _*)) if name.startsWith("derived$") => textOfType(tc)
}
case _ => Seq.empty
}
case _ => Seq.empty
}
if (derived.nonEmpty) {
sb ++= " derives " + derived.mkString(", ")
}
}
val selfType = children.find(_.is(SELFDEF)) match {
// Is there a more reliable way to determine whether self type refers to the same type definition?
case Some(Node3(SELFDEF, Seq(name), Seq(tail))) if !definition.exists(_.contains(OBJECT)) &&
definition.forall(it => !tail.refName.contains(it.name) && !tail.children.headOption.exists(_.refName.contains(it.name))) =>
val isWith = (tail.is(APPLIEDtpt) || tail.is(APPLIEDtype)) && !tail.firstChild.is(IDENTtpt) && textOfType(tail.firstChild) == "_root_.scala.&"
" " + (if (name == "_") "this" else name) + ": " + simple(textOfType(tail, parens = if (isWith) 0 else 1)) + " =>"
case _ => ""
}
val members = {
val cases =
if (isInEnum) {
def casesIn(pair: (Node, Node), name: String): Option[Seq[Node]] = Some(pair).collect {
case (Node2(VALDEF, Seq(name1)), Node3(TYPEDEF, Seq(name2), Seq(Node3(TEMPLATE, _, children), _*))) if name1 == name && name2 == name + "$" =>
children.filter(_.is(VALDEF, TYPEDEF))
}
val name = definition.get.name
def nextPair = definition.get.nextSibling.flatMap(n => n.nextSibling.map((n, _)))
def previousPair = definition.get.prevSibling.flatMap(n => n.prevSibling.map((_, n)))
nextPair.flatMap(casesIn(_, name)).orElse(previousPair.flatMap(casesIn(_, name))).getOrElse(Seq.empty)
} else {
Seq.empty
}
children.filter(it => it.is(DEFDEF, VALDEF, TYPEDEF) && !primaryConstructor.contains(it)) ++ cases // Type member?
}
if (selfType.nonEmpty || members.nonEmpty) {
sb ++= " {"
sb ++= selfType
sb ++= "\n"
val previousLength = sb.length
var delimiterRequired = false
currentExtension = None
members.foreach { member =>
val previousLength = sb.length
textOfMember(sb, indent + Indent, member, definition, if (delimiterRequired) "\n\n" else "")
delimiterRequired = delimiterRequired || sb.length > previousLength
}
if (selfType.nonEmpty || sb.length > previousLength) {
if (sb.length > previousLength) {
sb ++= "\n"
}
sb ++= indent
sb ++= "}"
} else {
sb.delete(previousLength - 3, previousLength)
}
} else {
if (isInGiven) {
sb ++= " {}"
}
}
}