Skip to content

Commit b3d2b2c

Browse files
committed
Separate ContextDocstrings from Context and make it pluggable
1 parent 73e160e commit b3d2b2c

File tree

13 files changed

+143
-109
lines changed

13 files changed

+143
-109
lines changed

dottydoc/src/dotty/tools/dottydoc/DottyDoc.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import core.transform._
77
import dotc.config.CompilerCommand
88
import dotc.config.Printers.dottydoc
99
import dotc.core.Contexts._
10+
import dotc.core.Comments.ContextDoc
1011
import dotc.core.Phases.Phase
1112
import dotc.typer.FrontEnd
1213
import dotc.{ CompilationUnit, Compiler, Driver, Run }
@@ -57,7 +58,7 @@ abstract class DocDriver extends Driver {
5758

5859
ctx.setSettings(summary.sstate)
5960
ctx.setSetting(ctx.settings.YkeepComments, true)
60-
ctx.setProperty(DocContext, new DocBase)
61+
ctx.setProperty(ContextDoc, new ContextDottydoc)
6162

6263
val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ctx)
6364
(fileNames, ctx)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dotty.tools
2+
package dottydoc
3+
package core
4+
5+
import dotc.core.Symbols.Symbol
6+
import dotc.core.Comments.ContextDocstrings
7+
import model.Package
8+
9+
class ContextDottydoc extends ContextDocstrings {
10+
import scala.collection.mutable
11+
12+
private[this] val _packages: mutable.Map[String, Package] = mutable.Map.empty
13+
def packages: Map[String, Package] = _packages.toMap
14+
def packagesMutable: mutable.Map[String, Package] = _packages
15+
16+
/** Should perhaps factorize this into caches that get flushed */
17+
private var _defs: Map[Symbol, Set[Symbol]] = Map.empty
18+
def defs(sym: Symbol): Set[Symbol] = _defs.get(sym).getOrElse(Set.empty)
19+
20+
def addDef(s: Symbol, d: Symbol): Unit = _defs = (_defs + {
21+
s -> _defs.get(s).map(xs => xs + d).getOrElse(Set(d))
22+
})
23+
}

dottydoc/src/dotty/tools/dottydoc/core/DocASTPhase.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ package core
66
import dotc.ast.Trees._
77
import dotc.CompilationUnit
88
import dotc.config.Printers.dottydoc
9-
import dotc.core.Contexts.{ Context, DocBase }
9+
import dotc.core.Contexts.Context
10+
import dotc.core.Comments.ContextDocstrings
1011
import dotc.core.Phases.Phase
1112
import dotc.core.Symbols.{ Symbol, NoSymbol }
1213

dottydoc/src/dotty/tools/dottydoc/core/MiniPhaseTransform.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package dottydoc
33
package core
44

55
import dotc.CompilationUnit
6-
import dotc.core.Contexts.{ Context, DocBase }
6+
import dotc.core.Contexts.Context
7+
import dotc.core.Comments.ContextDocstrings
78
import dotc.core.Phases.Phase
89
import model._
910
import model.internal._

dottydoc/src/dotty/tools/dottydoc/util/syntax.scala

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,15 @@ package dotty.tools
22
package dottydoc
33
package util
44

5-
import dotc.core.Contexts.{ Context, DocBase }
5+
import dotc.core.Contexts.Context
6+
import dotc.core.Comments._
67
import model.Package
8+
import core.ContextDottydoc
79

