From 302126067d6b05b26c7f2fffe7fda5d058b32b33 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Tue, 3 Jan 2017 16:23:56 +0100 Subject: Add markdown parsing to dottydoc --- .../dotty/tools/dotc/config/ScalaSettings.scala | 1 + .../tools/dottydoc/core/ContextDottydoc.scala | 10 + .../dotty/tools/dottydoc/core/DocstringPhase.scala | 6 +- .../tools/dottydoc/core/TypeLinkingPhases.scala | 2 +- .../tools/dottydoc/model/comment/BodyParsers.scala | 82 ------- .../dottydoc/model/comment/CommentParser.scala | 104 +++++---- .../tools/dottydoc/model/comment/HtmlParsers.scala | 133 +++++++++++ .../dotty/tools/dottydoc/util/MemberLookup.scala | 14 +- doc-tool/test/DottyDocTest.scala | 1 + doc-tool/test/MarkdownTests.scala | 247 +++++++++++++++++++++ doc-tool/test/SimpleComments.scala | 1 - project/Build.scala | 2 + 12 files changed, 467 insertions(+), 136 deletions(-) delete mode 100644 doc-tool/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala create mode 100644 doc-tool/src/dotty/tools/dottydoc/model/comment/HtmlParsers.scala create mode 100644 doc-tool/test/MarkdownTests.scala diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 21a6c1165..fa4f61fe4 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -101,6 +101,7 @@ class ScalaSettings extends Settings.SettingGroup { val XoldPatmat = BooleanSetting("-Xoldpatmat", "Use the pre-2.10 pattern matcher. Otherwise, the 'virtualizing' pattern matcher is used in 2.10.") val XnoPatmatAnalysis = BooleanSetting("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") val XfullLubs = BooleanSetting("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") + val wikiSyntax = BooleanSetting("-Xwiki-syntax", "Retains the Scala2 behavior of using Wiki Syntax in Scaladoc") /** -Y "Private" settings */ 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/BodyParsers.scala b/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala deleted file mode 100644 index 8c1fa8d49..000000000 --- a/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyParsers.scala +++ /dev/null @@ -1,82 +0,0 @@ -package dotty.tools.dottydoc -package model -package comment - -object BodyParsers { - - implicit class BodyToHtml(val body: Body) extends AnyVal { - def toHtml(origin: Entity): String = { - val inlineToHtml = InlineToHtml(origin) - - def bodyToHtml(body: Body): String = - (body.blocks map blockToHtml).mkString - - def blockToHtml(block: Block): String = block match { - case Title(in, 1) => s"

${inlineToHtml(in)}

" - case Title(in, 2) => s"

${inlineToHtml(in)}

" - case Title(in, 3) => s"

${inlineToHtml(in)}

" - case Title(in, _) => s"

${inlineToHtml(in)}

" - case Paragraph(in) => s"

${inlineToHtml(in)}

" - case Code(data) => s"""
$data
""" - case UnorderedList(items) => - s"" - case OrderedList(items, listStyle) => - s"
    ${listItemsToHtml(items)}
" - case DefinitionList(items) => - s"
${items map { case (t, d) => s"
${inlineToHtml(t)}
${blockToHtml(d)}
" } }
" - case HorizontalRule() => - "
" - } - - 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"
  • ${blockToHtml(item)}
  • " - case Paragraph(inline) => - list + s"
  • ${inlineToHtml(inline)}
  • " // LIs are blocks, no need to use Ps - case block => - list + s"
  • ${blockToHtml(block)}
  • " - } - } - - bodyToHtml(body) - } - } - - case class InlineToHtml(origin: Entity) { - def apply(inline: Inline) = toHtml(inline) - - def relativePath(target: Entity) = - util.traversing.relativePath(origin, target) - - def toHtml(inline: Inline): String = inline match { - case Chain(items) => (items map toHtml).mkString - case Italic(in) => s"${toHtml(in)}" - case Bold(in) => s"${toHtml(in)}" - case Underline(in) => s"${toHtml(in)}" - case Superscript(in) => s"${toHtml(in)}" - case Subscript(in) => s"${toHtml(in) }" - case Link(raw, title) => s"""${toHtml(title)}""" - case Monospace(in) => s"${toHtml(in)}" - case Text(text) => text - case Summary(in) => toHtml(in) - case HtmlTag(tag) => tag - case EntityLink(target, link) => enityLinkToHtml(target, link) - } - - def enityLinkToHtml(target: Inline, link: LinkTo) = link match { - case Tooltip(_) => toHtml(target) - case LinkToExternal(n, url) => s"""$n""" - case LinkToEntity(t: Entity) => t match { - // Entity is a package member - case e: Entity with Members => - s"""${toHtml(target)}""" - // Entity is a Val / Def - case x => x.parent.fold(toHtml(target)) { xpar => - s"""${toHtml(target)}""" - } - } - } - } -} 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/HtmlParsers.scala b/doc-tool/src/dotty/tools/dottydoc/model/comment/HtmlParsers.scala new file mode 100644 index 000000000..27a2c2587 --- /dev/null +++ b/doc-tool/src/dotty/tools/dottydoc/model/comment/HtmlParsers.scala @@ -0,0 +1,133 @@ +package dotty.tools +package dottydoc +package model +package comment + +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 fromBody(origin: Entity): String = { + val inlineToHtml = InlineToHtml(origin) + + def bodyToHtml(body: Body): String = + (body.blocks map blockToHtml).mkString + + def blockToHtml(block: Block): String = block match { + case Title(in, 1) => s"

    ${inlineToHtml(in)}

    " + case Title(in, 2) => s"

    ${inlineToHtml(in)}

    " + case Title(in, 3) => s"

    ${inlineToHtml(in)}

    " + case Title(in, _) => s"

    ${inlineToHtml(in)}

    " + case Paragraph(in) => s"

    ${inlineToHtml(in)}

    " + case Code(data) => s"""
    $data
    """ + case UnorderedList(items) => + s"" + case OrderedList(items, listStyle) => + s"
      ${listItemsToHtml(items)}
    " + case DefinitionList(items) => + s"
    ${items map { case (t, d) => s"
    ${inlineToHtml(t)}
    ${blockToHtml(d)}
    " } }
    " + case HorizontalRule() => + "
    " + } + + 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"
  • ${blockToHtml(item)}
  • " + case Paragraph(inline) => + list + s"
  • ${inlineToHtml(inline)}
  • " // LIs are blocks, no need to use Ps + case block => + list + s"
  • ${blockToHtml(block)}
  • " + } + } + + bodyToHtml(body) + } + } + + case class InlineToHtml(origin: Entity) { + def apply(inline: Inline) = toHtml(inline) + + def relativePath(target: Entity) = + util.traversing.relativePath(origin, target) + + def toHtml(inline: Inline): String = inline match { + case Chain(items) => (items map toHtml).mkString + case Italic(in) => s"${toHtml(in)}" + case Bold(in) => s"${toHtml(in)}" + case Underline(in) => s"${toHtml(in)}" + case Superscript(in) => s"${toHtml(in)}" + case Subscript(in) => s"${toHtml(in) }" + case Link(raw, title) => s"""${toHtml(title)}""" + case Monospace(in) => s"${toHtml(in)}" + case Text(text) => text + case Summary(in) => toHtml(in) + case HtmlTag(tag) => tag + case EntityLink(target, link) => enityLinkToHtml(target, link) + } + + def enityLinkToHtml(target: Inline, link: LinkTo) = link match { + case Tooltip(_) => toHtml(target) + case LinkToExternal(n, url) => s"""$n""" + case LinkToEntity(t: Entity) => t match { + // Entity is a package member + case e: Entity with Members => + s"""${toHtml(target)}""" + // Entity is a Val / Def + case x => x.parent.fold(toHtml(target)) { xpar => + s"""${toHtml(target)}""" + } + } + } + } +} 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)) } diff --git a/doc-tool/test/DottyDocTest.scala b/doc-tool/test/DottyDocTest.scala index 67e468f9a..3f22ec8ec 100644 --- a/doc-tool/test/DottyDocTest.scala +++ b/doc-tool/test/DottyDocTest.scala @@ -20,6 +20,7 @@ trait DottyDocTest { ctx.setSetting(ctx.settings.language, List("Scala2")) ctx.setSetting(ctx.settings.YkeepComments, true) ctx.setSetting(ctx.settings.YnoInline, true) + ctx.setSetting(ctx.settings.wikiSyntax, true) ctx.setProperty(ContextDoc, new ContextDottydoc) ctx.setSetting( ctx.settings.classpath, diff --git a/doc-tool/test/MarkdownTests.scala b/doc-tool/test/MarkdownTests.scala new file mode 100644 index 000000000..e8596b634 --- /dev/null +++ b/doc-tool/test/MarkdownTests.scala @@ -0,0 +1,247 @@ +package dotty.tools +package dottydoc + +import org.junit.Test +import org.junit.Assert._ + +import dotc.core.Contexts.{ Context, ContextBase, FreshContext } +import dotc.core.Comments.{ ContextDoc, ContextDocstrings } +import dottydoc.core.ContextDottydoc + +class MarkdownTests extends DottyDocTest { + override implicit val ctx: FreshContext = { + // TODO: check if can reuse parent instead of copy-paste + val base = new ContextBase + import base.settings._ + val ctx = base.initialCtx.fresh + ctx.setSetting(ctx.settings.language, List("Scala2")) + ctx.setSetting(ctx.settings.YkeepComments, true) + ctx.setSetting(ctx.settings.YnoInline, true) + // No wiki syntax! + ctx.setSetting(ctx.settings.wikiSyntax, false) + ctx.setProperty(ContextDoc, new ContextDottydoc) + ctx.setSetting( + ctx.settings.classpath, + "../library/target/scala-2.11/dotty-library_2.11-0.1-SNAPSHOT.jar" + ) + base.initialize()(ctx) + ctx + } + + @Test def simpleMarkdown = { + val source = + """ + |package scala + | + |/** *Hello*, world! */ + |trait HelloWorld + """.stripMargin + + checkSource(source) { packages => + val traitCmt = + packages("scala") + .children.find(_.path.mkString(".") == "scala.HelloWorld") + .flatMap(_.comment.map(_.body)) + .get + .trim + + assertEquals("

    Hello, world!

    ", traitCmt) + } + } + + @Test def outerLink = { + val source = + """ + |package scala + | + |/** [out](http://www.google.com) */ + |trait HelloWorld + """.stripMargin + + checkSource(source) { packages => + val traitCmt = + packages("scala") + .children.find(_.path.mkString(".") == "scala.HelloWorld") + .flatMap(_.comment.map(_.body)) + .get + .trim + + assertEquals("""

    out

    """, traitCmt) + } + } + + @Test def relativeLink = { + val source = + """ + |package scala + | + |/** [None](./None.html) */ + |trait HelloWorld + | + |trait None + """.stripMargin + + checkSource(source) { packages => + val traitCmt = + packages("scala") + .children.find(_.path.mkString(".") == "scala.HelloWorld") + .flatMap(_.comment.map(_.body)) + .get + .trim + + assertEquals("""

    None

    """, traitCmt) + } + } + + @Test def absoluteLink = { + val source = + """ + |package scala + | + |/** [None](scala.None) */ + |trait HelloWorld + | + |trait None + """.stripMargin + + checkSource(source) { packages => + val traitCmt = + packages("scala") + .children.find(_.path.mkString(".") == "scala.HelloWorld") + .flatMap(_.comment.map(_.body)) + .get + .trim + + assertEquals("""

    None

    """, traitCmt) + } + } + + @Test def handleLists = { + val source = + """ + |package scala + | + |/** - Item1 + | * - Item2 + | * - Item3 + | */ + |trait HelloWorld + | + |trait None + """.stripMargin + + checkSource(source) { packages => + val traitCmt = + packages("scala") + .children.find(_.path.mkString(".") == "scala.HelloWorld") + .flatMap(_.comment.map(_.body)) + .get + .trim + + assertEquals( + """|""".stripMargin, traitCmt) + } + } + + @Test def handleNestedLists = { + val source = + """ + |package scala + | + |/** - Item1 + | * - Item1a + | * - Item1b + | * - Item2 + | * - Item3 + | */ + |trait HelloWorld + | + |trait None + """.stripMargin + + checkSource(source) { packages => + val traitCmt = + packages("scala") + .children.find(_.path.mkString(".") == "scala.HelloWorld") + .flatMap(_.comment.map(_.body)) + .get + .trim + + assertEquals( + """|""".stripMargin, traitCmt) + } + } + + @Test def handleCodeBlock = { + val source = + """ + |package scala + | + |/** ```scala + | * val x = 1 + 5 + | * ``` + | */ + |trait HelloWorld + | + |trait None + """.stripMargin + + checkSource(source) { packages => + val traitCmt = + packages("scala") + .children.find(_.path.mkString(".") == "scala.HelloWorld") + .flatMap(_.comment.map(_.body)) + .get + .trim + + assertEquals( + """|
    val x = 1 + 5
    +             |
    """.stripMargin, traitCmt) + } + } + + @Test def handleCodeBlockJavaDocstring = { + // the following works, but not when the start of the codeblock is on the + // first line + val source = + """ + |package scala + | + |/** + | * ```scala + | * val x = 1 + 5 + | * ``` + | */ + |trait HelloWorld + | + |trait None + """.stripMargin + + checkSource(source) { packages => + val traitCmt = + packages("scala") + .children.find(_.path.mkString(".") == "scala.HelloWorld") + .flatMap(_.comment.map(_.body)) + .get + .trim + println(traitCmt) + + assertEquals( + """|
    val x = 1 + 5
    +             |
    """.stripMargin, traitCmt) + } + } +} diff --git a/doc-tool/test/SimpleComments.scala b/doc-tool/test/SimpleComments.scala index eb9abca22..bc420feeb 100644 --- a/doc-tool/test/SimpleComments.scala +++ b/doc-tool/test/SimpleComments.scala @@ -25,5 +25,4 @@ class TestSimpleComments extends DottyDocTest { assertEquals(traitCmt, "

    Hello, world!

    ") } } - } diff --git a/project/Build.scala b/project/Build.scala index 778492624..57a915c9c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -185,6 +185,8 @@ object DottyBuild extends Build { libraryDependencies ++= Seq("org.scala-lang.modules" %% "scala-xml" % "1.0.1", "org.scala-lang.modules" %% "scala-partest" % "1.0.11" % "test", dottyOrganization % "dottydoc-client" % "0.1.0", + "com.vladsch.flexmark" % "flexmark" % "0.10.1", + "com.vladsch.flexmark" % "flexmark-ext-tables" % "0.10.1", "com.novocode" % "junit-interface" % "0.11" % "test", "com.github.spullara.mustache.java" % "compiler" % "0.9.3", "com.typesafe.sbt" % "sbt-interface" % sbtVersion.value), -- cgit v1.2.3