aboutsummaryrefslogtreecommitdiff
path: root/dottydoc
diff options
context:
space:
mode:
Diffstat (limited to 'dottydoc')
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/DottyDoc.scala9
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/core/ContextDottydoc.scala23
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/core/DocASTPhase.scala76
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala1
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/core/DocstringPhase.scala47
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/core/MiniPhaseTransform.scala15
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala13
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/core/UsecasePhase.scala31
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/model/comment/CommentExpander.scala344
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/model/comment/CommentUtils.scala224
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/model/entities.scala4
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/model/internal.scala8
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/model/parsers.scala98
-rw-r--r--dottydoc/src/dotty/tools/dottydoc/util/syntax.scala16
-rw-r--r--dottydoc/test/BaseTest.scala10
-rw-r--r--dottydoc/test/ConstructorTest.scala14
-rw-r--r--dottydoc/test/PackageStructure.scala7
-rw-r--r--dottydoc/test/UsecaseTest.scala234
18 files changed, 435 insertions, 739 deletions
diff --git a/dottydoc/src/dotty/tools/dottydoc/DottyDoc.scala b/dottydoc/src/dotty/tools/dottydoc/DottyDoc.scala
index 2d4c7abcf..0dea96134 100644
--- a/dottydoc/src/dotty/tools/dottydoc/DottyDoc.scala
+++ b/dottydoc/src/dotty/tools/dottydoc/DottyDoc.scala
@@ -1,11 +1,13 @@
package dotty.tools
package dottydoc
+import dotty.tools.dottydoc.util.syntax._
import core._
import core.transform._
import dotc.config.CompilerCommand
import dotc.config.Printers.dottydoc
import dotc.core.Contexts._
+import dotc.core.Comments.ContextDoc
import dotc.core.Phases.Phase
import dotc.typer.FrontEnd
import dotc.{ CompilationUnit, Compiler, Driver, Run }
@@ -31,7 +33,9 @@ class DocCompiler extends Compiler {
List(new DocFrontEnd),
List(new DocImplicitsPhase),
List(new DocASTPhase),
- List(DocMiniTransformations(new LinkReturnTypes,
+ List(DocMiniTransformations(new UsecasePhase,
+ new DocstringPhase,
+ new LinkReturnTypes,
new LinkParamListTypes,
new LinkImplicitlyAddedTypes,
new LinkSuperTypes,
@@ -54,6 +58,7 @@ abstract class DocDriver extends Driver {
ctx.setSettings(summary.sstate)
ctx.setSetting(ctx.settings.YkeepComments, true)
+ ctx.setProperty(ContextDoc, new ContextDottydoc)
val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ctx)
(fileNames, ctx)
@@ -65,7 +70,7 @@ abstract class DocDriver extends Driver {
val (fileNames, ctx) = setup(args, initCtx.fresh)
doCompile(newCompiler(ctx), fileNames)(ctx)
- ctx.docbase.packages[Package]
+ ctx.docbase.packages
}
def compiledDocsJava(args: Array[String]): JMap[String, Package] =
diff --git a/dottydoc/src/dotty/tools/dottydoc/core/ContextDottydoc.scala b/dottydoc/src/dotty/tools/dottydoc/core/ContextDottydoc.scala
new file mode 100644
index 000000000..c60038836
--- /dev/null
+++ b/dottydoc/src/dotty/tools/dottydoc/core/ContextDottydoc.scala
@@ -0,0 +1,23 @@
+package dotty.tools
+package dottydoc
+package core
+
+import dotc.core.Symbols.Symbol
+import dotc.core.Comments.ContextDocstrings
+import model.Package
+
+class ContextDottydoc extends ContextDocstrings {
+ import scala.collection.mutable
+
+ private[this] val _packages: mutable.Map[String, Package] = mutable.Map.empty
+ def packages: Map[String, Package] = _packages.toMap
+ def packagesMutable: mutable.Map[String, Package] = _packages
+
+ /** Should perhaps factorize this into caches that get flushed */
+ private var _defs: Map[Symbol, Set[Symbol]] = Map.empty
+ def defs(sym: Symbol): Set[Symbol] = _defs.get(sym).getOrElse(Set.empty)
+
+ def addDef(s: Symbol, d: Symbol): Unit = _defs = (_defs + {
+ s -> _defs.get(s).map(xs => xs + d).getOrElse(Set(d))
+ })
+}
diff --git a/dottydoc/src/dotty/tools/dottydoc/core/DocASTPhase.scala b/dottydoc/src/dotty/tools/dottydoc/core/DocASTPhase.scala
index 7744752ce..806d9d0ae 100644
--- a/dottydoc/src/dotty/tools/dottydoc/core/DocASTPhase.scala
+++ b/dottydoc/src/dotty/tools/dottydoc/core/DocASTPhase.scala
@@ -7,6 +7,7 @@ import dotc.ast.Trees._
import dotc.CompilationUnit
import dotc.config.Printers.dottydoc
import dotc.core.Contexts.Context
+import dotc.core.Comments.ContextDocstrings
import dotc.core.Phases.Phase
import dotc.core.Symbols.{ Symbol, NoSymbol }
@@ -14,29 +15,17 @@ 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 dotty.tools.dottydoc.util.syntax._
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, parent: Symbol = NoSymbol)(op: => Entity) = {
- val entity = op
-
- if (entity != NonEntity)
- commentParser += (entity, symbol, parent, ctx)
-
- entity
- }
-
/** Build documentation hierarchy from existing tree */
- def collect(tree: Tree, prev: List[String] = Nil)(implicit ctx: Context): Entity = track(tree.symbol, ctx) {
+ def collect(tree: Tree, prev: List[String] = Nil)(implicit ctx: Context): Entity = {
val implicitConversions = ctx.docbase.defs(tree.symbol)
def collectList(xs: List[Tree], ps: List[String]): List[Entity] =
@@ -58,28 +47,26 @@ class DocASTPhase extends Phase {
val defs = sym.info.bounds.hi.membersBasedOnFlags(Flags.Method, Flags.Synthetic | Flags.Private)
.filterNot(_.symbol.owner.name.show == "Any")
.map { meth =>
- track(meth.symbol, ctx, tree.symbol) {
- DefImpl(
- meth.symbol.name.show,
- Nil,
- path(meth.symbol),
- returnType(meth.info),
- typeParams(meth.symbol),
- paramLists(meth.info),
- implicitlyAddedFrom = Some(returnType(meth.symbol.owner.info))
- )
- }
+ DefImpl(
+ meth.symbol,
+ meth.symbol.name.show,
+ Nil,
+ path(meth.symbol),
+ returnType(meth.info),
+ typeParams(meth.symbol),
+ paramLists(meth.info),
+ implicitlyAddedFrom = Some(returnType(meth.symbol.owner.info))
+ )
}.toList
val vals = sym.info.fields.filterNot(_.symbol.is(Flags.Private | Flags.Synthetic)).map { value =>
- track(value.symbol, ctx, tree.symbol) {
- ValImpl(
- value.symbol.name.show,
- Nil, path(value.symbol),
- returnType(value.info),
- implicitlyAddedFrom = Some(returnType(value.symbol.owner.info))
- )
- }
+ ValImpl(
+ value.symbol,
+ value.symbol.name.show,
+ Nil, path(value.symbol),
+ returnType(value.info),
+ implicitlyAddedFrom = Some(returnType(value.symbol.owner.info))
+ )
}
defs ++ vals
@@ -90,38 +77,38 @@ class DocASTPhase extends Phase {
/** package */
case pd @ PackageDef(pid, st) =>
val newPath = prev :+ pid.name.toString
- addEntity(PackageImpl(newPath.mkString("."), collectEntityMembers(st, newPath), newPath))
+ addEntity(PackageImpl(pd.symbol, 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), traitParameters(t.symbol), superTypes(t))
+ TraitImpl(t.symbol, name, collectMembers(rhs), flags(t), newPath, typeParams(t.symbol), traitParameters(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))
+ ObjectImpl(o.symbol, 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), constructors(c.symbol), superTypes(c), None) match {
+ (c.symbol, name, collectMembers(rhs), flags(c), newPath, typeParams(c.symbol), constructors(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))
+ DefImpl(d.symbol, 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))
+ ValImpl(v.symbol, 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}")
@@ -175,15 +162,8 @@ class DocASTPhase extends Phase {
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.docbase.packages += kv
+ // (3) Update Doc AST in ctx.base
+ for (kv <- packages) ctx.docbase.packagesMutable += kv
// Return super's result
compUnits
diff --git a/dottydoc/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala b/dottydoc/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala
index f322d7a5a..6577f0cb7 100644
--- a/dottydoc/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala
+++ b/dottydoc/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala
@@ -5,6 +5,7 @@ package core
import dotty.tools.dotc.transform.TreeTransforms.{ MiniPhaseTransform, TransformerInfo }
import dotty.tools.dotc.core.Flags
import dotc.core.Contexts.Context
+import util.syntax._
class DocImplicitsPhase extends MiniPhaseTransform { thisTransformer =>
import dotty.tools.dotc.ast.tpd._
diff --git a/dottydoc/src/dotty/tools/dottydoc/core/DocstringPhase.scala b/dottydoc/src/dotty/tools/dottydoc/core/DocstringPhase.scala
new file mode 100644
index 000000000..cff614528
--- /dev/null
+++ b/dottydoc/src/dotty/tools/dottydoc/core/DocstringPhase.scala
@@ -0,0 +1,47 @@
+package dotty.tools
+package dottydoc
+package core
+
+import dotc.core.Contexts.Context
+import transform.DocMiniPhase
+import model._
+import model.internal._
+import model.comment._
+import BodyParsers._
+import util.syntax._
+
+class DocstringPhase extends DocMiniPhase with CommentParser with CommentCleaner {
+ private def parsedComment[E <: Entity](ent: E)(implicit ctx: Context): Option[Comment] =
+ ctx.docbase.docstring(ent.symbol).map { cmt =>
+ parse(ent, ctx.docbase.packages, clean(cmt.raw), cmt.raw, cmt.pos)
+ .toComment(_.toHtml(ent))
+ }
+
+ override def transformPackage(implicit ctx: Context) = { case ent: PackageImpl =>
+ ent.copy(comment = parsedComment(ent))
+ }
+
+ override def transformClass(implicit ctx: Context) = { case ent: ClassImpl =>
+ ent.copy(comment = parsedComment(ent))
+ }
+
+ override def transformCaseClass(implicit ctx: Context) = { case ent: CaseClassImpl =>
+ ent.copy(comment = parsedComment(ent))
+ }
+
+ override def transformTrait(implicit ctx: Context) = { case ent: TraitImpl =>
+ ent.copy(comment = parsedComment(ent))
+ }
+
+ override def transformObject(implicit ctx: Context) = { case ent: ObjectImpl =>
+ ent.copy(comment = parsedComment(ent))
+ }
+
+ override def transformDef(implicit ctx: Context) = { case ent: DefImpl =>
+ ent.copy(comment = parsedComment(ent))
+ }
+
+ override def transformVal(implicit ctx: Context) = { case ent: ValImpl =>
+ ent.copy(comment = parsedComment(ent))
+ }
+}
diff --git a/dottydoc/src/dotty/tools/dottydoc/core/MiniPhaseTransform.scala b/dottydoc/src/dotty/tools/dottydoc/core/MiniPhaseTransform.scala
index 2690ac7b7..150a4e08f 100644
--- a/dottydoc/src/dotty/tools/dottydoc/core/MiniPhaseTransform.scala
+++ b/dottydoc/src/dotty/tools/dottydoc/core/MiniPhaseTransform.scala
@@ -4,9 +4,11 @@ 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._
object transform {
/**
@@ -43,9 +45,9 @@ object transform {
override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
for {
rootName <- rootPackages
- pack = ctx.docbase.packages[Package](rootName)
+ pack = ctx.docbase.packages(rootName)
transformed = performPackageTransform(pack)
- } yield ctx.docbase.packages(rootName) = transformed
+ } yield ctx.docbase.packagesMutable(rootName) = transformed
super.runOn(units)
}
@@ -77,6 +79,7 @@ object transform {
def traverse(ent: Entity): Entity = ent match {
case p: Package => transformEntity(p, _.packageTransformation) { p =>
val newPackage = PackageImpl(
+ p.symbol,
p.name,
p.members.map(traverse),
p.path,
@@ -84,12 +87,13 @@ object transform {
)
// Update reference in context to newPackage
- ctx.docbase.packages[Package] += (newPackage.path.mkString(".") -> newPackage)
+ ctx.docbase.packagesMutable += (newPackage.path.mkString(".") -> newPackage)
newPackage
}
case c: Class => transformEntity(c, _.classTransformation) { cls =>
ClassImpl(
+ cls.symbol,
cls.name,
cls.members.map(traverse),
cls.modifiers,
@@ -102,6 +106,7 @@ object transform {
}
case cc: CaseClass => transformEntity(cc, _.caseClassTransformation) { cc =>
CaseClassImpl(
+ cc.symbol,
cc.name,
cc.members.map(traverse),
cc.modifiers,
@@ -114,6 +119,7 @@ object transform {
}
case trt: Trait => transformEntity(trt, _.traitTransformation) { trt =>
TraitImpl(
+ trt.symbol,
trt.name,
trt.members.map(traverse),
trt.modifiers,
@@ -126,6 +132,7 @@ object transform {
}
case obj: Object => transformEntity(obj, _.objectTransformation) { obj =>
ObjectImpl(
+ obj.symbol,
obj.name,
obj.members.map(traverse),
obj.modifiers,
@@ -136,6 +143,7 @@ object transform {
}
case df: Def => transformEntity(df, _.defTransformation) { df =>
DefImpl(
+ df.symbol,
df.name,
df.modifiers,
df.path,
@@ -148,6 +156,7 @@ object transform {
}
case vl: Val => transformEntity(vl, _.valTransformation) { vl =>
ValImpl(
+ vl.symbol,
vl.name,
vl.modifiers,
vl.path,
diff --git a/dottydoc/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala b/dottydoc/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala
index ae07effa9..1aecca9e1 100644
--- a/dottydoc/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala
+++ b/dottydoc/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala
@@ -14,15 +14,16 @@ import BodyParsers._
import util.MemberLookup
import util.traversing._
import util.internal.setters._
+import util.syntax._
class LinkReturnTypes extends DocMiniPhase with TypeLinker {
override def transformDef(implicit ctx: Context) = { case df: DefImpl =>
- val returnValue = linkReference(df, df.returnValue, ctx.docbase.packages[Package].toMap)
+ val returnValue = linkReference(df, df.returnValue, ctx.docbase.packages)
df.copy(returnValue = returnValue)
}
override def transformVal(implicit ctx: Context) = { case vl: ValImpl =>
- val returnValue = linkReference(vl, vl.returnValue, ctx.docbase.packages[Package].toMap)
+ val returnValue = linkReference(vl, vl.returnValue, ctx.docbase.packages)
vl.copy(returnValue = returnValue)
}
}
@@ -31,7 +32,7 @@ class LinkParamListTypes extends DocMiniPhase with TypeLinker {
override def transformDef(implicit ctx: Context) = { case df: DefImpl =>
val newParamLists = for {
ParamListImpl(list, isImplicit) <- df.paramLists
- newList = list.map(linkReference(df, _, ctx.docbase.packages[Package].toMap))
+ newList = list.map(linkReference(df, _, ctx.docbase.packages))
} yield ParamListImpl(newList.asInstanceOf[List[NamedReference]], isImplicit)
df.copy(paramLists = newParamLists)
@@ -42,7 +43,7 @@ class LinkSuperTypes extends DocMiniPhase with TypeLinker {
def linkSuperTypes(ent: Entity with SuperTypes)(implicit ctx: Context): List[MaterializableLink] =
ent.superTypes.collect {
case UnsetLink(title, query) =>
- val packages = ctx.docbase.packages[Package].toMap
+ val packages = ctx.docbase.packages
val entityLink = makeEntityLink(ent, packages, Text(title), NoPosition, query).link
handleEntityLink(title, entityLink, ent)
}
@@ -67,13 +68,13 @@ class LinkSuperTypes extends DocMiniPhase with TypeLinker {
class LinkImplicitlyAddedTypes extends DocMiniPhase with TypeLinker {
override def transformDef(implicit ctx: Context) = {
case df: DefImpl if df.implicitlyAddedFrom.isDefined =>
- val implicitlyAddedFrom = linkReference(df, df.implicitlyAddedFrom.get, ctx.docbase.packages[Package].toMap)
+ val implicitlyAddedFrom = linkReference(df, df.implicitlyAddedFrom.get, ctx.docbase.packages)
df.copy(implicitlyAddedFrom = Some(implicitlyAddedFrom))
}
override def transformVal(implicit ctx: Context) = {
case vl: ValImpl if vl.implicitlyAddedFrom.isDefined =>
- val implicitlyAddedFrom = linkReference(vl, vl.implicitlyAddedFrom.get, ctx.docbase.packages[Package].toMap)
+ val implicitlyAddedFrom = linkReference(vl, vl.implicitlyAddedFrom.get, ctx.docbase.packages)
vl.copy(implicitlyAddedFrom = Some(implicitlyAddedFrom))
}
}
diff --git a/dottydoc/src/dotty/tools/dottydoc/core/UsecasePhase.scala b/dottydoc/src/dotty/tools/dottydoc/core/UsecasePhase.scala
new file mode 100644
index 000000000..47376eb26
--- /dev/null
+++ b/dottydoc/src/dotty/tools/dottydoc/core/UsecasePhase.scala
@@ -0,0 +1,31 @@
+package dotty.tools
+package dottydoc
+package core
+
+import dotc.core.Contexts.Context
+import dotc.ast.tpd
+
+import transform.DocMiniPhase
+import model.internal._
+import model.factories._
+import dotty.tools.dotc.core.Symbols.Symbol
+import util.syntax._
+
+class UsecasePhase extends DocMiniPhase {
+ private def defdefToDef(d: tpd.DefDef, sym: Symbol)(implicit ctx: Context) = {
+ val name = d.name.show.split("\\$").head // UseCase defs get $pos appended to their names
+ DefImpl(
+ sym,
+ name,
+ flags(d),
+ path(d.symbol).init :+ name,
+ returnType(d.tpt.tpe),
+ typeParams(d.symbol),
+ paramLists(d.symbol.info)
+ )
+ }
+
+ override def transformDef(implicit ctx: Context) = { case df: DefImpl =>
+ ctx.docbase.docstring(df.symbol).flatMap(_.usecases.headOption.map(_.tpdCode)).map(defdefToDef(_, df.symbol)).getOrElse(df)
+ }
+}
diff --git a/dottydoc/src/dotty/tools/dottydoc/model/comment/CommentExpander.scala b/dottydoc/src/dotty/tools/dottydoc/model/comment/CommentExpander.scala
deleted file mode 100644
index 32a0d8128..000000000
--- a/dottydoc/src/dotty/tools/dottydoc/model/comment/CommentExpander.scala
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Port of DocComment.scala from nsc
- * @author Martin Odersky
- * @author Felix Mulder
- */
-
-package dotty.tools
-package dottydoc
-package model
-package comment
-
-import dotc.config.Printers.dottydoc
-import dotc.core.Contexts.Context
-import dotc.core.Symbols._
-import dotc.core.Flags
-import dotc.util.Positions._
-
-import scala.collection.mutable
-
-trait CommentExpander {
- import CommentUtils._
-
- def expand(sym: Symbol, site: Symbol)(implicit ctx: Context): String = {
- val parent = if (site != NoSymbol) site else sym
- defineVariables(parent)
- expandedDocComment(sym, parent)
- }
-
- /** The cooked doc comment of symbol `sym` after variable expansion, or "" if missing.
- *
- * @param sym The symbol for which doc comment is returned
- * @param site The class for which doc comments are generated
- * @throws ExpansionLimitExceeded when more than 10 successive expansions
- * of the same string are done, which is
- * interpreted as a recursive variable definition.
- */
- def expandedDocComment(sym: Symbol, site: Symbol, docStr: String = "")(implicit ctx: Context): String = {
- // when parsing a top level class or module, use the (module-)class itself to look up variable definitions
- val parent = if ((sym.is(Flags.Module) || sym.isClass) && site.is(Flags.Package)) sym
- else site
- expandVariables(cookedDocComment(sym, docStr), sym, parent)
- }
-
- private def template(raw: String): String = {
- val sections = tagIndex(raw)
-
- val defines = sections filter { startsWithTag(raw, _, "@define") }
- val usecases = sections filter { startsWithTag(raw, _, "@usecase") }
-
- val end = startTag(raw, (defines ::: usecases).sortBy(_._1))
-
- if (end == raw.length - 2) raw else raw.substring(0, end) + "*/"
- }
-
- def defines(raw: String): List[String] = {
- val sections = tagIndex(raw)
- val defines = sections filter { startsWithTag(raw, _, "@define") }
- val usecases = sections filter { startsWithTag(raw, _, "@usecase") }
- val end = startTag(raw, (defines ::: usecases).sortBy(_._1))
-
- defines map { case (start, end) => raw.substring(start, end) }
- }
-
- private def replaceInheritDocToInheritdoc(docStr: String): String =
- docStr.replaceAll("""\{@inheritDoc\p{Zs}*\}""", "@inheritdoc")
-
- /** The cooked doc comment of an overridden symbol */
- protected def superComment(sym: Symbol)(implicit ctx: Context): Option[String] =
- allInheritedOverriddenSymbols(sym).iterator map (x => cookedDocComment(x)) find (_ != "")
-
- private val cookedDocComments = mutable.HashMap[Symbol, String]()
-
- /** The raw doc comment of symbol `sym`, minus usecase and define sections, augmented by
- * missing sections of an inherited doc comment.
- * If a symbol does not have a doc comment but some overridden version of it does,
- * the doc comment of the overridden version is copied instead.
- */
- def cookedDocComment(sym: Symbol, docStr: String = "")(implicit ctx: Context): String = cookedDocComments.getOrElseUpdate(sym, {
- var ownComment =
- if (docStr.length == 0) ctx.docbase.docstring(sym).map(c => template(c.chrs)).getOrElse("")
- else template(docStr)
- ownComment = replaceInheritDocToInheritdoc(ownComment)
-
- superComment(sym) match {
- case None =>
- // SI-8210 - The warning would be false negative when this symbol is a setter
- if (ownComment.indexOf("@inheritdoc") != -1 && ! sym.isSetter)
- dottydoc.println(s"${sym.pos}: the comment for ${sym} contains @inheritdoc, but no parent comment is available to inherit from.")
- ownComment.replaceAllLiterally("@inheritdoc", "<invalid inheritdoc annotation>")
- case Some(sc) =>
- if (ownComment == "") sc
- else expandInheritdoc(sc, merge(sc, ownComment, sym), sym)
- }
- })
-
- private def isMovable(str: String, sec: (Int, Int)): Boolean =
- startsWithTag(str, sec, "@param") ||
- startsWithTag(str, sec, "@tparam") ||
- startsWithTag(str, sec, "@return")
-
- def merge(src: String, dst: String, sym: Symbol, copyFirstPara: Boolean = false): String = {
- val srcSections = tagIndex(src)
- val dstSections = tagIndex(dst)
- val srcParams = paramDocs(src, "@param", srcSections)
- val dstParams = paramDocs(dst, "@param", dstSections)
- val srcTParams = paramDocs(src, "@tparam", srcSections)
- val dstTParams = paramDocs(dst, "@tparam", dstSections)
- val out = new StringBuilder
- var copied = 0
- var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _)))
-
- if (copyFirstPara) {
- val eop = // end of comment body (first para), which is delimited by blank line, or tag, or end of comment
- (findNext(src, 0)(src.charAt(_) == '\n')) min startTag(src, srcSections)
- out append src.substring(0, eop).trim
- copied = 3
- tocopy = 3
- }
-
- def mergeSection(srcSec: Option[(Int, Int)], dstSec: Option[(Int, Int)]) = dstSec match {
- case Some((start, end)) =>
- if (end > tocopy) tocopy = end
- case None =>
- srcSec match {
- case Some((start1, end1)) => {
- out append dst.substring(copied, tocopy).trim
- out append "\n"
- copied = tocopy
- out append src.substring(start1, end1).trim
- }
- case None =>
- }
- }
-
- //TODO: enable this once you know how to get `sym.paramss`
- /*
- for (params <- sym.paramss; param <- params)
- mergeSection(srcParams get param.name.toString, dstParams get param.name.toString)
- for (tparam <- sym.typeParams)
- mergeSection(srcTParams get tparam.name.toString, dstTParams get tparam.name.toString)
-
- mergeSection(returnDoc(src, srcSections), returnDoc(dst, dstSections))
- mergeSection(groupDoc(src, srcSections), groupDoc(dst, dstSections))
- */
-
- if (out.length == 0) dst
- else {
- out append dst.substring(copied)
- out.toString
- }
- }
-
- /**
- * Expand inheritdoc tags
- * - for the main comment we transform the inheritdoc into the super variable,
- * and the variable expansion can expand it further
- * - for the param, tparam and throws sections we must replace comments on the spot
- *
- * This is done separately, for two reasons:
- * 1. It takes longer to run compared to merge
- * 2. The inheritdoc annotation should not be used very often, as building the comment from pieces severely
- * impacts performance
- *
- * @param parent The source (or parent) comment
- * @param child The child (overriding member or usecase) comment
- * @param sym The child symbol
- * @return The child comment with the inheritdoc sections expanded
- */
- def expandInheritdoc(parent: String, child: String, sym: Symbol): String =
- if (child.indexOf("@inheritdoc") == -1)
- child
- else {
- val parentSections = tagIndex(parent)
- val childSections = tagIndex(child)
- val parentTagMap = sectionTagMap(parent, parentSections)
- val parentNamedParams = Map() +
- ("@param" -> paramDocs(parent, "@param", parentSections)) +
- ("@tparam" -> paramDocs(parent, "@tparam", parentSections)) +
- ("@throws" -> paramDocs(parent, "@throws", parentSections))
-
- val out = new StringBuilder
-
- def replaceInheritdoc(childSection: String, parentSection: => String) =
- if (childSection.indexOf("@inheritdoc") == -1)
- childSection
- else
- childSection.replaceAllLiterally("@inheritdoc", parentSection)
-
- def getParentSection(section: (Int, Int)): String = {
-
- def getSectionHeader = extractSectionTag(child, section) match {
- case param@("@param"|"@tparam"|"@throws") => param + " " + extractSectionParam(child, section)
- case other => other
- }
-
- def sectionString(param: String, paramMap: Map[String, (Int, Int)]): String =
- paramMap.get(param) match {
- case Some(section) =>
- // Cleanup the section tag and parameter
- val sectionTextBounds = extractSectionText(parent, section)
- cleanupSectionText(parent.substring(sectionTextBounds._1, sectionTextBounds._2))
- case None =>
- dottydoc.println(s"""${sym.pos}: the """" + getSectionHeader + "\" annotation of the " + sym +
- " comment contains @inheritdoc, but the corresponding section in the parent is not defined.")
- "<invalid inheritdoc annotation>"
- }
-
- child.substring(section._1, section._1 + 7) match {
- case param@("@param "|"@tparam"|"@throws") =>
- sectionString(extractSectionParam(child, section), parentNamedParams(param.trim))
- case _ =>
- sectionString(extractSectionTag(child, section), parentTagMap)
- }
- }
-
- def mainComment(str: String, sections: List[(Int, Int)]): String =
- if (str.trim.length > 3)
- str.trim.substring(3, startTag(str, sections))
- else
- ""
-
- // Append main comment
- out.append("/**")
- out.append(replaceInheritdoc(mainComment(child, childSections), mainComment(parent, parentSections)))
-
- // Append sections
- for (section <- childSections)
- out.append(replaceInheritdoc(child.substring(section._1, section._2), getParentSection(section)))
-
- out.append("*/")
- out.toString
- }
-
- protected def expandVariables(initialStr: String, sym: Symbol, site: Symbol)(implicit ctx: Context): String = {
- val expandLimit = 10
-
- def expandInternal(str: String, depth: Int): String = {
- if (depth >= expandLimit)
- throw new ExpansionLimitExceeded(str)
-
- val out = new StringBuilder
- var copied, idx = 0
- // excluding variables written as \$foo so we can use them when
- // necessary to document things like Symbol#decode
- def isEscaped = idx > 0 && str.charAt(idx - 1) == '\\'
- while (idx < str.length) {
- if ((str charAt idx) != '$' || isEscaped)
- idx += 1
- else {
- val vstart = idx
- idx = skipVariable(str, idx + 1)
- def replaceWith(repl: String) {
- out append str.substring(copied, vstart)
- out append repl
- copied = idx
- }
- variableName(str.substring(vstart + 1, idx)) match {
- case "super" =>
- superComment(sym) foreach { sc =>
- val superSections = tagIndex(sc)
- replaceWith(sc.substring(3, startTag(sc, superSections)))
- for (sec @ (start, end) <- superSections)
- if (!isMovable(sc, sec)) out append sc.substring(start, end)
- }
- case "" => idx += 1
- case vname =>
- lookupVariable(vname, site) match {
- case Some(replacement) => replaceWith(replacement)
- case None =>
- dottydoc.println(s"Variable $vname undefined in comment for $sym in $site")
- }
- }
- }
- }
- if (out.length == 0) str
- else {
- out append str.substring(copied)
- expandInternal(out.toString, depth + 1)
- }
- }
-
- // We suppressed expanding \$ throughout the recursion, and now we
- // need to replace \$ with $ so it looks as intended.
- expandInternal(initialStr, 0).replaceAllLiterally("""\$""", "$")
- }
-
- def defineVariables(sym: Symbol)(implicit ctx: Context) = {
- val Trim = "(?s)^[\\s&&[^\n\r]]*(.*?)\\s*$".r
-
- val raw = ctx.docbase.docstring(sym).map(_.chrs).getOrElse("")
- defs(sym) ++= defines(raw).map {
- str => {
- val start = skipWhitespace(str, "@define".length)
- val (key, value) = str.splitAt(skipVariable(str, start))
- key.drop(start) -> value
- }
- } map {
- case (key, Trim(value)) =>
- variableName(key) -> value.replaceAll("\\s+\\*+$", "")
- }
- }
-
- /** Maps symbols to the variable -> replacement maps that are defined
- * in their doc comments
- */
- private val defs = mutable.HashMap[Symbol, Map[String, String]]() withDefaultValue Map()
-
- /** Lookup definition of variable.
- *
- * @param vble The variable for which a definition is searched
- * @param site The class for which doc comments are generated
- */
- def lookupVariable(vble: String, site: Symbol)(implicit ctx: Context): Option[String] = site match {
- case NoSymbol => None
- case _ =>
- val searchList =
- if (site.flags.is(Flags.Module)) site :: site.info.baseClasses
- else site.info.baseClasses
-
- searchList collectFirst { case x if defs(x) contains vble => defs(x)(vble) } match {
- case Some(str) if str startsWith "$" => lookupVariable(str.tail, site)
- case res => res orElse lookupVariable(vble, site.owner)
- }
- }
-
- /** The position of the raw doc comment of symbol `sym`, or NoPosition if missing
- * If a symbol does not have a doc comment but some overridden version of it does,
- * the position of the doc comment of the overridden version is returned instead.
- */
- def docCommentPos(sym: Symbol)(implicit ctx: Context): Position =
- ctx.docbase.docstring(sym).map(_.pos).getOrElse(NoPosition)
-
- /** A version which doesn't consider self types, as a temporary measure:
- * an infinite loop has broken out between superComment and cookedDocComment
- * since r23926.
- */
- private def allInheritedOverriddenSymbols(sym: Symbol)(implicit ctx: Context): List[Symbol] = {
- if (!sym.owner.isClass) Nil
- else sym.allOverriddenSymbols.toList.filter(_ != NoSymbol) //TODO: could also be `sym.owner.allOverrid..`
- //else sym.owner.ancestors map (sym overriddenSymbol _) filter (_ != NoSymbol)
- }
-
- class ExpansionLimitExceeded(str: String) extends Exception
-}
diff --git a/dottydoc/src/dotty/tools/dottydoc/model/comment/CommentUtils.scala b/dottydoc/src/dotty/tools/dottydoc/model/comment/CommentUtils.scala
deleted file mode 100644
index e5307bd3c..000000000
--- a/dottydoc/src/dotty/tools/dottydoc/model/comment/CommentUtils.scala
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Port of DocStrings.scala from nsc
- * @author Martin Odersky
- * @author Felix Mulder
- */
-
-package dotty.tools
-package dottydoc
-package model
-package comment
-
-import scala.reflect.internal.Chars._
-
-object CommentUtils {
-
- /** Returns index of string `str` following `start` skipping longest
- * sequence of whitespace characters characters (but no newlines)
- */
- def skipWhitespace(str: String, start: Int): Int =
- if (start < str.length && isWhitespace(str charAt start)) skipWhitespace(str, start + 1)
- else start
-
- /** Returns index of string `str` following `start` skipping
- * sequence of identifier characters.
- */
- def skipIdent(str: String, start: Int): Int =
- if (start < str.length && isIdentifierPart(str charAt start)) skipIdent(str, start + 1)
- else start
-
- /** Returns index of string `str` following `start` skipping
- * sequence of identifier characters.
- */
- def skipTag(str: String, start: Int): Int =
- if (start < str.length && (str charAt start) == '@') skipIdent(str, start + 1)
- else start
-
-
- /** Returns index of string `str` after `start` skipping longest
- * sequence of space and tab characters, possibly also containing
- * a single `*` character or the `/``**` sequence.
- * @pre start == str.length || str(start) == `\n`
- */
- def skipLineLead(str: String, start: Int): Int =
- if (start == str.length) start
- else {
- val idx = skipWhitespace(str, start + 1)
- if (idx < str.length && (str charAt idx) == '*') skipWhitespace(str, idx + 1)
- else if (idx + 2 < str.length && (str charAt idx) == '/' && (str charAt (idx + 1)) == '*' && (str charAt (idx + 2)) == '*')
- skipWhitespace(str, idx + 3)
- else idx
- }
-
- /** Skips to next occurrence of `\n` or to the position after the `/``**` sequence following index `start`.
- */
- def skipToEol(str: String, start: Int): Int =
- if (start + 2 < str.length && (str charAt start) == '/' && (str charAt (start + 1)) == '*' && (str charAt (start + 2)) == '*') start + 3
- else if (start < str.length && (str charAt start) != '\n') skipToEol(str, start + 1)
- else start
-
- /** Returns first index following `start` and starting a line (i.e. after skipLineLead) or starting the comment
- * which satisfies predicate `p`.
- */
- def findNext(str: String, start: Int)(p: Int => Boolean): Int = {
- val idx = skipLineLead(str, skipToEol(str, start))
- if (idx < str.length && !p(idx)) findNext(str, idx)(p)
- else idx
- }
-
- /** Return first index following `start` and starting a line (i.e. after skipLineLead)
- * which satisfies predicate `p`.
- */
- def findAll(str: String, start: Int)(p: Int => Boolean): List[Int] = {
- val idx = findNext(str, start)(p)
- if (idx == str.length) List()
- else idx :: findAll(str, idx)(p)
- }
-
- /** Produces a string index, which is a list of `sections`, i.e
- * pairs of start/end positions of all tagged sections in the string.
- * Every section starts with an at sign and extends to the next at sign,
- * or to the end of the comment string, but excluding the final two
- * characters which terminate the comment.
- *
- * Also take usecases into account - they need to expand until the next
- * usecase or the end of the string, as they might include other sections
- * of their own
- */
- def tagIndex(str: String, p: Int => Boolean = (idx => true)): List[(Int, Int)] = {
- var indices = findAll(str, 0) (idx => str(idx) == '@' && p(idx))
- indices = mergeUsecaseSections(str, indices)
- indices = mergeInheritdocSections(str, indices)
-
- indices match {
- case List() => List()
- case idxs => idxs zip (idxs.tail ::: List(str.length - 2))
- }
- }
-
- /**
- * Merge sections following an usecase into the usecase comment, so they
- * can override the parent symbol's sections
- */
- def mergeUsecaseSections(str: String, idxs: List[Int]): List[Int] = {
- idxs.indexWhere(str.startsWith("@usecase", _)) match {
- case firstUCIndex if firstUCIndex != -1 =>
- val commentSections = idxs.take(firstUCIndex)
- val usecaseSections = idxs.drop(firstUCIndex).filter(str.startsWith("@usecase", _))
- commentSections ::: usecaseSections
- case _ =>
- idxs
- }
- }
-
- /**
- * Merge the inheritdoc sections, as they never make sense on their own
- */
- def mergeInheritdocSections(str: String, idxs: List[Int]): List[Int] =
- idxs.filterNot(str.startsWith("@inheritdoc", _))
-
- /** Does interval `iv` start with given `tag`?
- */
- def startsWithTag(str: String, section: (Int, Int), tag: String): Boolean =
- startsWithTag(str, section._1, tag)
-
- def startsWithTag(str: String, start: Int, tag: String): Boolean =
- str.startsWith(tag, start) && !isIdentifierPart(str charAt (start + tag.length))
-
- /** The first start tag of a list of tag intervals,
- * or the end of the whole comment string - 2 if list is empty
- */
- def startTag(str: String, sections: List[(Int, Int)]) = sections match {
- case Nil => str.length - 2
- case (start, _) :: _ => start
- }
-
- /** A map from parameter names to start/end indices describing all parameter
- * sections in `str` tagged with `tag`, where `sections` is the index of `str`.
- */
- def paramDocs(str: String, tag: String, sections: List[(Int, Int)]): Map[String, (Int, Int)] =
- Map() ++ {
- for (section <- sections if startsWithTag(str, section, tag)) yield {
- val start = skipWhitespace(str, section._1 + tag.length)
- str.substring(start, skipIdent(str, start)) -> section
- }
- }
-
- /** Optionally start and end index of return section in `str`, or `None`
- * if `str` does not have a @group. */
- def groupDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] =
- sections find (startsWithTag(str, _, "@group"))
-
-
- /** Optionally start and end index of return section in `str`, or `None`
- * if `str` does not have a @return.
- */
- def returnDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] =
- sections find (startsWithTag(str, _, "@return"))
-
- /** Extracts variable name from a string, stripping any pair of surrounding braces */
- def variableName(str: String): String =
- if (str.length >= 2 && (str charAt 0) == '{' && (str charAt (str.length - 1)) == '}')
- str.substring(1, str.length - 1)
- else
- str
-
- /** Returns index following variable, or start index if no variable was recognized
- */
- def skipVariable(str: String, start: Int): Int = {
- var idx = start
- if (idx < str.length && (str charAt idx) == '{') {
- do idx += 1
- while (idx < str.length && (str charAt idx) != '}')
- if (idx < str.length) idx + 1 else start
- } else {
- while (idx < str.length && isVarPart(str charAt idx))
- idx += 1
- idx
- }
- }
-
- /** A map from the section tag to section parameters */
- def sectionTagMap(str: String, sections: List[(Int, Int)]): Map[String, (Int, Int)] =
- Map() ++ {
- for (section <- sections) yield
- extractSectionTag(str, section) -> section
- }
-
- /** Extract the section tag, treating the section tag as an identifier */
- def extractSectionTag(str: String, section: (Int, Int)): String =
- str.substring(section._1, skipTag(str, section._1))
-
- /** Extract the section parameter */
- def extractSectionParam(str: String, section: (Int, Int)): String = {
- val (beg, _) = section
- assert(str.startsWith("@param", beg) ||
- str.startsWith("@tparam", beg) ||
- str.startsWith("@throws", beg))
-
- val start = skipWhitespace(str, skipTag(str, beg))
- val finish = skipIdent(str, start)
-
- str.substring(start, finish)
- }
-
- /** Extract the section text, except for the tag and comment newlines */
- def extractSectionText(str: String, section: (Int, Int)): (Int, Int) = {
- val (beg, end) = section
- if (str.startsWith("@param", beg) ||
- str.startsWith("@tparam", beg) ||
- str.startsWith("@throws", beg))
- (skipWhitespace(str, skipIdent(str, skipWhitespace(str, skipTag(str, beg)))), end)
- else
- (skipWhitespace(str, skipTag(str, beg)), end)
- }
-
- /** Cleanup section text */
- def cleanupSectionText(str: String) = {
- var result = str.trim.replaceAll("\n\\s+\\*\\s+", " \n")
- while (result.endsWith("\n"))
- result = result.substring(0, str.length - 1)
- result
- }
-
-}
diff --git a/dottydoc/src/dotty/tools/dottydoc/model/entities.scala b/dottydoc/src/dotty/tools/dottydoc/model/entities.scala
index 76792070c..52379f9ad 100644
--- a/dottydoc/src/dotty/tools/dottydoc/model/entities.scala
+++ b/dottydoc/src/dotty/tools/dottydoc/model/entities.scala
@@ -3,8 +3,11 @@ package model
import comment._
import references._
+import dotty.tools.dotc.core.Symbols.{ Symbol, NoSymbol }
trait Entity {
+ def symbol: Symbol
+
def name: String
/** Path from root, i.e. `scala.Option$` */
@@ -103,6 +106,7 @@ trait Var extends Entity with Modifiers with ReturnValue {
trait NonEntity extends Entity {
val name = ""
+ val symbol = NoSymbol
val comment = None
val path = Nil
val kind = ""
diff --git a/dottydoc/src/dotty/tools/dottydoc/model/internal.scala b/dottydoc/src/dotty/tools/dottydoc/model/internal.scala
index 6afb1ec9b..09f642d0b 100644
--- a/dottydoc/src/dotty/tools/dottydoc/model/internal.scala
+++ b/dottydoc/src/dotty/tools/dottydoc/model/internal.scala
@@ -3,6 +3,7 @@ package model
import comment.Comment
import references._
+import dotty.tools.dotc.core.Symbols.Symbol
object internal {
@@ -11,6 +12,7 @@ object internal {
}
final case class PackageImpl(
+ symbol: Symbol,
name: String,
var members: List[Entity],
path: List[String],
@@ -21,6 +23,7 @@ object internal {
}
final case class ClassImpl(
+ symbol: Symbol,
name: String,
members: List[Entity],
modifiers: List[String],
@@ -32,6 +35,7 @@ object internal {
) extends Class with Impl
final case class CaseClassImpl(
+ symbol: Symbol,
name: String,
members: List[Entity],
modifiers: List[String],
@@ -43,6 +47,7 @@ object internal {
) extends CaseClass with Impl
final case class TraitImpl(
+ symbol: Symbol,
name: String,
members: List[Entity],
modifiers: List[String],
@@ -54,6 +59,7 @@ object internal {
) extends Trait with Impl
final case class ObjectImpl(
+ symbol: Symbol,
name: String,
members: List[Entity],
modifiers: List[String],
@@ -63,6 +69,7 @@ object internal {
) extends Object with Impl
final case class DefImpl(
+ symbol: Symbol,
name: String,
modifiers: List[String],
path: List[String],
@@ -74,6 +81,7 @@ object internal {
) extends Def with Impl
final case class ValImpl(
+ symbol: Symbol,
name: String,
modifiers: List[String],
path: List[String],
diff --git a/dottydoc/src/dotty/tools/dottydoc/model/parsers.scala b/dottydoc/src/dotty/tools/dottydoc/model/parsers.scala
deleted file mode 100644
index fa54163e5..000000000
--- a/dottydoc/src/dotty/tools/dottydoc/model/parsers.scala
+++ /dev/null
@@ -1,98 +0,0 @@
-package dotty.tools
-package dottydoc
-package model
-
-import dotc.core.Symbols.Symbol
-import dotc.core.Contexts.Context
-import dotc.util.Positions.NoPosition
-
-object parsers {
- import comment._
- import BodyParsers._
- import model.internal._
- import util.MemberLookup
- import util.traversing._
- import util.internal.setters._
-
- class WikiParser extends CommentCleaner with CommentParser with CommentExpander {
- private[this] var commentCache: Map[String, (Entity, Map[String, Package]) => Option[Comment]] = Map.empty
-
- /** Parses comment and returns the path to the entity with an optional comment
- *
- * The idea here is to use this fact to create `Future[Seq[(String, Option[Comment]]]`
- * which can then be awaited near the end of the run - before the pickling.
- */
- def parseHtml(sym: Symbol, parent: Symbol, entity: Entity, packages: Map[String, Package])(implicit ctx: Context): (String, Option[Comment]) = {
- val cmt = ctx.docbase.docstring(sym).map { d =>
- val expanded = expand(sym, parent)
- parse(entity, packages, clean(expanded), expanded, d.pos).toComment(_.toHtml(entity))
- }
-
- (entity.path.mkString("."), cmt)
- }
-
-
- def add(entity: Entity, symbol: Symbol, parent: Symbol, ctx: Context): Unit = {
- val commentParser = { (entity: Entity, packs: Map[String, Package]) =>
- parseHtml(symbol, parent, entity, packs)(ctx)._2
- }
-
- /** TODO: this if statement searches for doc comments in parent
- * definitions if one is not defined for the current symbol.
- *
- * It might be a good idea to factor this out of the WikiParser - since
- * it mutates the state of docbase sort of silently.
- */
- implicit val implCtx = ctx
- if (!ctx.docbase.docstring(symbol).isDefined) {
- val parentCmt =
- symbol.extendedOverriddenSymbols
- .find(ctx.docbase.docstring(_).isDefined)
- .flatMap(p => ctx.docbase.docstring(p))
-
- ctx.docbase.addDocstring(symbol, parentCmt)
- }
-
-
- val path = entity.path.mkString(".")
- if (!commentCache.contains(path) || ctx.docbase.docstring(symbol).isDefined)
- commentCache = commentCache + (path -> commentParser)
- }
-
- def +=(entity: Entity, symbol: Symbol, parent: Symbol, ctx: Context) = add(entity, symbol, parent, ctx)
-
- def size: Int = commentCache.size
-
- private def parse(entity: Entity, packs: Map[String, Package]): Option[Comment] =
- commentCache(entity.path.mkString("."))(entity, packs)
-
- def parse(packs: Map[String, Package]): Unit = {
- def rootPackages: List[String] = {
- var currentDepth = Int.MaxValue
- var packages: List[String] = Nil
-
- for (key <- packs.keys) {
- val keyDepth = key.split("\\.").length
- packages =
- if (keyDepth < currentDepth) {
- currentDepth = keyDepth
- key :: Nil
- } else if (keyDepth == currentDepth) {
- key :: packages
- } else packages
- }
-
- packages
- }
-
- for (pack <- rootPackages) {
- mutateEntities(packs(pack)) { e =>
- val comment = parse(e, packs)
- setComment(e, to = comment)
- }
- }
- }
-
- def clear(): Unit = commentCache = Map.empty
- }
-}
diff --git a/dottydoc/src/dotty/tools/dottydoc/util/syntax.scala b/dottydoc/src/dotty/tools/dottydoc/util/syntax.scala
new file mode 100644
index 000000000..dd3d21f8d
--- /dev/null
+++ b/dottydoc/src/dotty/tools/dottydoc/util/syntax.scala
@@ -0,0 +1,16 @@
+package dotty.tools
+package dottydoc
+package util
+
+import dotc.core.Contexts.Context
+import dotc.core.Comments._
+import model.Package
+import core.ContextDottydoc
+
+object syntax {
+ implicit class ContextWithContextDottydoc(val ctx: Context) extends AnyVal {
+ def docbase: ContextDottydoc = ctx.docCtx.getOrElse {
+ throw new IllegalStateException("DocBase must be set before running dottydoc phases")
+ }.asInstanceOf[ContextDottydoc]
+ }
+}
diff --git a/dottydoc/test/BaseTest.scala b/dottydoc/test/BaseTest.scala
index 46a24c579..303c476a5 100644
--- a/dottydoc/test/BaseTest.scala
+++ b/dottydoc/test/BaseTest.scala
@@ -1,13 +1,14 @@
package dotty.tools
package dottydoc
-import dotc.core.Contexts
-import Contexts.{ Context, ContextBase, FreshContext }
+import dotc.core.Contexts.{ Context, ContextBase, FreshContext }
+import dotc.core.Comments.{ ContextDoc, ContextDocstrings }
import dotc.util.SourceFile
import dotc.core.Phases.Phase
import dotc.typer.FrontEnd
-import dottydoc.core.DocASTPhase
+import dottydoc.core.{ DocASTPhase, ContextDottydoc }
import model.Package
+import dotty.tools.dottydoc.util.syntax._
trait DottyTest {
dotty.tools.dotc.parsing.Scanners // initialize keywords
@@ -19,6 +20,7 @@ trait DottyTest {
ctx.setSetting(ctx.settings.language, List("Scala2"))
ctx.setSetting(ctx.settings.YkeepComments, true)
ctx.setSetting(ctx.settings.YnoInline, true)
+ ctx.setProperty(ContextDoc, new ContextDottydoc)
base.initialize()(ctx)
ctx
}
@@ -28,7 +30,7 @@ trait DottyTest {
List(new Phase {
def phaseName = "assertionPhase"
override def run(implicit ctx: Context): Unit =
- assertion(ctx.docbase.packages[Package].toMap)
+ assertion(ctx.docbase.packages)
}) :: Nil
override def phases =
diff --git a/dottydoc/test/ConstructorTest.scala b/dottydoc/test/ConstructorTest.scala
index 8aa883022..44a05acad 100644
--- a/dottydoc/test/ConstructorTest.scala
+++ b/dottydoc/test/ConstructorTest.scala
@@ -22,7 +22,7 @@ class Constructors extends DottyTest {
checkSources(source :: Nil) { packages =>
packages("scala") match {
- case PackageImpl(_, List(cls: Class), _, _) =>
+ case PackageImpl(_, _, List(cls: Class), _, _) =>
cls.constructors.headOption match {
case Some(ParamListImpl(NamedReference("str", _, false, false) :: Nil, false) :: Nil) =>
// success!
@@ -44,7 +44,7 @@ class Constructors extends DottyTest {
checkSources(source :: Nil) { packages =>
packages("scala") match {
- case PackageImpl(_, List(cls: Class), _, _) =>
+ case PackageImpl(_, _, List(cls: Class), _, _) =>
cls.constructors match {
case (
ParamListImpl(NamedReference("str1", _, false, false) :: Nil, false) ::
@@ -69,7 +69,7 @@ class Constructors extends DottyTest {
checkSources(source :: Nil) { packages =>
packages("scala") match {
- case PackageImpl(_, List(cls: Class), _, _) =>
+ case PackageImpl(_, _, List(cls: Class), _, _) =>
cls.constructors match {
case (
ParamListImpl(NamedReference("str1", _, false, false) :: Nil, false) ::
@@ -101,7 +101,7 @@ class Constructors extends DottyTest {
checkSources(source :: Nil) { packages =>
packages("scala") match {
- case PackageImpl(_, List(cls: Class), _, _) =>
+ case PackageImpl(_, _, List(cls: Class), _, _) =>
cls.constructors match {
case (
ParamListImpl(NamedReference("main", _, false, false) :: Nil, false) :: Nil
@@ -139,7 +139,7 @@ class Constructors extends DottyTest {
checkSources(source :: Nil) { packages =>
packages("scala") match {
- case PackageImpl(_, List(cls: CaseClass, obj: Object), _, _) =>
+ case PackageImpl(_, _, List(cls: CaseClass, obj: Object), _, _) =>
cls.constructors match {
case (
ParamListImpl(NamedReference("main", _, false, false) :: Nil, false) :: Nil
@@ -172,7 +172,7 @@ class Constructors extends DottyTest {
checkSources(source :: Nil) { packages =>
packages("scala") match {
- case PackageImpl(_, List(trt: Trait), _, _) =>
+ case PackageImpl(_, _, List(trt: Trait), _, _) =>
trt.traitParams match {
case ParamListImpl(NamedReference("main", _, false, false) :: Nil, false) :: Nil =>
case _ =>
@@ -199,7 +199,7 @@ class Constructors extends DottyTest {
checkSources(source :: Nil) { packages =>
packages("scala") match {
- case PackageImpl(_, List(cc: CaseClass, _, cls: Class, trt: Trait), _, _) =>
+ case PackageImpl(_, _, List(cc: CaseClass, _, cls: Class, trt: Trait), _, _) =>
import model.json._
lazy val incorrectJson = s"The json generated for:\n$actualSource\n\nIs not correct"
assert(cc.json.contains(s""""constructors":[[{"list":[{"title":"main""""), incorrectJson)
diff --git a/dottydoc/test/PackageStructure.scala b/dottydoc/test/PackageStructure.scala
index 00caaa2c0..4e7006bfe 100644
--- a/dottydoc/test/PackageStructure.scala
+++ b/dottydoc/test/PackageStructure.scala
@@ -29,7 +29,7 @@ class PackageStructure extends DottyTest {
checkSources(source1 :: source2 :: Nil) { packages =>
packages("scala") match {
- case PackageImpl(_, List(tA, tB), _, _) =>
+ case PackageImpl(_, _, List(tA, tB), _, _) =>
assert(
tA.name == "A" && tB.name == "B",
s"trait A had name '${tA.name}' and trait B had name '${tB.name}'"
@@ -62,8 +62,9 @@ class PackageStructure extends DottyTest {
checkSources(source1 :: source2 :: Nil) { packages =>
packages("scala") match {
case PackageImpl(
+ _,
"scala",
- List(PackageImpl("scala.collection", List(tA, tB), _, _)),
+ List(PackageImpl(_, "scala.collection", List(tA, tB), _, _)),
_, _
) =>
assert(
@@ -76,7 +77,7 @@ class PackageStructure extends DottyTest {
}
packages("scala.collection") match {
- case PackageImpl("scala.collection", List(tA, tB), _, _) =>
+ case PackageImpl(_, "scala.collection", List(tA, tB), _, _) =>
assert(
tA.name == "A" && tB.name == "B",
s"trait A had name '${tA.name}' and trait B had name '${tB.name}'"
diff --git a/dottydoc/test/UsecaseTest.scala b/dottydoc/test/UsecaseTest.scala
new file mode 100644
index 000000000..b5d47e4b6
--- /dev/null
+++ b/dottydoc/test/UsecaseTest.scala
@@ -0,0 +1,234 @@
+package dotty.tools
+package dottydoc
+
+import org.junit.Test
+import org.junit.Assert._
+
+import dotc.util.SourceFile
+import model._
+import model.internal._
+import model.references._
+import util.syntax._
+
+class UsecaseTest extends DottyTest {
+ @Test def simpleUsecase = {
+ val source = new SourceFile(
+ "DefWithUseCase.scala",
+ """
+ |package scala
+ |
+ |trait Test[A] {
+ | /** Definition with a "disturbing" signature
+ | *
+ | * @usecase def foo: A
+ | */
+ | def foo[B]: A => B
+ |}
+ """.stripMargin
+ )
+
+ checkSources(source :: Nil) { packages =>
+ packages("scala") match {
+ case PackageImpl(_, _, List(trt: Trait), _, _) =>
+ val List(foo: Def) = trt.members
+
+ assert(foo.comment.isDefined, "Lost comment in transformations")
+
+ val returnValue = foo.returnValue match {
+ case ref: TypeReference => ref.title
+ case _ =>
+ assert(
+ false,
+ "Incorrect return value after usecase transformation"
+ )
+ ""
+ }
+
+ assert(
+ foo.typeParams.isEmpty,
+ "Type parameters were not stripped by usecase"
+ )
+ assert(returnValue == "A", "Incorrect return type after usecase")
+
+ assert(foo.name == "foo", s"Incorrect name after transform: ${foo.name}")
+ }
+ }
+ }
+
+ @Test def simpleUsecaseAddedArg = {
+ val source = new SourceFile(
+ "DefWithUseCase.scala",
+ """
+ |package scala
+ |
+ |trait Test[A] {
+ | /** Definition with a "disturbing" signature
+ | *
+ | * @usecase def foo(a: A): A
+ | */
+ | def foo[B]: A => B
+ |}
+ """.stripMargin
+ )
+
+ checkSources(source :: Nil) { packages =>
+ packages("scala") match {
+ case PackageImpl(_, _, List(trt: Trait), _, _) =>
+ val List(foo: Def) = trt.members
+
+ val returnValue = foo.returnValue match {
+ case ref: TypeReference => ref.title
+ case _ =>
+ assert(
+ false,
+ "Incorrect return value after usecase transformation"
+ )
+ ""
+ }
+
+ assert(
+ foo.typeParams.isEmpty,
+ "Type parameters were not stripped by usecase"
+ )
+ assert(returnValue == "A", "Incorrect return type after usecase")
+ assert(
+ foo.paramLists.head.list.head.title == "a",
+ "Incorrect parameter to function after usecase transformation"
+ )
+ assert(foo.name == "foo", s"Incorrect name after transform: ${foo.name}")
+ }
+ }
+ }
+
+ @Test def simpleTparamUsecase = {
+ val source = new SourceFile(
+ "DefWithUseCase.scala",
+ """
+ |package scala
+ |
+ |trait Test[A] {
+ | /** Definition with a "disturbing" signature
+ | *
+ | * @usecase def foo[C]: A
+ | */
+ | def foo[B]: A => B
+ |}
+ """.stripMargin
+ )
+
+ checkSources(source :: Nil) { packages =>
+ packages("scala") match {
+ case PackageImpl(_, _, List(trt: Trait), _, _) =>
+ val List(foo: Def) = trt.members
+
+ val returnValue = foo.returnValue match {
+ case ref: TypeReference => ref.title
+ case _ =>
+ assert(
+ false,
+ "Incorrect return value after usecase transformation"
+ )
+ ""
+ }
+
+ assert(
+ foo.typeParams.nonEmpty,
+ "Type parameters were incorrectly stripped by usecase"
+ )
+
+ assert(foo.typeParams.head == "C", "Incorrectly switched tparam")
+ assert(returnValue == "A", "Incorrect return type after usecase")
+
+ assert(foo.name == "foo", s"Incorrect name after transform: ${foo.name}")
+ }
+ }
+ }
+
+ @Test def expandColl = {
+ val source = new SourceFile(
+ "ExpandColl.scala",
+ """
+ |package scala
+ |
+ |/** The trait $Coll
+ | *
+ | * @define Coll Iterable
+ | */
+ |trait Iterable[A] {
+ | /** Definition with a "disturbing" signature
+ | *
+ | * @usecase def map[B](f: A => B): $Coll[B]
+ | */
+ | def map[B, M[B]](f: A => B): M[B] = ???
+ |}
+ """.stripMargin
+ )
+
+ checkSources(source :: Nil) { packages =>
+ packages("scala") match {
+ case PackageImpl(_, _, List(trt: Trait), _, _) =>
+ val List(map: Def) = trt.members
+
+ val returnValue = map.returnValue match {
+ case ref: TypeReference => ref.title
+ case _ =>
+ assert(
+ false,
+ "Incorrect return value after usecase transformation"
+ )
+ ""
+ }
+
+ assert(
+ returnValue == "Iterable",
+ "Incorrect return type after usecase transformation"
+ )
+ }
+ }
+ }
+
+ @Test def checkStripping = {
+ val source = new SourceFile(
+ "CheckStripping.scala",
+ """
+ |package scala
+ |
+ |/** The trait $Coll
+ | *
+ | * @define Coll Iterable
+ | */
+ |trait Iterable[A] {
+ | /** Definition with a "disturbing" signature
+ | *
+ | * @usecase def map[B](f: A => B): $Coll[B]
+ | */
+ | def map[B, M[B]](f: A => B): M[B] = ???
+ |}
+ """.stripMargin
+ )
+
+ checkSources(source :: Nil) { packages =>
+ packages("scala") match {
+ case PackageImpl(_, _, List(trt: Trait), _, _) =>
+ val List(map: Def) = trt.members
+ assert(map.comment.isDefined, "Lost comment in transformations")
+
+ val docstr = ctx.docbase.docstring(map.symbol).get.raw
+ assert(
+ !docstr.contains("@usecase"),
+ s"Comment should not contain usecase after stripping, but was:\n$docstr"
+ )
+ }
+ }
+ }
+
+ @Test def checkIterator =
+ checkFiles("./scala-scala/src/library/scala/collection/Iterator.scala" :: Nil) { _ =>
+ // success if typer throws no errors! :)
+ }
+
+ @Test def checkIterableLike =
+ checkFiles("./scala-scala/src/library/scala/collection/IterableLike.scala" :: Nil) { _ =>
+ // success if typer throws no errors! :)
+ }
+}