package dotty.tools package dottydoc package model package comment import dotc.core.Contexts.Context import dotc.util.Positions._ import dotty.tools.dottydoc.util.syntax._ import util.MemberLookup import com.vladsch.flexmark.ast.{ Node => MarkdownNode } import com.vladsch.flexmark.html.HtmlRenderer import com.vladsch.flexmark.parser.Parser import com.vladsch.flexmark.util.sequence.CharSubSequence object HtmlParsers { implicit class StringToMarkdown(val text: String) extends AnyVal { def toMarkdown(origin: Entity)(implicit ctx: Context): MarkdownNode = { import com.vladsch.flexmark.ast.{ Link, Visitor, VisitHandler, NodeVisitor } val inlineToHtml = InlineToHtml(origin) val node = Parser.builder(staticsite.Site.markdownOptions) .build.parse(text) 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(CharSubSequence.of(queryToUrl(linkUrl))) } }) ) linkVisitor.visit(node) node } def toMarkdownString(origin: Entity)(implicit ctx: Context): String = toMarkdown(origin).show } implicit class MarkdownToHtml(val markdown: MarkdownNode) extends AnyVal { def show(implicit ctx: Context): String = HtmlRenderer.builder(staticsite.Site.markdownOptions).build().render(markdown) def shortenAndShow(implicit ctx: Context): String = (new MarkdownShortener).shorten(markdown).show } implicit class StringToWiki(val text: String) extends AnyVal { def toWiki(origin: Entity, packages: Map[String, Package], pos: Position): Body = new WikiParser(origin, packages, text, pos, origin.symbol).document() } implicit class BodyToHtml(val body: Body) extends AnyVal { def show(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)}""" } } } } }