All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sourcecode.Macros.scala Maven / Gradle / Ivy

package sourcecode

import language.experimental.macros

trait NameMacros {
  implicit def generate: Name = macro Macros.nameImpl
}

trait NameMachineMacros {
  implicit def generate: Name.Machine = macro Macros.nameMachineImpl
}

trait FullNameMacros {
  implicit def generate: FullName = macro Macros.fullNameImpl
}

trait FullNameMachineMacros {
  implicit def generate: FullName.Machine = macro Macros.fullNameMachineImpl
}

trait FileMacros {
  implicit def generate: sourcecode.File = macro Macros.fileImpl
}

trait FileNameMacros {
  implicit def generate: sourcecode.FileName = macro Macros.fileNameImpl
}

trait LineMacros {
  implicit def generate: sourcecode.Line = macro Macros.lineImpl
}

trait EnclosingMacros {
  implicit def generate: Enclosing = macro Macros.enclosingImpl
}

trait EnclosingMachineMacros {
  implicit def generate: Enclosing.Machine = macro Macros.enclosingMachineImpl
}

trait PkgMacros {
  implicit def generate: Pkg = macro Macros.pkgImpl
}

trait TextMacros {
  implicit def generate[T](v: T): Text[T] = macro Macros.text[T]
  def apply[T](v: T): Text[T] = macro Macros.text[T]
}

trait ArgsMacros {
  implicit def generate: Args = macro Macros.argsImpl
}

object Util{
  def isSynthetic(c: Compat.Context)(s: c.Symbol) = isSyntheticName(getName(c)(s))
  def isSyntheticName(name: String) = {
    name == "" || (name.startsWith("")) || name == "$anonfun"
  }
  def getName(c: Compat.Context)(s: c.Symbol) = s.name.decoded.toString.trim
}

object Macros {

  def nameImpl(c: Compat.Context): c.Expr[Name] = {
    import c.universe._
    var owner = Compat.enclosingOwner(c)
    while(Util.isSynthetic(c)(owner)) owner = owner.owner
    val simpleName = Util.getName(c)(owner)
    c.Expr[sourcecode.Name](q"""${c.prefix}($simpleName)""")
  }

  def nameMachineImpl(c: Compat.Context): c.Expr[Name.Machine] = {
    import c.universe._
    val owner = Compat.enclosingOwner(c)
    val simpleName = Util.getName(c)(owner)
    c.Expr[Name.Machine](q"""${c.prefix}($simpleName)""")
  }

  def fullNameImpl(c: Compat.Context): c.Expr[FullName] = {
    import c.universe._
    val owner = Compat.enclosingOwner(c)
    val fullName =
      owner.fullName.trim
        .split("\\.", -1)
        .filterNot(Util.isSyntheticName)
        .mkString(".")
    c.Expr[sourcecode.FullName](q"""${c.prefix}($fullName)""")
  }

  def fullNameMachineImpl(c: Compat.Context): c.Expr[FullName.Machine] = {
    import c.universe._
    val owner = Compat.enclosingOwner(c)
    val fullName = owner.fullName.trim
    c.Expr[FullName.Machine](q"""${c.prefix}($fullName)""")
  }

  def fileImpl(c: Compat.Context): c.Expr[sourcecode.File] = {
    import c.universe._
    val file = c.enclosingPosition.source.path
    c.Expr[sourcecode.File](q"""${c.prefix}($file)""")
  }

  def fileNameImpl(c: Compat.Context): c.Expr[sourcecode.FileName] = {
    import c.universe._
    val fileName = c.enclosingPosition.source.path.split('/').last
    c.Expr[sourcecode.FileName](q"""${c.prefix}($fileName)""")
  }

  def lineImpl(c: Compat.Context): c.Expr[sourcecode.Line] = {
    import c.universe._
    val line = c.enclosingPosition.line
    c.Expr[sourcecode.Line](q"""${c.prefix}($line)""")
  }

  def enclosingImpl(c: Compat.Context): c.Expr[Enclosing] = enclosing[Enclosing](c)(
    !Util.isSynthetic(c)(_)
  )

  def enclosingMachineImpl(c: Compat.Context): c.Expr[Enclosing.Machine] =
    enclosing[Enclosing.Machine](c)(_ => true)

  def pkgImpl(c: Compat.Context): c.Expr[Pkg] = enclosing[Pkg](c)(_.isPackage)

  def argsImpl(c: Compat.Context): c.Expr[Args] = {
    import c.universe._
    val param = Compat.enclosingParamList(c)
    val texts = param.map(_.map(p => c.Expr[Text[_]](q"""sourcecode.Text($p, ${p.name.toString})""")))
    val textSeqs = texts.map(s => c.Expr(q"""Seq(..$s)"""))
    c.Expr[Args](q"""Seq(..$textSeqs)""")
  }


  def text[T: c.WeakTypeTag](c: Compat.Context)(v: c.Expr[T]): c.Expr[sourcecode.Text[T]] = {
    import c.universe._
    val fileContent = new String(v.tree.pos.source.content)
    val start = v.tree.collect {
      case treeVal => treeVal.pos match {
        case NoPosition ⇒ Int.MaxValue
        case p ⇒ p.startOrPoint
      }
    }.min
    val g = c.asInstanceOf[reflect.macros.runtime.Context].global
    val parser = g.newUnitParser(fileContent.drop(start))
    parser.expr()
    val end = parser.in.lastOffset
    val txt = fileContent.slice(start, start + end)
    val tree = q"""${c.prefix}(${v.tree}, $txt)"""
    c.Expr[sourcecode.Text[T]](tree)
  }
  sealed trait Chunk
  object Chunk{
    case class Pkg(name: String) extends Chunk
    case class Obj(name: String) extends Chunk
    case class Cls(name: String) extends Chunk
    case class Trt(name: String) extends Chunk
    case class Val(name: String) extends Chunk
    case class Var(name: String) extends Chunk
    case class Lzy(name: String) extends Chunk
    case class Def(name: String) extends Chunk

  }

  def enclosing[T](c: Compat.Context)(filter: c.Symbol => Boolean): c.Expr[T] = {

    import c.universe._
    var current = Compat.enclosingOwner(c)
    var path = List.empty[Chunk]
    while(current != NoSymbol && current.toString != "package "){
      if (filter(current)) {

        val chunk = current match {
          case x if x.isPackage => Chunk.Pkg
          case x if x.isModuleClass => Chunk.Obj
          case x if x.isClass && x.asClass.isTrait => Chunk.Trt
          case x if x.isClass => Chunk.Cls
          case x if x.isMethod => Chunk.Def
          case x if x.isTerm && x.asTerm.isVar => Chunk.Var
          case x if x.isTerm && x.asTerm.isLazy => Chunk.Lzy
          case x if x.isTerm && x.asTerm.isVal => Chunk.Val
        }

        path = chunk(Util.getName(c)(current)) :: path
      }
      current = current.owner
    }
    val renderedPath = path.map{
      case Chunk.Pkg(s) => s + "."
      case Chunk.Obj(s) => s + "."
      case Chunk.Cls(s) => s + "#"
      case Chunk.Trt(s) => s + "#"
      case Chunk.Val(s) => s + " "
      case Chunk.Var(s) => s + " "
      case Chunk.Lzy(s) => s + " "
      case Chunk.Def(s) => s + " "
    }.mkString.dropRight(1)
    c.Expr[T](q"""${c.prefix}($renderedPath)""")
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy