<commit_before>package scala.meta
package syntactic.show

import scala.meta.Aux._
import org.scalameta.show.Show
import Show.{ sequence => s, repeat => r, indent => i, newline => n, meta => m, adorn => a }
import scala.meta.syntactic.parsers.SyntacticInfo._
import scala.meta.semantic._
import scala.{Seq => _}
import scala.collection.immutable.Seq
import internal._

// TODO: fix occasional incorrectness when semicolons are omitted
// TODO: needs way more parens, esp in types and patterns
// TODO: soft wrapping
// TODO: one mega instance for tree isn't nice, maybe separate instances for leafs and inferred instances for branches
// TODO: strings and string interpolation needs to be smarter wrt " vs """

trait Code[T] extends Show[T]
object Code {
  def apply[T](f: T => Show.Result): Code[T] = new Code[T] { def apply(input: T) = f(input) }

  def templ(templ: Template) =
    // TODO: consider XXX.isEmpty
    if (templ.early.isEmpty && templ.parents.isEmpty && templ.self.name.isEmpty && templ.self.decltpe.isEmpty && templ.stats.isEmpty) s()
    else if (templ.parents.nonEmpty || templ.early.nonEmpty) s(" extends ", templ)
    else s(" ", templ)

  def p(oo: String, t: Tree, left: Boolean = false, right: Boolean = false) = {
    def needsParens(oo: String, io: String): Boolean = {
      implicit class MySyntacticInfo(name: String) {
        private def special = Set(".", "()", "=", ":")
        def myprecedence: Int = name match {
          case "." => 100
          case "()" => 100
          case "=" => 0
          case ":" => -1
          case _ if name.myunary => 99
          case _ => Term.Name(name).precedence
        }
        def myinfix: Boolean = !special.contains(name) && !name.myunary
        def myleftassoc: Boolean = name.last != ':'
        def myunary: Boolean = name.startsWith("unary_")
      }
      val (op, ip) = (oo.myprecedence, io.myprecedence)
      val (oi, ii) = (oo.myinfix, io.myinfix)
      val (oa, ia) = (oo.myleftassoc, io.myleftassoc)
      val (ou, iu) = (oo.myunary, io.myunary)
      val result = {
        if (ou && iu) true
        else if (oi && ii && (oa ^ ia)) true
        else if (oi && ii && (oo == io && left != oa && left != right)) true
        else op > ip
      }
      // println((oo, io, left, right) + " => " + (op, ip, oi, ii, oa, ia, ou, iu) + " => " + result)
      result
    }
    s(t) match {
      case Show.Meta(io: String, res) if needsParens(oo, io) => s("(", res, ")")
      case res => res
    }
  }