810
object syntax {
9-
implicit class RichDocContext(val ctx: Context) extends AnyVal {
10-
def docbase: DocBase = ctx.getDocbase getOrElse {
11+
implicit class ContextWithContextDottydoc(val ctx: Context) extends AnyVal {
12+
def docbase: ContextDottydoc = ctx.docCtx.getOrElse {
1113
throw new IllegalStateException("DocBase must be set before running dottydoc phases")
12-
}
13-
}
14-
15-
implicit class RichDocBase(val db: DocBase) {
16-
def packages: Map[String, Package] = db.packagesAs[Package].toMap
17-
18-
def packagesMutable: collection.mutable.Map[String, Package] =
19-
db.packagesAs[Package]
14+
}.asInstanceOf[ContextDottydoc]
2015
}
2116
}

dottydoc/test/BaseTest.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package dotty.tools
22
package dottydoc
33

4-
import dotc.core.Contexts
5-
import Contexts.{ Context, ContextBase, FreshContext, DocContext, DocBase }
4+
import dotc.core.Contexts.{ Context, ContextBase, FreshContext }
5+
import dotc.core.Comments.{ ContextDoc, ContextDocstrings }
66
import dotc.util.SourceFile
77
import dotc.core.Phases.Phase
88
import dotc.typer.FrontEnd
9-
import dottydoc.core.DocASTPhase
9+
import dottydoc.core.{ DocASTPhase, ContextDottydoc }
1010
import model.Package
1111
import dotty.tools.dottydoc.util.syntax._
1212

@@ -19,7 +19,7 @@ trait DottyTest {
1919
val ctx = base.initialCtx.fresh
2020
ctx.setSetting(ctx.settings.language, List("Scala2"))
2121
ctx.setSetting(ctx.settings.YkeepComments, true)
22-
ctx.setProperty(DocContext, new DocBase)
22+
ctx.setProperty(ContextDoc, new ContextDottydoc)
2323
base.initialize()(ctx)
2424
ctx
2525
}

src/dotty/tools/dotc/core/Comments.scala

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,40 @@ package dotty.tools
22
package dotc
33
package core
44

5-
import dotc.ast.{ untpd, tpd }
6-
import Decorators._
7-
import Symbols._
8-
import Contexts.Context
9-
import Flags.EmptyFlags
10-
import dotc.util.SourceFile
11-
import dotc.util.Positions._
12-
import dotc.util.CommentParsing._
13-
import dotc.parsing.Parsers.Parser
5+
import ast.{ untpd, tpd }
6+
import Decorators._, Symbols._, Contexts._, Flags.EmptyFlags
7+
import util.SourceFile
8+
import util.Positions._
9+
import util.CommentParsing._
10+
import util.Property.Key
11+
import parsing.Parsers.Parser
1412

1513
object Comments {
14+
val ContextDoc = new Key[ContextDocstrings]
15+
16+
/** Decorator for getting docbase out of context */
17+
implicit class CommentsContext(val ctx: Context) extends AnyVal {
18+
def docCtx: Option[ContextDocstrings] = ctx.property(ContextDoc)
19+
}
20+
21+
/** Context for Docstrings, contains basic functionality for getting
22+
* docstrings via `Symbol` and expanding templates
23+
*/
24+
class ContextDocstrings {
25+
import scala.collection.mutable
26+
27+
private[this] val _docstrings: mutable.Map[Symbol, Comment] =
28+
mutable.Map.empty
29+
30+
val templateExpander = new CommentExpander
31+
32+
def docstrings: Map[Symbol, Comment] = _docstrings.toMap
33+
34+
def docstring(sym: Symbol): Option[Comment] = _docstrings.get(sym)
35+
36+
def addDocstring(sym: Symbol, doc: Option[Comment]): Unit =
37+
doc.map(d => _docstrings += (sym -> d))
38+
}
1639

1740
abstract case class Comment(pos: Position, raw: String)(implicit ctx: Context) { self =>
1841
def isExpanded: Boolean
@@ -155,7 +178,7 @@ object Comments {
155178
*/
156179
def cookedDocComment(sym: Symbol, docStr: String = "")(implicit ctx: Context): String = cookedDocComments.getOrElseUpdate(sym, {
157180
var ownComment =
158-
if (docStr.length == 0) ctx.getDocbase.flatMap(_.docstring(sym).map(c => template(c.raw))).getOrElse("")
181+
if (docStr.length == 0) ctx.docCtx.flatMap(_.docstring(sym).map(c => template(c.raw))).getOrElse("")
159182
else template(docStr)
160183
ownComment = replaceInheritDocToInheritdoc(ownComment)
161184

@@ -365,7 +388,7 @@ object Comments {
365388
def defineVariables(sym: Symbol)(implicit ctx: Context) = {
366389
val Trim = "(?s)^[\\s&&[^\n\r]]*(.*?)\\s*$".r
367390

368-
val raw = ctx.getDocbase.flatMap(_.docstring(sym).map(_.raw)).getOrElse("")
391+
val raw = ctx.docCtx.flatMap(_.docstring(sym).map(_.raw)).getOrElse("")
369392
defs(sym) ++= defines(raw).map {
370393
str => {
371394
val start = skipWhitespace(str, "@define".length)
@@ -406,7 +429,7 @@ object Comments {
406429
* the position of the doc comment of the overridden version is returned instead.
407430
*/
408431
def docCommentPos(sym: Symbol)(implicit ctx: Context): Position =
409-
ctx.getDocbase.flatMap(_.docstring(sym).map(_.pos)).getOrElse(NoPosition)
432+
ctx.docCtx.flatMap(_.docstring(sym).map(_.pos)).getOrElse(NoPosition)
410433

411434
/** A version which doesn't consider self types, as a temporary measure:
412435
* an infinite loop has broken out between superComment and cookedDocComment

src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ object Contexts {
6969
/** The context base at the root */
7070
val base: ContextBase
7171

72-
/** Documentation base */
73-
def getDocbase = property(DocContext)
74-
7572
/** All outer contexts, ending in `base.initialCtx` and then `NoContext` */
7673
def outersIterator = new Iterator[Context] {
7774
var current = thiscontext
@@ -578,37 +575,6 @@ object Contexts {
578575
}
579576
}
580577

581-
val DocContext = new Key[DocBase]
582-
class DocBase {
583-
private[this] val _docstrings: mutable.Map[Symbol, Comment] =
584-
mutable.Map.empty
585-
586-
val templateExpander = new CommentExpander
587-
588-
def docstrings: Map[Symbol, Comment] = _docstrings.toMap
589-
590-
def docstring(sym: Symbol): Option[Comment] = _docstrings.get(sym)
591-
592-
def addDocstring(sym: Symbol, doc: Option[Comment]): Unit =
593-
doc.map(d => _docstrings += (sym -> d))
594-
595-
/*
596-
* Dottydoc places instances of `Package` in this map - but we do not want
597-
* to depend on `dottydoc` for the compiler, as such this is defined as a
598-
* map of `String -> AnyRef`
599-
*/
600-
private[this] val _packages: mutable.Map[String, AnyRef] = mutable.Map.empty
601-
def packagesAs[A]: mutable.Map[String, A] = _packages.asInstanceOf[mutable.Map[String, A]]
602-
603-
/** Should perhaps factorize this into caches that get flushed */
604-
private var _defs: Map[Symbol, Set[Symbol]] = Map.empty
605-
def defs(sym: Symbol): Set[Symbol] = _defs.get(sym).getOrElse(Set.empty)
606-
607-
def addDef(s: Symbol, d: Symbol): Unit = _defs = (_defs + {
608-
s -> _defs.get(s).map(xs => xs + d).getOrElse(Set(d))
609-
})
610-
}
611-
612578
/** The essential mutable state of a context base, collected into a common class */
613579
class ContextState {
614580
// Symbols state

src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package dotc
33
package core
44

55
import Periods._, Contexts._, Symbols._, Denotations._, Names._, NameOps._, Annotations._
6-
import Types._, Flags._, Decorators._, DenotTransformers._, StdNames._, Scopes._
6+
import Types._, Flags._, Decorators._, DenotTransformers._, StdNames._, Scopes._, Comments._
77
import NameOps._
88
import Scopes.Scope
99
import collection.mutable
@@ -1515,7 +1515,7 @@ object SymDenotations {
15151515

15161516
/** Enter a symbol in given `scope` without potentially replacing the old copy. */
15171517
def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = {
1518-
def isUsecase = ctx.property(DocContext).isDefined && sym.name.show.takeRight(4) == "$doc"
1518+
def isUsecase = ctx.docCtx.isDefined && sym.name.show.takeRight(4) == "$doc"
15191519

15201520
require((sym.denot.flagsUNSAFE is Private) || !(this is Frozen) || (scope ne this.unforcedDecls) || sym.hasAnnotation(defn.ScalaStaticAnnot) || isUsecase)
15211521
scope.enter(sym)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package dotty.tools
2+
package dotc
3+
package typer
4+
5+
import core._
6+
import Contexts._, Symbols._, Decorators._, Comments._
7+
import util.Positions._
8+
import ast.tpd
9+
10+
trait Docstrings { self: Typer =>
11+
12+
/** The Docstrings typer will handle the expansion of `@define` and
13+
* `@inheritdoc` if there is a `DocContext` present as a property in the
14+
* supplied `ctx`.
15+
*
16+
* It will also type any `@usecase` available in function definitions.
17+
*/
18+
def cookComments(syms: List[Symbol], owner: Symbol)(implicit ctx: Context): Unit =
19+
ctx.docCtx.foreach { docbase =>
20+
val relevantSyms = syms.filter(docbase.docstring(_).isDefined)
21+
relevantSyms.foreach { sym =>
22+
expandParentDocs(sym)
23+
val usecases = docbase.docstring(sym).map(_.usecases).getOrElse(Nil)
24+
25+
usecases.foreach { usecase =>
26+
enterSymbol(createSymbol(usecase.untpdCode))
27+
28+
typedStats(usecase.untpdCode :: Nil, owner) match {
29+
case List(df: tpd.DefDef) => usecase.tpdCode = df
30+
case _ => ctx.error("`@usecase` was not a valid definition", usecase.codePos)
31+
}
32+
}
33+
}
34+
}
35+
36+
private def expandParentDocs(sym: Symbol)(implicit ctx: Context): Unit =
37+
ctx.docCtx.foreach { docCtx =>
38+
docCtx.docstring(sym).foreach { cmt =>
39+
def expandDoc(owner: Symbol): Unit = if (!cmt.isExpanded) {
40+
val tplExp = docCtx.templateExpander
41+
tplExp.defineVariables(sym)
42+
43+
val newCmt = cmt
44+
.expand(tplExp.expandedDocComment(sym, owner, _))
45+
.withUsecases
46+
47+
docCtx.addDocstring(sym, Some(newCmt))
48+
}
49+
50+
if (sym ne NoSymbol) {
51+
expandParentDocs(sym.owner)
52+
expandDoc(sym.owner)
53+
}
54+
}
55+
}
56+
}

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ class Namer { typer: Typer =>
455455

456456
def setDocstring(sym: Symbol, tree: Tree)(implicit ctx: Context) = tree match {
457457
case t: MemberDef if t.rawComment.isDefined =>
458-
ctx.getDocbase.foreach(_.addDocstring(sym, t.rawComment))
458+
ctx.docCtx.foreach(_.addDocstring(sym, t.rawComment))
459459
case _ => ()
460460
}
461461

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ object Typer {
5858
assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}")
5959
}
6060

61-
class Typer extends Namer with TypeAssigner with Applications with Implicits with Dynamic with Checking {
61+
class Typer extends Namer with TypeAssigner with Applications with Implicits with Dynamic with Checking with Docstrings {
6262

6363
import Typer._
6464
import tpd.{cpy => _, _}
@@ -1242,8 +1242,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
12421242
val dummy = localDummy(cls, impl)
12431243
val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol))
12441244

1245-
if (ctx.property(DocContext).isDefined)
1246-
typedUsecases(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope)
1245+
// Expand comments and type usecases
1246+
cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope)
12471247

12481248
checkNoDoubleDefs(cls)
12491249
val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1)
@@ -1511,45 +1511,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15111511
traverse(stats)
15121512
}
15131513

1514-
private def typedUsecases(syms: List[Symbol], owner: Symbol)(implicit ctx: Context): Unit =
1515-
ctx.getDocbase.foreach { docbase =>
1516-
val relevantSyms = syms.filter(docbase.docstring(_).isDefined)
1517-
relevantSyms.foreach { sym =>
1518-
expandParentDocs(sym)
1519-
val usecases = docbase.docstring(sym).map(_.usecases).getOrElse(Nil)
1520-
1521-
usecases.foreach { usecase =>
1522-
enterSymbol(createSymbol(usecase.untpdCode))
1523-
1524-
typedStats(usecase.untpdCode :: Nil, owner) match {
1525-
case List(df: tpd.DefDef) => usecase.tpdCode = df
1526-
case _ => ctx.error("`@usecase` was not a valid definition", usecase.codePos)
1527-
}
1528-
}
1529-
}
1530-
}
1531-
1532-
private def expandParentDocs(sym: Symbol)(implicit ctx: Context): Unit =
1533-
ctx.getDocbase.foreach { docbase =>
1534-
docbase.docstring(sym).foreach { cmt =>
1535-
def expandDoc(owner: Symbol): Unit = if (!cmt.isExpanded) {
1536-
val tplExp = docbase.templateExpander
1537-
tplExp.defineVariables(sym)
1538-
1539-
val newCmt = cmt
1540-
.expand(tplExp.expandedDocComment(sym, owner, _))
1541-
.withUsecases
1542-
1543-
docbase.addDocstring(sym, Some(newCmt))
1544-
}
1545-
1546-
if (sym ne NoSymbol) {
1547-
expandParentDocs(sym.owner)
1548-
expandDoc(sym.owner)
1549-
}
1550-
}
1551-
}
1552-
15531514
def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree =
15541515
typed(tree, pt)(ctx retractMode Mode.PatternOrType)
15551516
def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = // todo: retract mode between Type and Pattern?

0 commit comments

Comments
 (0)