diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2017-01-03 16:23:56 +0100 |
---|---|---|
committer | Felix Mulder <felix.mulder@gmail.com> | 2017-01-31 14:29:15 +0100 |
commit | 302126067d6b05b26c7f2fffe7fda5d058b32b33 (patch) | |
tree | cd086eefe7d53186179f5cacb75511b01418fc85 /doc-tool/src/dotty/tools | |
parent | d2bf0b1443c094dba2e86d839bb8a1b8b9336eae (diff) | |
download | dotty-302126067d6b05b26c7f2fffe7fda5d058b32b33.tar.gz dotty-302126067d6b05b26c7f2fffe7fda5d058b32b33.tar.bz2 dotty-302126067d6b05b26c7f2fffe7fda5d058b32b33.zip |
Add markdown parsing to dottydoc
Diffstat (limited to 'doc-tool/src/dotty/tools')
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala | 10 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala | 6 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala | 2 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/model/comment/CommentParser.scala | 104 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/model/comment/HtmlParsers.scala (renamed from doc-tool/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala) | 57 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala | 14 |
6 files changed, 137 insertions, 56 deletions
diff --git a/doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala b/doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala index c60038836..f2122572b 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala @@ -5,6 +5,10 @@ package core import dotc.core.Symbols.Symbol import dotc.core.Comments.ContextDocstrings import model.Package +import com.vladsch.flexmark.parser.ParserEmulationFamily +import com.vladsch.flexmark.parser.Parser +import com.vladsch.flexmark.ext.tables.TablesExtension +import com.vladsch.flexmark.util.options.{ DataHolder, MutableDataSet } class ContextDottydoc extends ContextDocstrings { import scala.collection.mutable @@ -20,4 +24,10 @@ class ContextDottydoc extends ContextDocstrings { def addDef(s: Symbol, d: Symbol): Unit = _defs = (_defs + { s -> _defs.get(s).map(xs => xs + d).getOrElse(Set(d)) }) + + + val markdownOptions: DataHolder = + new MutableDataSet() + .setFrom(ParserEmulationFamily.KRAMDOWN.getOptions) + .set(Parser.EXTENSIONS, java.util.Collections.singleton(TablesExtension.create())) } diff --git a/doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala b/doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala index cff614528..be6e3b0e8 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala @@ -7,14 +7,14 @@ import transform.DocMiniPhase import model._ import model.internal._ import model.comment._ -import BodyParsers._ +import HtmlParsers._ 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)) + parse(ent, ctx.docbase.packages, clean(cmt.raw), cmt.raw, cmt.pos) + .toComment(_.fromBody(ent), _.fromMarkdown(ent)) } override def transformPackage(implicit ctx: Context) = { case ent: PackageImpl => diff --git a/doc-tool/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala b/doc-tool/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala index 1aecca9e1..5b13930f2 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/TypeLinkingPhases.scala @@ -10,7 +10,7 @@ import model._ import model.internal._ import model.comment._ import model.references._ -import BodyParsers._ +import HtmlParsers._ import util.MemberLookup import util.traversing._ import util.internal.setters._ diff --git a/doc-tool/src/dotty/tools/dottydoc/model/comment/CommentParser.scala b/doc-tool/src/dotty/tools/dottydoc/model/comment/CommentParser.scala index 9685b6934..02c9d2f0b 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/comment/CommentParser.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/comment/CommentParser.scala @@ -2,19 +2,23 @@ package dotty.tools.dottydoc package model package comment +import dotty.tools.dottydoc.util.syntax._ 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 com.vladsch.flexmark.ast.{ Node => MarkdownNode } +import com.vladsch.flexmark.parser.{ Parser => MarkdownParser } trait CommentParser extends util.MemberLookup { import Regexes._ import model.internal._ case class FullComment ( - body: Body, + private val parseBody: () => Body, + rawBody: String, authors: List[Body], see: List[Body], result: Option[Body], @@ -36,34 +40,52 @@ trait CommentParser extends util.MemberLookup { shortDescription: List[Body] ) { + /** The body parsed in Wiki format, should only be used when + * `-Xwiki-syntax` is passed as a command line argument. + */ + lazy val wikiBody: Body = parseBody() + + private[this] var _markdownBody: MarkdownNode = _ + def markdownBody(implicit ctx: Context): MarkdownNode = { + if (_markdownBody eq null) _markdownBody = MarkdownParser + .builder(ctx.docbase.markdownOptions).build + .parse(rawBody) + + _markdownBody + } + /** * Transform this CommentParser.FullComment to a Comment using the supplied * Body transformer */ - def toComment(transform: Body => String) = Comment( - transform(body), - short = - if (shortDescription.nonEmpty) shortDescription.map(transform).mkString - else body.summary.map(transform).getOrElse(""), - authors.map(transform), - see.map(transform), - result.map(transform), - throws.map { case (k, v) => (k, transform(v)) }, - valueParams.map { case (k, v) => (k, transform(v)) }, - typeParams.map { case (k, v) => (k, transform(v)) }, - version.map(transform), - since.map(transform), - todo.map(transform), - deprecated.map(transform), - note.map(transform), - example.map(transform), - constructor.map(transform), - group.map(transform), - groupDesc.map { case (k, v) => (k, transform(v)) }, - groupNames.map { case (k, v) => (k, transform(v)) }, - groupPrio.map { case (k, v) => (k, transform(v)) }, - hideImplicitConversions.map(transform) - ) + def toComment(fromBody: Body => String, fromMarkdown: MarkdownNode => String)(implicit ctx: Context) = + Comment( + body = + if (ctx.settings.wikiSyntax.value) fromBody(wikiBody) + else fromMarkdown(markdownBody), + short = + if (shortDescription.nonEmpty) shortDescription.map(fromBody).mkString + else if (!ctx.settings.wikiSyntax.value) fromMarkdown(markdownBody) + else wikiBody.summary.map(fromBody).getOrElse(""), + authors.map(fromBody), + see.map(fromBody), + result.map(fromBody), + throws.map { case (k, v) => (k, fromBody(v)) }, + valueParams.map { case (k, v) => (k, fromBody(v)) }, + typeParams.map { case (k, v) => (k, fromBody(v)) }, + version.map(fromBody), + since.map(fromBody), + todo.map(fromBody), + deprecated.map(fromBody), + note.map(fromBody), + example.map(fromBody), + constructor.map(fromBody), + group.map(fromBody), + groupDesc.map { case (k, v) => (k, fromBody(v)) }, + groupNames.map { case (k, v) => (k, fromBody(v)) }, + groupPrio.map { case (k, v) => (k, fromBody(v)) }, + hideImplicitConversions.map(fromBody) + ) } /** Parses a raw comment string into a `Comment` object. @@ -82,19 +104,19 @@ trait CommentParser extends util.MemberLookup { )(implicit ctx: Context): FullComment = { /** 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 - * into main body and tag bodies, then runs the `WikiParser` on each body - * before creating the comment instance. - * - * @param docBody The body of the comment parsed until now. - * @param tags All tags parsed until now. - * @param lastTagKey The last parsed tag, or `None` if the tag section - * hasn't started. Lines that are not tagged are part - * of the previous tag or, if none exists, of the body. - * @param remaining The lines that must still recursively be parsed. - * @param inCodeBlock Whether the next line is part of a code block (in - * which no tags must be read). - */ + * instance, recursively on lines. To do so, it splits the whole comment + * into main body and tag bodies, then runs the `WikiParser` on each body + * before creating the comment instance. + * + * @param docBody The body of the comment parsed until now. + * @param tags All tags parsed until now. + * @param lastTagKey The last parsed tag, or `None` if the tag section + * hasn't started. Lines that are not tagged are part + * of the previous tag or, if none exists, of the body. + * @param remaining The lines that must still recursively be parsed. + * @param inCodeBlock Whether the next line is part of a code block (in + * which no tags must be read). + */ def parseComment ( docBody: StringBuilder, tags: Map[TagKey, List[String]], @@ -234,7 +256,7 @@ trait CommentParser extends util.MemberLookup { val m = allSymsOneTag(SimpleTagKey("throws"), filterEmpty = false) m.map { case (targetStr,body) => - val link = lookup(entity, packages, targetStr, pos) + val link = lookup(entity, packages, targetStr) val newBody = body match { case Body(List(Paragraph(Chain(content)))) => val descr = Text(" ") +: content @@ -246,8 +268,10 @@ trait CommentParser extends util.MemberLookup { } } + val rawBody = docBody.toString val cmt = FullComment( - body = parseWikiAtSymbol(entity, packages, docBody.toString, pos, site), + parseBody = () => parseWikiAtSymbol(entity, packages, rawBody, pos, site), + rawBody = rawBody, authors = allTags(SimpleTagKey("author")), see = allTags(SimpleTagKey("see")), result = oneTag(SimpleTagKey("return")), diff --git a/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala b/doc-tool/src/dotty/tools/dottydoc/model/comment/HtmlParsers.scala index 8c1fa8d49..27a2c2587 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/comment/HtmlParsers.scala @@ -1,11 +1,62 @@ -package dotty.tools.dottydoc +package dotty.tools +package dottydoc package model package comment -object BodyParsers { +import dotc.core.Contexts.Context +import com.vladsch.flexmark.ast.{ Node => MarkdownNode } +import dotty.tools.dottydoc.util.syntax._ +import util.MemberLookup + +object HtmlParsers { + + implicit class MarkdownToHtml(val node: MarkdownNode) extends AnyVal { + def fromMarkdown(origin: Entity)(implicit ctx: Context): String = { + import com.vladsch.flexmark.util.sequence.CharSubSequence + import com.vladsch.flexmark.ast.{ Link, Visitor, VisitHandler, NodeVisitor } + import com.vladsch.flexmark.parser.Parser + import com.vladsch.flexmark.html.HtmlRenderer + + implicit def toCharSeq(str: String) = CharSubSequence.of(str) + + val inlineToHtml = InlineToHtml(origin) + + def isOuter(url: String) = + url.startsWith("http://") || + url.startsWith("https://") || + url.startsWith("ftp://") || + url.startsWith("ftps://") + + def isRelative(url: String) = + url.startsWith("../") || + url.startsWith("./") + + val linkVisitor = new NodeVisitor( + new VisitHandler(classOf[Link], new Visitor[Link] with MemberLookup { + def queryToUrl(link: String) = lookup(origin, ctx.docbase.packages, link) match { + case Tooltip(_) => "#" + case LinkToExternal(_, url) => url + case LinkToEntity(t: Entity) => t match { + case e: Entity with Members => inlineToHtml.relativePath(t) + case x => x.parent.fold("#") { xpar => inlineToHtml.relativePath(xpar) } + } + } + + override def visit(link: Link) = { + val linkUrl = link.getUrl.toString + if (!isOuter(linkUrl) && !isRelative(linkUrl)) + link.setUrl(queryToUrl(linkUrl)) + } + }) + ) + + linkVisitor.visit(node) + HtmlRenderer.builder(ctx.docbase.markdownOptions).build().render(node) + } + } implicit class BodyToHtml(val body: Body) extends AnyVal { - def toHtml(origin: Entity): String = { + def fromBody(origin: Entity): String = { val inlineToHtml = InlineToHtml(origin) def bodyToHtml(body: Body): String = diff --git a/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala b/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala index 40c775428..52b18f70c 100644 --- a/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala +++ b/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala @@ -17,14 +17,10 @@ import model._ trait MemberLookup { /** Performs a lookup based on the provided (pruned) query string * - * Will return a `Tooltip` if unsucessfull, otherwise a LinkToEntity or LinkToExternal + * Will return a `Tooltip` if unsucessfull, otherwise a LinkToEntity or + * LinkToExternal */ - def lookup( - entity: Entity, - packages: Map[String, Package], - query: String, - pos: Position - ): LinkTo = { + def lookup(entity: Entity, packages: Map[String, Package], query: String): LinkTo = { val notFound: LinkTo = Tooltip(query) val querys = query.split("\\.").toList @@ -78,7 +74,7 @@ trait MemberLookup { downwardLookup(e, querys) case (x :: xs, _) => if (xs.nonEmpty) globalLookup - else lookup(entity, packages, "scala." + query, pos) + else lookup(entity, packages, "scala." + query) } } @@ -88,5 +84,5 @@ trait MemberLookup { title: Inline, pos: Position, query: String - ): EntityLink = EntityLink(title, lookup(entity, packages, query, pos)) + ): EntityLink = EntityLink(title, lookup(entity, packages, query)) } |