diff options
Diffstat (limited to 'dottydoc/jvm/src/dotty/tools/dottydoc/core/DocASTPhase.scala')
-rw-r--r-- | dottydoc/jvm/src/dotty/tools/dottydoc/core/DocASTPhase.scala | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/dottydoc/jvm/src/dotty/tools/dottydoc/core/DocASTPhase.scala b/dottydoc/jvm/src/dotty/tools/dottydoc/core/DocASTPhase.scala new file mode 100644 index 000000000..72e679565 --- /dev/null +++ b/dottydoc/jvm/src/dotty/tools/dottydoc/core/DocASTPhase.scala @@ -0,0 +1,177 @@ +package dotty.tools +package dottydoc +package core + +/** Dotty and Dottydoc imports */ +import dotc.ast.Trees._ +import dotc.CompilationUnit +import dotc.config.Printers.dottydoc +import dotc.core.Contexts.Context +import dotc.core.Phases.Phase +import dotc.core.Symbols.Symbol + +class DocASTPhase extends Phase { + import model._ + import model.factories._ + import model.internal._ + import model.parsers.WikiParser + import model.comment.Comment + import dotty.tools.dotc.core.Flags + import dotty.tools.dotc.ast.tpd._ + import util.traversing._ + import util.internal.setters._ + + def phaseName = "docphase" + + private[this] val commentParser = new WikiParser + + /** Saves the commentParser function for later evaluation, for when the AST has been filled */ + def track(symbol: Symbol, ctx: Context)(op: => Entity) = { + val entity = op + + if (entity != NonEntity) + commentParser += (entity, symbol, ctx) + + entity + } + + /** Build documentation hierarchy from existing tree */ + def collect(tree: Tree, prev: List[String] = Nil)(implicit ctx: Context): Entity = track(tree.symbol, ctx) { + val implicitlyAddedMembers = ctx.base.defs(tree.symbol) + + def collectList(xs: List[Tree], ps: List[String])(implicit ctx: Context): List[Entity] = + xs.map(collect(_, ps)).filter(_ != NonEntity) + + def collectEntityMembers(xs: List[Tree], ps: List[String])(implicit ctx: Context) = + collectList(xs, ps).asInstanceOf[List[Entity with Members]] + + /** TODO: should be a set, not a uniqued list */ + def collectMembers(tree: Tree, ps: List[String] = prev)(implicit ctx: Context): List[Entity] = { + val defs = (tree match { + case t: Template => collectList(t.body, ps) + case _ => Nil + }) ++ implicitlyAddedMembers.flatMap(addedFromSymbol) + + defs + } + + def addedFromSymbol(sym: Symbol): List[Entity] = { + val defs = sym.info.membersBasedOnFlags(Flags.Method, Flags.Synthetic | Flags.Private).map { meth => + track(meth.symbol, ctx) { + DefImpl(meth.symbol.name.decode.toString, Nil, path(meth.symbol), returnType(meth.info), typeParams(meth.symbol), paramLists(meth.info)) + } + }.toList + + val vals = sym.info.fields.filterNot(_.symbol.is(Flags.Private | Flags.Synthetic)).map { value => + track(value.symbol, ctx) { + ValImpl(value.symbol.name.decode.toString, Nil, path(value.symbol), returnType(value.info)) + } + } + + defs ++ vals + } + + + tree match { + /** package */ + case pd @ PackageDef(pid, st) => + val newPath = prev :+ pid.name.toString + addEntity(PackageImpl(newPath.mkString("."), collectEntityMembers(st, newPath), newPath)) + + /** trait */ + case t @ TypeDef(n, rhs) if t.symbol.is(Flags.Trait) => + val name = n.decode.toString + val newPath = prev :+ name + //TODO: should not `collectMember` from `rhs` - instead: get from symbol, will get inherited members as well + TraitImpl(name, collectMembers(rhs), flags(t), newPath, typeParams(t.symbol), superTypes(t)) + + /** objects, on the format "Object$" so drop the last letter */ + case o @ TypeDef(n, rhs) if o.symbol.is(Flags.Module) => + val name = n.decode.toString.dropRight(1) + //TODO: should not `collectMember` from `rhs` - instead: get from symbol, will get inherited members as well + ObjectImpl(name, collectMembers(rhs, prev :+ name), flags(o), prev :+ (name + "$"), superTypes(o)) + + /** class / case class */ + case c @ TypeDef(n, rhs) if c.symbol.isClass => + val name = n.decode.toString + val newPath = prev :+ name + //TODO: should not `collectMember` from `rhs` - instead: get from symbol, will get inherited members as well + (name, collectMembers(rhs), flags(c), newPath, typeParams(c.symbol), superTypes(c), None) match { + case x if c.symbol.is(Flags.CaseClass) => CaseClassImpl.tupled(x) + case x => ClassImpl.tupled(x) + } + + /** def */ + case d: DefDef => + DefImpl(d.name.decode.toString, flags(d), path(d.symbol), returnType(d.tpt.tpe), typeParams(d.symbol), paramLists(d.symbol.info)) + + /** val */ + case v: ValDef if !v.symbol.is(Flags.ModuleVal) => + ValImpl(v.name.decode.toString, flags(v), path(v.symbol), returnType(v.tpt.tpe)) + + case x => { + //dottydoc.println(s"Found unwanted entity: $x (${x.pos},\n${x.show}") + NonEntity + } + } + } + + var packages: Map[String, Package] = Map.empty + + def addEntity(p: Package): Package = { + def mergedChildren(x1s: List[Entity], x2s: List[Entity]): List[Entity] = { + val (packs1, others1) = x1s.partition(_.kind == "package") + val (packs2, others2) = x2s.partition(_.kind == "package") + + val others = others1 ::: others2 + val packs = (packs1 ::: packs2).groupBy(_.path).map(_._2.head) + + (others ++ packs).sortBy(_.name) + } + + val path = p.path.mkString(".") + val newPack = packages.get(path).map { + case ex: PackageImpl => + if (!ex.comment.isDefined) ex.comment = p.comment + ex.members = mergedChildren(ex.members, p.members) + ex + }.getOrElse(p) + + packages = packages + (path -> newPack) + newPack + } + + private[this] var totalRuns = 0 + private[this] var currentRun = 0 + + override def run(implicit ctx: Context): Unit = { + currentRun += 1 + println(s"Compiling ($currentRun/$totalRuns): ${ctx.compilationUnit.source.file.name}") + collect(ctx.compilationUnit.tpdTree) // Will put packages in `packages` var + } + + override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { + // (1) Create package structure for all `units`, this will give us a complete structure + totalRuns = units.length + val compUnits = super.runOn(units) + + // (2) Set parents of entities, needed for linking + for { + parent <- packages.values + child <- parent.children + } setParent(child, to = parent) + + // (3) Create documentation template from docstrings, with internal links + println("Generating documentation, this might take a while...") + commentParser.parse(packages) + + // (4) Clear caches + commentParser.clear() + + // (5) Update Doc AST in ctx.base + for (kv <- packages) ctx.base.packages += kv + + // Return super's result + compUnits + } +} |