From 6f6dc9767badd4bcacd8f00ef0ed467bcabc6296 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Mon, 16 Jan 2017 14:05:15 +0100 Subject: Re-implement template expansion of references as filter The original implementation used the template engine to recursively expand references. This was very error-prone and with no typesafety and proper stack traces it was very hard to diagnose. As such, these two expansions (links and references) have been re-implemented as filters. --- doc-tool/src/dotty/tools/dottydoc/model/java.scala | 33 ++++-- .../dotty/tools/dottydoc/model/references.scala | 8 ++ .../tools/dottydoc/staticsite/LiquidTemplate.scala | 12 +-- .../src/dotty/tools/dottydoc/staticsite/Site.scala | 8 +- .../dotty/tools/dottydoc/staticsite/filters.scala | 116 +++++++++++++++++++++ 5 files changed, 154 insertions(+), 23 deletions(-) create mode 100644 doc-tool/src/dotty/tools/dottydoc/staticsite/filters.scala (limited to 'doc-tool/src') diff --git a/doc-tool/src/dotty/tools/dottydoc/model/java.scala b/doc-tool/src/dotty/tools/dottydoc/model/java.scala index c20206ced..c46414061 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/java.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/java.scala @@ -151,36 +151,42 @@ object java { "kind" -> "TypeReference", "title" -> title, "tpeLink" -> tpeLink.asJava, - "paramLinks" -> paramLinks.map(_.asJava).asJava + "paramLinks" -> paramLinks.map(_.asJava).asJava, + "scala" -> ref ).asJava case OrTypeReference(left, right) => Map( "kind" -> "OrTypeReference", "left" -> left.asJava, - "right" -> right.asJava + "right" -> right.asJava, + "scala" -> ref ).asJava case AndTypeReference(left, right) => Map( "kind" -> "AndTypeReference", "left" -> left.asJava, - "right" -> right.asJava + "right" -> right.asJava, + "scala" -> ref ).asJava case FunctionReference(args, returnValue) => Map( "kind" -> "FunctionReference", "args" -> args.map(_.asJava).asJava, - "returnValue" -> returnValue.asJava + "returnValue" -> returnValue.asJava, + "scala" -> ref ).asJava case TupleReference(args) => Map( "kind" -> "TupleReference", - "args" -> args.map(_.asJava).asJava + "args" -> args.map(_.asJava).asJava, + "scala" -> ref ).asJava case BoundsReference(low, high) => Map( "kind" -> "BoundsReference", "low" -> low.asJava, - "hight" -> high.asJava + "hight" -> high.asJava, + "scala" -> ref ).asJava case NamedReference(title, ref, isByName, isRepeated) => Map( @@ -188,12 +194,14 @@ object java { "title" -> title, "ref" -> ref.asJava, "isByName" -> isByName, - "isRepeated" -> isRepeated + "isRepeated" -> isRepeated, + "scala" -> ref ).asJava case ConstantReference(title) => Map( "kind" -> "ConstantReference", - "title" -> title + "title" -> title, + "scala" -> ref ).asJava } } @@ -203,19 +211,22 @@ object java { case UnsetLink(title, query) => Map( "kind" -> "UnsetLink", "title" -> title, - "query" -> query + "query" -> query, + "scala" -> link ).asJava case MaterializedLink(title, target) => Map( "kind" -> "MaterializedLink", "title" -> title, - "target" -> target + "target" -> target, + "scala" -> link ).asJava case NoLink(title, target) => Map( "kind" -> "NoLink", "title" -> title, - "target" -> target + "target" -> target, + "scala" -> link ).asJava } } diff --git a/doc-tool/src/dotty/tools/dottydoc/model/references.scala b/doc-tool/src/dotty/tools/dottydoc/model/references.scala index a28148fa7..766b2a340 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/references.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/references.scala @@ -17,4 +17,12 @@ object references { final case class UnsetLink(title: String, query: String) extends MaterializableLink final case class MaterializedLink(title: String, target: String) extends MaterializableLink final case class NoLink(title: String, target: String) extends MaterializableLink + + object AndOrTypeReference { + def unapply(ref: Reference): Option[(Reference, String, Reference)] = ref match { + case OrTypeReference(left, right) => Some((left, "|", right)) + case AndTypeReference(left, right) => Some((left, "&", right)) + case _ => None + } + } } diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/LiquidTemplate.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/LiquidTemplate.scala index 99ee54b74..a92e5d48e 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/LiquidTemplate.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/LiquidTemplate.scala @@ -12,14 +12,12 @@ case class LiquidTemplate(contents: String) extends ResourceFinder { import liqp.filters.Filter import liqp.parser.Flavor.JEKYLL import java.util.{ HashMap, Map => JMap } + import filters._ - Filter.registerFilter(new Filter("reverse") { - override def apply(value: Any, params: AnyRef*): AnyRef = { - val array = super.asArray(value) - if (array.length == 0) null - else array.reverse - } - }) + /** Register filters to static container */ + Filter.registerFilter(new Reverse) + Filter.registerFilter(new RenderReference) + Filter.registerFilter(new RenderLink) // For some reason, liqp rejects a straight conversion using `.asJava` private def toJavaMap(map: Map[String, AnyRef]): HashMap[String, Object] = diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala index 03e78024c..d314f0eab 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala @@ -10,7 +10,7 @@ import java.nio.file.Path import java.io.ByteArrayInputStream import java.nio.charset.StandardCharsets -import com.vladsch.flexmark.parser.ParserEmulationFamily +import com.vladsch.flexmark.parser.ParserEmulationProfile import com.vladsch.flexmark.parser.Parser import com.vladsch.flexmark.ext.gfm.tables.TablesExtension import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension @@ -311,9 +311,7 @@ case class Site(val root: JFile, val documentation: Map[String, Package]) extend val defaultIncludes: Map[String, String] = Map( "header.html" -> "/_includes/header.html", "scala-logo.html" -> "/_includes/scala-logo.html", - "toc.html" -> "/_includes/toc.html", - "reference.html" -> "/_includes/reference.html", - "link.html" -> "/_includes/link.html" + "toc.html" -> "/_includes/toc.html" ).mapValues(getResource) @@ -345,7 +343,7 @@ case class Site(val root: JFile, val documentation: Map[String, Package]) extend object Site { val markdownOptions: DataHolder = new MutableDataSet() - .setFrom(ParserEmulationFamily.KRAMDOWN.getOptions) + .setFrom(ParserEmulationProfile.KRAMDOWN.getOptions) .set(Parser.EXTENSIONS, Arrays.asList( TablesExtension.create(), TaskListExtension.create(), diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/filters.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/filters.scala new file mode 100644 index 000000000..bb6f314dc --- /dev/null +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/filters.scala @@ -0,0 +1,116 @@ +package dotty.tools +package dottydoc +package staticsite + +import model.references._ +import java.util.{ Map => JMap } + +import liqp.filters.Filter + +/** Custom liquid template filters */ +object filters { + + /** Used to reverse arrays: + * + * ```html + * {% assign array = "1,2,3,4,5" | split: "," %} + * {{ array | reverse }} + * ``` + */ + final class Reverse extends Filter("reverse") { + override def apply(value: Any, params: AnyRef*): AnyRef = { + val array = super.asArray(value) + if (array.length == 0) null + else array.reverse + } + } + + /** Renders a `Reference` as HTML. Example: + * + * ```html + * {{ ref | renderRef }} + * ``` + * + * where `ref` is: + * + * ```scala + * TypeReference("Seq", MaterializedLink("Seq", "../../scala/collection/Seq.html"), Nil) + * ``` + * + * will render: + * + * ```html + * Seq + * [ + * A + * ] + * ``` + */ + final class RenderReference extends Filter("renderRef") { + // might need to be rewritten to be stack safe + private def renderReference(ref: Reference): String = ref match { + case TypeReference(_, tpeLink, paramLinks) => { + if (paramLinks.nonEmpty) { + s"""|${renderLink(tpeLink)} + |[ + |${ paramLinks.map(renderReference).mkString(""", """) } + |]""".stripMargin + } + else renderLink(tpeLink) + } + + case AndOrTypeReference(left, sep, right) => + s"""${renderReference(left)} $sep ${renderReference(right)}""" + + case FunctionReference(args, returnValue) => { + val params = + if (args.isEmpty) "() => " + else if (args.tail.isEmpty) renderReference(args.head) + """ => """ + else args.map(renderReference).mkString("(", ", ", ") => ") + + params + renderReference(returnValue) + } + + case TupleReference(args) => + s"""|( + |${ args.map(renderReference).mkString(", ") } + |)""".stripMargin + + case BoundsReference(low, high) => + s"""${ renderReference(low) } <: ${ renderReference(high) }""" + + case NamedReference(title, _, _, _) => + /*dottydoc.*/println(s"received illegal named reference in rendering: $ref") + title + + case ConstantReference(title) => title + } + + override def apply(value: Any, params: AnyRef*): AnyRef = value match { + case value: JMap[String, _] @unchecked => + renderReference(value.get("scala").asInstanceOf[Reference]) + case _ => + /*dottydoc.*/println(s"couldn't render: '$value'") + null + } + } + + /** Renders a `MaterializableLink` into a HTML anchor tag. If the link is + * `NoLink` it will just return a string with the link's title. + */ + final class RenderLink extends Filter("renderLink") { + override def apply(value: Any, params: AnyRef*): AnyRef = value match { + case value: JMap[String, _] @unchecked => + renderLink(value.get("scala").asInstanceOf[MaterializableLink]) + case _ => + /*dottydoc.*/println(s"couldn't render: '$value'") + null + } + } + + private[this] def renderLink(link: MaterializableLink): String = link match { + case MaterializedLink(title, target) => + s"""$title""" + case _ => link.title + } +} -- cgit v1.2.3