  // Branches
  // TODO: this match is not exhaustive: if I remove Mod.Package, then I get no warning
  implicit def codeTree[T <: Tree]: Code[T] = Code { x => (x: Tree) match {
    case t: Name => if (t.isBackquoted) s("`", t.value, "`") else s(t.value)

    // Param.Type
    case t: Param.Type.Repeated => s(t.tpe, "*")
    case t: Param.Type.ByName   => s("=> ", t.tpe)

    // Type
    case t: Type.Project     => s(t.qual, "#", t.selector)
    case t: Type.Select      => s(t.qual, ".", t.selector)
    case t: Type.Singleton   => s(t.ref, ".type")
    case t: Type.Annotate    => s(t.tpe, " ", t.mods)
    case t: Type.Apply       => s(t.tpe, "[", r(t.args, ", "), "]")
    case t: Type.ApplyInfix  => s(t.lhs, " ", t.op, " ", t.rhs)
    case t: Type.Compound    => s(r(t.tpes, " with "), a(" {", a(" ", r(t.refinement, "; "), " "), "}", t.hasRefinement))
    case t: Type.Existential => s(t.tpe, " forSome { ", r(t.quants, "; "), " }")
    case t: Type.Placeholder => s("_", t.bounds)
    case t: Type.Tuple       => s("(", r(t.elements, ", "), ")")
    case t: Type.Function    =>
      val pparams = if (t.params.size == 1) s(t.params.head) else s("(", r(t.params, ", "), ")")
      s(pparams, " => ", t.res)

    // Lit
    case t: Lit.Bool     => s(t.value.toString)
    case t: Lit.Int      => s(t.value.toString)
    case t: Lit.Long     => s(t.value.toString + "l")
    case t: Lit.Float    => s(t.value.toString + "f")
    case t: Lit.Double   => s(t.value.toString + "d")
    case t: Lit.Char     => s(enquote(t.value.toString, SingleQuotes))
    case t: Lit.String   => s(enquote(t.value.toString, if (t.value.contains("\n")) TripleQuotes else DoubleQuotes))
    case t: Lit.Symbol   => s("'", t.value.name)
    case _: Lit.Null     => s("null")
    case _: Lit.Unit     => s("()")

    // Term
    case t: Term.This     => s(t.qual.map { qual => s(qual, ".") }.getOrElse(s()), "this")
    case t: Term.Select   => s(p(".", t.qual), if (t.isPostfix) " " else ".", t.selector)
    case t: Term.Assign   => m("=", s(p("=", t.lhs), " = ", t.rhs))
    case t: Term.Update   => m("=", s(p("=", t.lhs), " = ", t.rhs))
    case t: Term.Return   => s("return", if (t.hasExpr) s(" ", t.expr) else s())
    case t: Term.Throw    => s("throw ", t.expr)
    case t: Term.Ascribe  => m(":", s(t.expr, ": ", t.tpe))
    case t: Term.Annotate => m(":", s(t.expr, ": ", t.mods))
    case t: Term.Tuple    => s("(", r(t.elements, ", "), ")")
    case t: Term.Block    =>
      import Term.{Block, Function}
      def pstats(s: Seq[Stmt.Block]) = r(s.map(i(_)), "")
      t match {
        case Block(Function(Param.Named(mods, name, tptopt, _) :: Nil, Block(stats)) :: Nil) if mods.exists(_.isInstanceOf[Mod.Implicit]) =>
          s("{ implicit ", name, tptopt.map { tpt => s(": ", tpt) }.getOrElse(s()), " => ", pstats(stats), n("}"))
        case Block(Function(Param.Named(mods, name, None, _) :: Nil, Block(stats)) :: Nil) =>
          s("{ ", name, " => ", pstats(stats), n("}"))
        case Block(Function(Param.Anonymous(_, _) :: Nil, Block(stats)) :: Nil) =>
          s("{ _ => ", pstats(stats), n("}"))
        case Block(Function(params, Block(stats)) :: Nil) =>
          s("{ (", r(params, ", "), ") => ", pstats(stats), n("}"))
        case _ =>
          if (t.stats.isEmpty) s("{}") else s("{", pstats(t.stats), n("}"))
      }
    case t: Term.Cases       => s("{", r(t.cases.map(i(_)), ""), n("}"))
    case t: Term.While       => s("while (", t.expr, ") ", t.body)
    case t: Term.Do          => s("do ", t.body, " while (", t.expr, ")")
    case t: Term.For         => s("for (", r(t.enums, "; "), ") ", t.body)
    case t: Term.ForYield    => s("for (", r(t.enums, "; "), ") yield ", t.body)
    case t: Term.New         => s("new ", t.templ)
    case _: Term.Placeholder => s("_")
    case t: Term.Eta         => s(t.term, " _")
    case t: Term.Match       => s(t.scrut, " match ", t.cases)
    case t: Term.Apply       => m("()", s(p("()", t.fun), t.args))
    case t: Term.ApplyType   => s(t.fun, t.targs)
    case t: Term.ApplyUnary  => m("unary_" + t.op.value, s(t.op, p("unary_" + t.op.value, t.arg)))
    case t: Term.ApplyInfix  =>
      m(t.op.value, s(p(t.op.value, t.lhs, left = true), " ", t.op, t.targs, " ", t.args match {
        case (arg: Term) :: Nil => s(p(t.op.value, arg, right = true))
        case args               => s(args)
      }))
    case t: Term.Try      =>
      s("try ", t.expr,
        t.catchp.map { catchp => s(" catch ", catchp) }.getOrElse(s()),
        t.finallyp.map { finallyp => s(" finally ", finallyp) }.getOrElse(s()))
    case t: Term.If       => s("if (", t.cond, ") ", t.thenp, if (t.hasElsep) s(" else ", t.elsep) else s())
    case t: Term.Function =>
      t match {
        case Term.Function(Param.Named(mods, name, tptopt, _) :: Nil, body) if mods.exists(_.isInstanceOf[Mod.Implicit]) =>
          s("implicit ", name, tptopt.map { tpt => s(": ", tpt) }.getOrElse(s()), " => ", body)
        case Term.Function(Param.Named(mods, name, None, _) :: Nil, body) =>
          s(name, " => ", body)
        case Term.Function(Param.Anonymous(_, _) :: Nil, body) =>
          s("_ => ", body)
        case Term.Function(params, body) =>
          s("(", r(params, ", "), ") => ", body)
      }
    case t: Term.Interpolate =>
      val zipped = t.parts.zip(t.args).map {
        case (part, id: Name) if !id.isBackquoted => s(part.value, "$", id.value)
        case (part, arg)                          => s(part.value, "${", arg, "}")
      }
      val quote = if (t.parts.map(_.value).exists(s => s.contains("\n") || s.contains("\""))) "\"\"\"" else "\""
      s(t.prefix, quote, r(zipped), t.parts.last.value, quote)

    // Pat
    case t: Pat.Alternative  => s(t.lhs, " | ", t.rhs)
    case t: Pat.Bind         => s(t.lhs, " @ ", t.rhs)
    case t: Pat.Tuple        => s(t.elements)
    case _: Pat.SeqWildcard  => s("_*")
    case t: Pat.Typed        => m(":", s(t.lhs, ": ", t.rhs))
    case _: Pat.Wildcard     => s("_")
    case t: Pat.Extract      => m("()", s(t.ref, t.targs, t.elements))
    case t: Pat.ExtractInfix =>
      m(t.ref.value, s(p(t.ref.value, t.lhs, left = true), " ", t.ref, " ", t.rhs match {
        case pat :: Nil => s(p(t.ref.value, pat, right = true))
        case pats       => s(pats)
      }))
    case t: Pat.Interpolate  =>
      val zipped = t.parts.zip(t.args).map {
        case (part, id: Name) if !id.isBackquoted => s(part, "$", id.value)
        case (part, arg)                          => s(part, "${", arg, "}")
      }
      s(t.prefix, "\"", r(zipped), t.parts.last, "\"")

    // Arg
    case t: Arg.Named    => s(t.name, " = ", t.rhs)
    case t: Arg.Repeated => s(t.arg, ": _*")

    // Mod<commit_msg>plumbs parenthesization of non-operator expressions<commit_after>package scala.meta
package syntactic.show

import scala.meta.Aux._
import org.scalameta.show.Show
import Show.{ sequence => s, repeat => r, indent => i, newline => n, meta => m, adorn => a }
import scala.meta.syntactic.parsers.SyntacticInfo._
import scala.meta.semantic._
import scala.{Seq => _}
import scala.collection.immutable.Seq
import internal._

// TODO: fix occasional incorrectness when semicolons are omitted
// TODO: needs way more parens, esp in types and patterns
// TODO: soft wrapping
// TODO: one mega instance for tree isn't nice, maybe separate instances for leafs and inferred instances for branches
// TODO: strings and string interpolation needs to be smarter wrt " vs """

trait Code[T] extends Show[T]
object Code {
  def apply[T](f: T => Show.Result): Code[T] = new Code[T] { def apply(input: T) = f(input) }

  def templ(templ: Template) =
    // TODO: consider XXX.isEmpty
    if (templ.early.isEmpty && templ.parents.isEmpty && templ.self.name.isEmpty && templ.self.decltpe.isEmpty && templ.stats.isEmpty) s()
    else if (templ.parents.nonEmpty || templ.early.nonEmpty) s(" extends ", templ)
    else s(" ", templ)

  def p(oo: String, t: Tree, left: Boolean = false, right: Boolean = false) = {
    def needsParens(oo: String, io: String): Boolean = {
      implicit class MySyntacticInfo(name: String) {
        private def special = Set(".", "()", "=", ":", "match", "if", "lambda")
        def myprecedence: Int = name match {
          case "." => 100
          case "()" => 100
          case "=" => 0
          case ":" => -1
          case "match" => -2
          case "if" => -3
          case "lambda" => -4
          case _ if name.myunary => 99
          case _ => Term.Name(name).precedence
        }
        def myinfix: Boolean = !special.contains(name) && !name.myunary
        def myleftassoc: Boolean = name.last != ':'
        def myunary: Boolean = name.startsWith("unary_")
      }
      val (op, ip) = (oo.myprecedence, io.myprecedence)
      val (oi, ii) = (oo.myinfix, io.myinfix)
      val (oa, ia) = (oo.myleftassoc, io.myleftassoc)
      val (ou, iu) = (oo.myunary, io.myunary)
      val result = {
        if (ou && iu) true
        else if (oi && ii && (oa ^ ia)) true
        else if (oi && ii && (oo == io && left != oa && left != right)) true
        else op > ip
      }
      // println((oo, io, left, right) + " => " + (op, ip, oi, ii, oa, ia, ou, iu) + " => " + result)
      result
    }
    s(t) match {
      case Show.Meta(io: String, res) if needsParens(oo, io) => s("(", res, ")")
      case res => res
    }
  }

  // Branches
  // TODO: this match is not exhaustive: if I remove Mod.Package, then I get no warning
  implicit def codeTree[T <: Tree]: Code[T] = Code { x => (x: Tree) match {
    case t: Name => if (t.isBackquoted) s("`", t.value, "`") else s(t.value)

    // Param.Type
    case t: Param.Type.Repeated => s(t.tpe, "*")
    case t: Param.Type.ByName   => s("=> ", t.tpe)

    // Type
    case t: Type.Project     => s(t.qual, "#", t.selector)
    case t: Type.Select      => s(t.qual, ".", t.selector)
    case t: Type.Singleton   => s(t.ref, ".type")
    case t: Type.Annotate    => s(t.tpe, " ", t.mods)
    case t: Type.Apply       => s(t.tpe, "[", r(t.args, ", "), "]")
    case t: Type.ApplyInfix  => s(t.lhs, " ", t.op, " ", t.rhs)
    case t: Type.Compound    => s(r(t.tpes, " with "), a(" {", a(" ", r(t.refinement, "; "), " "), "}", t.hasRefinement))
    case t: Type.Existential => s(t.tpe, " forSome { ", r(t.quants, "; "), " }")
    case t: Type.Placeholder => s("_", t.bounds)
    case t: Type.Tuple       => s("(", r(t.elements, ", "), ")")
    case t: Type.Function    =>
      val pparams = if (t.params.size == 1) s(t.params.head) else s("(", r(t.params, ", "), ")")
      s(pparams, " => ", t.res)

    // Lit
    case t: Lit.Bool     => s(t.value.toString)
    case t: Lit.Int      => s(t.value.toString)
    case t: Lit.Long     => s(t.value.toString + "l")
    case t: Lit.Float    => s(t.value.toString + "f")
    case t: Lit.Double   => s(t.value.toString + "d")
    case t: Lit.Char     => s(enquote(t.value.toString, SingleQuotes))
    case t: Lit.String   => s(enquote(t.value.toString, if (t.value.contains("\n")) TripleQuotes else DoubleQuotes))
    case t: Lit.Symbol   => s("'", t.value.name)
    case _: Lit.Null     => s("null")
    case _: Lit.Unit     => s("()")

    // Term
    case t: Term.This     => s(t.qual.map { qual => s(qual, ".") }.getOrElse(s()), "this")
    case t: Term.Select   => s(p(".", t.qual), if (t.isPostfix) " " else ".", t.selector)
    case t: Term.Assign   => m("=", s(p("=", t.lhs), " = ", t.rhs))
    case t: Term.Update   => m("=", s(p("=", t.lhs), " = ", t.rhs))
    case t: Term.Return   => s("return", if (t.hasExpr) s(" ", t.expr) else s())
    case t: Term.Throw    => s("throw ", t.expr)
    case t: Term.Ascribe  => m(":", s(t.expr, ": ", t.tpe))
    case t: Term.Annotate => m(":", s(t.expr, ": ", t.mods))
    case t: Term.Tuple    => s("(", r(t.elements, ", "), ")")
    case t: Term.Block    =>
      import Term.{Block, Function}
      def pstats(s: Seq[Stmt.Block]) = r(s.map(i(_)), "")
      t match {
        case Block(Function(Param.Named(mods, name, tptopt, _) :: Nil, Block(stats)) :: Nil) if mods.exists(_.isInstanceOf[Mod.Implicit]) =>
          m("lambda", s("{ implicit ", name, tptopt.map { tpt => s(": ", tpt) }.getOrElse(s()), " => ", pstats(stats), n("}")))
        case Block(Function(Param.Named(mods, name, None, _) :: Nil, Block(stats)) :: Nil) =>
          m("lambda", s("{ ", name, " => ", pstats(stats), n("}")))
        case Block(Function(Param.Anonymous(_, _) :: Nil, Block(stats)) :: Nil) =>
          m("lambda", s("{ _ => ", pstats(stats), n("}")))
        case Block(Function(params, Block(stats)) :: Nil) =>
          m("lambda", s("{ (", r(params, ", "), ") => ", pstats(stats), n("}")))
        case _ =>
          if (t.stats.isEmpty) s("{}") else s("{", pstats(t.stats), n("}"))
      }
    case t: Term.Cases       => s("{", r(t.cases.map(i(_)), ""), n("}"))
    case t: Term.While       => s("while (", t.expr, ") ", t.body)
    case t: Term.Do          => s("do ", t.body, " while (", t.expr, ")")
    case t: Term.For         => s("for (", r(t.enums, "; "), ") ", t.body)
    case t: Term.ForYield    => s("for (", r(t.enums, "; "), ") yield ", t.body)
    case t: Term.New         => s("new ", t.templ)
    case _: Term.Placeholder => s("_")
    case t: Term.Eta         => s(t.term, " _")
    case t: Term.Match       => m("match", s(t.scrut, " match ", t.cases))
    case t: Term.Apply       => m("()", s(p("()", t.fun), t.args))
    case t: Term.ApplyType   => s(t.fun, t.targs)
    case t: Term.ApplyUnary  => m("unary_" + t.op.value, s(t.op, p("unary_" + t.op.value, t.arg)))
    case t: Term.ApplyInfix  =>
      m(t.op.value, s(p(t.op.value, t.lhs, left = true), " ", t.op, t.targs, " ", t.args match {
        case (arg: Term) :: Nil => s(p(t.op.value, arg, right = true))
        case args               => s(args)
      }))
    case t: Term.Try      =>
      s("try ", t.expr,
        t.catchp.map { catchp => s(" catch ", catchp) }.getOrElse(s()),
        t.finallyp.map { finallyp => s(" finally ", finallyp) }.getOrElse(s()))
    case t: Term.If       => m("if", s("if (", t.cond, ") ", t.thenp, if (t.hasElsep) s(" else ", t.elsep) else s()))
    case t: Term.Function =>
      t match {
        case Term.Function(Param.Named(mods, name, tptopt, _) :: Nil, body) if mods.exists(_.isInstanceOf[Mod.Implicit]) =>
          m("lambda", s("implicit ", name, tptopt.map { tpt => s(": ", tpt) }.getOrElse(s()), " => ", body))
        case Term.Function(Param.Named(mods, name, None, _) :: Nil, body) =>
          m("lambda", s(name, " => ", body))
        case Term.Function(Param.Anonymous(_, _) :: Nil, body) =>
          m("lambda", s("_ => ", body))
        case Term.Function(params, body) =>
          m("lambda", s("(", r(params, ", "), ") => ", body))
      }
    case t: Term.Interpolate =>
      val zipped = t.parts.zip(t.args).map {
        case (part, id: Name) if !id.isBackquoted => s(part.value, "$", id.value)
        case (part, arg)                          => s(part.value, "${", arg, "}")
      }
      val quote = if (t.parts.map(_.value).exists(s => s.contains("\n") || s.contains("\""))) "\"\"\"" else "\""
      s(t.prefix, quote, r(zipped), t.parts.last.value, quote)

    // Pat
    case t: Pat.Alternative  => s(t.lhs, " | ", t.rhs)
    case t: Pat.Bind         => s(t.lhs, " @ ", a("(", t.rhs, ")", t.rhs.isInstanceOf[Pat.Alternative] || t.rhs.isInstanceOf[Pat.Typed]))
    case t: Pat.Tuple        => s(t.elements)
    case _: Pat.SeqWildcard  => s("_*")
    case t: Pat.Typed        => m(":", s(t.lhs, ": ", t.rhs))
    case _: Pat.Wildcard     => s("_")
    case t: Pat.Extract      => m("()", s(t.ref, t.targs, t.elements))
    case t: Pat.ExtractInfix =>
      m(t.ref.value, s(p(t.ref.value, t.lhs, left = true), " ", t.ref, " ", t.rhs match {
        case pat :: Nil => s(p(t.ref.value, pat, right = true))
        case pats       => s(pats)
      }))
    case t: Pat.Interpolate  =>
      val zipped = t.parts.zip(t.args).map {
        case (part, id: Name) if !id.isBackquoted => s(part, "$", id.value)
        case (part, arg)                          => s(part, "${", arg, "}")
      }
      s(t.prefix, "\"", r(zipped), t.parts.last, "\"")

    // Arg
    case t: Arg.Named    => s(t.name, " = ", t.rhs)
    case t: Arg.Repeated => s(t.arg, ": _*")

    // Mod
{'fraction_non_alphanumeric': 0.21738667801659928, 'fraction_numerical': 0.001330070227708023, 'mean_word_length': 2.334575128614511, 'pattern_counts': {'":': 22, '<': 5, '<?xml version=': 0, '>': 236, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4997787', 'n_tokens_mistral': 7066, 'n_tokens_neox': 7010, 'n_words': 1694}