private def textOfTemplate()

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 ++= " {}"
      }
    }
  }