package dotty.tools package dottydoc package core import dotc.CompilationUnit import dotc.core.Contexts.Context import dotc.core.Comments.ContextDocstrings import dotc.core.Phases.Phase import model._ import model.internal._ import util.syntax._ import util.traversing._ object transform { /** * The idea behind DocMiniTransformations is to fuse transformations to the * doc AST, much like `MiniPhaseTransform` in dotty core - but in a much more * simple implementation * * Usage * ----- * * Create a `DocMiniPhase` which overrides the relevant method: * * ``` * override def transformDef(implicit ctx: Context) = { * case x if shouldTransform(x) => x.copy(newValue = ...) * } * ``` * * On each node in the AST, the appropriate method in `DocMiniPhase` will be * called in the order that they are supplied in * `DocMiniphaseTransformations`. * * There won't be a match-error as `transformX` is composed with an * `identity` function. * * The transformations in `DocMiniTransformations` will apply transformations * to all nodes - this means that you do _not_ need to transform children in * `transformPackage`, because `transformX` will be called for the relevant * children. If you want to add children to `Package` you need to do that in * `transformPackage`, these additions will be persisted. * * Deleting nodes in the AST * ------------------------- * To delete a node in the AST, simply return `NonEntity` from transforming method */ trait DocMiniTransformations extends Phase { def transformations: List[DocMiniPhase] override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { for { pack <- rootPackages(ctx.docbase.packages) transformed = performPackageTransform(pack) } yield ctx.docbase.packagesMutable(pack.name) = transformed units } private def performPackageTransform(pack: Package)(implicit ctx: Context): Package = { def transformEntity[E <: Entity](e: E, f: DocMiniPhase => E => E)(createNew: E => E): Entity = { val transformedEntity = transformations.foldLeft(e) { case (oldE, transf) => f(transf)(oldE) } if (transformedEntity eq NonEntity) NonEntity else createNew(transformedEntity) } def traverse(ent: Entity): Entity = ent match { case p: Package => transformEntity(p, _.packageTransformation) { p => val newPackage = PackageImpl( p.symbol, p.annotations, p.name, p.members.map(traverse).filterNot(_ eq NonEntity), p.path, p.superTypes, p.comment, p.parent ) // Update reference in context to newPackage ctx.docbase.packagesMutable += (newPackage.path.mkString(".") -> newPackage) newPackage } case t: TypeAlias => transformEntity(t, _.typeAliasTransformation) { t => TypeAliasImpl( t.symbol, t.annotations, t.modifiers, t.name, t.path, t.alias, t.typeParams, t.comment, t.parent ) } case c: Class => transformEntity(c, _.classTransformation) { cls => ClassImpl( cls.symbol, cls.annotations, cls.name, cls.members.map(traverse).filterNot(_ eq NonEntity), cls.modifiers, cls.path, cls.typeParams, cls.constructors, cls.superTypes, cls.comment, cls.companionPath, cls.parent ) } case cc: CaseClass => transformEntity(cc, _.caseClassTransformation) { cc => CaseClassImpl( cc.symbol, cc.annotations, cc.name, cc.members.map(traverse).filterNot(_ eq NonEntity), cc.modifiers, cc.path, cc.typeParams, cc.constructors, cc.superTypes, cc.comment, cc.companionPath, cc.parent ) } case trt: Trait => transformEntity(trt, _.traitTransformation) { trt => TraitImpl( trt.symbol, trt.annotations, trt.name, trt.members.map(traverse).filterNot(_ eq NonEntity), trt.modifiers, trt.path, trt.typeParams, trt.traitParams, trt.superTypes, trt.comment, trt.companionPath, trt.parent ) } case obj: Object => transformEntity(obj, _.objectTransformation) { obj => ObjectImpl( obj.symbol, obj.annotations, obj.name, obj.members.map(traverse).filterNot(_ eq NonEntity), obj.modifiers, obj.path, obj.superTypes, obj.comment, obj.companionPath, obj.parent ) } case df: Def => transformEntity(df, _.defTransformation) { df => DefImpl( df.symbol, df.annotations, df.name, df.modifiers, df.path, df.returnValue, df.typeParams, df.paramLists, df.comment, df.implicitlyAddedFrom, df.parent ) } case vl: Val => transformEntity(vl, _.valTransformation) { vl => ValImpl( vl.symbol, vl.annotations, vl.name, vl.modifiers, vl.path, vl.returnValue, vl.kind, vl.comment, vl.implicitlyAddedFrom, vl.parent ) } } traverse(pack).asInstanceOf[Package] } override def run(implicit ctx: Context): Unit = () } object DocMiniTransformations { private var previousPhase = 0 def apply(miniPhases: DocMiniPhase*) = new DocMiniTransformations { val transformations = miniPhases.toList val packages = Map.empty[String, Package] def phaseName = s"MiniTransformation${ previousPhase += 1 }" } } trait DocMiniPhase { phase => private def identity[E]: PartialFunction[E, E] = { case id: E @unchecked => id } def transformPackage(implicit ctx: Context): PartialFunction[Package, Package] = identity def transformTypeAlias(implicit ctx: Context): PartialFunction[TypeAlias, TypeAlias] = identity def transformClass(implicit ctx: Context): PartialFunction[Class, Class] = identity def transformCaseClass(implicit ctx: Context): PartialFunction[CaseClass, CaseClass] = identity def transformTrait(implicit ctx: Context): PartialFunction[Trait, Trait] = identity def transformObject(implicit ctx: Context): PartialFunction[Object, Object] = identity def transformDef(implicit ctx: Context): PartialFunction[Def, Def] = identity def transformVal(implicit ctx: Context): PartialFunction[Val, Val] = identity private[transform] def packageTransformation(p: Package)(implicit ctx: Context) = (transformPackage orElse identity)(p) private[transform] def typeAliasTransformation(alias: TypeAlias)(implicit ctx: Context) = (transformTypeAlias orElse identity)(alias) private[transform] def classTransformation(cls: Class)(implicit ctx: Context) = (transformClass orElse identity)(cls) private[transform] def caseClassTransformation(cc: CaseClass)(implicit ctx: Context) = (transformCaseClass orElse identity)(cc) private[transform] def traitTransformation(trt: Trait)(implicit ctx: Context) = (transformTrait orElse identity)(trt) private[transform] def objectTransformation(obj: Object)(implicit ctx: Context) = (transformObject orElse identity)(obj) private[transform] def defTransformation(df: Def)(implicit ctx: Context) = (transformDef orElse identity)(df) private[transform] def valTransformation(vl: Val)(implicit ctx: Context) = (transformVal orElse identity)(vl) } }