From 57fdfb7b8524004ace92a2f2ab5eb69e9b20568b Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Tue, 3 Nov 2020 21:26:04 +0100 Subject: [PATCH 1/6] Migrate most of dokka-site in Create utils over Java Map, Set and List --- project/Build.scala | 10 +- .../dotty/tools/sbtplugin/DottyPlugin.scala | 5 +- .../src/dotty/dokka/DottyDokkaConfig.scala | 29 ++-- .../src/dotty/dokka/DottyDokkaPlugin.scala | 33 ++-- scala3doc/src/dotty/dokka/Main.scala | 1 - .../src/dotty/dokka/ScalaModuleCreator.scala | 21 ++- scala3doc/src/dotty/dokka/compat.scala | 59 +++++++ scala3doc/src/dotty/dokka/model/api/api.scala | 2 - .../dokka/model/api/internalExtensions.scala | 3 - scala3doc/src/dotty/dokka/model/extras.scala | 1 - .../src/dotty/dokka/model/scalaModel.scala | 7 +- .../dotty/dokka/site/StaticSiteContext.scala | 136 +++++++++++++++ scala3doc/src/dotty/dokka/site/common.scala | 74 ++++++++ .../src/dotty/dokka/site/contentNodes.scala | 24 +++ .../dotty/dokka/site/locationProvider.scala | 42 +++++ .../src/dotty/dokka/site/processors.scala | 161 ++++++++++++++++++ .../src/dotty/dokka/site/templates.scala | 126 ++++++++++++++ .../src/dotty/dokka/tasty/BasicSupport.scala | 4 +- .../dotty/dokka/tasty/ClassLikeSupport.scala | 24 +-- .../dotty/dokka/tasty/PackageSupport.scala | 15 +- scala3doc/src/dotty/dokka/tasty/SymOps.scala | 2 +- .../src/dotty/dokka/tasty/TastyParser.scala | 24 +-- .../tasty/comments/MarkdownConverter.scala | 6 +- .../dotty/dokka/tasty/comments/package.scala | 5 +- .../ScalaSourceLinksTransformer.scala | 4 +- .../translators/ScalaContentBuilder.scala | 8 +- scala3doc/src/dotty/dokka/utils.scala | 22 --- .../dotty/renderers/ScalaHtmlRenderer.scala | 41 +++-- .../test/dotty/dokka/DottyTestRunner.scala | 2 +- 29 files changed, 753 insertions(+), 138 deletions(-) create mode 100644 scala3doc/src/dotty/dokka/compat.scala create mode 100644 scala3doc/src/dotty/dokka/site/StaticSiteContext.scala create mode 100644 scala3doc/src/dotty/dokka/site/common.scala create mode 100644 scala3doc/src/dotty/dokka/site/contentNodes.scala create mode 100644 scala3doc/src/dotty/dokka/site/locationProvider.scala create mode 100644 scala3doc/src/dotty/dokka/site/processors.scala create mode 100644 scala3doc/src/dotty/dokka/site/templates.scala diff --git a/project/Build.scala b/project/Build.scala index daba197295b5..1a9a48b9db2e 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1465,6 +1465,8 @@ object Build { def joinProducts(products: Seq[java.io.File]): String = products.iterator.map(_.getAbsolutePath.toString).mkString(java.io.File.pathSeparator) + + val dokkaVersion = "1.4.10.2" project.settings(commonBootstrappedSettings). dependsOn(`scala3-compiler-bootstrapped`). @@ -1472,15 +1474,15 @@ object Build { settings( // Needed to download dokka and its dependencies resolvers += Resolver.jcenterRepo, - // Needed to download dokka-site - resolvers += Resolver.bintrayRepo("virtuslab", "dokka"), libraryDependencies ++= Seq( - "com.virtuslab.dokka" % "dokka-site" % "0.1.9", + "org.jetbrains.dokka" % "dokka-core" % dokkaVersion, + "org.jetbrains.dokka" % "dokka-base" % dokkaVersion, + "org.jetbrains.kotlinx" % "kotlinx-html-jvm" % "0.7.2", // Needs update when dokka version changes "com.vladsch.flexmark" % "flexmark-all" % "0.42.12", "nl.big-o" % "liqp" % "0.6.7", "args4j" % "args4j" % "2.33", - "org.jetbrains.dokka" % "dokka-test-api" % "1.4.10.2" % "test", + "org.jetbrains.dokka" % "dokka-test-api" % dokkaVersion % "test", "com.novocode" % "junit-interface" % "0.11" % "test", ), Test / test := (Test / test).dependsOn(compile.in(Compile).in(`scala3doc-testcases`)).value, diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index 09b1356dd023..368560276a26 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -358,10 +358,7 @@ object DottyPlugin extends AutoPlugin { }.value, // Configuration for the doctool - resolvers ++= (if(!useScala3doc.value) Nil else Seq( - Resolver.jcenterRepo, - Resolver.bintrayRepo("virtuslab", "dokka"), - )), + resolvers ++= (if(!useScala3doc.value) Nil else Seq(Resolver.jcenterRepo)), useScala3doc := false, scala3docOptions := Nil, Compile / doc / scalacOptions := { diff --git a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala index 6e5b7287f087..57e8f05a6bf4 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala @@ -3,7 +3,6 @@ package dotty.dokka import org.jetbrains.dokka._ import org.jetbrains.dokka.DokkaSourceSetImpl import java.io.File -import java.util.{ List => JList, Map => JMap} import collection.JavaConverters._ case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaConfiguration: @@ -11,9 +10,9 @@ case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaCon override def getCacheRoot: File = null override def getOfflineMode: Boolean = false override def getFailOnWarning: Boolean = false - override def getSourceSets: JList[DokkaConfiguration.DokkaSourceSet] = List(mkSourceSet).asJava - override def getModules: JList[DokkaConfiguration.DokkaModuleDescription] = List().asJava - override def getPluginsClasspath: JList[File] = Nil.asJava + override def getSourceSets: JList[DokkaSourceSet] = JList(mkSourceSet) + override def getModules: JList[DokkaConfiguration.DokkaModuleDescription] = JList() + override def getPluginsClasspath: JList[File] = JList() override def getModuleName(): String = "ModuleName" override def getModuleVersion(): String = "" @@ -23,31 +22,31 @@ case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaCon DokkaConfiguration$SerializationFormat.JSON.asInstanceOf[DokkaConfiguration$SerializationFormat] override def getValues: String = docConfiguration.args.docsRoot.getOrElse("") - override def getPluginsConfiguration: JList[DokkaConfiguration.PluginConfiguration] = List(OurConfig).asJava + override def getPluginsConfiguration: JList[DokkaConfiguration.PluginConfiguration] = JList(OurConfig) - def mkSourceSet: DokkaConfiguration.DokkaSourceSet = + def mkSourceSet: DokkaSourceSet = val sourceLinks:Set[SourceLinkDefinitionImpl] = docConfiguration.args.sourceLinks.map(SourceLinkDefinitionImpl.Companion.parseSourceLinkDefinition(_)).toSet new DokkaSourceSetImpl( /*displayName=*/ docConfiguration.args.name, /*sourceSetID=*/ new DokkaSourceSetID(docConfiguration.args.name, "main"), - /*classpath=*/ Nil.asJava, - /*sourceRoots=*/ Set().asJava, - /*dependentSourceSets=*/ Set().asJava, - /*samples=*/ Set().asJava, - /*includes=*/ Set().asJava, + /*classpath=*/ JList(), + /*sourceRoots=*/ JSet(), + /*dependentSourceSets=*/ JSet(), + /*samples=*/ JSet(), + /*includes=*/ JSet(), /*includeNonPublic=*/ true, /*reportUndocumented=*/ false, /* changed because of exception in reportUndocumentedTransformer - there's 'when' which doesnt match because it contains only KotlinVisbility cases */ /*skipEmptyPackages=*/ false, // Now all our packages are empty from dokka perspective /*skipDeprecated=*/ true, /*jdkVersion=*/ 8, /*sourceLinks=*/ sourceLinks.asJava, - /*perPackageOptions=*/ Nil.asJava, - /*externalDocumentationLinks=*/ Set().asJava, + /*perPackageOptions=*/ JList(), + /*externalDocumentationLinks=*/ JSet(), /*languageVersion=*/ null, /*apiVersion=*/ null, /*noStdlibLink=*/ true, /*noJdkLink=*/ true, - /*suppressedFiles=*/ Set().asJava, + /*suppressedFiles=*/ JSet(), /*suppressedFiles=*/ Platform.jvm - ).asInstanceOf[DokkaConfiguration.DokkaSourceSet] // Why I do need to cast here? Kotlin magic? + ).asInstanceOf[DokkaSourceSet] // Why I do need to cast here? Kotlin magic? diff --git a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala index 212d7a59b69f..33dd9c7a9681 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala @@ -12,8 +12,6 @@ import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.base.parsers._ import org.jetbrains.dokka.plugability.DokkaContext -import com.virtuslab.dokka.site.SourceSetWrapper -import com.virtuslab.dokka.site.JavaSourceToDocumentableTranslator import collection.JavaConverters._ import org.jetbrains.dokka.model.properties.PropertyContainer import dotty.dokka.tasty.{DokkaTastyInspector, SbtDokkaTastyInspector} @@ -23,10 +21,7 @@ import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.pages._ import dotty.dokka.model.api._ import org.jetbrains.dokka.CoreExtensions -import com.virtuslab.dokka.site.StaticSitePlugin import org.jetbrains.dokka.base.DokkaBase -import com.virtuslab.dokka.site.ExtensionBuilderEx -import java.util.{List => JList} /** Main Dokka plugin for the doctool. * @@ -38,7 +33,6 @@ import java.util.{List => JList} class DottyDokkaPlugin extends DokkaJavaPlugin: lazy val dokkaBase = plugin(classOf[DokkaBase]) - lazy val dokkaSitePlugin = plugin(classOf[StaticSitePlugin]) val provideMembers = extend( _.extensionPoint(CoreExtensions.INSTANCE.getSourceToDocumentableTranslator) @@ -116,7 +110,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: val ourRenderer = extend( _.extensionPoint(CoreExtensions.INSTANCE.getRenderer) .fromRecipe(ScalaHtmlRenderer(_)) - .overrideExtension(dokkaSitePlugin.getCustomRenderer) + .overrideExtension(dokkaBase.getHtmlRenderer) ) val commentsToContentConverter = extend( @@ -140,10 +134,23 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: .name("muteDefaultSourceLinksTransformer") ) -// TODO remove once problem is fixed in Dokka +// TODO (https://github.com/lampepfl/scala3doc/issues/232): remove once problem is fixed in Dokka extension [T] (builder: ExtensionBuilder[T]): - def before(exts: Extension[_, _, _]*): ExtensionBuilder[T] = - (new ExtensionBuilderEx).newOrdering(builder, exts.toArray, Array.empty) - - def after(exts: Extension[_, _, _]*): ExtensionBuilder[T] = - (new ExtensionBuilderEx).newOrdering(builder, Array.empty, exts.toArray) + def ordered(before: Seq[Extension[_, _, _]], after: Seq[Extension[_, _, _]]): ExtensionBuilder[T] = + val byDsl = new OrderingKind.ByDsl(dsl => { + dsl.after(after:_*) + dsl.before(before:_*) + kotlin.Unit.INSTANCE // TODO why U does not work here? + }) + // Does not compile but compiles in scala 2 + // ExtensionBuilder.copy$default(builder, null, null, null, byDsl, null, null, 55, null) + val m = classOf[ExtensionBuilder[_]].getDeclaredMethods().find(_.getName == "copy$default").get + m.setAccessible(true) + // All nulls and 55 is taken from Kotlin bytecode and represent how defaut parameter are represented in Kotlin + // Defaut arguments are encoded by null and mapping that 55 represent whic arguments are actually provided + m.invoke(null, builder, null, null, null, byDsl, null, null, 55, null).asInstanceOf[ExtensionBuilder[T]] + + + def before(exts: Extension[_, _, _]*): ExtensionBuilder[T] = ordered(exts, Nil) + + def after(exts: Extension[_, _, _]*): ExtensionBuilder[T] = ordered(Nil, exts) diff --git a/scala3doc/src/dotty/dokka/Main.scala b/scala3doc/src/dotty/dokka/Main.scala index 581c2c111ecf..98207a368288 100644 --- a/scala3doc/src/dotty/dokka/Main.scala +++ b/scala3doc/src/dotty/dokka/Main.scala @@ -8,7 +8,6 @@ import java.io.File import java.util.jar._ import collection.JavaConverters._ import collection.immutable.ArraySeq -import java.util.{List => JList} import scala.tasty.Reflection import scala.tasty.inspector.TastyInspector diff --git a/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala b/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala index 86ccd58739a0..e8ba83931d7a 100644 --- a/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala +++ b/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala @@ -1,23 +1,22 @@ package dotty.dokka import org.jetbrains.dokka.{ DokkaConfiguration$DokkaSourceSet => DokkaSourceSet } -import com.virtuslab.dokka.site.JavaSourceToDocumentableTranslator -import com.virtuslab.dokka.site.SourceSetWrapper import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator import dotty.dokka.tasty.{DokkaTastyInspector, SbtDokkaTastyInspector} import org.jetbrains.dokka.pages._ import dotty.dokka.model.api._ import org.jetbrains.dokka.model._ import org.jetbrains.dokka.links.DRI -import java.util.{List => JList} import org.jetbrains.dokka.base.parsers.MarkdownParser import collection.JavaConverters._ +import kotlin.coroutines.Continuation -object ScalaModuleProvider extends JavaSourceToDocumentableTranslator: - override def process(rawSourceSet: DokkaSourceSet, cxt: DokkaContext) = - val sourceSet = SourceSetWrapper(rawSourceSet) +object ScalaModuleProvider extends SourceToDocumentableTranslator: + override def invoke(sourceSet: DokkaSourceSet, cxt: DokkaContext, unused: Continuation[? >: DModule]) = cxt.getConfiguration match case dottyConfig: DottyDokkaConfig => val result = dottyConfig.docConfiguration match { @@ -41,9 +40,9 @@ object ScalaModuleProvider extends JavaSourceToDocumentableTranslator: def flattenMember(m: Member): Seq[(DRI, Member)] = (m.dri -> m) +: m.allMembers.flatMap(flattenMember) new DModule( - sourceSet.getSourceSet.getDisplayName, + sourceSet.getDisplayName, result.asJava, - Map().asJava, + JMap(), null, sourceSet.toSet, PropertyContainer.Companion.empty() plus ModuleExtension(result.flatMap(flattenMember).toMap) @@ -51,6 +50,6 @@ object ScalaModuleProvider extends JavaSourceToDocumentableTranslator: case _ => ??? -object EmptyModuleProvider extends JavaSourceToDocumentableTranslator: - override def process(sourceSet: DokkaSourceSet, context: DokkaContext) = - DModule("", Nil.asJava, Map.empty.asJava, null, Set(sourceSet).asJava, PropertyContainer.Companion.empty()) +object EmptyModuleProvider extends SourceToDocumentableTranslator: + override def invoke(sourceSet: DokkaSourceSet, cxt: DokkaContext, unused: Continuation[? >: DModule]) = + DModule("", JList(), Map.empty.asJava, null, Set(sourceSet).asJava, PropertyContainer.Companion.empty()) diff --git a/scala3doc/src/dotty/dokka/compat.scala b/scala3doc/src/dotty/dokka/compat.scala new file mode 100644 index 000000000000..492f39266ec4 --- /dev/null +++ b/scala3doc/src/dotty/dokka/compat.scala @@ -0,0 +1,59 @@ +package dotty.dokka + +import org.jetbrains.dokka.links.{DRI, PointingToDeclaration} +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet +import collection.JavaConverters._ +import org.jetbrains.dokka.model.DisplaySourceSet +import org.jetbrains.dokka.model.properties.WithExtraProperties +// TODO reproduction! - comment line below to broke compiler! +import org.jetbrains.dokka.model.properties.ExtraProperty +// import java.util.Stream // TODO reproduction uncomment +import java.util.stream.Stream // comment out - wrong error! +import java.util.stream.Collectors + +def mkDRI(classNames: String = null, extra: String = null) = new DRI(null, classNames, null, PointingToDeclaration.INSTANCE, extra) + +val U: kotlin.Unit = kotlin.Unit.INSTANCE + +def JList[T](e: T*): JList[T] = e.asJava +def JSet[T](e: T*): JSet[T] = e.toSet.asJava +def JMap[K, V](e: (K, V)*): JMap[K, V] = e.toMap.asJava +def JMap2[K, V](): JMap[K, V] = ??? // e.toMap.asJava + +type JList[T] = java.util.List[T] +type JSet[T] = java.util.Set[T] +type JMap[K, V] = java.util.Map[K, V] + +type SourceSetWrapper = DokkaConfiguration$DokkaSourceSet +type DokkaSourceSet = DokkaConfiguration.DokkaSourceSet + +extension [T] (wrapper: SourceSetWrapper): + def toSet: JSet[DokkaConfiguration$DokkaSourceSet] = JSet(wrapper) + def toMap(value: T): JMap[DokkaConfiguration$DokkaSourceSet, T] = JMap(wrapper -> value) + +extension [T] (wrapper: DokkaSourceSet): + // when named `toSet` fails in runtime -- TODO: create a minimal! + // def toSet: JSet[DokkaConfiguration$DokkaSourceSet] = JSet(wrapper.asInstanceOf[SourceSetWrapper]) + def asSet: JSet[DokkaConfiguration$DokkaSourceSet] = JSet(wrapper.asInstanceOf[SourceSetWrapper]) + def asMap(value: T): JMap[DokkaConfiguration$DokkaSourceSet, T] = JMap(wrapper.asInstanceOf[SourceSetWrapper] -> value) + +extension (sourceSets: JList[DokkaSourceSet]): + def asDokka: JSet[SourceSetWrapper] = sourceSets.asScala.toSet.asJava.asInstanceOf[JSet[SourceSetWrapper]] + def toDisplaySourceSet = sourceSets.asScala.map(ss => DisplaySourceSet(ss.asInstanceOf[SourceSetWrapper])).toSet.asJava + +extension (sourceSets: Set[SourceSetWrapper]): + def toDisplay = sourceSets.map(DisplaySourceSet(_)).asJava + +extension [V] (a: WithExtraProperties[_]): + def get(key: ExtraProperty.Key[_, V]): V = a.getExtra().getMap().get(key).asInstanceOf[V] + +extension [E <: WithExtraProperties[E]] (a: E): + def put(value: ExtraProperty[_ >: E]): E = a.withNewExtras(a.getExtra plus value) + +extension [V] (map: JMap[SourceSetWrapper, V]): + def defaultValue: V = map.values.asScala.head + +extension [V](jlist: JList[V]): + def ++ (other: JList[V]): JList[V] = + Stream.of(jlist, other).flatMap(_.stream).collect(Collectors.toList()) \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/model/api/api.scala b/scala3doc/src/dotty/dokka/model/api/api.scala index 4e2d86a27606..a5d537bbe2a0 100644 --- a/scala3doc/src/dotty/dokka/model/api/api.scala +++ b/scala3doc/src/dotty/dokka/model/api/api.scala @@ -10,8 +10,6 @@ import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.pages._ -import java.util.{List => JList, Set => JSet} - enum Visibility(val name: String): case Unrestricted extends Visibility("") diff --git a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala index cf53fc2a1a27..ea3e64445ac5 100644 --- a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala +++ b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala @@ -21,9 +21,6 @@ import collection.JavaConverters._ import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.model.properties._ -import java.util.{List => JList, Set => JSet} - -import com.virtuslab.dokka.site.SourceSetWrapper private [model] case class MemberExtension( visibility: Visibility, diff --git a/scala3doc/src/dotty/dokka/model/extras.scala b/scala3doc/src/dotty/dokka/model/extras.scala index 9e5a86d7a1a8..179f5a5d51ff 100644 --- a/scala3doc/src/dotty/dokka/model/extras.scala +++ b/scala3doc/src/dotty/dokka/model/extras.scala @@ -9,7 +9,6 @@ import collection.JavaConverters._ import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.model.properties._ -import java.util.{List => JList, Set => JSet} import dotty.dokka.model.api._ case class ModuleExtension(driMap: Map[DRI, Member]) extends ExtraProperty[DModule]: diff --git a/scala3doc/src/dotty/dokka/model/scalaModel.scala b/scala3doc/src/dotty/dokka/model/scalaModel.scala index 329768236bc1..ad4cb5b3b3e2 100644 --- a/scala3doc/src/dotty/dokka/model/scalaModel.scala +++ b/scala3doc/src/dotty/dokka/model/scalaModel.scala @@ -8,7 +8,6 @@ import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.pages._ -import java.util.{List => JList, Set => JSet} import dotty.dokka.model.api.Signature import dotty.dokka.model.api.HierarchyGraph @@ -33,7 +32,7 @@ case class HtmlContentNode( override def getStyle = style.asJava override def hasAnyContent = !body.isEmpty def withSourceSets(sourceSets: JSet[DisplaySourceSet]) = copy(sourceSets = sourceSets.asScala.toSet) - override def getChildren: JList[ContentNode] = Nil.asJava + override def getChildren: JList[ContentNode] = JList() override def getExtra = extra override def withNewExtras(p: PropertyContainer[ContentNode]) = copy(extra = p) @@ -70,7 +69,7 @@ case class HierarchyGraphContentNode( override def getStyle = style.asJava override def hasAnyContent = !diagram.edges.isEmpty def withSourceSets(sourceSets: JSet[DisplaySourceSet]) = copy(sourceSets = sourceSets.asScala.toSet) - override def getChildren: JList[ContentNode] = Nil.asJava + override def getChildren: JList[ContentNode] = JList() override def getExtra = extra override def withNewExtras(p: PropertyContainer[ContentNode]) = copy(extra = p) @@ -91,7 +90,7 @@ abstract class ScalaContentNode(params: ContentNodeParams) extends ContentNode: override def hasAnyContent = true def withSourceSets(sourceSets: JSet[DisplaySourceSet]) = newInstance(params.copy(sourceSets = sourceSets)) - override def getChildren: JList[ContentNode] = Nil.asJava + override def getChildren: JList[ContentNode] = JList() override def getExtra = params.extra override def withNewExtras(p: PropertyContainer[ContentNode]) = newInstance(params.copy(extra = p)) diff --git a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala new file mode 100644 index 000000000000..46618b745886 --- /dev/null +++ b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala @@ -0,0 +1,136 @@ +package dotty.dokka +package site + +import java.io.File + +import org.jetbrains.dokka.base.parsers.MarkdownParser +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.{DocTag, Text} +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.{ContentKind, ContentNode, DCI, PageNode} +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.pages.Style + +import scala.collection.JavaConverters._ + +class StaticSiteContext(val root: File, cxt: DokkaContext): + + val docsFile = new File(root, "docs") + + def indexPage():Option[StaticPageNode] = + val files = List(new File(root, "index.html"), new File(root, "index.md")).filter { _.exists() } + if (files.size > 1) println("ERROR: Multiple root index pages found: ${files.map { it.absolutePath }}") // TODO (#14): provide proper error handling + loadFiles(files).headOption + + private lazy val layouts: Map[String, TemplateFile] = + val layoutRoot = new File(root, "_layouts") + val dirs: Array[File] = Option(layoutRoot.listFiles()).getOrElse(Array()) + dirs.map { it => loadTemplateFile(it) }.map { it => it.name() -> it }.toMap + + private def isValidTemplate(file: File): Boolean = + (file.isDirectory && !file.getName.startsWith("_")) || + file.getName.endsWith(".md") || + file.getName.endsWith(".html") + + + private def loadTemplate(from: File): Option[LoadedTemplate] = + if (!isValidTemplate(from)) None else + try + val (indexes, children) = Option(from.listFiles()).toSeq.flatten.flatMap(loadTemplate).partition(_.isIndexPage()) + if (indexes.size > 1) + println("ERROR: Multiple index pages for $from found in ${indexes.map { it.file }}") // TODO (#14): provide proper error handling + + def loadIndexPage(): TemplateFile = { + val indexFiles = from.listFiles { file =>file.getName == "index.md" || file.getName == "index.html" } + indexFiles.size match { + case 0 => emptyTemplate(from) + case 1 => loadTemplateFile(indexFiles.head).copy(file = from) + case _ => throw new java.lang.RuntimeException("ERROR: Multiple index pages found under ${from.path}") + } + } + + val templateFile = if (from.isDirectory) loadIndexPage() else loadTemplateFile(from) + + Some(LoadedTemplate(templateFile, children.toList, from)) + catch + case e: RuntimeException => + e.printStackTrace() // TODO (#14): provide proper error handling + None + + private def parseMarkdown(page: PreResolvedPage, dri: DRI, allDRIs: Map[String, DRI]): ContentNode = + val nodes: JList[ContentNode] = if (!page.hasMarkdown) JList() else + val parser = new MarkdownParser ( link => { + val driKey = + if (link.startsWith("/")) then + // handle root related links + link.replace('/', '.').stripPrefix(".") + else + val unSuffixedDri = dri.getPackageName.stripSuffix(".html").stripSuffix(".md") + val parentDri = unSuffixedDri.take(unSuffixedDri.lastIndexOf('.')).stripPrefix("_.") + "${parentDri}.${link.replace('/', '.')}" + + allDRIs.get(driKey).orNull + }) + val docTag = try parser.parseStringToDocNode(page.code) catch + case (e: Throwable) => + val msg = "Error rendering (dri = $dri): ${e.message}" + println("ERROR: $msg") // TODO (#14): provide proper error handling + new Text() + + asContent(docTag, dri) + + new PartiallyRenderedContent( + page, + nodes, + new DCI(Set(dri).asJava, ContentKind.Empty), + cxt.getConfiguration.getSourceSets.toDisplaySourceSet, + JSet() + ) + + def asContent(d: DocTag, dri: DRI) = new DocTagToContentConverter().buildContent( + d, + new DCI(Set(dri).asJava, ContentKind.Empty), + cxt.getConfiguration.getSourceSets.asDokka, + JSet(), + new PropertyContainer(JMap()) + ) + + def loadFiles( + files: List[File], + customChildren: List[PageNode] = Nil + ): List[StaticPageNode] = + val all = files.flatMap(loadTemplate) + def flatten(it: LoadedTemplate): List[String] = + List(it.relativePath(root)) ++ it.children.flatMap(flatten) + + def pathTomkDRI(path: String) = mkDRI("_.$path") + + val driMap = all.flatMap { it => flatten(it) }.map { it => it -> pathTomkDRI(it) }.toMap + + def templateToPage(myTemplate: LoadedTemplate): StaticPageNode = + val dri = pathTomkDRI(myTemplate.relativePath(root)) + val page = try { + val properties = myTemplate.templateFile.layout() + .map(c => Map("content" -> myTemplate.templateFile.rawCode)).getOrElse(Map.empty) + + myTemplate.templateFile.resolveMarkdown(RenderingContext(properties, layouts)) + } catch { case e: Throwable => + val msg = "Error rendering $myTemplate: ${e.message}" + println("ERROR: $msg") // TODO (#14): provide proper error handling + PreResolvedPage("", None, true) + } + val content = parseMarkdown(page, dri, driMap) + val children = myTemplate.children.map(templateToPage) + StaticPageNode( + myTemplate, + myTemplate.templateFile.title(), + content, + JSet(dri), + JList(), + (children ++ customChildren).asJava + ) + + all.map(templateToPage) diff --git a/scala3doc/src/dotty/dokka/site/common.scala b/scala3doc/src/dotty/dokka/site/common.scala new file mode 100644 index 000000000000..d77a49b27228 --- /dev/null +++ b/scala3doc/src/dotty/dokka/site/common.scala @@ -0,0 +1,74 @@ +package dotty.dokka +package site + +import java.io.File +import java.nio.file.Files + +import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension +import com.vladsch.flexmark.ext.autolink.AutolinkExtension +import com.vladsch.flexmark.ext.emoji.EmojiExtension +import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension +import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension +import com.vladsch.flexmark.ext.tables.TablesExtension +import com.vladsch.flexmark.ext.yaml.front.matter.{AbstractYamlFrontMatterVisitor, YamlFrontMatterExtension} +import com.vladsch.flexmark.parser.{Parser, ParserEmulationProfile} +import com.vladsch.flexmark.util.options.{DataHolder, MutableDataSet} +import org.jetbrains.dokka.links.{DRI, PointingToDeclaration} +import org.jetbrains.dokka.model.doc.Text + +import scala.collection.JavaConverters._ + +val ExternalDocsTooKey = "ExternalDocsTooKey" +val docsRootDRI: DRI = mkDRI(extra = "_top_level_index") +val docsDRI: DRI = mkDRI(extra = "_docs_level_index") +val apiPageDRI: DRI = mkDRI(classNames = "api", extra = "__api__") + + +val defaultMarkdownOptions: DataHolder = + new MutableDataSet() + .setFrom(ParserEmulationProfile.KRAMDOWN.getOptions) + .set( + Parser.EXTENSIONS, List( + TablesExtension.create(), + TaskListExtension.create(), + AutolinkExtension.create(), + AnchorLinkExtension.create(), + EmojiExtension.create(), + YamlFrontMatterExtension.create(), + StrikethroughExtension.create() + ).asJava) + .set( + EmojiExtension.ROOT_IMAGE_PATH, + "https://github.global.ssl.fastly.net/images/icons/emoji/" + ) + +def emptyTemplate(file: File): TemplateFile = TemplateFile(file, isHtml = true, "", Map()) + +final val ConfigSeparator = "---" +final val LineSeparator = "\n" + +val yamlParser: Parser = Parser.builder(defaultMarkdownOptions).build() + +def loadTemplateFile(file: File): TemplateFile = { + val lines = Files.readAllLines(file.toPath).asScala.toList + + val (config, content) = if (lines.head == ConfigSeparator) { + // Taking the second occurrence of ConfigSeparator. + // The rest may appear within the content. + val index = lines.drop(1).indexOf(ConfigSeparator) + 2 + (lines.take(index), lines.drop(index)) + } else (Nil, lines) + + val configParsed = yamlParser.parse(config.mkString(LineSeparator)) + val yamlCollector = new AbstractYamlFrontMatterVisitor() + yamlCollector.visit(configParsed) + + TemplateFile( + file = file, + file.getName.endsWith(".html"), + rawCode = content.mkString(LineSeparator), + settings = yamlCollector.getData.asScala.toMap.transform((_, v) => v.asScala.toList) + ) +} + +def Text(msg: String = "") = new Text(msg, JList(), JMap()) diff --git a/scala3doc/src/dotty/dokka/site/contentNodes.scala b/scala3doc/src/dotty/dokka/site/contentNodes.scala new file mode 100644 index 000000000000..6f684a8f8c0a --- /dev/null +++ b/scala3doc/src/dotty/dokka/site/contentNodes.scala @@ -0,0 +1,24 @@ +package dotty.dokka +package site + +import org.jetbrains.dokka.model.DisplaySourceSet +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.{ContentNode, DCI, Style} + +case class PartiallyRenderedContent( + page: PreResolvedPage, + override val getChildren: JList[ContentNode], + override val getDci: DCI, + override val getSourceSets: JSet[DisplaySourceSet], + override val getStyle: JSet[Style] = JSet(), + override val getExtra: PropertyContainer[ContentNode] = new PropertyContainer(JMap()) +) extends ContentNode: + override def hasAnyContent(): Boolean = getChildren.stream().filter(_.hasAnyContent()).count() > 0 + + override def withNewExtras(newExtras: PropertyContainer[ContentNode]): ContentNode = + copy(getExtra = newExtras) + + override def withSourceSets(sourceSets: JSet[DisplaySourceSet]): ContentNode = + copy(getSourceSets = sourceSets) + + val allResources: List[String] = page.render("").resources diff --git a/scala3doc/src/dotty/dokka/site/locationProvider.scala b/scala3doc/src/dotty/dokka/site/locationProvider.scala new file mode 100644 index 000000000000..8ff086895c31 --- /dev/null +++ b/scala3doc/src/dotty/dokka/site/locationProvider.scala @@ -0,0 +1,42 @@ +package dotty.dokka +package site + +import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider +import org.jetbrains.dokka.base.resolvers.local.LocationProvider +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext + +import scala.collection.JavaConverters._ + +class StaticSiteLocationProviderFactory(private val ctx: DokkaContext) extends LocationProviderFactory: + override def getLocationProvider(pageNode: RootPageNode): LocationProvider = + new StaticSiteLocationProvider(ctx, pageNode) + +class StaticSiteLocationProvider(ctx: DokkaContext, pageNode: RootPageNode) + extends DokkaLocationProvider(pageNode, ctx, ".html"): + private def updatePageEntry(page: PageNode, jpath: JList[String]): JList[String] = + page match + case page: StaticPageNode => + if (page.getDri.contains(docsRootDRI)) JList("index") + else { + val path = jpath.asScala.toList + val start = if (path.head == "--root--") List("docs") else path.take(1) + val pageName = page.loadedTemplate.file.getName + val dotIndex = pageName.lastIndexOf('.') + val newName = if (dotIndex < 0) pageName else pageName.substring(0, dotIndex) + (start ++ path.drop(1).dropRight(1) ++ List(newName)).asJava + } + case page: ContentPage if page.getDri.contains(docsDRI) => + JList("docs") + case page: ContentPage if page.getDri.contains(apiPageDRI) => + JList("api", "index") + case _ if jpath.size() > 1 && jpath.get(0) == "--root--" && jpath.get(1) == "-a-p-i" => + (List("api") ++ jpath.asScala.drop(2)).asJava + case _ => + jpath + + override val getPathsIndex: JMap[PageNode, JList[String]] = + super.getPathsIndex.asScala.mapValuesInPlace(updatePageEntry).asJava diff --git a/scala3doc/src/dotty/dokka/site/processors.scala b/scala3doc/src/dotty/dokka/site/processors.scala new file mode 100644 index 000000000000..009e6d29500c --- /dev/null +++ b/scala3doc/src/dotty/dokka/site/processors.scala @@ -0,0 +1,161 @@ +package dotty.dokka +package site + +import java.io.File +import java.nio.file.Files + +import org.jetbrains.dokka.base.renderers.html.{NavigationNode, NavigationPage} +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.pages._ +import org.jetbrains.dokka.transformers.pages.PageTransformer + +import scala.collection.JavaConverters._ + +case class LoadedTemplate(templateFile: TemplateFile, children: List[LoadedTemplate], file: File) { + def isIndexPage() = file.isFile && (file.getName == "index.md" || file.getName == "index.html") + def relativePath(root: File): String = + root.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '.') +} + +case class StaticPageNode( + loadedTemplate: LoadedTemplate, + override val getName: String, + override val getContent: ContentNode, + override val getDri: JSet[DRI], + override val getEmbeddedResources: JList[String], + override val getChildren: JList[PageNode], + ) extends ContentPage: + override def getDocumentable: Documentable = null + + def title(): String = loadedTemplate.templateFile.title() + def hasFrame(): Boolean = loadedTemplate.templateFile.hasFrame() + + override def modified(name: String, content: ContentNode, dri: JSet[DRI], embeddedResources: JList[String], children: JList[_ <: PageNode]): ContentPage = + copy(loadedTemplate, name, content, dri, embeddedResources, children.asInstanceOf[JList[PageNode]]) + + override def modified(name: String, children: JList[_ <: PageNode]): PageNode = + copy(getName = name, getChildren = children.asInstanceOf[JList[PageNode]]) + + def resources(): List[String] = getContent match + case p: PartiallyRenderedContent => p.allResources + case _ => Nil + + +abstract class BaseStaticSiteProcessor(staticSiteContext: Option[StaticSiteContext]) extends PageTransformer: + final override def invoke(input: RootPageNode): RootPageNode = staticSiteContext.fold(input)(transform(input, _)) + + protected def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode + +class SiteResourceManager(ctx: Option[StaticSiteContext]) extends BaseStaticSiteProcessor(ctx): + + private def listResources(nodes: Seq[PageNode]): Set[String] = + nodes.flatMap { + case it: StaticPageNode => listResources(it.getChildren.asScala.toList) ++ it.resources() + case _ => Seq.empty + }.toSet + + override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = + val imgPath = ctx.root.toPath().resolve("images") + val allImgPaths = Files.walk(imgPath).filter(Files.isRegularFile(_)) + val images = allImgPaths.iterator().asScala.toList.map(_.toString) + + val resources = listResources(input.getChildren.asScala.toList) ++ images + val resourcePages = resources.map { path => + val content = Files.readString(ctx.root.toPath.resolve(path)) + new RendererSpecificResourcePage(path, JList(), new RenderingStrategy.Write(content)) + }.toList + val modified = input.transformContentPagesTree { + case it: StaticPageNode => + val newRes = JList[String]() + newRes.addAll(it.getEmbeddedResources) + newRes.addAll(it.resources().asJava) + it.copy(getEmbeddedResources = newRes) + case it => it + } + modified.modified(modified.getName, (resourcePages ++ modified.getChildren.asScala).asJava) + +case class AContentPage( + override val getName: String, + override val getChildren: JList[PageNode], + override val getContent: ContentNode, + override val getDri: JSet[DRI], + override val getEmbeddedResources: JList[String] = JList(), +) extends ContentPage: + override def getDocumentable: Documentable = null + + override def modified( + name: String, + content: ContentNode, + dri: JSet[DRI], + embeddedResources: JList[String], + children: JList[_ <: PageNode] + ): ContentPage = copy(name, children.asInstanceOf[JList[PageNode]], content, dri, embeddedResources) + + override def modified(name: String, children: JList[_ <: PageNode]): PageNode = copy(name, getChildren = children.asInstanceOf[JList[PageNode]]) + +class SitePagesCreator(ctx: Option[StaticSiteContext]) extends BaseStaticSiteProcessor(ctx): + + private def processRootPage(input: RootPageNode, children: List[PageNode] = Nil): AContentPage = input match + case input: ContentPage => + AContentPage( + input.getName, + children.asJava, + input.getContent, + JSet(apiPageDRI), + input.getEmbeddedResources + ) + case _: RendererSpecificRootPage => + children.filter(_.isInstanceOf[RootPageNode]) match { + case List(nestedRoot: RootPageNode) => + processRootPage( + nestedRoot, + children.filter { _ != nestedRoot } ++ nestedRoot.getChildren.asScala) + case other => + throw new RuntimeException(s"Expected single nested roor but get: $other") + } + case _ => throw new RuntimeException(s"UNSUPPORTED! ${input.getClass.getName}") + + override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = + val (contentPage, others) = input.getChildren.asScala.toList.partition { _.isInstanceOf[ContentPage] } + val modifiedModuleRoot = processRootPage(input, contentPage) + val allFiles = Option(ctx.docsFile.listFiles()).toList.flatten + val (indexes, children) = ctx.loadFiles(allFiles).partition(_.loadedTemplate.isIndexPage()) + if (indexes.size > 1) println("ERROR: Multiple index pages found $indexes}") // TODO (#14): provide proper error handling + + val rootContent = indexes.headOption.fold(ctx.asContent(Text(), mkDRI(extra = "root_content")).get(0))(_.getContent) + + val root = AContentPage( + input.getName, + (List(modifiedModuleRoot.modified("API", modifiedModuleRoot.getChildren)) ++ children).asJava, + rootContent, + JSet(docsDRI), + JList() + ) + + new RendererSpecificRootPage( + modifiedModuleRoot.getName, + (List(root) ++ others).asJava, + RenderingStrategy.DoNothing.INSTANCE + ) + +class RootIndexPageCreator(ctx: Option[StaticSiteContext]) extends BaseStaticSiteProcessor(ctx): + override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = + ctx.indexPage().fold(input){ it => + val (contentNodes, nonContent) = input.getChildren.asScala.partition { _.isInstanceOf[ContentNode] } + val (navigations, rest) = nonContent.partition { _.isInstanceOf[NavigationPage] } + val modifiedNavigation = navigations.map { it => + val root = it.asInstanceOf[NavigationPage].getRoot + val (api, rest) = root.getChildren.asScala.partition { _.getDri == apiPageDRI } + new NavigationPage( + new NavigationNode( + input.getName, + docsRootDRI, + root.getSourceSets, + (rest ++ api).asJava + ) + ) + } + val newRoot = it.copy(getDri = JSet(docsRootDRI), getChildren = contentNodes.asJava) + input.modified(input.getName, (List(newRoot) ++ rest ++ modifiedNavigation).asJava) + } diff --git a/scala3doc/src/dotty/dokka/site/templates.scala b/scala3doc/src/dotty/dokka/site/templates.scala new file mode 100644 index 000000000000..8f79b14fa874 --- /dev/null +++ b/scala3doc/src/dotty/dokka/site/templates.scala @@ -0,0 +1,126 @@ +package dotty.dokka.site + +import java.io.File +import java.nio.file.Files + +import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension +import com.vladsch.flexmark.ext.autolink.AutolinkExtension +import com.vladsch.flexmark.ext.emoji.EmojiExtension +import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension +import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension +import com.vladsch.flexmark.ext.tables.TablesExtension +import com.vladsch.flexmark.ext.yaml.front.matter.{AbstractYamlFrontMatterVisitor, YamlFrontMatterExtension} +import com.vladsch.flexmark.parser.{Parser, ParserEmulationProfile} +import com.vladsch.flexmark.util.options.{DataHolder, MutableDataSet} +import com.vladsch.flexmark.html.HtmlRenderer +import liqp.Template + +import scala.collection.JavaConverters._ +import scala.io.Source + +case class RenderingContext( + properties: Map[String, Object], + layouts: Map[String, TemplateFile] = Map(), + resolving: Set[String] = Set(), + markdownOptions: DataHolder = defaultMarkdownOptions, + resources: List[String] = Nil + ): + + def nest(code: String, file: File, resources: List[String]) = + copy( + resolving = resolving + file.getAbsolutePath, + properties = properties + ("content" -> code), + resources = this.resources ++ resources + ) + +case class LayoutInfo(htmlTemple: TemplateFile, ctx: RenderingContext) + +case class PreResolvedPage( + code: String, + nextLayoutInfo: Option[LayoutInfo], + hasMarkdown: Boolean, + resources: List[String] = Nil + ): + def render(renderedMarkdown: String): ResolvedPage = nextLayoutInfo match + case None => ResolvedPage(renderedMarkdown, hasMarkdown, resources) + case Some(nextLayoutInfo) => + val newCtx = + nextLayoutInfo.ctx.copy(properties = nextLayoutInfo.ctx.properties + ("content" -> renderedMarkdown)) + val res = nextLayoutInfo.htmlTemple.resolveToHtml(newCtx, hasMarkdown) + ResolvedPage(res.code, hasMarkdown, res.resources) + +case class ResolvedPage( + val code: String, + val hasMarkdown: Boolean, + val resources: List[String] = Nil + ) + +/** + * case class for the template files. + * Template file is a file `.md` or `.html` handling settings. + * + * @param file The Actual file defining the template. + * @param rawCode The content, what is to be shown, everything but settings. + * @param settings The config defined in the begging of the file, between the pair of `---` (e.g. layout: basic). + */ +case class TemplateFile( + val file: File, + val isHtml: Boolean, + val rawCode: String, + private val settings: Map[String, List[String]] + ): + + private def stringSetting(name: String): Option[String] = + settings.get(name) map { + case List(single) => single.stripPrefix("\"").stripSuffix("\"") + case nonSingle => + throw new RuntimeException(s"Setting $name is a not a singlel-ement list but $nonSingle") + } + + + private def listSetting(name: String): List[String] = settings.getOrElse(name, Nil) + + def name(): String = stringSetting("name").getOrElse(file.getName.stripSuffix(if (isHtml) ".html" else ".md")) + + def title(): String = stringSetting("title").getOrElse(name()) + + def layout(): Option[String] = stringSetting("layout") + + def hasFrame(): Boolean = !stringSetting("hasFrame").contains("false") + + + def resolveMarkdown(ctx: RenderingContext): PreResolvedPage = + resolveInner( + ctx = ctx.copy(properties = ctx.properties + ("page" -> Map("title" -> title()))), + stopAtHtml = true, + !isHtml // This is top level template + ) + + def resolveToHtml(ctx: RenderingContext, hasMarkdown: Boolean): PreResolvedPage = + resolveInner(ctx, stopAtHtml = false, hasMarkdown) + + private def resolveInner(ctx: RenderingContext, stopAtHtml: Boolean, hasMarkdown: Boolean): PreResolvedPage = + if (ctx.resolving.contains(file.getAbsolutePath)) + throw new RuntimeException("Cycle in templates involving $file: ${ctx.resolving}") + + val layoutTemplate = layout().map(name => + ctx.layouts.getOrElse(name, throw new RuntimeException(s"No layouts named $name in ${ctx.layouts}"))) + + if (!stopAtHtml && !isHtml) + throw new java.lang.RuntimeException( + "Markdown layout cannot be applied after .html. Rendering $file after: ${ctx.resolving}" + ) + + if stopAtHtml && isHtml then + PreResolvedPage(ctx.properties.getOrElse("content", "").toString, Some(LayoutInfo(this, ctx)), hasMarkdown) + else + val rendered = Template.parse(this.rawCode).render(ctx.properties.asJava) // Library requires mutable maps.. + val code = if (!isHtml) rendered else + val parser: Parser = Parser.builder().build() + HtmlRenderer.builder(ctx.markdownOptions).build().render(parser.parse(rendered)) + + val resources = listSetting("extraCSS") ++ listSetting("extraJS") + layoutTemplate match + case None => PreResolvedPage(code, None, hasMarkdown, resources ++ ctx.resources) + case Some(layoutTemplate) => + layoutTemplate.resolveInner(ctx.nest(code, file, resources), stopAtHtml, hasMarkdown) diff --git a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala index 9b80d12efd94..0d2dcac86833 100644 --- a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala @@ -35,14 +35,14 @@ trait BasicSupport: extension (sym: Symbol): def documentation(using cxt: Context) = sym.documentation match case Some(comment) => - Map(sourceSet.getSourceSet -> parseComment(comment, sym.tree)) + Map(sourceSet -> parseComment(comment, sym.tree)) case None => Map.empty def source(using ctx: Context) = val path = Some(sym.pos.sourceFile.jpath).filter(_ != null).map(_.toAbsolutePath).map(_.toString) path match{ - case Some(p) => Map(sourceSet.getSourceSet -> TastyDocumentableSource(p, sym.pos.startLine)) + case Some(p) => Map(sourceSet -> TastyDocumentableSource(p, sym.pos.startLine)) case None => Map.empty } diff --git a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala index fbad85e22c80..d478569fdf7e 100644 --- a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala @@ -18,8 +18,8 @@ trait ClassLikeSupport: self: TastyParser => import qctx.reflect._ - private val placeholderVisibility = Map(sourceSet.getSourceSet -> KotlinVisibility.Public.INSTANCE).asJava - private val placeholderModifier = Map(sourceSet.getSourceSet -> KotlinModifier.Empty.INSTANCE).asJava + private val placeholderVisibility = JMap(sourceSet -> KotlinVisibility.Public.INSTANCE) + private val placeholderModifier = JMap(sourceSet -> KotlinModifier.Empty.INSTANCE) private def kindForClasslike(sym: Symbol): Kind = if sym.flags.is(Flags.Object) then Kind.Object @@ -79,9 +79,9 @@ trait ClassLikeSupport: dri, name, (if(signatureOnly) Nil else classDef.getConstructors.map(parseMethod(_))).asJava, - Nil.asJava, - Nil.asJava, - Nil.asJava, + JList(), + JList(), + JList(), classDef.symbol.source.asJava, placeholderVisibility, null, @@ -250,7 +250,7 @@ trait ClassLikeSupport: /*generics =*/ genericTypes.map(parseTypeArgument).asJava, /*receiver =*/ null, // Not used /*modifier =*/ placeholderModifier, - sourceSet.toSet(), + sourceSet.toSet, /*isExpectActual =*/ false, PropertyContainer.Companion.empty() plus MethodExtension(paramLists.map(_.size)) @@ -270,7 +270,7 @@ trait ClassLikeSupport: argument.symbol.documentation.asJava, null, argument.tpt.dokkaType, - sourceSet.toSet(), + sourceSet.toSet, PropertyContainer.Companion.empty() .plus(ParameterExtension(isExtendedSymbol, isGrouped)) .plus(MemberExtension.empty.copy(annotations = argument.symbol.getAnnotations())) @@ -287,8 +287,8 @@ trait ClassLikeSupport: Invariance(TypeParameter(argument.symbol.dri, variancePrefix + argument.symbol.name, null)), argument.symbol.documentation.asJava, null, - List(argument.rhs.dokkaType).asJava, - sourceSet.toSet(), + JList(argument.rhs.dokkaType), + sourceSet.toSet, PropertyContainer.Companion.empty() ) @@ -317,7 +317,7 @@ trait ClassLikeSupport: /*setter =*/ null, /*getter =*/ null, /*modifier =*/ placeholderModifier, - sourceSet.toSet(), + sourceSet.toSet, /*generics =*/ generics.asJava, // TODO /*isExpectActual =*/ false, PropertyContainer.Companion.empty() plus MemberExtension( @@ -355,8 +355,8 @@ trait ClassLikeSupport: /*setter =*/ null, /*getter =*/ null, /*modifier =*/ placeholderModifier, - sourceSet.toSet(), - /*generics =*/ Nil.asJava, + sourceSet.toSet, + /*generics =*/ JList(), /*isExpectActual =*/ false, PropertyContainer.Companion.empty().plus(MemberExtension( valDef.symbol.getVisibility(), diff --git a/scala3doc/src/dotty/dokka/tasty/PackageSupport.scala b/scala3doc/src/dotty/dokka/tasty/PackageSupport.scala index 01abbb289059..6dd08843364f 100644 --- a/scala3doc/src/dotty/dokka/tasty/PackageSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/PackageSupport.scala @@ -1,4 +1,5 @@ -package dotty.dokka.tasty +package dotty.dokka +package tasty import org.jetbrains.dokka.model._ import org.jetbrains.dokka.links._ @@ -18,10 +19,10 @@ trait PackageSupport: val documentation = pck.symbol.documentation DPackage( new DRI(name, null, null, PointingToDeclaration.INSTANCE, null), - Nil.asJava, - Nil.asJava, - Nil.asJava, - Nil.asJava, + JList(), + JList(), + JList(), + JList(), documentation.asJava, null, sourceSet.toSet, @@ -36,8 +37,8 @@ trait PackageSupport: new DRI(pckObj.symbol.dri.getPackageName, null, null, PointingToDeclaration.INSTANCE, null), clazz.getFunctions, clazz.getProperties, - Nil.asJava, - Nil.asJava, + JList(), + JList(), pckObj.symbol.documentation.asJava, null, sourceSet.toSet, diff --git a/scala3doc/src/dotty/dokka/tasty/SymOps.scala b/scala3doc/src/dotty/dokka/tasty/SymOps.scala index b4b5663fcbb9..bd7d589704c6 100644 --- a/scala3doc/src/dotty/dokka/tasty/SymOps.scala +++ b/scala3doc/src/dotty/dokka/tasty/SymOps.scala @@ -113,7 +113,7 @@ class SymOps[R <: Reflection](val r: R): new DRI( sym.packageName, sym.topLevelEntryName.orNull, // TODO do we need any of this fields? - method.map(s => new org.jetbrains.dokka.links.Callable(s.name, null, Nil.asJava)).orNull, + method.map(s => new org.jetbrains.dokka.links.Callable(s.name, null, JList())).orNull, pointsTo, // TODO different targets? s"${sym.show}/${sym.signature.resultSig}/[${sym.signature.paramSigs.mkString("/")}]" ) diff --git a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala index 0a7f2621fbf1..0f21f6ffdb31 100644 --- a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala +++ b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala @@ -14,12 +14,10 @@ import collection.JavaConverters._ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.properties.PropertyContainerKt._ import org.jetbrains.dokka.model.properties.{WithExtraProperties} -import java.util.{List => JList} import quoted.QuoteContext import scala.tasty.inspector.TastyInspector import dotty.dokka.model.api.withNewMembers -import com.virtuslab.dokka.site.SourceSetWrapper /** Responsible for collectively inspecting all the Tasty files we're interested in. * @@ -124,8 +122,8 @@ trait DokkaBaseTastyInspector: f.getDri, f.getFunctions, f.getProperties, - Nil.asJava, // TODO add support for other things like type or package object entries - Nil.asJava, + JList(), // TODO add support for other things like type or package object entries + JList(), f.getDocumentation, null, sourceSet.toSet, @@ -137,22 +135,18 @@ trait DokkaBaseTastyInspector: }.toList extension (self: DPackage) def mergeWith(other: DPackage): DPackage = - val doc1 = self.getDocumentation.asScala.get(sourceSet.getSourceSet).map(_.getChildren).getOrElse(Nil.asJava) - val doc2 = other.getDocumentation.asScala.get(sourceSet.getSourceSet).map(_.getChildren).getOrElse(Nil.asJava) + def nodes(p: DPackage): JList[TagWrapper] = p.getDocumentation.get(sourceSet) match + case null => JList[TagWrapper]() + case node => node.getChildren + mergeExtras( DPackage( self.getDri, (self.getFunctions.asScala ++ other.getFunctions.asScala).asJava, (self.getProperties.asScala ++ other.getProperties.asScala).asJava, - Nil.asJava, // WARNING Merging is done before collecting classlikes, if it changes it needs to be refactored - Nil.asJava, - sourceSet.asMap( - DocumentationNode( - ( - doc1.asScala ++ doc2.asScala - ).asJava - ) - ), + JList(), // WARNING Merging is done before collecting classlikes, if it changes it needs to be refactored + JList(), + sourceSet.toMap(DocumentationNode(nodes(self) ++ nodes(other))), null, sourceSet.toSet, PropertyContainer.Companion.empty() diff --git a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala index fda6e78f2f7b..f2e22e6896ea 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala @@ -1,4 +1,5 @@ -package dotty.dokka.tasty.comments +package dotty.dokka +package tasty.comments import scala.jdk.CollectionConverters._ import scala.tasty.Reflection @@ -40,7 +41,8 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter { emit(dkkd.P(convertChildren(n).asJava, kt.emptyMap)) case n: mda.Heading => emit(n.getLevel match { - case 1 => dkkd.H1(List(dkk.text(n.getText().toString)).asJava, kt.emptyMap) + case 1 => dkkd.H1(List(dkk.text(n.getText().toString)).asJava, JMap()) + // case -1 => dkkd.H1(List(dkk.text(n.getText().toString)).asJava, JMap()) // This does not compile but should! case 2 => dkkd.H2(List(dkk.text(n.getText().toString)).asJava, kt.emptyMap) case 3 => dkkd.H3(List(dkk.text(n.getText().toString)).asJava, kt.emptyMap) case 4 => dkkd.H4(List(dkk.text(n.getText().toString)).asJava, kt.emptyMap) diff --git a/scala3doc/src/dotty/dokka/tasty/comments/package.scala b/scala3doc/src/dotty/dokka/tasty/comments/package.scala index 55cc90954528..aba26cb94d98 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/package.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/package.scala @@ -1,4 +1,5 @@ -package dotty.dokka.tasty.comments +package dotty.dokka +package tasty.comments import scala.jdk.CollectionConverters._ @@ -17,7 +18,7 @@ object dkk: def p(params: (String, String)*)(children: dkkd.DocTag*) = dkkd.P(children.asJava, params.toMap.asJava) - def text(str: String) = dkkd.Text(str, Nil.asJava, Map.empty.asJava) + def text(str: String) = dkkd.Text(str, JList(), Map.empty.asJava) def a(children: dkkd.DocTag*) = dkkd.A(children.asJava, Map.empty.asJava) diff --git a/scala3doc/src/dotty/dokka/transformers/ScalaSourceLinksTransformer.scala b/scala3doc/src/dotty/dokka/transformers/ScalaSourceLinksTransformer.scala index 0815ab5b6011..dec0bcb6aa14 100644 --- a/scala3doc/src/dotty/dokka/transformers/ScalaSourceLinksTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/ScalaSourceLinksTransformer.scala @@ -24,10 +24,10 @@ class ScalaSourceLinksTransformer( val sourceLinks = ctx.getConfiguration.getSourceSets.asScala.flatMap(s => s.getSourceLinks.asScala.map(l => SourceLink(l, s))) val pageBuilder = ScalaPageContentBuilder(commentsToContentConverter, signatureProvider, logger) - case class SourceLink(val path: String, val url: String, val lineSuffix: Option[String], val sourceSetData: DokkaConfiguration.DokkaSourceSet) + case class SourceLink(val path: String, val url: String, val lineSuffix: Option[String], val sourceSetData: DokkaSourceSet) object SourceLink { - def apply(sourceLinkDef: DokkaConfiguration$SourceLinkDefinition, sourceSetData: DokkaConfiguration.DokkaSourceSet): SourceLink = + def apply(sourceLinkDef: DokkaConfiguration$SourceLinkDefinition, sourceSetData: DokkaSourceSet): SourceLink = SourceLink(sourceLinkDef.getLocalDirectory, sourceLinkDef.getRemoteUrl.toString, Option(sourceLinkDef.getRemoteLineSuffix), sourceSetData) } diff --git a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala index af5bc9a83405..e15296aa88a8 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala @@ -370,7 +370,7 @@ class ScalaPageContentBuilder( address, DCI(mainDRI.asJava, kind), sourceSets.toDisplay, - Set().asJava, + JSet(), PropertyContainer.Companion.empty() ) ) @@ -388,7 +388,7 @@ class ScalaPageContentBuilder( address, DCI(mainDRI.asJava, kind), sourceSets.toDisplay, - Set().asJava, + JSet(), PropertyContainer.Companion.empty() ) ) @@ -407,7 +407,7 @@ class ScalaPageContentBuilder( address, DCI(mainDRI.asJava, kind), sourceSets.toDisplay, - Set().asJava, + JSet(), PropertyContainer.Companion.empty() ) ) @@ -434,7 +434,7 @@ class ScalaPageContentBuilder( docTag, DCI(mainDRI.asJava, kind), sourceSets.asJava, - Set().asJava, + JSet(), PropertyContainer.Companion.empty() ).asScala.toSeq diff --git a/scala3doc/src/dotty/dokka/utils.scala b/scala3doc/src/dotty/dokka/utils.scala index bf6d272aae43..67986989ad29 100644 --- a/scala3doc/src/dotty/dokka/utils.scala +++ b/scala3doc/src/dotty/dokka/utils.scala @@ -13,24 +13,10 @@ import org.jetbrains.dokka.base.translators.documentables._ import org.jetbrains.dokka.model.properties.PropertyContainer import java.util.function.Consumer import kotlin.jvm.functions.Function2 -import java.util.{List => JList, Set => JSet, Map => JMap} import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet import org.jetbrains.dokka.plugability._ import kotlin.jvm.JvmClassMappingKt.getKotlinClass -extension [V] (a: WithExtraProperties[_]): - def get(key: ExtraProperty.Key[_, V]): V = a.getExtra().getMap().get(key).asInstanceOf[V] - -extension [E <: WithExtraProperties[E]] (a: E): - def put(value: ExtraProperty[_ >: E]): E = // TODO remove some of the InstanceOf - a.withNewExtras(a.getExtra plus value).asInstanceOf[E] - -extension [V] (map: JMap[DokkaConfiguration$DokkaSourceSet, V]): - def defaultValue: V = map.values.asScala.toSeq(0) - -extension (sourceSets: Set[DokkaConfiguration$DokkaSourceSet]): - def toDisplay = sourceSets.map(DisplaySourceSet(_)).asJava - class BaseKey[T, V] extends ExtraProperty.Key[T, V]: override def mergeStrategyFor(left: V, right: V): MergeStrategy[T] = MergeStrategy.Remove.INSTANCE.asInstanceOf[MergeStrategy[T]] @@ -51,14 +37,6 @@ def getFromExtra[V](e: WithExtraProperties[_], k: ExtraProperty.Key[_, V]): Opti extension (f: DFunction): def isRightAssociative(): Boolean = f.getName.endsWith(":") -object JList: - def apply[T](elem: T): JList[T] = List(elem).asJava - def apply[T]() = List[T]().asJava - -object JSet: - def apply[T](elem: T): JSet[T] = Set(elem).asJava - def apply[T]() = Set[T]().asJava - def modifyContentGroup(originalContentNodeWithParents: Seq[ContentGroup], modifiedContentNode: ContentGroup): ContentGroup = originalContentNodeWithParents match { case head :: tail => tail match { diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index c145a9de75b1..0f3879b414f1 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -3,21 +3,23 @@ package dotty.dokka import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.pages._ import org.jetbrains.dokka.model._ +import org.jetbrains.dokka.base.renderers.html.HtmlRenderer import org.jetbrains.dokka._ import HTML._ import collection.JavaConverters._ -import com.virtuslab.dokka.site.SiteRenderer -import com.virtuslab.dokka.site.BaseStaticSiteProcessor import java.net.URI import java.util.{List => JList, Set => JSet} import kotlinx.html.FlowContent import kotlinx.html.stream.StreamKt import kotlinx.html.Gen_consumer_tagsKt +import kotlinx.html.ApiKt +import kotlinx.html.HTMLTag +import kotlinx.html.DIV import org.jetbrains.dokka.links.DRI import dotty.dokka.model.api.Link import dotty.dokka.model.api.HierarchyGraph import org.jetbrains.dokka.base.resolvers.local.LocationProvider - +import dotty.dokka.site.StaticPageNode class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet], locationProvider: LocationProvider): def link(dri: DRI): Option[String] = Option(locationProvider.resolve(dri, sourceSetRestriciton, pageContext)) @@ -35,7 +37,27 @@ class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[Dis def renderElement(e: String | (String, DRI) | Link) = renderElementWith(e) -class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { +class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { // TODO migrate from dokka site! + + // Implementation below is based on Kotlin bytecode and we will try to migrate it to dokka + // TODO (https://github.com/lampepfl/scala3doc/issues/232): Move this method to dokka + def withHtml(context: FlowContent, content: String): Unit = context match { + case tag: HTMLTag => + ApiKt.unsafe(tag, { x => + x.unaryPlus(content) + U + }) + case _ => + val div = new DIV(JMap(), context.getConsumer()) + try + div.getConsumer().onTagStart(div) + withHtml(div, content) + catch + case e: Throwable => + div.getConsumer.onTagError(div, e) + finally + div.getConsumer.onTagEnd(div) + } lazy val sourceSets = ctx.getConfiguration.getSourceSets.asScala .map(s => DisplaySourceSetKt.toDisplaySourceSet(s.asInstanceOf[DokkaConfiguration$DokkaSourceSet])).toSet.asJava @@ -166,7 +188,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { ): Unit = { import kotlinx.html.{Gen_consumer_tagsKt => dsl} val c = f.getConsumer - val U = kotlin.Unit.INSTANCE + dsl.a(c, node.getAddress, /*target*/ null, /*classes*/ null, { e => import ScalaCommentToContentConverter._ // node.getExtra.getMap.asScala.get(LinkAttributesKey) @@ -186,7 +208,6 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { // we cannot use Scalatags, because we need to call buildContentNode import kotlinx.html.{Gen_consumer_tagsKt => dsl} val c = f.getConsumer - val U = kotlin.Unit.INSTANCE dsl.div(c, "sample-container", { e => dsl.pre(c, null, { e => @@ -212,10 +233,10 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { override def buildHtml(page: PageNode, resources: JList[String], kotlinxContent: FlowContentConsumer): String = val (pageTitle, pageResources, fromTemplate) = page match - case static: BaseStaticSiteProcessor.StaticPageNode => - val res = if static.hasFrame then resources else static.resources - val title = static.getLoadedTemplate.getTemplateFile.title - (title, res, !static.hasFrame) + case static: StaticPageNode => + val res = if static.hasFrame() then resources else static.getEmbeddedResources() + val title = static.loadedTemplate.templateFile.title() + (title, res, !static.hasFrame()) case _ => (page.getName, resources, false) html( diff --git a/scala3doc/test/dotty/dokka/DottyTestRunner.scala b/scala3doc/test/dotty/dokka/DottyTestRunner.scala index 1345ac644288..a9ad7e92e4e6 100644 --- a/scala3doc/test/dotty/dokka/DottyTestRunner.scala +++ b/scala3doc/test/dotty/dokka/DottyTestRunner.scala @@ -66,7 +66,7 @@ abstract class DottyAbstractCoreTest extends AbstractCoreTest: config, new TestLogger(DokkaConsoleLogger.INSTANCE), tests.build(), - Nil.asJava + JList() ).generate() signatures From 163beafe39f71b4a6c738fd977b5114b99099c62 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Tue, 3 Nov 2020 21:55:13 +0100 Subject: [PATCH 2/6] Migrate remaning parts of Site Renderer --- .../dotty/renderers/ScalaHtmlRenderer.scala | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 0f3879b414f1..5c7fc6502902 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -20,6 +20,7 @@ import dotty.dokka.model.api.Link import dotty.dokka.model.api.HierarchyGraph import org.jetbrains.dokka.base.resolvers.local.LocationProvider import dotty.dokka.site.StaticPageNode +import dotty.dokka.site.PartiallyRenderedContent class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet], locationProvider: LocationProvider): def link(dri: DRI): Option[String] = Option(locationProvider.resolve(dri, sourceSetRestriciton, pageContext)) @@ -37,7 +38,7 @@ class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[Dis def renderElement(e: String | (String, DRI) | Link) = renderElementWith(e) -class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { // TODO migrate from dokka site! +class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { // Implementation below is based on Kotlin bytecode and we will try to migrate it to dokka // TODO (https://github.com/lampepfl/scala3doc/issues/232): Move this method to dokka @@ -206,6 +207,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { // TODO m pageContext: ContentPage, ): Unit = { // we cannot use Scalatags, because we need to call buildContentNode + // TODO rewrite it to using HTML import kotlinx.html.{Gen_consumer_tagsKt => dsl} val c = f.getConsumer @@ -231,6 +233,30 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { // TODO m ).toString() ) + private def render(c: PartiallyRenderedContent, p: ContentPage): String = + val parsed = if (!c.page.hasMarkdown) c.page.code else + div(raw( + buildWithKotlinx{ div => + c.getChildren.forEach(build(_, div, p, /*sourceSetRestriction=*/null)) + U + } + )).toString + + return c.page.render(parsed).code + + + override def buildPageContent(context: FlowContent, page: ContentPage): Unit = + page match + case s: StaticPageNode if !s.hasFrame() => + case _ => buildNavigation(context, page) + + page.getContent match + case prc: PartiallyRenderedContent => + withHtml(context, render(prc, page)) + case content => + build(content, context, page, /*sourceSetRestriction=*/null) + + override def buildHtml(page: PageNode, resources: JList[String], kotlinxContent: FlowContentConsumer): String = val (pageTitle, pageResources, fromTemplate) = page match case static: StaticPageNode => @@ -316,5 +342,4 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { // TODO m null, func ).toString.stripPrefix("
").stripSuffix("
\n") - } From aafbe9195f5dda2e73a7f21dd1a301d805b0896f Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Wed, 4 Nov 2020 15:36:39 +0100 Subject: [PATCH 3/6] Migrate dokka-site plugin configuration Dokka-site works as before migration. --- scala3doc/dotty-docs/docs/index.html | 161 ++---------------- .../src/dotty/dokka/DottyDokkaConfig.scala | 11 +- .../src/dotty/dokka/DottyDokkaPlugin.scala | 56 +++++- scala3doc/src/dotty/dokka/compat.scala | 4 +- ...s.scala => PartiallyRenderedContent.scala} | 7 +- .../src/dotty/dokka/site/StaticPageNode.scala | 39 +++++ .../dotty/dokka/site/StaticSiteContext.scala | 77 +++------ ...scala => StaticSiteLocationProvider.scala} | 2 +- scala3doc/src/dotty/dokka/site/common.scala | 4 +- .../src/dotty/dokka/site/processors.scala | 44 +---- .../src/dotty/dokka/site/templates.scala | 62 ++----- .../dotty/renderers/ScalaHtmlRenderer.scala | 31 +--- 12 files changed, 176 insertions(+), 322 deletions(-) rename scala3doc/src/dotty/dokka/site/{contentNodes.scala => PartiallyRenderedContent.scala} (80%) create mode 100644 scala3doc/src/dotty/dokka/site/StaticPageNode.scala rename scala3doc/src/dotty/dokka/site/{locationProvider.scala => StaticSiteLocationProvider.scala} (97%) diff --git a/scala3doc/dotty-docs/docs/index.html b/scala3doc/dotty-docs/docs/index.html index f31017f8e264..b0065cf5d2c8 100644 --- a/scala3doc/dotty-docs/docs/index.html +++ b/scala3doc/dotty-docs/docs/index.html @@ -49,158 +49,31 @@

Dotty

- -
-
+
-

Try Dotty

-

If you are a Mac user, you can install Dotty with brew:

-
brew install lampepfl/brew/dotty
+

Try Dotty

+

If you are a Mac user, you can install Dotty with brew:

+
brew install lampepfl/brew/dotty
-

If you are a Linux or Windows user, download the latest release. Optionally add path of the folder bin/ to the system environment variable PATH.

+

If you are a Linux or Windows user, download the latest release. Optionally add path of the folder bin/ to the system environment variable PATH.

-

Now you can compile Scala source code:

-
dotc hello.scala
+

Now you can compile Scala source code:

+
scalac hello.scala
-

To start the REPL, run: dotr.

+

To start the REPL, run: scala.

-

Or, you can try Dotty in your browser with Scastie.

+

Or, you can try Dotty in your browser with Scastie.

-

Create a Dotty Project

-

The fastest way to create a new project in Dotty is using sbt (1.1.4+).

+

Create a Dotty Project

+

The fastest way to create a new project in Dotty is using sbt (1.1.4+).

-

Create a Dotty project:

-
sbt new lampepfl/dotty.g8
+

Create a Dotty project:

+
sbt new lampepfl/dotty.g8
-

Or a Dotty project that cross compiles with Scala 2:

-
sbt new lampepfl/dotty-cross.g8
+

Or a Dotty project that cross compiles with Scala 2:

+
sbt new lampepfl/dotty-cross.g8
-

For documentation see the Dotty Example Project.

-
+

For documentation see the Dotty Example Project.

+
- -
-
-

So, features?

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Intersection TypesImplemented
Union TypesImplemented
Type lambdasImplemented
Context queryImplemented
Trait parametersImplemented
Implied InstancesImplemented
Inferable parametersImplemented
Extension MethodsImplemented
Opaque Type AliasesImplemented
Toplevel definitionsImplemented
Export clausesImplemented
Vararg patternsImplemented
Creator applicationsImplemented
@static methods and fieldsImplemented
SBT incremental buildImplemented
Option-less pattern matchingImplemented
Multiversal equalityImplemented
Erased TermsImplemented
Auto-SpecializationIn progress
Whole program optimizerIn progress
HList & HMaps/Record typesIn progress
EffectsConsidered
…and many more, check the overview page for a comprehensive list
-
-

Talks on Dotty?

- -

I have more questions!

-
-

That’s great! We have more details on the docs and please join our Gitter channel!

-
-
-
-
- diff --git a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala index 57e8f05a6bf4..4c69b057cfa8 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala @@ -4,6 +4,7 @@ import org.jetbrains.dokka._ import org.jetbrains.dokka.DokkaSourceSetImpl import java.io.File import collection.JavaConverters._ +import dotty.dokka.site.StaticSiteContext case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaConfiguration: override def getOutputDir: File = docConfiguration.args.output @@ -16,15 +17,11 @@ case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaCon override def getModuleName(): String = "ModuleName" override def getModuleVersion(): String = "" - private object OurConfig extends DokkaConfiguration.PluginConfiguration: - override def getFqPluginName = "ExternalDocsTooKey" - override def getSerializationFormat: DokkaConfiguration$SerializationFormat = - DokkaConfiguration$SerializationFormat.JSON.asInstanceOf[DokkaConfiguration$SerializationFormat] - override def getValues: String = docConfiguration.args.docsRoot.getOrElse("") + lazy val staticSiteContext = docConfiguration.args.docsRoot.map(path => StaticSiteContext(File(path).getAbsoluteFile(), Set(mkSourceSet.asInstanceOf[SourceSetWrapper]))) - override def getPluginsConfiguration: JList[DokkaConfiguration.PluginConfiguration] = JList(OurConfig) + override def getPluginsConfiguration: JList[DokkaConfiguration.PluginConfiguration] = JList() - def mkSourceSet: DokkaSourceSet = + lazy val mkSourceSet: DokkaSourceSet = val sourceLinks:Set[SourceLinkDefinitionImpl] = docConfiguration.args.sourceLinks.map(SourceLinkDefinitionImpl.Companion.parseSourceLinkDefinition(_)).toSet new DokkaSourceSetImpl( /*displayName=*/ docConfiguration.args.name, diff --git a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala index 33dd9c7a9681..2eb45308e8ae 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala @@ -23,6 +23,12 @@ import dotty.dokka.model.api._ import org.jetbrains.dokka.CoreExtensions import org.jetbrains.dokka.base.DokkaBase +import dotty.dokka.site.SitePagesCreator +import dotty.dokka.site.StaticSiteContext +import dotty.dokka.site.RootIndexPageCreator +import dotty.dokka.site.SiteResourceManager +import dotty.dokka.site.StaticSiteLocationProviderFactory + /** Main Dokka plugin for the doctool. * * Wires together classes responsible for consuming Tasty and generating @@ -120,7 +126,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: ) val implicitMembersExtensionTransformer = extend( - _.extensionPoint(CoreExtensions.INSTANCE.getDocumentableTransformer ) + _.extensionPoint(CoreExtensions.INSTANCE.getDocumentableTransformer) .fromRecipe(ImplicitMembersExtensionTransformer(_)) .name("implicitMembersExtensionTransformer") ) @@ -134,6 +140,54 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: .name("muteDefaultSourceLinksTransformer") ) + val customDocumentationProvider = extend( + _.extensionPoint(dokkaBase.getHtmlPreprocessors) + .fromRecipe(c => SitePagesCreator(c.siteContext)) + .name("customDocumentationProvider") + .ordered( + before = Seq( + dokkaBase.getNavigationPageInstaller, + dokkaBase.getScriptsInstaller, + dokkaBase.getStylesInstaller, + dokkaBase.getPackageListCreator, + ), + after = Seq(dokkaBase.getRootCreator) + ) + ) + + val customIndexRootProvider = extend( + _.extensionPoint(dokkaBase.getHtmlPreprocessors) + .fromRecipe(c => RootIndexPageCreator(c.siteContext)) + .name("customIndexRootProvider") + .ordered( + before = Seq( + dokkaBase.getScriptsInstaller, + dokkaBase.getStylesInstaller, + ), + after = Seq(dokkaBase.getNavigationPageInstaller) + ) + ) + + val customDocumentationResources = extend( + _.extensionPoint(dokkaBase.getHtmlPreprocessors) + .fromRecipe(c => SiteResourceManager(c.siteContext)) + .name("customDocumentationResources") + .after( + scalaEmbeddedResourceAppender.getValue + ) + ) + + val locationProvider = extend( + _.extensionPoint(dokkaBase.getLocationProviderFactory) + .fromRecipe(StaticSiteLocationProviderFactory(_)) + .overrideExtension(dokkaBase.getLocationProvider) + ) + + extension (ctx: DokkaContext): + def siteContext: Option[StaticSiteContext] = ctx.getConfiguration match + case d: DottyDokkaConfig => d.staticSiteContext + case _ => None + // TODO (https://github.com/lampepfl/scala3doc/issues/232): remove once problem is fixed in Dokka extension [T] (builder: ExtensionBuilder[T]): def ordered(before: Seq[Extension[_, _, _]], after: Seq[Extension[_, _, _]]): ExtensionBuilder[T] = diff --git a/scala3doc/src/dotty/dokka/compat.scala b/scala3doc/src/dotty/dokka/compat.scala index 492f39266ec4..520a105d81b1 100644 --- a/scala3doc/src/dotty/dokka/compat.scala +++ b/scala3doc/src/dotty/dokka/compat.scala @@ -12,7 +12,7 @@ import org.jetbrains.dokka.model.properties.ExtraProperty import java.util.stream.Stream // comment out - wrong error! import java.util.stream.Collectors -def mkDRI(classNames: String = null, extra: String = null) = new DRI(null, classNames, null, PointingToDeclaration.INSTANCE, extra) +def mkDRI(packageName: String = null, extra: String = null) = new DRI(packageName, null, null, PointingToDeclaration.INSTANCE, extra) val U: kotlin.Unit = kotlin.Unit.INSTANCE @@ -56,4 +56,4 @@ extension [V] (map: JMap[SourceSetWrapper, V]): extension [V](jlist: JList[V]): def ++ (other: JList[V]): JList[V] = - Stream.of(jlist, other).flatMap(_.stream).collect(Collectors.toList()) \ No newline at end of file + Stream.of(jlist, other).flatMap(_.stream).collect(Collectors.toList()) diff --git a/scala3doc/src/dotty/dokka/site/contentNodes.scala b/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala similarity index 80% rename from scala3doc/src/dotty/dokka/site/contentNodes.scala rename to scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala index 6f684a8f8c0a..7e92c46b87d8 100644 --- a/scala3doc/src/dotty/dokka/site/contentNodes.scala +++ b/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala @@ -6,14 +6,15 @@ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.{ContentNode, DCI, Style} case class PartiallyRenderedContent( - page: PreResolvedPage, + template: TemplateFile, + context: StaticSiteContext, override val getChildren: JList[ContentNode], override val getDci: DCI, override val getSourceSets: JSet[DisplaySourceSet], override val getStyle: JSet[Style] = JSet(), override val getExtra: PropertyContainer[ContentNode] = new PropertyContainer(JMap()) ) extends ContentNode: - override def hasAnyContent(): Boolean = getChildren.stream().filter(_.hasAnyContent()).count() > 0 + override def hasAnyContent(): Boolean = true override def withNewExtras(newExtras: PropertyContainer[ContentNode]): ContentNode = copy(getExtra = newExtras) @@ -21,4 +22,4 @@ case class PartiallyRenderedContent( override def withSourceSets(sourceSets: JSet[DisplaySourceSet]): ContentNode = copy(getSourceSets = sourceSets) - val allResources: List[String] = page.render("").resources + lazy val resolved = template.resolveToHtml(context) diff --git a/scala3doc/src/dotty/dokka/site/StaticPageNode.scala b/scala3doc/src/dotty/dokka/site/StaticPageNode.scala new file mode 100644 index 000000000000..8c61ec8dfe96 --- /dev/null +++ b/scala3doc/src/dotty/dokka/site/StaticPageNode.scala @@ -0,0 +1,39 @@ +package dotty.dokka +package site + +import java.io.File +import java.nio.file.Files + +import org.jetbrains.dokka.base.renderers.html.{NavigationNode, NavigationPage} +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.pages._ +import org.jetbrains.dokka.transformers.pages.PageTransformer + +case class LoadedTemplate(templateFile: TemplateFile, children: List[LoadedTemplate], file: File) { + def relativePath(root: File): String = + root.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '.') +} + +case class StaticPageNode( + template: TemplateFile, + override val getName: String, + override val getContent: ContentNode, + override val getDri: JSet[DRI], + override val getEmbeddedResources: JList[String], + override val getChildren: JList[PageNode], + ) extends ContentPage: + override def getDocumentable: Documentable = null + + def title(): String = template.title() + def hasFrame(): Boolean = template.hasFrame() + + override def modified(name: String, content: ContentNode, dri: JSet[DRI], embeddedResources: JList[String], children: JList[_ <: PageNode]): ContentPage = + copy(template, name, content, dri, embeddedResources, children.asInstanceOf[JList[PageNode]]) + + override def modified(name: String, children: JList[_ <: PageNode]): PageNode = + copy(getName = name, getChildren = children.asInstanceOf[JList[PageNode]]) + + def resources(): List[String] = getContent match + case p: PartiallyRenderedContent => p.resolved.resources + case _ => Nil \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala index 46618b745886..e4b51395f955 100644 --- a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala +++ b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala @@ -5,7 +5,6 @@ import java.io.File import org.jetbrains.dokka.base.parsers.MarkdownParser import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter -import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.{DocTag, Text} @@ -13,19 +12,19 @@ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.{ContentKind, ContentNode, DCI, PageNode} import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.pages.Style +import org.jetbrains.dokka.model.DisplaySourceSet import scala.collection.JavaConverters._ -class StaticSiteContext(val root: File, cxt: DokkaContext): - +class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper]): val docsFile = new File(root, "docs") def indexPage():Option[StaticPageNode] = val files = List(new File(root, "index.html"), new File(root, "index.md")).filter { _.exists() } - if (files.size > 1) println("ERROR: Multiple root index pages found: ${files.map { it.absolutePath }}") // TODO (#14): provide proper error handling + if (files.size > 1) println(s"ERROR: Multiple root index pages found: ${files.map(_.getAbsolutePath)}") // TODO (#14): provide proper error handling loadFiles(files).headOption - private lazy val layouts: Map[String, TemplateFile] = + lazy val layouts: Map[String, TemplateFile] = val layoutRoot = new File(root, "_layouts") val dirs: Array[File] = Option(layoutRoot.listFiles()).getOrElse(Array()) dirs.map { it => loadTemplateFile(it) }.map { it => it.name() -> it }.toMap @@ -39,16 +38,16 @@ class StaticSiteContext(val root: File, cxt: DokkaContext): private def loadTemplate(from: File): Option[LoadedTemplate] = if (!isValidTemplate(from)) None else try - val (indexes, children) = Option(from.listFiles()).toSeq.flatten.flatMap(loadTemplate).partition(_.isIndexPage()) + val (indexes, children) = Option(from.listFiles()).toSeq.flatten.flatMap(loadTemplate).partition(_.templateFile.isIndexPage()) if (indexes.size > 1) - println("ERROR: Multiple index pages for $from found in ${indexes.map { it.file }}") // TODO (#14): provide proper error handling + println(s"ERROR: Multiple index pages for $from found in ${indexes.map(_.file)}") // TODO (#14): provide proper error handling def loadIndexPage(): TemplateFile = { val indexFiles = from.listFiles { file =>file.getName == "index.md" || file.getName == "index.html" } indexFiles.size match { case 0 => emptyTemplate(from) case 1 => loadTemplateFile(indexFiles.head).copy(file = from) - case _ => throw new java.lang.RuntimeException("ERROR: Multiple index pages found under ${from.path}") + case _ => throw new java.lang.RuntimeException(s"ERROR: Multiple index pages found under ${from.toPath}") } } @@ -60,40 +59,10 @@ class StaticSiteContext(val root: File, cxt: DokkaContext): e.printStackTrace() // TODO (#14): provide proper error handling None - private def parseMarkdown(page: PreResolvedPage, dri: DRI, allDRIs: Map[String, DRI]): ContentNode = - val nodes: JList[ContentNode] = if (!page.hasMarkdown) JList() else - val parser = new MarkdownParser ( link => { - val driKey = - if (link.startsWith("/")) then - // handle root related links - link.replace('/', '.').stripPrefix(".") - else - val unSuffixedDri = dri.getPackageName.stripSuffix(".html").stripSuffix(".md") - val parentDri = unSuffixedDri.take(unSuffixedDri.lastIndexOf('.')).stripPrefix("_.") - "${parentDri}.${link.replace('/', '.')}" - - allDRIs.get(driKey).orNull - }) - val docTag = try parser.parseStringToDocNode(page.code) catch - case (e: Throwable) => - val msg = "Error rendering (dri = $dri): ${e.message}" - println("ERROR: $msg") // TODO (#14): provide proper error handling - new Text() - - asContent(docTag, dri) - - new PartiallyRenderedContent( - page, - nodes, - new DCI(Set(dri).asJava, ContentKind.Empty), - cxt.getConfiguration.getSourceSets.toDisplaySourceSet, - JSet() - ) - def asContent(d: DocTag, dri: DRI) = new DocTagToContentConverter().buildContent( d, new DCI(Set(dri).asJava, ContentKind.Empty), - cxt.getConfiguration.getSourceSets.asDokka, + sourceSets.asJava, JSet(), new PropertyContainer(JMap()) ) @@ -106,31 +75,27 @@ class StaticSiteContext(val root: File, cxt: DokkaContext): def flatten(it: LoadedTemplate): List[String] = List(it.relativePath(root)) ++ it.children.flatMap(flatten) - def pathTomkDRI(path: String) = mkDRI("_.$path") + def pathToDRI(path: String) = mkDRI(s"_.$path") - val driMap = all.flatMap { it => flatten(it) }.map { it => it -> pathTomkDRI(it) }.toMap + val driMap = all.flatMap { it => flatten(it) }.map { it => it -> pathToDRI(it) }.toMap def templateToPage(myTemplate: LoadedTemplate): StaticPageNode = - val dri = pathTomkDRI(myTemplate.relativePath(root)) - val page = try { - val properties = myTemplate.templateFile.layout() - .map(c => Map("content" -> myTemplate.templateFile.rawCode)).getOrElse(Map.empty) - - myTemplate.templateFile.resolveMarkdown(RenderingContext(properties, layouts)) - } catch { case e: Throwable => - val msg = "Error rendering $myTemplate: ${e.message}" - println("ERROR: $msg") // TODO (#14): provide proper error handling - PreResolvedPage("", None, true) - } - val content = parseMarkdown(page, dri, driMap) - val children = myTemplate.children.map(templateToPage) + val dri = pathToDRI(myTemplate.relativePath(root)) + val content = new PartiallyRenderedContent( + myTemplate.templateFile, + this, + JList(), + new DCI(Set(dri).asJava, ContentKind.Empty), + sourceSets.toDisplay, + JSet() + ) StaticPageNode( - myTemplate, + myTemplate.templateFile, myTemplate.templateFile.title(), content, JSet(dri), JList(), - (children ++ customChildren).asJava + (myTemplate.children.map(templateToPage) ++ customChildren).asJava ) all.map(templateToPage) diff --git a/scala3doc/src/dotty/dokka/site/locationProvider.scala b/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala similarity index 97% rename from scala3doc/src/dotty/dokka/site/locationProvider.scala rename to scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala index 8ff086895c31..ec82eeb64687 100644 --- a/scala3doc/src/dotty/dokka/site/locationProvider.scala +++ b/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala @@ -24,7 +24,7 @@ class StaticSiteLocationProvider(ctx: DokkaContext, pageNode: RootPageNode) else { val path = jpath.asScala.toList val start = if (path.head == "--root--") List("docs") else path.take(1) - val pageName = page.loadedTemplate.file.getName + val pageName = page.template.file.getName val dotIndex = pageName.lastIndexOf('.') val newName = if (dotIndex < 0) pageName else pageName.substring(0, dotIndex) (start ++ path.drop(1).dropRight(1) ++ List(newName)).asJava diff --git a/scala3doc/src/dotty/dokka/site/common.scala b/scala3doc/src/dotty/dokka/site/common.scala index d77a49b27228..50d2eb6090b7 100644 --- a/scala3doc/src/dotty/dokka/site/common.scala +++ b/scala3doc/src/dotty/dokka/site/common.scala @@ -18,11 +18,9 @@ import org.jetbrains.dokka.model.doc.Text import scala.collection.JavaConverters._ -val ExternalDocsTooKey = "ExternalDocsTooKey" val docsRootDRI: DRI = mkDRI(extra = "_top_level_index") val docsDRI: DRI = mkDRI(extra = "_docs_level_index") -val apiPageDRI: DRI = mkDRI(classNames = "api", extra = "__api__") - +val apiPageDRI: DRI = mkDRI(packageName = "api", extra = "__api__") val defaultMarkdownOptions: DataHolder = new MutableDataSet() diff --git a/scala3doc/src/dotty/dokka/site/processors.scala b/scala3doc/src/dotty/dokka/site/processors.scala index 009e6d29500c..7aefbd102266 100644 --- a/scala3doc/src/dotty/dokka/site/processors.scala +++ b/scala3doc/src/dotty/dokka/site/processors.scala @@ -12,36 +12,6 @@ import org.jetbrains.dokka.transformers.pages.PageTransformer import scala.collection.JavaConverters._ -case class LoadedTemplate(templateFile: TemplateFile, children: List[LoadedTemplate], file: File) { - def isIndexPage() = file.isFile && (file.getName == "index.md" || file.getName == "index.html") - def relativePath(root: File): String = - root.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '.') -} - -case class StaticPageNode( - loadedTemplate: LoadedTemplate, - override val getName: String, - override val getContent: ContentNode, - override val getDri: JSet[DRI], - override val getEmbeddedResources: JList[String], - override val getChildren: JList[PageNode], - ) extends ContentPage: - override def getDocumentable: Documentable = null - - def title(): String = loadedTemplate.templateFile.title() - def hasFrame(): Boolean = loadedTemplate.templateFile.hasFrame() - - override def modified(name: String, content: ContentNode, dri: JSet[DRI], embeddedResources: JList[String], children: JList[_ <: PageNode]): ContentPage = - copy(loadedTemplate, name, content, dri, embeddedResources, children.asInstanceOf[JList[PageNode]]) - - override def modified(name: String, children: JList[_ <: PageNode]): PageNode = - copy(getName = name, getChildren = children.asInstanceOf[JList[PageNode]]) - - def resources(): List[String] = getContent match - case p: PartiallyRenderedContent => p.allResources - case _ => Nil - - abstract class BaseStaticSiteProcessor(staticSiteContext: Option[StaticSiteContext]) extends PageTransformer: final override def invoke(input: RootPageNode): RootPageNode = staticSiteContext.fold(input)(transform(input, _)) @@ -57,7 +27,8 @@ class SiteResourceManager(ctx: Option[StaticSiteContext]) extends BaseStaticSite override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = val imgPath = ctx.root.toPath().resolve("images") - val allImgPaths = Files.walk(imgPath).filter(Files.isRegularFile(_)) + val allImgPaths = Files.walk(imgPath) + .filter(p => Files.isRegularFile(p) && p.getFileName().toString().endsWith(".svg")) val images = allImgPaths.iterator().asScala.toList.map(_.toString) val resources = listResources(input.getChildren.asScala.toList) ++ images @@ -65,12 +36,13 @@ class SiteResourceManager(ctx: Option[StaticSiteContext]) extends BaseStaticSite val content = Files.readString(ctx.root.toPath.resolve(path)) new RendererSpecificResourcePage(path, JList(), new RenderingStrategy.Write(content)) }.toList + val modified = input.transformContentPagesTree { case it: StaticPageNode => - val newRes = JList[String]() - newRes.addAll(it.getEmbeddedResources) - newRes.addAll(it.resources().asJava) - it.copy(getEmbeddedResources = newRes) + it.copy(getEmbeddedResources = + if it.template.hasFrame() then it.getEmbeddedResources ++ it.resources().asJava + else it.resources().asJava + ) case it => it } modified.modified(modified.getName, (resourcePages ++ modified.getChildren.asScala).asJava) @@ -120,7 +92,7 @@ class SitePagesCreator(ctx: Option[StaticSiteContext]) extends BaseStaticSitePro val (contentPage, others) = input.getChildren.asScala.toList.partition { _.isInstanceOf[ContentPage] } val modifiedModuleRoot = processRootPage(input, contentPage) val allFiles = Option(ctx.docsFile.listFiles()).toList.flatten - val (indexes, children) = ctx.loadFiles(allFiles).partition(_.loadedTemplate.isIndexPage()) + val (indexes, children) = ctx.loadFiles(allFiles).partition(_.template.isIndexPage()) if (indexes.size > 1) println("ERROR: Multiple index pages found $indexes}") // TODO (#14): provide proper error handling val rootContent = indexes.headOption.fold(ctx.asContent(Text(), mkDRI(extra = "root_content")).get(0))(_.getContent) diff --git a/scala3doc/src/dotty/dokka/site/templates.scala b/scala3doc/src/dotty/dokka/site/templates.scala index 8f79b14fa874..412a4c57806b 100644 --- a/scala3doc/src/dotty/dokka/site/templates.scala +++ b/scala3doc/src/dotty/dokka/site/templates.scala @@ -33,25 +33,8 @@ case class RenderingContext( resources = this.resources ++ resources ) -case class LayoutInfo(htmlTemple: TemplateFile, ctx: RenderingContext) - -case class PreResolvedPage( - code: String, - nextLayoutInfo: Option[LayoutInfo], - hasMarkdown: Boolean, - resources: List[String] = Nil - ): - def render(renderedMarkdown: String): ResolvedPage = nextLayoutInfo match - case None => ResolvedPage(renderedMarkdown, hasMarkdown, resources) - case Some(nextLayoutInfo) => - val newCtx = - nextLayoutInfo.ctx.copy(properties = nextLayoutInfo.ctx.properties + ("content" -> renderedMarkdown)) - val res = nextLayoutInfo.htmlTemple.resolveToHtml(newCtx, hasMarkdown) - ResolvedPage(res.code, hasMarkdown, res.resources) - case class ResolvedPage( val code: String, - val hasMarkdown: Boolean, val resources: List[String] = Nil ) @@ -88,39 +71,26 @@ case class TemplateFile( def hasFrame(): Boolean = !stringSetting("hasFrame").contains("false") + def isIndexPage() = file.isFile && (file.getName == "index.md" || file.getName == "index.html") - def resolveMarkdown(ctx: RenderingContext): PreResolvedPage = - resolveInner( - ctx = ctx.copy(properties = ctx.properties + ("page" -> Map("title" -> title()))), - stopAtHtml = true, - !isHtml // This is top level template - ) - - def resolveToHtml(ctx: RenderingContext, hasMarkdown: Boolean): PreResolvedPage = - resolveInner(ctx, stopAtHtml = false, hasMarkdown) + def resolveToHtml(ctx: StaticSiteContext): ResolvedPage = resolveInner(RenderingContext(Map(), ctx.layouts)) - private def resolveInner(ctx: RenderingContext, stopAtHtml: Boolean, hasMarkdown: Boolean): PreResolvedPage = + private def resolveInner(ctx: RenderingContext): ResolvedPage = if (ctx.resolving.contains(file.getAbsolutePath)) - throw new RuntimeException("Cycle in templates involving $file: ${ctx.resolving}") + throw new RuntimeException(s"Cycle in templates involving $file: ${ctx.resolving}") val layoutTemplate = layout().map(name => ctx.layouts.getOrElse(name, throw new RuntimeException(s"No layouts named $name in ${ctx.layouts}"))) - if (!stopAtHtml && !isHtml) - throw new java.lang.RuntimeException( - "Markdown layout cannot be applied after .html. Rendering $file after: ${ctx.resolving}" - ) - - if stopAtHtml && isHtml then - PreResolvedPage(ctx.properties.getOrElse("content", "").toString, Some(LayoutInfo(this, ctx)), hasMarkdown) - else - val rendered = Template.parse(this.rawCode).render(ctx.properties.asJava) // Library requires mutable maps.. - val code = if (!isHtml) rendered else - val parser: Parser = Parser.builder().build() - HtmlRenderer.builder(ctx.markdownOptions).build().render(parser.parse(rendered)) - - val resources = listSetting("extraCSS") ++ listSetting("extraJS") - layoutTemplate match - case None => PreResolvedPage(code, None, hasMarkdown, resources ++ ctx.resources) - case Some(layoutTemplate) => - layoutTemplate.resolveInner(ctx.nest(code, file, resources), stopAtHtml, hasMarkdown) + // Library requires mutable maps.. + val mutableProperties = new java.util.HashMap[String, Object](ctx.properties.asJava) + val rendered = Template.parse(this.rawCode).render(mutableProperties) + val code = if (!isHtml) rendered else + val parser: Parser = Parser.builder().build() + HtmlRenderer.builder(ctx.markdownOptions).build().render(parser.parse(rendered)) + + val resources = listSetting("extraCSS") ++ listSetting("extraJS") + layoutTemplate match + case None => ResolvedPage(code, resources ++ ctx.resources) + case Some(layoutTemplate) => + layoutTemplate.resolveInner(ctx.nest(code, file, resources)) diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 5c7fc6502902..a9de675ada02 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -233,50 +233,35 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { ).toString() ) - private def render(c: PartiallyRenderedContent, p: ContentPage): String = - val parsed = if (!c.page.hasMarkdown) c.page.code else - div(raw( - buildWithKotlinx{ div => - c.getChildren.forEach(build(_, div, p, /*sourceSetRestriction=*/null)) - U - } - )).toString - - return c.page.render(parsed).code - - override def buildPageContent(context: FlowContent, page: ContentPage): Unit = page match case s: StaticPageNode if !s.hasFrame() => - case _ => buildNavigation(context, page) + case _ => buildNavigation(context, page) page.getContent match case prc: PartiallyRenderedContent => - withHtml(context, render(prc, page)) + withHtml(context, prc.resolved.code) case content => build(content, context, page, /*sourceSetRestriction=*/null) override def buildHtml(page: PageNode, resources: JList[String], kotlinxContent: FlowContentConsumer): String = - val (pageTitle, pageResources, fromTemplate) = page match + val (pageTitle, noFrame) = page match case static: StaticPageNode => - val res = if static.hasFrame() then resources else static.getEmbeddedResources() - val title = static.loadedTemplate.templateFile.title() - (title, res, !static.hasFrame()) + (static.template.title(), !static.hasFrame()) case _ => - (page.getName, resources, false) + (page.getName, false) + html( head( meta(charset := "utf-8"), meta(name := "viewport", content := "width=device-width, initial-scale=1"), title(pageTitle), - linkResources(page, pageResources.asScala).toSeq, + linkResources(page, resources.asScala).toSeq, script(raw(s"""var pathToRoot = "${getLocationProvider.pathToRoot(page)}";""")) ), body( - if fromTemplate then - raw(buildWithKotlinx(kotlinxContent)) - else + if noFrame then raw(buildWithKotlinx(kotlinxContent)) else div(id := "container")( div(id := "leftColumn")( div(id := "logo"), From 17ed48b9fe5e589c533c2be3f99d27201665ee4f Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 5 Nov 2020 09:31:36 +0100 Subject: [PATCH 4/6] Migrate template tests from dokka-site --- scala3doc/src/dotty/dokka/model/api/api.scala | 2 +- .../src/dotty/dokka/site/processors.scala | 8 +- .../src/dotty/dokka/site/templates.scala | 2 +- .../dotty/dokka/site/TemplateFileTests.scala | 218 ++++++++++++++++++ 4 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 scala3doc/test/dotty/dokka/site/TemplateFileTests.scala diff --git a/scala3doc/src/dotty/dokka/model/api/api.scala b/scala3doc/src/dotty/dokka/model/api/api.scala index a5d537bbe2a0..9a956f57c481 100644 --- a/scala3doc/src/dotty/dokka/model/api/api.scala +++ b/scala3doc/src/dotty/dokka/model/api/api.scala @@ -87,7 +87,7 @@ object Annotation: // TODO (longterm) properly represent signatures case class Link(name: String, dri: DRI) -type Signature = Seq[String | Link]// TODO migrate tupes to Links +type Signature = Seq[String | Link] object Signature: def apply(names: (String | Link)*): Signature = names // TO batter dotty shortcommings in union types diff --git a/scala3doc/src/dotty/dokka/site/processors.scala b/scala3doc/src/dotty/dokka/site/processors.scala index 7aefbd102266..21f532e54a73 100644 --- a/scala3doc/src/dotty/dokka/site/processors.scala +++ b/scala3doc/src/dotty/dokka/site/processors.scala @@ -27,9 +27,11 @@ class SiteResourceManager(ctx: Option[StaticSiteContext]) extends BaseStaticSite override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = val imgPath = ctx.root.toPath().resolve("images") - val allImgPaths = Files.walk(imgPath) - .filter(p => Files.isRegularFile(p) && p.getFileName().toString().endsWith(".svg")) - val images = allImgPaths.iterator().asScala.toList.map(_.toString) + val images = + if !Files.exists(imgPath) then Nil + else + val stream = Files.walk(imgPath)filter(p => Files.isRegularFile(p) && p.getFileName().toString().endsWith(".svg")) + stream.iterator().asScala.toList.map(_.toString) val resources = listResources(input.getChildren.asScala.toList) ++ images val resourcePages = resources.map { path => diff --git a/scala3doc/src/dotty/dokka/site/templates.scala b/scala3doc/src/dotty/dokka/site/templates.scala index 412a4c57806b..53a1830de4c8 100644 --- a/scala3doc/src/dotty/dokka/site/templates.scala +++ b/scala3doc/src/dotty/dokka/site/templates.scala @@ -75,7 +75,7 @@ case class TemplateFile( def resolveToHtml(ctx: StaticSiteContext): ResolvedPage = resolveInner(RenderingContext(Map(), ctx.layouts)) - private def resolveInner(ctx: RenderingContext): ResolvedPage = + private[site] def resolveInner(ctx: RenderingContext): ResolvedPage = if (ctx.resolving.contains(file.getAbsolutePath)) throw new RuntimeException(s"Cycle in templates involving $file: ${ctx.resolving}") diff --git a/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala b/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala new file mode 100644 index 000000000000..a02cc1d768fd --- /dev/null +++ b/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala @@ -0,0 +1,218 @@ +package dotty.dokka.site + +import com.vladsch.flexmark.html.HtmlRenderer +import com.vladsch.flexmark.parser.Parser +import org.junit.Assert.assertEquals +import org.junit.Test +import java.nio.file.Files + +class TemplateFileTests: + private def testTemplate(code: String, ext: String = "html")(op: TemplateFile => Unit): Unit = + val tmpFile = Files.createTempFile("headerTests", s".${ext}").toFile() + try + Files.writeString(tmpFile.toPath, code) + op(loadTemplateFile(tmpFile)) + finally tmpFile.delete() + + + private def testTemplates( + props: Map[String, String], + template: List[(String, String)])( + op: RenderingContext => Unit + ) = + def rec(cxt: RenderingContext, remaining: List[(String, String)]): Unit = + if (remaining.isEmpty) op(cxt) + else + val (code, ext) = remaining.head + testTemplate(code, ext) { template => + val newCtx = cxt.copy(layouts = cxt.layouts + (template.name() -> template)) + rec(newCtx, remaining.drop(1)) + } + + rec(RenderingContext(props), template) + + private def fullRender(template: TemplateFile, ctx: RenderingContext): String = template.resolveInner(ctx).code.trim() + + @Test + def testParsingHeaders(): Unit = + testTemplate( + """--- + |title: myTitle + |--- + |code""".stripMargin + ) { t => + assertEquals(t.rawCode, "code") + assertEquals(t.title(), "myTitle") + } + + + @Test + def testLinks(): Unit = + val base = + """--- + |title: myTitle + |name: base + |--- + |Ala {{ content }}. {{p2}} with [link](link/target.md)! + |""".stripMargin + + val content = + """--- + |layout: base + |name: content + |--- + |ma kota w **{{ p1 }}** from [here](link/here.md) + |""".stripMargin + + + val expected = """

Ala ma kota w paski from here. Hej with link!

""" + + testTemplates( + Map("p1" -> "paski", "p2" -> "Hej"), + List(base -> "html", content -> "md") + ) { it => + assertEquals( + expected, + fullRender(it.layouts("content"), it) + ) + } + + @Test + def layout(): Unit = + val base = + """--- + |title: myTitle + |name: base + |--- + |Ala {{ content }}. {{p2}}! + |""".stripMargin + + val content = + """--- + |layout: base + |name: content + |--- + |ma kota w **{{ p1 }}** + |""".stripMargin + + + val expected = """

Ala ma kota w paski. Hej!

""".stripMargin + + testTemplates( + Map("p1" -> "paski", "p2" -> "Hej"), + List(base -> "html", content -> "md") + ) { it => + assertEquals( + expected, + fullRender(it.layouts("content"), it) + ) + } + + @Test + def nestedLayout_htmlMdHtml(): Unit = + val toplevel = + """--- + |name: toplevel + |--- + |[div id="root"]{{ content }}[/div] + |""".stripMargin + + val basePage = + """--- + |layout: toplevel + |name: basePage + |--- + |# {{ pageName }} + | + |{{content}} + | + |## {{ pageName }} end + |""".stripMargin + + val content = + """--- + |layout: basePage + |name: content + |--- + |Hello {{ name }}! + |""".stripMargin + + + val expected = + """[div id="root"][h1]Test page[/h1] + |[p]Hello world!![/p] + |[h2]Test page end[/h2] + |[/div]""".stripMargin + + testTemplates( + Map("pageName" -> "Test page", "name" -> "world!"), + List( + toplevel -> "html", + basePage -> "md", + content -> "md" + ) + ) (it => fullRender(it.layouts("content"), it)) + + @Test + def nestedLayout_mdHtmlMd(): Unit = + val toplevel = + """--- + |name: toplevel + |--- + |

The Page

+ |{{ content }} + |""".stripMargin + + val basePage = + """--- + |layout: toplevel + |name: basePage + |--- + |

{{ pageName }}

+ | + |{{content}} + | + |

{{ pageName }} end

+ |""".stripMargin + + val content = + """--- + |layout: basePage + |name: content + |--- + |Hello {{ name }}! + |""".stripMargin + + + val expected = + """

The Page

+ |

Test page

+ |

Hello world!!

+ |

Test page end

""".stripMargin + + testTemplates( + Map("pageName" -> "Test page", "name" -> "world!"), + List( + toplevel -> "html", + basePage -> "html", + content -> "md" + ) + ) { ctx => assertEquals(expected, fullRender(ctx.layouts("content"), ctx)) } + + @Test + def markdown(): Unit = + testTemplate( + """# Hello {{ msg }}!""", + ext = "md" + ) { t => + assertEquals("# Hello there!", t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim()) + } + + @Test + def mixedTemplates() : Unit = + testTemplate( + """# Hello {{ msg }}!""", + ext = "md" + ) { t => + assertEquals("# Hello there!", t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim()) + } \ No newline at end of file From 92a1399e343276184b353a66a59ce41d9bae1cc2 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 5 Nov 2020 09:49:40 +0100 Subject: [PATCH 5/6] Fix whitespaces in newly added files --- project/Build.scala | 2 +- .../src/dotty/dokka/DottyDokkaPlugin.scala | 8 +- scala3doc/src/dotty/dokka/compat.scala | 10 +- .../dokka/site/PartiallyRenderedContent.scala | 26 +-- .../src/dotty/dokka/site/StaticPageNode.scala | 29 ++- .../dotty/dokka/site/StaticSiteContext.scala | 151 ++++++------- .../site/StaticSiteLocationProvider.scala | 8 +- .../src/dotty/dokka/site/processors.scala | 210 +++++++++--------- .../src/dotty/dokka/site/templates.scala | 11 +- .../src/dotty/dokka/tasty/TastyParser.scala | 2 +- .../dotty/renderers/ScalaHtmlRenderer.scala | 10 +- .../dotty/dokka/site/TemplateFileTests.scala | 8 +- 12 files changed, 236 insertions(+), 239 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 1a9a48b9db2e..ab6671ddc0c6 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1465,7 +1465,7 @@ object Build { def joinProducts(products: Seq[java.io.File]): String = products.iterator.map(_.getAbsolutePath.toString).mkString(java.io.File.pathSeparator) - + val dokkaVersion = "1.4.10.2" project.settings(commonBootstrappedSettings). diff --git a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala index 2eb45308e8ae..f435ebbe26ec 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala @@ -149,7 +149,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: dokkaBase.getNavigationPageInstaller, dokkaBase.getScriptsInstaller, dokkaBase.getStylesInstaller, - dokkaBase.getPackageListCreator, + dokkaBase.getPackageListCreator, ), after = Seq(dokkaBase.getRootCreator) ) @@ -184,7 +184,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: ) extension (ctx: DokkaContext): - def siteContext: Option[StaticSiteContext] = ctx.getConfiguration match + def siteContext: Option[StaticSiteContext] = ctx.getConfiguration match case d: DottyDokkaConfig => d.staticSiteContext case _ => None @@ -194,7 +194,7 @@ extension [T] (builder: ExtensionBuilder[T]): val byDsl = new OrderingKind.ByDsl(dsl => { dsl.after(after:_*) dsl.before(before:_*) - kotlin.Unit.INSTANCE // TODO why U does not work here? + kotlin.Unit.INSTANCE // TODO why U does not work here? }) // Does not compile but compiles in scala 2 // ExtensionBuilder.copy$default(builder, null, null, null, byDsl, null, null, 55, null) @@ -206,5 +206,5 @@ extension [T] (builder: ExtensionBuilder[T]): def before(exts: Extension[_, _, _]*): ExtensionBuilder[T] = ordered(exts, Nil) - + def after(exts: Extension[_, _, _]*): ExtensionBuilder[T] = ordered(Nil, exts) diff --git a/scala3doc/src/dotty/dokka/compat.scala b/scala3doc/src/dotty/dokka/compat.scala index 520a105d81b1..cf9440bd6913 100644 --- a/scala3doc/src/dotty/dokka/compat.scala +++ b/scala3doc/src/dotty/dokka/compat.scala @@ -36,15 +36,15 @@ extension [T] (wrapper: DokkaSourceSet): // when named `toSet` fails in runtime -- TODO: create a minimal! // def toSet: JSet[DokkaConfiguration$DokkaSourceSet] = JSet(wrapper.asInstanceOf[SourceSetWrapper]) def asSet: JSet[DokkaConfiguration$DokkaSourceSet] = JSet(wrapper.asInstanceOf[SourceSetWrapper]) - def asMap(value: T): JMap[DokkaConfiguration$DokkaSourceSet, T] = JMap(wrapper.asInstanceOf[SourceSetWrapper] -> value) + def asMap(value: T): JMap[DokkaConfiguration$DokkaSourceSet, T] = JMap(wrapper.asInstanceOf[SourceSetWrapper] -> value) extension (sourceSets: JList[DokkaSourceSet]): def asDokka: JSet[SourceSetWrapper] = sourceSets.asScala.toSet.asJava.asInstanceOf[JSet[SourceSetWrapper]] - def toDisplaySourceSet = sourceSets.asScala.map(ss => DisplaySourceSet(ss.asInstanceOf[SourceSetWrapper])).toSet.asJava + def toDisplaySourceSet = sourceSets.asScala.map(ss => DisplaySourceSet(ss.asInstanceOf[SourceSetWrapper])).toSet.asJava extension (sourceSets: Set[SourceSetWrapper]): - def toDisplay = sourceSets.map(DisplaySourceSet(_)).asJava - + def toDisplay = sourceSets.map(DisplaySourceSet(_)).asJava + extension [V] (a: WithExtraProperties[_]): def get(key: ExtraProperty.Key[_, V]): V = a.getExtra().getMap().get(key).asInstanceOf[V] @@ -55,5 +55,5 @@ extension [V] (map: JMap[SourceSetWrapper, V]): def defaultValue: V = map.values.asScala.head extension [V](jlist: JList[V]): - def ++ (other: JList[V]): JList[V] = + def ++ (other: JList[V]): JList[V] = Stream.of(jlist, other).flatMap(_.stream).collect(Collectors.toList()) diff --git a/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala b/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala index 7e92c46b87d8..6b02f20086f1 100644 --- a/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala +++ b/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala @@ -6,20 +6,20 @@ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.{ContentNode, DCI, Style} case class PartiallyRenderedContent( - template: TemplateFile, - context: StaticSiteContext, - override val getChildren: JList[ContentNode], - override val getDci: DCI, - override val getSourceSets: JSet[DisplaySourceSet], - override val getStyle: JSet[Style] = JSet(), - override val getExtra: PropertyContainer[ContentNode] = new PropertyContainer(JMap()) + template: TemplateFile, + context: StaticSiteContext, + override val getChildren: JList[ContentNode], + override val getDci: DCI, + override val getSourceSets: JSet[DisplaySourceSet], + override val getStyle: JSet[Style] = JSet(), + override val getExtra: PropertyContainer[ContentNode] = new PropertyContainer(JMap()) ) extends ContentNode: - override def hasAnyContent(): Boolean = true + override def hasAnyContent(): Boolean = true - override def withNewExtras(newExtras: PropertyContainer[ContentNode]): ContentNode = - copy(getExtra = newExtras) + override def withNewExtras(newExtras: PropertyContainer[ContentNode]): ContentNode = + copy(getExtra = newExtras) - override def withSourceSets(sourceSets: JSet[DisplaySourceSet]): ContentNode = - copy(getSourceSets = sourceSets) + override def withSourceSets(sourceSets: JSet[DisplaySourceSet]): ContentNode = + copy(getSourceSets = sourceSets) - lazy val resolved = template.resolveToHtml(context) + lazy val resolved = template.resolveToHtml(context) diff --git a/scala3doc/src/dotty/dokka/site/StaticPageNode.scala b/scala3doc/src/dotty/dokka/site/StaticPageNode.scala index 8c61ec8dfe96..5d00be49a6bd 100644 --- a/scala3doc/src/dotty/dokka/site/StaticPageNode.scala +++ b/scala3doc/src/dotty/dokka/site/StaticPageNode.scala @@ -11,8 +11,8 @@ import org.jetbrains.dokka.pages._ import org.jetbrains.dokka.transformers.pages.PageTransformer case class LoadedTemplate(templateFile: TemplateFile, children: List[LoadedTemplate], file: File) { - def relativePath(root: File): String = - root.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '.') + def relativePath(root: File): String = + root.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '.') } case class StaticPageNode( @@ -23,17 +23,22 @@ case class StaticPageNode( override val getEmbeddedResources: JList[String], override val getChildren: JList[PageNode], ) extends ContentPage: - override def getDocumentable: Documentable = null + override def getDocumentable: Documentable = null - def title(): String = template.title() - def hasFrame(): Boolean = template.hasFrame() + def title(): String = template.title() + def hasFrame(): Boolean = template.hasFrame() - override def modified(name: String, content: ContentNode, dri: JSet[DRI], embeddedResources: JList[String], children: JList[_ <: PageNode]): ContentPage = - copy(template, name, content, dri, embeddedResources, children.asInstanceOf[JList[PageNode]]) + override def modified( + name: String, + content: ContentNode, + dri: JSet[DRI], + embeddedResources: JList[String], + children: JList[_ <: PageNode]): ContentPage = + copy(template, name, content, dri, embeddedResources, children.asInstanceOf[JList[PageNode]]) - override def modified(name: String, children: JList[_ <: PageNode]): PageNode = - copy(getName = name, getChildren = children.asInstanceOf[JList[PageNode]]) + override def modified(name: String, children: JList[_ <: PageNode]): PageNode = + copy(getName = name, getChildren = children.asInstanceOf[JList[PageNode]]) - def resources(): List[String] = getContent match - case p: PartiallyRenderedContent => p.resolved.resources - case _ => Nil \ No newline at end of file + def resources(): List[String] = getContent match + case p: PartiallyRenderedContent => p.resolved.resources + case _ => Nil \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala index e4b51395f955..a177f6352927 100644 --- a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala +++ b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala @@ -17,85 +17,82 @@ import org.jetbrains.dokka.model.DisplaySourceSet import scala.collection.JavaConverters._ class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper]): - val docsFile = new File(root, "docs") - - def indexPage():Option[StaticPageNode] = - val files = List(new File(root, "index.html"), new File(root, "index.md")).filter { _.exists() } - if (files.size > 1) println(s"ERROR: Multiple root index pages found: ${files.map(_.getAbsolutePath)}") // TODO (#14): provide proper error handling - loadFiles(files).headOption - - lazy val layouts: Map[String, TemplateFile] = - val layoutRoot = new File(root, "_layouts") - val dirs: Array[File] = Option(layoutRoot.listFiles()).getOrElse(Array()) - dirs.map { it => loadTemplateFile(it) }.map { it => it.name() -> it }.toMap - - private def isValidTemplate(file: File): Boolean = - (file.isDirectory && !file.getName.startsWith("_")) || - file.getName.endsWith(".md") || - file.getName.endsWith(".html") - - - private def loadTemplate(from: File): Option[LoadedTemplate] = - if (!isValidTemplate(from)) None else - try - val (indexes, children) = Option(from.listFiles()).toSeq.flatten.flatMap(loadTemplate).partition(_.templateFile.isIndexPage()) - if (indexes.size > 1) - println(s"ERROR: Multiple index pages for $from found in ${indexes.map(_.file)}") // TODO (#14): provide proper error handling - - def loadIndexPage(): TemplateFile = { - val indexFiles = from.listFiles { file =>file.getName == "index.md" || file.getName == "index.html" } - indexFiles.size match { - case 0 => emptyTemplate(from) - case 1 => loadTemplateFile(indexFiles.head).copy(file = from) - case _ => throw new java.lang.RuntimeException(s"ERROR: Multiple index pages found under ${from.toPath}") - } - } - - val templateFile = if (from.isDirectory) loadIndexPage() else loadTemplateFile(from) - - Some(LoadedTemplate(templateFile, children.toList, from)) - catch + val docsFile = new File(root, "docs") + + def indexPage():Option[StaticPageNode] = + val files = List(new File(root, "index.html"), new File(root, "index.md")).filter { _.exists() } + if (files.size > 1) println(s"ERROR: Multiple root index pages found: ${files.map(_.getAbsolutePath)}") // TODO (#14): provide proper error handling + loadFiles(files).headOption + + lazy val layouts: Map[String, TemplateFile] = + val layoutRoot = new File(root, "_layouts") + val dirs: Array[File] = Option(layoutRoot.listFiles()).getOrElse(Array()) + dirs.map { it => loadTemplateFile(it) }.map { it => it.name() -> it }.toMap + + private def isValidTemplate(file: File): Boolean = + (file.isDirectory && !file.getName.startsWith("_")) || + file.getName.endsWith(".md") || + file.getName.endsWith(".html") + + + private def loadTemplate(from: File): Option[LoadedTemplate] = + if (!isValidTemplate(from)) None else + try + val (indexes, children) = Option(from.listFiles()).toSeq.flatten.flatMap(loadTemplate).partition(_.templateFile.isIndexPage()) + if (indexes.size > 1) + println(s"ERROR: Multiple index pages for $from found in ${indexes.map(_.file)}") // TODO (#14): provide proper error handling + + def loadIndexPage(): TemplateFile = + val indexFiles = from.listFiles { file =>file.getName == "index.md" || file.getName == "index.html" } + indexFiles.size match + case 0 => emptyTemplate(from) + case 1 => loadTemplateFile(indexFiles.head).copy(file = from) + case _ => + val msg = s"ERROR: Multiple index pages found under ${from.toPath}" + throw new java.lang.RuntimeException(msg) + + val templateFile = if (from.isDirectory) loadIndexPage() else loadTemplateFile(from) + + Some(LoadedTemplate(templateFile, children.toList, from)) + catch case e: RuntimeException => e.printStackTrace() // TODO (#14): provide proper error handling None - def asContent(d: DocTag, dri: DRI) = new DocTagToContentConverter().buildContent( - d, + def asContent(doctag: DocTag, dri: DRI) = new DocTagToContentConverter().buildContent( + doctag, + new DCI(Set(dri).asJava, ContentKind.Empty), + sourceSets.asJava, + JSet(), + new PropertyContainer(JMap()) + ) + + def loadFiles(files: List[File], customChildren: List[PageNode] = Nil): List[StaticPageNode] = + val all = files.flatMap(loadTemplate) + def flatten(it: LoadedTemplate): List[String] = + List(it.relativePath(root)) ++ it.children.flatMap(flatten) + + def pathToDRI(path: String) = mkDRI(s"_.$path") + + val driMap = all.flatMap(flatten).map(it => it -> pathToDRI(it)).toMap + + def templateToPage(myTemplate: LoadedTemplate): StaticPageNode = + val dri = pathToDRI(myTemplate.relativePath(root)) + val content = new PartiallyRenderedContent( + myTemplate.templateFile, + this, + JList(), new DCI(Set(dri).asJava, ContentKind.Empty), - sourceSets.asJava, - JSet(), - new PropertyContainer(JMap()) - ) - - def loadFiles( - files: List[File], - customChildren: List[PageNode] = Nil - ): List[StaticPageNode] = - val all = files.flatMap(loadTemplate) - def flatten(it: LoadedTemplate): List[String] = - List(it.relativePath(root)) ++ it.children.flatMap(flatten) - - def pathToDRI(path: String) = mkDRI(s"_.$path") - - val driMap = all.flatMap { it => flatten(it) }.map { it => it -> pathToDRI(it) }.toMap - - def templateToPage(myTemplate: LoadedTemplate): StaticPageNode = - val dri = pathToDRI(myTemplate.relativePath(root)) - val content = new PartiallyRenderedContent( - myTemplate.templateFile, - this, - JList(), - new DCI(Set(dri).asJava, ContentKind.Empty), - sourceSets.toDisplay, - JSet() - ) - StaticPageNode( - myTemplate.templateFile, - myTemplate.templateFile.title(), - content, - JSet(dri), - JList(), - (myTemplate.children.map(templateToPage) ++ customChildren).asJava - ) - - all.map(templateToPage) + sourceSets.toDisplay, + JSet() + ) + StaticPageNode( + myTemplate.templateFile, + myTemplate.templateFile.title(), + content, + JSet(dri), + JList(), + (myTemplate.children.map(templateToPage) ++ customChildren).asJava + ) + + all.map(templateToPage) diff --git a/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala b/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala index ec82eeb64687..3092446d6b5d 100644 --- a/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala +++ b/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala @@ -12,13 +12,13 @@ import org.jetbrains.dokka.plugability.DokkaContext import scala.collection.JavaConverters._ class StaticSiteLocationProviderFactory(private val ctx: DokkaContext) extends LocationProviderFactory: - override def getLocationProvider(pageNode: RootPageNode): LocationProvider = - new StaticSiteLocationProvider(ctx, pageNode) + override def getLocationProvider(pageNode: RootPageNode): LocationProvider = + new StaticSiteLocationProvider(ctx, pageNode) -class StaticSiteLocationProvider(ctx: DokkaContext, pageNode: RootPageNode) +class StaticSiteLocationProvider(ctx: DokkaContext, pageNode: RootPageNode) extends DokkaLocationProvider(pageNode, ctx, ".html"): private def updatePageEntry(page: PageNode, jpath: JList[String]): JList[String] = - page match + page match case page: StaticPageNode => if (page.getDri.contains(docsRootDRI)) JList("index") else { diff --git a/scala3doc/src/dotty/dokka/site/processors.scala b/scala3doc/src/dotty/dokka/site/processors.scala index 21f532e54a73..019a7c54a647 100644 --- a/scala3doc/src/dotty/dokka/site/processors.scala +++ b/scala3doc/src/dotty/dokka/site/processors.scala @@ -13,123 +13,121 @@ import org.jetbrains.dokka.transformers.pages.PageTransformer import scala.collection.JavaConverters._ abstract class BaseStaticSiteProcessor(staticSiteContext: Option[StaticSiteContext]) extends PageTransformer: - final override def invoke(input: RootPageNode): RootPageNode = staticSiteContext.fold(input)(transform(input, _)) + final override def invoke(input: RootPageNode): RootPageNode = staticSiteContext.fold(input)(transform(input, _)) - protected def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode + protected def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode class SiteResourceManager(ctx: Option[StaticSiteContext]) extends BaseStaticSiteProcessor(ctx): - - private def listResources(nodes: Seq[PageNode]): Set[String] = - nodes.flatMap { - case it: StaticPageNode => listResources(it.getChildren.asScala.toList) ++ it.resources() - case _ => Seq.empty - }.toSet - - override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = - val imgPath = ctx.root.toPath().resolve("images") - val images = - if !Files.exists(imgPath) then Nil - else - val stream = Files.walk(imgPath)filter(p => Files.isRegularFile(p) && p.getFileName().toString().endsWith(".svg")) - stream.iterator().asScala.toList.map(_.toString) - - val resources = listResources(input.getChildren.asScala.toList) ++ images - val resourcePages = resources.map { path => - val content = Files.readString(ctx.root.toPath.resolve(path)) - new RendererSpecificResourcePage(path, JList(), new RenderingStrategy.Write(content)) - }.toList - - val modified = input.transformContentPagesTree { - case it: StaticPageNode => - it.copy(getEmbeddedResources = - if it.template.hasFrame() then it.getEmbeddedResources ++ it.resources().asJava - else it.resources().asJava - ) - case it => it - } - modified.modified(modified.getName, (resourcePages ++ modified.getChildren.asScala).asJava) + private def listResources(nodes: Seq[PageNode]): Set[String] = + nodes.flatMap { + case it: StaticPageNode => listResources(it.getChildren.asScala.toList) ++ it.resources() + case _ => Seq.empty + }.toSet + + override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = + val imgPath = ctx.root.toPath().resolve("images") + val images = + if !Files.exists(imgPath) then Nil + else + val stream = Files.walk(imgPath)filter(p => Files.isRegularFile(p) && p.getFileName().toString().endsWith(".svg")) + stream.iterator().asScala.toList.map(_.toString) + + val resources = listResources(input.getChildren.asScala.toList) ++ images + val resourcePages = resources.map { path => + val content = Files.readString(ctx.root.toPath.resolve(path)) + new RendererSpecificResourcePage(path, JList(), new RenderingStrategy.Write(content)) + }.toList + + val modified = input.transformContentPagesTree { + case it: StaticPageNode => + it.copy(getEmbeddedResources = + if it.template.hasFrame() then it.getEmbeddedResources ++ it.resources().asJava + else it.resources().asJava + ) + case it => it + } + modified.modified(modified.getName, (resourcePages ++ modified.getChildren.asScala).asJava) case class AContentPage( - override val getName: String, - override val getChildren: JList[PageNode], - override val getContent: ContentNode, - override val getDri: JSet[DRI], - override val getEmbeddedResources: JList[String] = JList(), + override val getName: String, + override val getChildren: JList[PageNode], + override val getContent: ContentNode, + override val getDri: JSet[DRI], + override val getEmbeddedResources: JList[String] = JList(), ) extends ContentPage: - override def getDocumentable: Documentable = null + override def getDocumentable: Documentable = null - override def modified( - name: String, - content: ContentNode, - dri: JSet[DRI], - embeddedResources: JList[String], - children: JList[_ <: PageNode] - ): ContentPage = copy(name, children.asInstanceOf[JList[PageNode]], content, dri, embeddedResources) + override def modified( + name: String, + content: ContentNode, + dri: JSet[DRI], + embeddedResources: JList[String], + children: JList[_ <: PageNode] + ): ContentPage = copy(name, children.asInstanceOf[JList[PageNode]], content, dri, embeddedResources) - override def modified(name: String, children: JList[_ <: PageNode]): PageNode = copy(name, getChildren = children.asInstanceOf[JList[PageNode]]) + override def modified(name: String, children: JList[_ <: PageNode]): PageNode = + copy(name, getChildren = children.asInstanceOf[JList[PageNode]]) class SitePagesCreator(ctx: Option[StaticSiteContext]) extends BaseStaticSiteProcessor(ctx): + private def processRootPage(input: RootPageNode, children: List[PageNode] = Nil): AContentPage = input match + case input: ContentPage => + AContentPage( + input.getName, + children.asJava, + input.getContent, + JSet(apiPageDRI), + input.getEmbeddedResources + ) + case _: RendererSpecificRootPage => + children.filter(_.isInstanceOf[RootPageNode]) match + case List(nestedRoot: RootPageNode) => + processRootPage(nestedRoot, children.filter { _ != nestedRoot } ++ nestedRoot.getChildren.asScala) + case other => + throw new RuntimeException(s"Expected single nested roor but get: $other") + + case _ => throw new RuntimeException(s"UNSUPPORTED! ${input.getClass.getName}") + + override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = + val (contentPage, others) = input.getChildren.asScala.toList.partition { _.isInstanceOf[ContentPage] } + val modifiedModuleRoot = processRootPage(input, contentPage) + val allFiles = Option(ctx.docsFile.listFiles()).toList.flatten + val (indexes, children) = ctx.loadFiles(allFiles).partition(_.template.isIndexPage()) + // TODO (#14): provide proper error handling + if (indexes.size > 1) println("ERROR: Multiple index pages found $indexes}") + + val rootContent = indexes.headOption.fold(ctx.asContent(Text(), mkDRI(extra = "root_content")).get(0))(_.getContent) + + val root = AContentPage( + input.getName, + (List(modifiedModuleRoot.modified("API", modifiedModuleRoot.getChildren)) ++ children).asJava, + rootContent, + JSet(docsDRI), + JList() + ) + + new RendererSpecificRootPage( + modifiedModuleRoot.getName, + (List(root) ++ others).asJava, + RenderingStrategy.DoNothing.INSTANCE + ) - private def processRootPage(input: RootPageNode, children: List[PageNode] = Nil): AContentPage = input match - case input: ContentPage => - AContentPage( - input.getName, - children.asJava, - input.getContent, - JSet(apiPageDRI), - input.getEmbeddedResources - ) - case _: RendererSpecificRootPage => - children.filter(_.isInstanceOf[RootPageNode]) match { - case List(nestedRoot: RootPageNode) => - processRootPage( - nestedRoot, - children.filter { _ != nestedRoot } ++ nestedRoot.getChildren.asScala) - case other => - throw new RuntimeException(s"Expected single nested roor but get: $other") - } - case _ => throw new RuntimeException(s"UNSUPPORTED! ${input.getClass.getName}") - - override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = - val (contentPage, others) = input.getChildren.asScala.toList.partition { _.isInstanceOf[ContentPage] } - val modifiedModuleRoot = processRootPage(input, contentPage) - val allFiles = Option(ctx.docsFile.listFiles()).toList.flatten - val (indexes, children) = ctx.loadFiles(allFiles).partition(_.template.isIndexPage()) - if (indexes.size > 1) println("ERROR: Multiple index pages found $indexes}") // TODO (#14): provide proper error handling - - val rootContent = indexes.headOption.fold(ctx.asContent(Text(), mkDRI(extra = "root_content")).get(0))(_.getContent) - - val root = AContentPage( +class RootIndexPageCreator(ctx: Option[StaticSiteContext]) extends BaseStaticSiteProcessor(ctx): + override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = + ctx.indexPage().fold(input){ it => + val (contentNodes, nonContent) = input.getChildren.asScala.partition { _.isInstanceOf[ContentNode] } + val (navigations, rest) = nonContent.partition { _.isInstanceOf[NavigationPage] } + val modifiedNavigation = navigations.map { it => + val root = it.asInstanceOf[NavigationPage].getRoot + val (api, rest) = root.getChildren.asScala.partition { _.getDri == apiPageDRI } + new NavigationPage( + new NavigationNode( input.getName, - (List(modifiedModuleRoot.modified("API", modifiedModuleRoot.getChildren)) ++ children).asJava, - rootContent, - JSet(docsDRI), - JList() + docsRootDRI, + root.getSourceSets, + (rest ++ api).asJava + ) ) - - new RendererSpecificRootPage( - modifiedModuleRoot.getName, - (List(root) ++ others).asJava, - RenderingStrategy.DoNothing.INSTANCE - ) - -class RootIndexPageCreator(ctx: Option[StaticSiteContext]) extends BaseStaticSiteProcessor(ctx): - override def transform(input: RootPageNode, ctx: StaticSiteContext): RootPageNode = - ctx.indexPage().fold(input){ it => - val (contentNodes, nonContent) = input.getChildren.asScala.partition { _.isInstanceOf[ContentNode] } - val (navigations, rest) = nonContent.partition { _.isInstanceOf[NavigationPage] } - val modifiedNavigation = navigations.map { it => - val root = it.asInstanceOf[NavigationPage].getRoot - val (api, rest) = root.getChildren.asScala.partition { _.getDri == apiPageDRI } - new NavigationPage( - new NavigationNode( - input.getName, - docsRootDRI, - root.getSourceSets, - (rest ++ api).asJava - ) - ) - } - val newRoot = it.copy(getDri = JSet(docsRootDRI), getChildren = contentNodes.asJava) - input.modified(input.getName, (List(newRoot) ++ rest ++ modifiedNavigation).asJava) } + val newRoot = it.copy(getDri = JSet(docsRootDRI), getChildren = contentNodes.asJava) + input.modified(input.getName, (List(newRoot) ++ rest ++ modifiedNavigation).asJava) + } diff --git a/scala3doc/src/dotty/dokka/site/templates.scala b/scala3doc/src/dotty/dokka/site/templates.scala index 53a1830de4c8..65dff5441714 100644 --- a/scala3doc/src/dotty/dokka/site/templates.scala +++ b/scala3doc/src/dotty/dokka/site/templates.scala @@ -33,10 +33,7 @@ case class RenderingContext( resources = this.resources ++ resources ) -case class ResolvedPage( - val code: String, - val resources: List[String] = Nil - ) +case class ResolvedPage(val code: String, val resources: List[String] = Nil) /** * case class for the template files. @@ -84,13 +81,13 @@ case class TemplateFile( // Library requires mutable maps.. val mutableProperties = new java.util.HashMap[String, Object](ctx.properties.asJava) - val rendered = Template.parse(this.rawCode).render(mutableProperties) + val rendered = Template.parse(this.rawCode).render(mutableProperties) val code = if (!isHtml) rendered else val parser: Parser = Parser.builder().build() HtmlRenderer.builder(ctx.markdownOptions).build().render(parser.parse(rendered)) - + val resources = listSetting("extraCSS") ++ listSetting("extraJS") - layoutTemplate match + layoutTemplate match case None => ResolvedPage(code, resources ++ ctx.resources) case Some(layoutTemplate) => layoutTemplate.resolveInner(ctx.nest(code, file, resources)) diff --git a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala index 0f21f6ffdb31..33861eeb741a 100644 --- a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala +++ b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala @@ -135,7 +135,7 @@ trait DokkaBaseTastyInspector: }.toList extension (self: DPackage) def mergeWith(other: DPackage): DPackage = - def nodes(p: DPackage): JList[TagWrapper] = p.getDocumentation.get(sourceSet) match + def nodes(p: DPackage): JList[TagWrapper] = p.getDocumentation.get(sourceSet) match case null => JList[TagWrapper]() case node => node.getChildren diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index a9de675ada02..32dbd744f530 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -50,14 +50,14 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { }) case _ => val div = new DIV(JMap(), context.getConsumer()) - try + try div.getConsumer().onTagStart(div) withHtml(div, content) catch case e: Throwable => div.getConsumer.onTagError(div, e) finally - div.getConsumer.onTagEnd(div) + div.getConsumer.onTagEnd(div) } lazy val sourceSets = ctx.getConfiguration.getSourceSets.asScala @@ -189,7 +189,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { ): Unit = { import kotlinx.html.{Gen_consumer_tagsKt => dsl} val c = f.getConsumer - + dsl.a(c, node.getAddress, /*target*/ null, /*classes*/ null, { e => import ScalaCommentToContentConverter._ // node.getExtra.getMap.asScala.get(LinkAttributesKey) @@ -233,7 +233,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { ).toString() ) - override def buildPageContent(context: FlowContent, page: ContentPage): Unit = + override def buildPageContent(context: FlowContent, page: ContentPage): Unit = page match case s: StaticPageNode if !s.hasFrame() => case _ => buildNavigation(context, page) @@ -243,7 +243,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { withHtml(context, prc.resolved.code) case content => build(content, context, page, /*sourceSetRestriction=*/null) - + override def buildHtml(page: PageNode, resources: JList[String], kotlinxContent: FlowContentConsumer): String = val (pageTitle, noFrame) = page match diff --git a/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala b/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala index a02cc1d768fd..eab4310a2e54 100644 --- a/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala +++ b/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala @@ -9,11 +9,11 @@ import java.nio.file.Files class TemplateFileTests: private def testTemplate(code: String, ext: String = "html")(op: TemplateFile => Unit): Unit = val tmpFile = Files.createTempFile("headerTests", s".${ext}").toFile() - try + try Files.writeString(tmpFile.toPath, code) op(loadTemplateFile(tmpFile)) finally tmpFile.delete() - + private def testTemplates( props: Map[String, String], @@ -28,7 +28,7 @@ class TemplateFileTests: val newCtx = cxt.copy(layouts = cxt.layouts + (template.name() -> template)) rec(newCtx, remaining.drop(1)) } - + rec(RenderingContext(props), template) private def fullRender(template: TemplateFile, ctx: RenderingContext): String = template.resolveInner(ctx).code.trim() @@ -44,7 +44,7 @@ class TemplateFileTests: assertEquals(t.rawCode, "code") assertEquals(t.title(), "myTitle") } - + @Test def testLinks(): Unit = From 6c1b609abe765c937b6253f660e49cb942a51301 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 5 Nov 2020 11:36:54 +0100 Subject: [PATCH 6/6] Fix support for java 8 --- scala3doc/src/dotty/dokka/site/processors.scala | 2 +- scala3doc/test/dotty/dokka/site/TemplateFileTests.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scala3doc/src/dotty/dokka/site/processors.scala b/scala3doc/src/dotty/dokka/site/processors.scala index 019a7c54a647..c6a6db695be7 100644 --- a/scala3doc/src/dotty/dokka/site/processors.scala +++ b/scala3doc/src/dotty/dokka/site/processors.scala @@ -34,7 +34,7 @@ class SiteResourceManager(ctx: Option[StaticSiteContext]) extends BaseStaticSite val resources = listResources(input.getChildren.asScala.toList) ++ images val resourcePages = resources.map { path => - val content = Files.readString(ctx.root.toPath.resolve(path)) + val content = Files.readAllLines(ctx.root.toPath.resolve(path)).asScala.mkString("\n") new RendererSpecificResourcePage(path, JList(), new RenderingStrategy.Write(content)) }.toList diff --git a/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala b/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala index eab4310a2e54..ecdb58f4432a 100644 --- a/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala +++ b/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala @@ -10,7 +10,7 @@ class TemplateFileTests: private def testTemplate(code: String, ext: String = "html")(op: TemplateFile => Unit): Unit = val tmpFile = Files.createTempFile("headerTests", s".${ext}").toFile() try - Files.writeString(tmpFile.toPath, code) + Files.write(tmpFile.toPath, code.getBytes) op(loadTemplateFile(tmpFile)) finally tmpFile.delete()