aboutsummaryrefslogtreecommitdiff
path: root/dottydoc/jvm/src/dotty/tools
diff options
context:
space:
mode:
Diffstat (limited to 'dottydoc/jvm/src/dotty/tools')
-rw-r--r--dottydoc/jvm/src/dotty/tools/dottydoc/DottyDoc.scala5
-rw-r--r--dottydoc/jvm/src/dotty/tools/dottydoc/core/Phases.scala84
-rw-r--r--dottydoc/jvm/src/dotty/tools/dottydoc/model/CommentParsers.scala20
-rw-r--r--dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala18
-rw-r--r--dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala109
-rw-r--r--dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/CommentParser.scala76
-rw-r--r--dottydoc/jvm/src/dotty/tools/dottydoc/util/MemberLookup.scala74
7 files changed, 280 insertions, 106 deletions
diff --git a/dottydoc/jvm/src/dotty/tools/dottydoc/DottyDoc.scala b/dottydoc/jvm/src/dotty/tools/dottydoc/DottyDoc.scala
index 1c24b67a7..11957b01a 100644
--- a/dottydoc/jvm/src/dotty/tools/dottydoc/DottyDoc.scala
+++ b/dottydoc/jvm/src/dotty/tools/dottydoc/DottyDoc.scala
@@ -4,7 +4,7 @@ package dottydoc
import core.Phases.DocPhase
import dotc.config.CompilerCommand
import dotc.config.Printers.dottydoc
-import dotc.core.Contexts.Context
+import dotc.core.Contexts.{FreshContext, Context}
import dotc.core.Phases.Phase
import dotc.typer.FrontEnd
import dotc.{Compiler, Driver}
@@ -17,7 +17,8 @@ import dotc.{Compiler, Driver}
*
* Example:
* 1. Use the existing FrontEnd to typecheck the code being fed to dottydoc
- * 2. Create JSON from the results of the FrontEnd phase
+ * 2. Create an AST that is serializable
+ * 3. Serialize to JSON
*/
case object DottyDocCompiler extends Compiler {
override def phases: List[List[Phase]] =
diff --git a/dottydoc/jvm/src/dotty/tools/dottydoc/core/Phases.scala b/dottydoc/jvm/src/dotty/tools/dottydoc/core/Phases.scala
index f76d013a6..2e24d02d7 100644
--- a/dottydoc/jvm/src/dotty/tools/dottydoc/core/Phases.scala
+++ b/dottydoc/jvm/src/dotty/tools/dottydoc/core/Phases.scala
@@ -19,11 +19,24 @@ object Phases {
import model.EntityFactories._
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.ast.tpd._
+ import util.Traversing._
def phaseName = "docphase"
+ private[this] var commentCache: Map[Entity, (Entity, Map[String, Package]) => Option[Comment]] = Map.empty
+
+ /** 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
+ val commentParser = { (entity: Entity, packs: Map[String, Package]) =>
+ wikiParser.parseHtml(symbol, entity, packs)(ctx)
+ }
+ commentCache = commentCache + (entity -> commentParser)
+ entity
+ }
+
/** Build documentation hierarchy from existing tree */
- def collect(tree: Tree)(implicit ctx: Context): Entity = {
+ def collect(tree: Tree)(implicit ctx: Context): Entity = track(tree.symbol, ctx) {
def collectList(xs: List[Tree])(implicit ctx: Context): List[Entity] =
xs.map(collect).filter(_ != NonEntity)
@@ -36,41 +49,39 @@ object Phases {
case _ => Nil
}
- val comment = wikiParser.parseHtml(tree.symbol)
-
tree match {
/** package */
case p @ PackageDef(pid, st) =>
val name = pid.name.toString
- Package(name, collectPackageMembers(st), comment, path(p, name))
+ Package(name, collectPackageMembers(st), path(p))
/** trait */
case t @ TypeDef(n, rhs) if t.symbol.is(Flags.Trait) =>
val name = n.toString
- Trait(name, collectMembers(rhs), comment, flags(t), path(t, name))
+ Trait(name, collectMembers(rhs), flags(t), path(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.toString.dropRight(1)
- Object(name, collectMembers(rhs), comment, flags(o), path(o, name))
+ Object(name, collectMembers(rhs), flags(o), path(o))
/** class / case class */
case c @ TypeDef(name, rhs) if c.symbol.isClass =>
- (name.toString, collectMembers(rhs), comment, flags(c), path(c, name.toString)) match {
+ (name.toString, collectMembers(rhs), flags(c), path(c), None) match {
case x if c.symbol.is(Flags.CaseClass) => CaseClass.tupled(x)
case x => Class.tupled(x)
}
/** def */
case d: DefDef =>
- Def(d.name.toString, comment, flags(d), path(d, d.name.toString))
+ Def(d.name.toString, flags(d), path(d))
/** val */
case v: ValDef if !v.symbol.is(Flags.ModuleVal) =>
- Val(v.name.toString, comment, flags(v), path(v, v.name.toString))
+ Val(v.name.toString, flags(v), path(v))
case x => {
- dottydoc.println(s"Found unwanted entity: $x (${x.pos}, ${comment})\n${x.show}")
+ //dottydoc.println(s"Found unwanted entity: $x (${x.pos}, ${comment})\n${x.show}")
NonEntity
}
}
@@ -82,8 +93,7 @@ object Phases {
val path = p.path.mkString(".")
packages = packages + (path -> packages.get(path).map { ex =>
val children = (ex.children ::: p.children).distinct.sortBy(_.name)
- val comment = ex.comment.orElse(p.comment)
- Package(p.name, children, comment, p.path)
+ Package(p.name, children, p.path, None)
}.getOrElse(p))
}
@@ -94,8 +104,58 @@ object Phases {
}
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
val compUnits = super.runOn(units)
+
+ // (2) Set parent of all package children
+ def setParent(ent: Entity, to: Entity): Unit =
+ ent match {
+ case e: Class =>
+ e.parent = Some(to)
+ e.members.foreach(setParent(_, e))
+ case e: CaseClass =>
+ e.parent = Some(to)
+ e.members.foreach(setParent(_, e))
+ case e: Object =>
+ e.parent = Some(to)
+ e.members.foreach(setParent(_, e))
+ case e: Trait =>
+ e.parent = Some(to)
+ e.members.foreach(setParent(_, e))
+ case e: Val =>
+ e.parent = Some(to)
+ case e: Def =>
+ e.parent = Some(to)
+ case _ => ()
+ }
+
+ for {
+ parent <- packages.values
+ child <- parent.children
+ } setParent(child, to = parent)
+
+ // (3) Create documentation template from docstrings, with internal links
+ packages.values.foreach { p =>
+ mutateEntities(p) {
+ case e: Package => e.comment = commentCache(e)(e, packages)
+ case e: Class => e.comment = commentCache(e)(e, packages)
+ case e: CaseClass => e.comment = commentCache(e)(e, packages)
+ case e: Object => e.comment = commentCache(e)(e, packages)
+ case e: Trait => e.comment = commentCache(e)(e, packages)
+ case e: Val => e.comment = commentCache(e)(e, packages)
+ case e: Def => e.comment = commentCache(e)(e, packages)
+ case _ => ()
+ }
+ }
+
+ // (4) Write the finished model to JSON
util.IndexWriters.writeJs(packages, "../js/out")
+
+
+ // (5) Clear caches
+ commentCache = Map.empty
+
+ // Return super's result
compUnits
}
}
diff --git a/dottydoc/jvm/src/dotty/tools/dottydoc/model/CommentParsers.scala b/dottydoc/jvm/src/dotty/tools/dottydoc/model/CommentParsers.scala
index cc7bf6949..08d9d4a5a 100644
--- a/dottydoc/jvm/src/dotty/tools/dottydoc/model/CommentParsers.scala
+++ b/dottydoc/jvm/src/dotty/tools/dottydoc/model/CommentParsers.scala
@@ -8,21 +8,19 @@ import dotc.core.Contexts.Context
object CommentParsers {
import comment._
import BodyParsers._
+ import Entities.{Entity, Package}
sealed class WikiParser
extends CommentCleaner with CommentParser with CommentExpander {
- def parseHtml(sym: Symbol)(implicit ctx: Context): Option[Comment]= {
- println("Original ---------------------")
- println(ctx.base.docstring(sym).map(_.chrs).getOrElse(""))
- val expanded = expand(sym)
- println("Expanded ---------------------")
- println(expanded)
- parse(clean(expanded), expanded).toHtml match {
- case "" => None
- case x => Some(Comment(x))
- }
+ def parseHtml(sym: Symbol, entity: Entity, packages: Map[String, Package])(implicit ctx: Context): Option[Comment] =
+ ctx.base.docstring(sym).map { d =>
+ val expanded = expand(sym)
+ parse(entity, packages, clean(expanded), expanded, d.pos).toHtml(entity) match {
+ case "" => None
+ case x => Some(Comment(x))
+ }
+ }.flatten
}
- }
val wikiParser = new WikiParser
}
diff --git a/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala b/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala
index e06789e98..683fc6b19 100644
--- a/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala
+++ b/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala
@@ -3,6 +3,7 @@ package model
package comment
import scala.collection._
+import Entities.Entity
/** A body of text. A comment has a single body, which is composed of
* at least one block. Inside every body is exactly one summary (see
@@ -61,12 +62,11 @@ final case class Subscript(text: Inline) extends Inline
final case class Link(target: String, title: Inline) extends Inline
final case class Monospace(text: Inline) extends Inline
final case class Text(text: String) extends Inline
-//TODO: this should be used
-//abstract class EntityLink(val title: Inline) extends Inline { def link: LinkTo }
-//object EntityLink {
-// def apply(title: Inline, linkTo: LinkTo) = new EntityLink(title) { def link: LinkTo = linkTo }
-// def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link))
-//}
+abstract class EntityLink(val title: Inline) extends Inline { def link: LinkTo }
+object EntityLink {
+ def apply(title: Inline, linkTo: LinkTo) = new EntityLink(title) { def link: LinkTo = linkTo }
+ def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link))
+}
final case class HtmlTag(data: String) extends Inline {
private val Pattern = """(?ms)\A<(/?)(.*?)[\s>].*\z""".r
private val (isEnd, tagName) = data match {
@@ -86,3 +86,9 @@ final case class HtmlTag(data: String) extends Inline {
/** The summary of a comment, usually its first sentence. There must be exactly one summary per body. */
final case class Summary(text: Inline) extends Inline
+
+sealed trait LinkTo
+final case class LinkToMember(mbr: Entity, parent: Entity) extends LinkTo
+final case class LinkToEntity(entity: Entity) extends LinkTo
+final case class LinkToExternal(name: String, url: String) extends LinkTo
+final case class Tooltip(name: String) extends LinkTo
diff --git a/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala b/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala
index 9b6ec600d..6e7988f43 100644
--- a/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala
+++ b/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala
@@ -3,55 +3,72 @@ package model
package comment
object BodyParsers {
+ import model.Entities.{Entity, Members}
+
implicit class BodyToHtml(val body: Body) extends AnyVal {
- def toHtml: String = bodyToHtml(body)
-
- private def inlineToHtml(inl: Inline): String = inl match {
- case Chain(items) => (items map inlineToHtml).mkString
- case Italic(in) => s"<i>${inlineToHtml(in)}</i>"
- case Bold(in) => s"<b>${inlineToHtml(in)}</b>"
- case Underline(in) => s"<u>${inlineToHtml(in)}</u>"
- case Superscript(in) => s"<sup>${inlineToHtml(in)}</sup>"
- case Subscript(in) => s"<sub>${inlineToHtml(in) }</sub>"
- case Link(raw, title) => s"""<a href=$raw target="_blank">${inlineToHtml(title)}</a>"""
- case Monospace(in) => s"<code>${inlineToHtml(in)}</code>"
- case Text(text) => text
- case Summary(in) => inlineToHtml(in)
- case HtmlTag(tag) => tag
- //TODO: when we have EntityLinks, they should be enabled here too
- //case EntityLink(target, link) => linkToHtml(target, link, hasLinks = true)
- }
+ def toHtml(origin: Entity): String = {
+ def inlineToHtml(inl: Inline): String = inl match {
+ case Chain(items) => (items map inlineToHtml).mkString
+ case Italic(in) => s"<i>${inlineToHtml(in)}</i>"
+ case Bold(in) => s"<b>${inlineToHtml(in)}</b>"
+ case Underline(in) => s"<u>${inlineToHtml(in)}</u>"
+ case Superscript(in) => s"<sup>${inlineToHtml(in)}</sup>"
+ case Subscript(in) => s"<sub>${inlineToHtml(in) }</sub>"
+ case Link(raw, title) => s"""<a href=$raw target="_blank">${inlineToHtml(title)}</a>"""
+ case Monospace(in) => s"<code>${inlineToHtml(in)}</code>"
+ case Text(text) => text
+ case Summary(in) => inlineToHtml(in)
+ case HtmlTag(tag) => tag
+ case EntityLink(target, link) => enityLinkToHtml(target, link)
+ }
- private def bodyToHtml(body: Body): String =
- (body.blocks map blockToHtml).mkString
-
- private def blockToHtml(block: Block): String = block match {
- case Title(in, 1) => s"<h1>${inlineToHtml(in)}</h1>"
- case Title(in, 2) => s"<h2>${inlineToHtml(in)}</h2>"
- case Title(in, 3) => s"<h3>${inlineToHtml(in)}</h3>"
- case Title(in, _) => s"<h4>${inlineToHtml(in)}</h4>"
- case Paragraph(in) => s"<p>${inlineToHtml(in)}</p>"
- case Code(data) => s"""<pre><code class="scala">$data</code></pre>"""
- case UnorderedList(items) =>
- s"<ul>${listItemsToHtml(items)}</ul>"
- case OrderedList(items, listStyle) =>
- s"<ol class=${listStyle}>${listItemsToHtml(items)}</ol>"
- case DefinitionList(items) =>
- s"<dl>${items map { case (t, d) => s"<dt>${inlineToHtml(t)}</dt><dd>${blockToHtml(d)}</dd>" } }</dl>"
- case HorizontalRule() =>
- "<hr/>"
- }
+ def enityLinkToHtml(target: Inline, link: LinkTo) = link match {
+ case Tooltip(_) => inlineToHtml(target)
+ case LinkToExternal(n, url) => s"""<a href="$url">$n</a>"""
+ case LinkToMember(mbr, parent) =>
+ s"""<a href="${relativePath(parent)}#${mbr.name}">${mbr.name}</a>"""
+ case LinkToEntity(target: Entity) =>
+ s"""<a href="${relativePath(target)}">${target.name}</a>"""
+ }
+
+ def relativePath(target: Entity) =
+ "../" * (origin.path.length - 1) +
+ target.path.mkString("/") +
+ ".html"
+
+ def bodyToHtml(body: Body): String =
+ (body.blocks map blockToHtml).mkString
+
+ def blockToHtml(block: Block): String = block match {
+ case Title(in, 1) => s"<h1>${inlineToHtml(in)}</h1>"
+ case Title(in, 2) => s"<h2>${inlineToHtml(in)}</h2>"
+ case Title(in, 3) => s"<h3>${inlineToHtml(in)}</h3>"
+ case Title(in, _) => s"<h4>${inlineToHtml(in)}</h4>"
+ case Paragraph(in) => s"<p>${inlineToHtml(in)}</p>"
+ case Code(data) => s"""<pre><code class="scala">$data</code></pre>"""
+ case UnorderedList(items) =>
+ s"<ul>${listItemsToHtml(items)}</ul>"
+ case OrderedList(items, listStyle) =>
+ s"<ol class=${listStyle}>${listItemsToHtml(items)}</ol>"
+ case DefinitionList(items) =>
+ s"<dl>${items map { case (t, d) => s"<dt>${inlineToHtml(t)}</dt><dd>${blockToHtml(d)}</dd>" } }</dl>"
+ case HorizontalRule() =>
+ "<hr/>"
+ }
+
+ def listItemsToHtml(items: Seq[Block]) =
+ items.foldLeft(""){ (list, item) =>
+ item match {
+ case OrderedList(_, _) | UnorderedList(_) => // html requires sub ULs to be put into the last LI
+ list + s"<li>${blockToHtml(item)}</li>"
+ case Paragraph(inline) =>
+ list + s"<li>${inlineToHtml(inline)}</li>" // LIs are blocks, no need to use Ps
+ case block =>
+ list + s"<li>${blockToHtml(block)}</li>"
+ }
+ }
- private def listItemsToHtml(items: Seq[Block]) =
- items.foldLeft(""){ (list, item) =>
- item match {
- case OrderedList(_, _) | UnorderedList(_) => // html requires sub ULs to be put into the last LI
- list + s"<li>${blockToHtml(item)}</li>"
- case Paragraph(inline) =>
- list + s"<li>${inlineToHtml(inline)}</li>" // LIs are blocks, no need to use Ps
- case block =>
- list + s"<li>${blockToHtml(block)}</li>"
- }
+ bodyToHtml(body)
}
}
}
diff --git a/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/CommentParser.scala b/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/CommentParser.scala
index b0ea0773a..3c5411c5c 100644
--- a/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/CommentParser.scala
+++ b/dottydoc/jvm/src/dotty/tools/dottydoc/model/comment/CommentParser.scala
@@ -4,20 +4,29 @@ package comment
import dotty.tools.dotc.util.Positions._
import dotty.tools.dotc.core.Symbols._
+import dotty.tools.dotc.core.Contexts.Context
import scala.collection.mutable
import dotty.tools.dotc.config.Printers.dottydoc
import scala.util.matching.Regex
+import Entities.{Entity, Package}
-//TODO: re-enable pos?
-trait CommentParser {
+trait CommentParser extends util.MemberLookup {
import Regexes._
/** Parses a raw comment string into a `Comment` object.
+ * @param packages all packages parsed by Scaladoc tool, used for lookup
* @param cleanComment a cleaned comment to be parsed
* @param src the raw comment source string.
* @param pos the position of the comment in source.
*/
- def parse(comment: List[String], src: String, /*pos: Position,*/ site: Symbol = NoSymbol): Body = {
+ def parse(
+ entity: Entity,
+ packages: Map[String, Package],
+ comment: List[String],
+ src: String,
+ pos: Position,
+ site: Symbol = NoSymbol
+ )(implicit ctx: Context): Body = {
/** Parses a comment (in the form of a list of lines) to a `Comment`
* instance, recursively on lines. To do so, it splits the whole comment
@@ -136,12 +145,12 @@ trait CommentParser {
val tagsWithoutDiagram = tags.filterNot(pair => stripTags.contains(pair._1))
val bodyTags: mutable.Map[TagKey, List[Body]] =
- mutable.Map((tagsWithoutDiagram mapValues {tag => tag map (parseWikiAtSymbol(_, /*pos,*/ site))}).toSeq: _*)
+ mutable.Map((tagsWithoutDiagram mapValues {tag => tag map (parseWikiAtSymbol(entity, packages, _, pos, site))}).toSeq: _*)
def oneTag(key: SimpleTagKey, filterEmpty: Boolean = true): Option[Body] =
((bodyTags remove key): @unchecked) match {
case Some(r :: rs) if !(filterEmpty && r.blocks.isEmpty) =>
- //if (!rs.isEmpty) dottydoc.println(s"$pos: only one '@${key.name}' tag is allowed")
+ if (!rs.isEmpty) dottydoc.println(s"$pos: only one '@${key.name}' tag is allowed")
Some(r)
case _ => None
}
@@ -154,15 +163,15 @@ trait CommentParser {
bodyTags.keys.toSeq flatMap {
case stk: SymbolTagKey if (stk.name == key.name) => Some(stk)
case stk: SimpleTagKey if (stk.name == key.name) =>
- //dottydoc.println(s"$pos: tag '@${stk.name}' must be followed by a symbol name")
+ dottydoc.println(s"$pos: tag '@${stk.name}' must be followed by a symbol name")
None
case _ => None
}
val pairs: Seq[(String, Body)] =
for (key <- keys) yield {
val bs = (bodyTags remove key).get
- //if (bs.length > 1)
- //dottydoc.println(s"$pos: only one '@${key.name}' tag for symbol ${key.symbol} is allowed")
+ if (bs.length > 1)
+ dottydoc.println(s"$pos: only one '@${key.name}' tag for symbol ${key.symbol} is allowed")
(key.symbol, bs.head)
}
Map.empty[String, Body] ++ (if (filterEmpty) pairs.filterNot(_._2.blocks.isEmpty) else pairs)
@@ -171,16 +180,16 @@ trait CommentParser {
def linkedExceptions: Map[String, Body] = {
val m = allSymsOneTag(SimpleTagKey("throws"), filterEmpty = false)
- m.map { case (name,body) =>
- //val link = memberLookup(pos, name, site)
+ m.map { case (targetStr,body) =>
+ val link = lookup(entity, packages, targetStr, pos)
val newBody = body match {
case Body(List(Paragraph(Chain(content)))) =>
val descr = Text(" ") +: content
- //val entityLink = EntityLink(Monospace(Text(name)), link)
- Body(List(Paragraph(Chain(/*entityLink +: */descr))))
+ val entityLink = EntityLink(Monospace(Text(targetStr)), link)
+ Body(List(Paragraph(Chain(entityLink +: descr))))
case _ => body
}
- (name, newBody)
+ (targetStr, newBody)
}
}
@@ -214,7 +223,7 @@ trait CommentParser {
//for ((key, _) <- bodyTags)
// dottydoc.println(s"$pos: Tag '@${key.name}' is not recognised")
- parseWikiAtSymbol(docBody.toString, /*pos,*/ site)
+ parseWikiAtSymbol(entity, packages, docBody.toString, pos, site)
}
}
@@ -241,14 +250,26 @@ trait CommentParser {
* - Removed start-of-line star and one whitespace afterwards (if present).
* - Removed all end-of-line whitespace.
* - Only `endOfLine` is used to mark line endings. */
- def parseWikiAtSymbol(string: String, /*pos: Position,*/ site: Symbol): Body = new WikiParser(string, /*pos,*/ site).document()
-
- /** TODO
- *
+ def parseWikiAtSymbol(
+ entity: Entity,
+ packages: Map[String, Package],
+ string: String,
+ pos: Position,
+ site: Symbol
+ )(implicit ctx: Context): Body = new WikiParser(entity, packages, string, pos, site).document()
+
+ /** Original wikiparser from NSC
* @author Ingo Maier
* @author Manohar Jonnalagedda
- * @author Gilles Dubochet */
- protected final class WikiParser(val buffer: String, /*pos: Position,*/ site: Symbol) extends CharReader(buffer) { wiki =>
+ * @author Gilles Dubochet
+ */
+ protected final class WikiParser(
+ entity: Entity,
+ packages: Map[String, Package],
+ val buffer: String,
+ pos: Position,
+ site: Symbol
+ )(implicit ctx: Context) extends CharReader(buffer) { wiki =>
var summaryParsed = false
def document(): Body = {
@@ -335,7 +356,7 @@ trait CommentParser {
jump("{{{")
val str = readUntil("}}}")
if (char == endOfText)
- reportError(/*pos,*/ "unclosed code block")
+ reportError(pos, "unclosed code block")
else
jump("}}}")
blockEnded("code block")
@@ -349,7 +370,7 @@ trait CommentParser {
val text = inline(check("=" * inLevel))
val outLevel = repeatJump('=', inLevel)
if (inLevel != outLevel)
- reportError(/*pos,*/ "unbalanced or unclosed heading")
+ reportError(pos, "unbalanced or unclosed heading")
blockEnded("heading")
Title(text, inLevel)
}
@@ -560,9 +581,7 @@ trait CommentParser {
case (SchemeUri(uri), optTitle) =>
Link(uri, optTitle getOrElse Text(uri))
case (qualName, optTitle) =>
- //TODO: this should be enabled
- //makeEntityLink(optTitle getOrElse Text(target), pos, target, site)
- title.getOrElse(Text(qualName))
+ makeEntityLink(entity, packages, optTitle getOrElse Text(target), pos, target)
}
}
@@ -571,7 +590,7 @@ trait CommentParser {
/** {{{ eol ::= { whitespace } '\n' }}} */
def blockEnded(blockType: String): Unit = {
if (char != endOfLine && char != endOfText) {
- reportError(/*pos,*/ "no additional content on same line after " + blockType)
+ reportError(pos, "no additional content on same line after " + blockType)
jumpUntil(endOfLine)
}
while (char == endOfLine)
@@ -629,9 +648,8 @@ trait CommentParser {
}
}
- def reportError(/*pos: Position,*/ message: String) =
- //dottydoc.println(s"$pos: $message")
- dottydoc.println(s"$message")
+ def reportError(pos: Position, message: String) =
+ dottydoc.println(s"$pos: $message")
}
protected sealed class CharReader(buffer: String) { reader =>
diff --git a/dottydoc/jvm/src/dotty/tools/dottydoc/util/MemberLookup.scala b/dottydoc/jvm/src/dotty/tools/dottydoc/util/MemberLookup.scala
new file mode 100644
index 000000000..ad8d99341
--- /dev/null
+++ b/dottydoc/jvm/src/dotty/tools/dottydoc/util/MemberLookup.scala
@@ -0,0 +1,74 @@
+package dotty.tools
+package dottydoc
+package util
+
+import dotc.config.Printers.dottydoc
+import dotc.core.Contexts.Context
+import dotc.core.Flags
+import dotc.core.Names._
+import dotc.core.Symbols._
+import dotc.core.Types._
+import dotc.core.Names._
+import dotc.util.Positions._
+import model.comment._
+import model.Entities._
+
+trait MemberLookup {
+ def lookup(
+ entity: Entity,
+ packages: Map[String, Package],
+ query: String,
+ pos: Position
+ ): LinkTo = {
+ val notFound: LinkTo = Tooltip(query)
+ val querys = query.split("\\.").toList
+
+ def localLookup(ent: Entity with Members, searchStr: String): LinkTo =
+ ent.members.find(_.name == searchStr).fold(notFound)(e => LinkToEntity(e))
+
+ def downwardLookup(ent: Entity with Members, search: List[String]): LinkTo =
+ search match {
+ case Nil => notFound
+ case x :: Nil =>
+ localLookup(ent, x)
+ case x :: xs =>
+ ent
+ .members
+ .collect { case e: Entity with Members => e }
+ .find(_.name == x)
+ .fold(notFound)(e => downwardLookup(e, xs))
+ }
+
+ def globalLookup: LinkTo = {
+ def longestMatch(list: List[String]): List[String] =
+ if (list == Nil) Nil
+ else
+ packages
+ .get(list.mkString("."))
+ .map(_ => list)
+ .getOrElse(longestMatch(list.dropRight(1)))
+
+ longestMatch(querys) match {
+ case Nil => notFound
+ case xs => downwardLookup(packages(xs.mkString(".")), querys diff xs)
+ }
+ }
+
+ (querys, entity) match {
+ case (x :: Nil, e: Entity with Members) =>
+ localLookup(e, x)
+ case (x :: _, e: Entity with Members) if x == entity.name =>
+ downwardLookup(e, querys)
+ case _ =>
+ globalLookup
+ }
+ }
+
+ def makeEntityLink(
+ entity: Entity,
+ packages: Map[String, Package],
+ title: Inline,
+ pos: Position,
+ query: String
+ ): Inline = EntityLink(title, lookup(entity, packages, query, pos))
+}