diff --git a/build.sbt b/build.sbt index 16dd00e..b4ab684 100644 --- a/build.sbt +++ b/build.sbt @@ -40,12 +40,23 @@ lazy val sourcecode = crossProject(JSPlatform, JVMPlatform, NativePlatform) libraryDependencies ++= macroDependencies(scalaVersion.value), test in Test := (run in Test).toTask("").value, unmanagedSourceDirectories in Compile ++= { - CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, n)) if n >= 12 => - Seq(baseDirectory.value / ".." / "shared" / "src" / "main" / "scala-2.11") + val crossVer = CrossVersion.partialVersion(scalaVersion.value) + + val scala211plus = crossVer match { + case Some((2, n)) if n >= 11 => + Seq(baseDirectory.value / ".." / "shared" / "src" / "main" / "scala-2.11+") + case _ => + Seq() + } + + val scala2 = crossVer match { + case Some((2, _)) => + Seq(baseDirectory.value / ".." / "shared" / "src" / "main" / "scala-2.x") case _ => Seq() } + + scala211plus ++ scala2 }, // Osgi settings osgiSettings, diff --git a/sourcecode/shared/src/main/scala-2.11/sourcecode/Compat.scala b/sourcecode/shared/src/main/scala-2.11+/sourcecode/Compat.scala similarity index 100% rename from sourcecode/shared/src/main/scala-2.11/sourcecode/Compat.scala rename to sourcecode/shared/src/main/scala-2.11+/sourcecode/Compat.scala diff --git a/sourcecode/shared/src/main/scala-2.x/sourcecode/Macros.scala b/sourcecode/shared/src/main/scala-2.x/sourcecode/Macros.scala new file mode 100644 index 0000000..02b572f --- /dev/null +++ b/sourcecode/shared/src/main/scala-2.x/sourcecode/Macros.scala @@ -0,0 +1,188 @@ +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 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("")) + } + 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 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)""") + } +} diff --git a/sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala b/sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala index 66d67f7..9c42345 100644 --- a/sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala +++ b/sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala @@ -1,15 +1,6 @@ package sourcecode -import language.experimental.macros - -object Util{ - def isSynthetic(c: Compat.Context)(s: c.Symbol) = isSyntheticName(getName(c)(s)) - def isSyntheticName(name: String) = { - name == "" || (name.startsWith("")) - } - def getName(c: Compat.Context)(s: c.Symbol) = s.name.decoded.toString.trim -} abstract class SourceValue[T]{ def value: T } @@ -18,176 +9,32 @@ abstract class SourceCompanion[T, V <: SourceValue[T]](build: T => V){ implicit def wrap(s: T): V = build(s) } case class Name(value: String) extends SourceValue[String] -object Name extends SourceCompanion[String, Name](new Name(_)){ - implicit def generate: Name = macro impl - - def impl(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)""") - } +object Name extends SourceCompanion[String, Name](new Name(_)) with NameMacros { case class Machine(value: String) extends SourceValue[String] - object Machine extends SourceCompanion[String, Machine](new Machine(_)){ - implicit def generate: Machine = macro impl - def impl(c: Compat.Context): c.Expr[Machine] = { - import c.universe._ - val owner = Compat.enclosingOwner(c) - val simpleName = Util.getName(c)(owner) - c.Expr[Machine](q"""${c.prefix}($simpleName)""") - } - } + object Machine extends SourceCompanion[String, Machine](new Machine(_)) with NameMachineMacros } case class FullName(value: String) extends SourceValue[String] -object FullName extends SourceCompanion[String, FullName](new FullName(_)){ - implicit def generate: FullName = macro impl - - def impl(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)""") - } +object FullName extends SourceCompanion[String, FullName](new FullName(_)) with FullNameMacros { case class Machine(value: String) extends SourceValue[String] - object Machine extends SourceCompanion[String, Machine](new Machine(_)){ - implicit def generate: Machine = macro impl - - def impl(c: Compat.Context): c.Expr[Machine] = { - import c.universe._ - val owner = Compat.enclosingOwner(c) - val fullName = owner.fullName.trim - c.Expr[Machine](q"""${c.prefix}($fullName)""") - } - } + object Machine extends SourceCompanion[String, Machine](new Machine(_)) with FullNameMachineMacros } case class File(value: String) extends SourceValue[String] -object File extends SourceCompanion[String, File](new File(_)){ - implicit def generate: sourcecode.File = macro impl - - def impl(c: Compat.Context): c.Expr[sourcecode.File] = { - import c.universe._ - val file = c.enclosingPosition.source.path - c.Expr[sourcecode.File](q"""${c.prefix}($file)""") - } -} +object File extends SourceCompanion[String, File](new File(_)) with FileMacros case class Line(value: Int) extends SourceValue[Int] -object Line extends SourceCompanion[Int, Line](new Line(_)){ - implicit def generate: sourcecode.Line = macro impl - def impl(c: Compat.Context): c.Expr[sourcecode.Line] = { - import c.universe._ - val line = c.enclosingPosition.line - c.Expr[sourcecode.Line](q"""${c.prefix}($line)""") - } -} +object Line extends SourceCompanion[Int, Line](new Line(_)) with LineMacros case class Enclosing(value: String) extends SourceValue[String] -object Enclosing extends SourceCompanion[String, Enclosing](new Enclosing(_)){ - implicit def generate: Enclosing = macro impl - def impl(c: Compat.Context): c.Expr[Enclosing] = Impls.enclosing[Enclosing](c)( - !Util.isSynthetic(c)(_) - ) +object Enclosing extends SourceCompanion[String, Enclosing](new Enclosing(_)) with EnclosingMacros { case class Machine(value: String) extends SourceValue[String] - object Machine extends SourceCompanion[String, Machine](new Machine(_)){ - implicit def generate: Machine = macro impl - def impl(c: Compat.Context): c.Expr[Machine] = Impls.enclosing[Machine](c)(_ => true) - } + object Machine extends SourceCompanion[String, Machine](new Machine(_)) with EnclosingMachineMacros } case class Pkg(value: String) extends SourceValue[String] -object Pkg extends SourceCompanion[String, Pkg](new Pkg(_)){ - implicit def generate: Pkg = macro impl - def impl(c: Compat.Context): c.Expr[Pkg] = Impls.enclosing[Pkg](c)(_.isPackage) -} +object Pkg extends SourceCompanion[String, Pkg](new Pkg(_)) with PkgMacros case class Text[T](value: T, source: String) -object Text{ - implicit def generate[T](v: T): Text[T] = macro Impls.text[T] - def apply[T](v: T): Text[T] = macro Impls.text[T] - -} +object Text extends TextMacros case class Args(value: Seq[Seq[Text[_]]]) extends SourceValue[Seq[Seq[Text[_]]]] -object Args extends SourceCompanion[Seq[Seq[Text[_]]], Args](new Args(_)) { - implicit def generate: Args = macro impl - def impl(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)""") - } -} - -object Impls{ - 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)""") - } -} +object Args extends SourceCompanion[Seq[Seq[Text[_]]], Args](new Args(_)) with ArgsMacros