diff --git a/project/Build.scala b/project/Build.scala index fa951c794fb1..ea8b4105f179 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1455,10 +1455,11 @@ object Build { def asScala3doc: Project = { def generateDocumentation(targets: String, name: String, outDir: String, params: String = "") = Def.taskDyn { - val sourcesAndRevision = "-s github://lampepfl/dotty --revision master" + val projectVersion = version.value + val sourcesAndRevision = s"-s github://lampepfl/dotty --projectVersion $projectVersion" run.in(Compile).toTask( - s""" -d output/$outDir -t $targets -n "$name" $sourcesAndRevision $params""" - ) + s""" -d scala3doc/output/$outDir -t $targets -n "$name" $sourcesAndRevision $params""" + ) } def joinProducts(products: Seq[java.io.File]): String = @@ -1491,8 +1492,14 @@ object Build { Compile / mainClass := Some("dotty.dokka.Main"), // There is a bug in dokka that prevents parallel tests withing the same jvm fork.in(test) := true, + baseDirectory.in(run) := baseDirectory.in(ThisBuild).value, generateSelfDocumentation := Def.taskDyn { - generateDocumentation(classDirectory.in(Compile).value.getAbsolutePath, "scala3doc", "self", "-p documentation") + val revision = VersionUtil.gitHash + generateDocumentation( + classDirectory.in(Compile).value.getAbsolutePath, + "scala3doc", "self", + s"-p scala3doc/documentation --projectLogo scala3doc/documentation/logo.svg --revision $revision", + ) }.value, generateScala3Documentation := Def.taskDyn { val dottyJars: Seq[java.io.File] = Seq( @@ -1506,10 +1513,10 @@ object Build { val roots = joinProducts(dottyJars) if (dottyJars.isEmpty) Def.task { streams.value.log.error("Dotty lib wasn't found") } - else generateDocumentation(roots, "Scala 3", "scala3", "-p scala3-docs") + else generateDocumentation(roots, "Scala 3", "scala3", "-p scala3doc/scala3-docs --projectLogo scala3doc/scala3-docs/logo.svg --revision master") }.value, generateTestcasesDocumentation := Def.taskDyn { - generateDocumentation(Build.testcasesOutputDir.in(Test).value, "Scala3doc testcases", "testcases") + generateDocumentation(Build.testcasesOutputDir.in(Test).value, "Scala3doc testcases", "testcases", "--revision master") }.value, buildInfoKeys in Test := Seq[BuildInfoKey]( Build.testcasesOutputDir.in(Test), diff --git a/scala3doc-testcases/src/tests/links.scala b/scala3doc-testcases/src/tests/links.scala new file mode 100644 index 000000000000..542b1d85f13a --- /dev/null +++ b/scala3doc-testcases/src/tests/links.scala @@ -0,0 +1,16 @@ +package tests.links + +object AnObject: + def method(a: Int): Int + = 123 + a + val field = + 123 + +/** + * Broken link, that should result a warning not break compilation + * [[tests.links.AnObject]] + + */ +class LinksTest: + def verifyIfLinksTestIsGenerated(b: Int): Int + = 123 \ No newline at end of file diff --git a/scala3doc/documentation/docs/design.md b/scala3doc/documentation/docs/design.md new file mode 100644 index 000000000000..cbcb40282faf --- /dev/null +++ b/scala3doc/documentation/docs/design.md @@ -0,0 +1,7 @@ +# Design of scala3doc + +**BEWARE this is not complete documentation yet but rather a draft mainly to test linking from static site to code** + +## Interface + +Scala3doc is intednted to be use with sbt as well as from commend line or from other buildtools. The main entry point to processing is [Main](dotty.dokka.Main$) class with [[dotty.dokka.Main$.main]]. \ No newline at end of file diff --git a/scala3doc/documentation/logo.svg b/scala3doc/documentation/logo.svg new file mode 100644 index 000000000000..6dc3fa8b6aaa --- /dev/null +++ b/scala3doc/documentation/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scala3doc/resources/dotty_res/images/scala3doc_logo.svg b/scala3doc/resources/dotty_res/images/scala3doc_logo.svg new file mode 100644 index 000000000000..40101df90c9d --- /dev/null +++ b/scala3doc/resources/dotty_res/images/scala3doc_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scala3doc/resources/dotty_res/styles/scalastyle.css b/scala3doc/resources/dotty_res/styles/scalastyle.css index f6a160068ade..71dc3913732f 100644 --- a/scala3doc/resources/dotty_res/styles/scalastyle.css +++ b/scala3doc/resources/dotty_res/styles/scalastyle.css @@ -129,22 +129,33 @@ th { #paneSearch { display: none; } -#logo { - background-size: contain; - background-repeat: no-repeat; - background-image: url(../images/dotty-logo-white.svg); - background-origin: content-box; - padding: 8px 0 8px 16px; - height: 42px; +#logo>span { + display: inline-block; + padding: 8px 8px 8px 8px; + vertical-align: middle; +} + +#logo img { + max-height: 54px; + max-width: 54px; cursor: pointer; } -#logo::after { + +#logo .projectName { color: var(--leftbar-fg); - font-size: 22px; - content: "Scala3doc"; - margin-left: 42px; - line-height: 42px; + font-size: 28px; + font-weight: bold; } + +#logo .projectVersion { + color: var(--leftbar-fg); + font-size: 12px; +} + +.scala3doc_logo { + width: 116px; +} + .sideMenuPart { padding-left: 1em; } diff --git a/scala3doc/resources/dotty_res/styles/search-bar.css b/scala3doc/resources/dotty_res/styles/search-bar.css index 3321054a3b03..820dcd8f21ad 100644 --- a/scala3doc/resources/dotty_res/styles/search-bar.css +++ b/scala3doc/resources/dotty_res/styles/search-bar.css @@ -2,7 +2,7 @@ .search-content { padding: 0; margin: var(--content-padding); - position: absolute; + position: fixed; top: 0; right: 0; z-index: 5; diff --git a/scala3doc/scala3-docs/_layouts/doc-page.html b/scala3doc/scala3-docs/_layouts/doc-page.html index 694da12f1f20..2f2cf2a85b53 100644 --- a/scala3doc/scala3-docs/_layouts/doc-page.html +++ b/scala3doc/scala3-docs/_layouts/doc-page.html @@ -5,7 +5,7 @@

{{ page.title }}

- + Edit this page on GitHub diff --git a/scala3doc/scala3-docs/logo.svg b/scala3doc/scala3-docs/logo.svg new file mode 100644 index 000000000000..44c8b71850ce --- /dev/null +++ b/scala3doc/scala3-docs/logo.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala index 16b47ff266c3..d97d0fc475da 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala @@ -17,10 +17,15 @@ case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaCon override def getModuleName(): String = "ModuleName" override def getModuleVersion(): String = "" - lazy val staticSiteContext = docConfiguration.args.docsRoot.map(path => StaticSiteContext(File(path).getAbsoluteFile(), Set(mkSourceSet.asInstanceOf[SourceSetWrapper]))) - lazy val sourceLinks: SourceLinks = SourceLinks.load(docConfiguration) + lazy val staticSiteContext = docConfiguration.args.docsRoot.map(path => StaticSiteContext( + File(path).getAbsoluteFile(), + Set(mkSourceSet.asInstanceOf[SourceSetWrapper]), + docConfiguration.args, + sourceLinks + )) + override def getPluginsConfiguration: JList[DokkaConfiguration.PluginConfiguration] = JList() lazy val mkSourceSet: DokkaSourceSet = diff --git a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala index f1d34132416c..08fa3456611f 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala @@ -9,7 +9,6 @@ import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslat import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.{ DokkaConfiguration$DokkaSourceSet => DokkaSourceSet } import org.jetbrains.dokka.model._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.base.parsers._ import org.jetbrains.dokka.plugability.DokkaContext @@ -70,7 +69,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: val scalaResourceInstaller = extend( _.extensionPoint(dokkaBase.getHtmlPreprocessors) - .fromInstance(new ScalaResourceInstaller()) + .fromRecipe(ctx => new ScalaResourceInstaller(ctx.args)) .after(dokkaBase.getCustomResourceInstaller) ) @@ -110,7 +109,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: val ourRenderer = extend( _.extensionPoint(CoreExtensions.INSTANCE.getRenderer) - .fromRecipe(ScalaHtmlRenderer(_)) + .fromRecipe(ctx => ScalaHtmlRenderer(ctx, ctx.args)) .overrideExtension(dokkaBase.getHtmlRenderer) ) @@ -170,9 +169,8 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: ) extension (ctx: DokkaContext): - def siteContext: Option[StaticSiteContext] = ctx.getConfiguration match - case d: DottyDokkaConfig => d.staticSiteContext - case _ => None + def siteContext: Option[StaticSiteContext] = ctx.getConfiguration.asInstanceOf[DottyDokkaConfig].staticSiteContext + def args: Args = ctx.getConfiguration.asInstanceOf[DottyDokkaConfig].docConfiguration.args // TODO (https://github.com/lampepfl/scala3doc/issues/232): remove once problem is fixed in Dokka extension [T] (builder: ExtensionBuilder[T]): diff --git a/scala3doc/src/dotty/dokka/Main.scala b/scala3doc/src/dotty/dokka/Main.scala index aa9e5ec15899..7a568c73aa94 100644 --- a/scala3doc/src/dotty/dokka/Main.scala +++ b/scala3doc/src/dotty/dokka/Main.scala @@ -64,7 +64,7 @@ class RawArgs: classpath, new File(output), Option(docsRoot), - projectVersion, + Option(projectVersion), Option(projectTitle), Option(projectLogo), parsedSyntax, @@ -79,7 +79,7 @@ case class Args( classpath: String, output: File, docsRoot: Option[String], - projectVersion: String, + projectVersion: Option[String], projectTitle: Option[String], projectLogo: Option[String], defaultSyntax: Option[Args.CommentSyntax], diff --git a/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala b/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala index e8ba83931d7a..30e217fc23ad 100644 --- a/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala +++ b/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala @@ -10,7 +10,6 @@ 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 org.jetbrains.dokka.base.parsers.MarkdownParser import collection.JavaConverters._ import kotlin.coroutines.Continuation diff --git a/scala3doc/src/dotty/dokka/SourceLinks.scala b/scala3doc/src/dotty/dokka/SourceLinks.scala index 11e3c517bb2b..be66d794a7c1 100644 --- a/scala3doc/src/dotty/dokka/SourceLinks.scala +++ b/scala3doc/src/dotty/dokka/SourceLinks.scala @@ -85,12 +85,12 @@ case class SourceLinks(links: Seq[SourceLink], projectRoot: Path): else resolveRelativePath(rawPath) def pathTo(member: Member): Option[String] = - member.sources.flatMap(s => pathTo(Paths.get(s.path), Option(s.lineNumber))) + member.sources.flatMap(s => pathTo(Paths.get(s.path), Option(s.lineNumber).map(_ + 1))) object SourceLinks: val usage = - """Source links provide a mapping between file in documentation and code repositry (usual)." + + """Source links provide a mapping between file in documentation and code repositry. |Accepted formats: |= | @@ -135,5 +135,6 @@ object SourceLinks: load( config.args.sourceLinks, config.args.revision, + // TODO (https://github.com/lampepfl/scala3doc/issues/240): configure source root Paths.get("").toAbsolutePath ) \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/compat.scala b/scala3doc/src/dotty/dokka/compat.scala index c45ff57692b2..f68476cb631c 100644 --- a/scala3doc/src/dotty/dokka/compat.scala +++ b/scala3doc/src/dotty/dokka/compat.scala @@ -1,6 +1,6 @@ package dotty.dokka -import org.jetbrains.dokka.links.{DRI, PointingToDeclaration} +import org.jetbrains.dokka.links.PointingToDeclaration import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet import collection.JavaConverters._ @@ -21,13 +21,14 @@ 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 -def newHMap[K, V](m: JMap[K, V]): HMap[K, V] = new HMap[K, V](m) type JList[T] = java.util.List[T] type JSet[T] = java.util.Set[T] type JMap[K, V] = java.util.Map[K, V] -type HMap[K, V] = java.util.HashMap[K, V] +type JHashMap[K, V] = java.util.HashMap[K, V] +type JMapEntry[K, V] = java.util.Map.Entry[K, V] + +type DRI = org.jetbrains.dokka.links.DRI type SourceSetWrapper = DokkaConfiguration$DokkaSourceSet type DokkaSourceSet = DokkaConfiguration.DokkaSourceSet @@ -62,14 +63,18 @@ extension [V](jlist: JList[V]): def ++ (other: JList[V]): JList[V] = Stream.of(jlist, other).flatMap(_.stream).collect(Collectors.toList()) +extension [V](jset: JSet[V]): + def ++ (other: JSet[V]): JSet[V] = + Stream.of(jset, other).flatMap(_.stream).collect(Collectors.toSet()) + object PluginUtils: import scala.reflect.ClassTag import scala.reflect._ - def plugin[T <: DokkaPlugin: ClassTag](ctx: DokkaContext) = + def plugin[T <: DokkaPlugin: ClassTag](ctx: DokkaContext) = ctx.plugin[T](getKotlinClass(implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]])) - def query[T <: DokkaPlugin: ClassTag, E](ctx: DokkaContext, queryFunction: (T) => ExtensionPoint[E]): List[E] = + def query[T <: DokkaPlugin: ClassTag, E](ctx: DokkaContext, queryFunction: (T) => ExtensionPoint[E]): List[E] = ctx.get(queryFunction(plugin[T](ctx))).asScala.toList - def querySingle[T <: DokkaPlugin: ClassTag, E](ctx: DokkaContext, queryFunction: (T) => ExtensionPoint[E]): E = + def querySingle[T <: DokkaPlugin: ClassTag, E](ctx: DokkaContext, queryFunction: (T) => ExtensionPoint[E]): E = ctx.single(queryFunction(plugin[T](ctx))) diff --git a/scala3doc/src/dotty/dokka/model/api/api.scala b/scala3doc/src/dotty/dokka/model/api/api.scala index 4ba85604945a..a6cbae170c32 100644 --- a/scala3doc/src/dotty/dokka/model/api/api.scala +++ b/scala3doc/src/dotty/dokka/model/api/api.scala @@ -3,10 +3,8 @@ package model package api import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model._ import collection.JavaConverters._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.pages._ diff --git a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala index 6cf9a8f18d0b..d6236a3f3631 100644 --- a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala +++ b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala @@ -3,7 +3,6 @@ package model package api import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.{Projection => JProjection} import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.DFunction @@ -18,7 +17,6 @@ import org.jetbrains.dokka.model.DPackage import org.jetbrains.dokka.model.DModule import collection.JavaConverters._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.model.properties._ diff --git a/scala3doc/src/dotty/dokka/model/extras.scala b/scala3doc/src/dotty/dokka/model/extras.scala index f11fffe24d07..5e49f617c929 100644 --- a/scala3doc/src/dotty/dokka/model/extras.scala +++ b/scala3doc/src/dotty/dokka/model/extras.scala @@ -1,12 +1,10 @@ package dotty.dokka import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.{Projection => JProjection} import org.jetbrains.dokka.model._ import org.jetbrains.dokka.pages._ import collection.JavaConverters._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.model.properties._ import dotty.dokka.model.api._ diff --git a/scala3doc/src/dotty/dokka/model/scalaModel.scala b/scala3doc/src/dotty/dokka/model/scalaModel.scala index bfaa37085035..0973bb5c8d4b 100644 --- a/scala3doc/src/dotty/dokka/model/scalaModel.scala +++ b/scala3doc/src/dotty/dokka/model/scalaModel.scala @@ -1,10 +1,8 @@ package dotty.dokka import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model._ import collection.JavaConverters._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.pages._ diff --git a/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala b/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala index 7d55a5972f30..17879af5073a 100644 --- a/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala +++ b/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala @@ -5,13 +5,15 @@ import org.jetbrains.dokka.pages.{RootPageNode, RendererSpecificResourcePage, Re import scala.jdk.CollectionConverters._ import com.fasterxml.jackson.databind.ObjectMapper import dotty.dokka.translators.FilterAttributes +import java.nio.file.Paths -class ScalaResourceInstaller extends PageTransformer: +class ScalaResourceInstaller(args: Args) extends PageTransformer: private def dottyRes(resourceName: String) = new RendererSpecificResourcePage(resourceName, java.util.ArrayList(), RenderingStrategy$Copy(s"/dotty_res/$resourceName")) override def invoke(input: RootPageNode): RootPageNode = - val newResources = input.getChildren.asScala ++ Seq("fonts", "images", "styles", "scripts", "hljs").map(dottyRes) ++ Seq(dynamicJsData) + val defaultResources = input.getChildren.asScala ++ Seq("fonts", "images", "styles", "scripts", "hljs").map(dottyRes) + val newResources = projectLogo ++ defaultResources ++ Seq(dynamicJsData) input.modified(input.getName, newResources.asJava) private def dynamicJsData = @@ -20,3 +22,9 @@ class ScalaResourceInstaller extends PageTransformer: val str = new ObjectMapper().writeValueAsString(data.transform((_, v) => v.asJava).asJava) new RendererSpecificResourcePage("scripts/data.js", java.util.ArrayList(), RenderingStrategy$Write(s"var scala3DocData = $str")) + + private def projectLogo = args.projectLogo.toSeq.map { path => + val fileName = Paths.get(path).getFileName() + val strategy = new RenderingStrategy$Copy(path) + new RendererSpecificResourcePage(s"project-logo/$fileName", JList(), strategy) + } diff --git a/scala3doc/src/dotty/dokka/site/LoadedTemplate.scala b/scala3doc/src/dotty/dokka/site/LoadedTemplate.scala new file mode 100644 index 000000000000..dad0136b375d --- /dev/null +++ b/scala3doc/src/dotty/dokka/site/LoadedTemplate.scala @@ -0,0 +1,48 @@ +package dotty.dokka +package site + +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths + +import org.jetbrains.dokka.base.renderers.html.{NavigationNode, NavigationPage} +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.pages._ +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jsoup.Jsoup +import scala.collection.JavaConverters._ + + +case class LazyEntry(getKey: String, value: () => String) extends JMapEntry[String, Object]: + lazy val getValue: Object = value() + def setValue(x$0: Object): Object = ??? + +case class LoadedTemplate(templateFile: TemplateFile, children: List[LoadedTemplate], file: File): + + private def brief(ctx: StaticSiteContext): String = + val code = Jsoup.parse(resolveToHtml(ctx).code) + code.select("p").first().outerHtml() + + def lazyTemplateProperties(ctx: StaticSiteContext): JMap[String, Object] = new java.util.AbstractMap[String, Object](): + def entrySet(): JSet[JMapEntry[String, Object]] = + val site = templateFile.settings.getOrElse("page", Map.empty).asInstanceOf[Map[String, Object]] + site.asJava.entrySet() ++ JSet( + LazyEntry("url", () => ctx.relativePath(LoadedTemplate.this).toString), + LazyEntry("title", () => templateFile.title), + LazyEntry("excerpt", () => brief(ctx)) + ) + + def resolveToHtml(ctx: StaticSiteContext): ResolvedPage = + val posts = children.map(_.lazyTemplateProperties(ctx)) + val site = templateFile.settings.getOrElse("site", Map.empty).asInstanceOf[Map[String, Object]] + val sourceLinks = if !file.exists() then Nil else + // TODO (https://github.com/lampepfl/scala3doc/issues/240): configure source root + // toRealPath is used to turn symlinks into proper paths + val actualPath = Paths.get("").toAbsolutePath.relativize(file.toPath.toRealPath()) + ctx.sourceLinks.pathTo(actualPath).map("viewSource" -> _ ) ++ + ctx.sourceLinks.pathTo(actualPath, operation = "edit").map("editSource" -> _ ) + + val updatedSettings = templateFile.settings ++ ctx.projectWideProperties + + ("site" -> (site + ("posts" -> posts))) + ("urls" -> sourceLinks.toMap) + + templateFile.resolveInner(RenderingContext(updatedSettings, ctx.layouts)) diff --git a/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala b/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala index 88ca16e1146a..a0b6a3e45349 100644 --- a/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala +++ b/scala3doc/src/dotty/dokka/site/PartiallyRenderedContent.scala @@ -7,9 +7,12 @@ import org.jetbrains.dokka.pages.{ContentNode, DCI, Style} import org.jetbrains.dokka.base.resolvers.local.LocationProvider import com.vladsch.flexmark.convert.html.FlexmarkHtmlParser import org.jsoup.Jsoup +import scala.collection.JavaConverters._ +import scala.util.Try +import java.net.URL case class PartiallyRenderedContent( - template: TemplateFile, + template: LoadedTemplate, context: StaticSiteContext, override val getChildren: JList[ContentNode], override val getDci: DCI, @@ -27,7 +30,13 @@ case class PartiallyRenderedContent( lazy val resolved = template.resolveToHtml(context) - def procsesHtml(linkTo: String => String): String = + def procsesHtml(linkTo: String => String, absoluteResource: String => String): String = val document = Jsoup.parse(resolved.code) - document.select("a").forEach(element => element.attr("href", linkTo(element.attr("href"))))// forrach does not work here + document.select("a").forEach(element => element.attr("href", linkTo(element.attr("href")))) + document.select("img").forEach { element => + val link = element.attr("src") + Try(new URL(link)).getOrElse { + if(link.startsWith("/")) element.attr("src", absoluteResource(link.drop(1))) + } + }// forrach does not work here document.outerHtml() diff --git a/scala3doc/src/dotty/dokka/site/StaticPageNode.scala b/scala3doc/src/dotty/dokka/site/StaticPageNode.scala index b543ee82ff54..58f35f1961f4 100644 --- a/scala3doc/src/dotty/dokka/site/StaticPageNode.scala +++ b/scala3doc/src/dotty/dokka/site/StaticPageNode.scala @@ -5,13 +5,10 @@ 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) - case class StaticPageNode( template: TemplateFile, override val getName: String, diff --git a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala index 3ba27f23f512..d4acc738e197 100644 --- a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala +++ b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala @@ -10,7 +10,6 @@ import java.nio.file.Paths import org.jetbrains.dokka.base.parsers.MarkdownParser import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter 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} @@ -21,7 +20,9 @@ import util.Try import scala.collection.JavaConverters._ -class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper]): +class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper], args: Args, val sourceLinks: SourceLinks): + + var memberLinkResolver: String => Option[DRI] = _ => None def indexPage():Option[StaticPageNode] = val files = List(new File(root, "index.html"), new File(root, "index.md")).filter { _.exists() } @@ -78,7 +79,6 @@ class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper]): if (indexes.size > 1) // TODO (https://github.com/lampepfl/scala3doc/issues/238): provide proper error handling println(s"ERROR: Multiple index pages for $from found in ${indexes.map(_.file)}") - def loadIndexPage(): TemplateFile = val indexFiles = from.listFiles { file =>file.getName == "index.md" || file.getName == "index.html" } indexFiles.size match @@ -90,7 +90,13 @@ class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper]): val templateFile = if (from.isDirectory) loadIndexPage() else loadTemplateFile(from) - Some(LoadedTemplate(templateFile, children.toList, from)) + val processedChildren = if !isBlog then children else + def dateFrom(p: LoadedTemplate): String = + val pageSettings = p.templateFile.settings.get("page").collect{ case m: Map[String @unchecked, _] => m } + pageSettings.flatMap(_.get("date").collect{ case s: String => s}).getOrElse("1900-01-01") // blogs without date are last + children.sortBy(dateFrom).reverse + + Some(LoadedTemplate(templateFile, processedChildren.toList, from)) catch case e: RuntimeException => // TODO (https://github.com/lampepfl/scala3doc/issues/238): provide proper error handling @@ -111,7 +117,7 @@ class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper]): val path = if isBlog then "blog" else url.stripSuffix(".html") + ".md" val file = root.toPath.resolve(path) // Add support for .html files! val LoadedTemplate(template, children, tFile) = loadTemplate(file.toFile, isBlog).get // Add proper logging if file does not exisits - LoadedTemplate(template.copy(settings = template.settings + ("title" -> title)), children, tFile) + LoadedTemplate(template.copy(settings = template.settings + ("title" -> title)), children, tFile) case Sidebar.Category(title, nested) => // Add support for index.html/index.md files! val fakeFile = new File(root, title) @@ -122,17 +128,23 @@ class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper]): dir("docs").flatMap(_.listFiles()).flatMap(loadTemplate(_, isBlog = false)) ++ dir("blog").flatMap(loadTemplate(_, isBlog = true)) - def driForLink(template: TemplateFile, link: String): Try[DRI] = Try(driFor( + def driForLink(template: TemplateFile, link: String): Option[DRI] = + val pathDri = Try { + val path = if link.startsWith("/") then root.toPath.resolve(link.drop(1)) else template.file.toPath.getParent().resolve(link) - )) + if Files.exists(path) then Some(driFor(path)) else None + }.toOption.flatten + pathDri.orElse(memberLinkResolver(link)) - private def driFor(dest: Path): DRI = mkDRI(s"_.${root.toPath.relativize(dest)}") + def driFor(dest: Path): DRI = mkDRI(s"_.${root.toPath.relativize(dest)}") + + def relativePath(myTemplate: LoadedTemplate) = root.toPath.relativize(myTemplate.file.toPath) def templateToPage(myTemplate: LoadedTemplate): StaticPageNode = val dri = driFor(myTemplate.file.toPath) val content = new PartiallyRenderedContent( - myTemplate.templateFile, + myTemplate, this, JList(), new DCI(Set(dri).asJava, ContentKind.Empty), @@ -147,3 +159,8 @@ class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper]): JList(), (myTemplate.children.map(templateToPage)).asJava ) + + val projectWideProperties = + Seq("projectName" -> args.name) ++ + args.projectVersion.map("projectVersion" -> _) ++ + args.projectTitle.map("projectTitle" -> _) diff --git a/scala3doc/src/dotty/dokka/site/common.scala b/scala3doc/src/dotty/dokka/site/common.scala index 7d21243c05c2..9f93d75c51a6 100644 --- a/scala3doc/src/dotty/dokka/site/common.scala +++ b/scala3doc/src/dotty/dokka/site/common.scala @@ -13,7 +13,7 @@ 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 com.vladsch.flexmark.ext.wikilink.WikiLinkExtension import org.jetbrains.dokka.model.doc.Text import scala.collection.JavaConverters._ @@ -25,20 +25,19 @@ val apiPageDRI: DRI = mkDRI(packageName = "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/" - ) + .set(AnchorLinkExtension.ANCHORLINKS_WRAP_TEXT, false) + .set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor") + .set(EmojiExtension.ROOT_IMAGE_PATH, "https://github.global.ssl.fastly.net/images/icons/emoji/") + .set(Parser.EXTENSIONS, java.util.Arrays.asList( + TablesExtension.create(), + TaskListExtension.create(), + AutolinkExtension.create(), + AnchorLinkExtension.create(), + EmojiExtension.create(), + YamlFrontMatterExtension.create(), + StrikethroughExtension.create(), + WikiLinkExtension.create() + )) def emptyTemplate(file: File, title: String): TemplateFile = TemplateFile( file = file, @@ -84,11 +83,11 @@ def loadTemplateFile(file: File): TemplateFile = { case elem: String => elem case other => throw new RuntimeException(s"Expected a string setting for $name in $file but got $other") }.map(_.stripPrefix("\"").stripSuffix("\"")) - + def listSetting(settings: Map[String, Object], name: String): Option[List[String]] = settings.get(name).map { case elems: List[_] => elems.zipWithIndex.map { case (s: String, _) => s - case (other, index) => + case (other, index) => throw new RuntimeException(s"Expected a string at index $index for $name in $file but got $other") } case elem: String => List(elem) diff --git a/scala3doc/src/dotty/dokka/site/processors.scala b/scala3doc/src/dotty/dokka/site/processors.scala index 27375978c706..fcccd9f02f4f 100644 --- a/scala3doc/src/dotty/dokka/site/processors.scala +++ b/scala3doc/src/dotty/dokka/site/processors.scala @@ -6,7 +6,6 @@ import java.nio.file.Files import java.nio.file.FileVisitOption 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 diff --git a/scala3doc/src/dotty/dokka/site/templates.scala b/scala3doc/src/dotty/dokka/site/templates.scala index eecab0a286f6..bc2f754fe8fb 100644 --- a/scala3doc/src/dotty/dokka/site/templates.scala +++ b/scala3doc/src/dotty/dokka/site/templates.scala @@ -55,8 +55,6 @@ case class TemplateFile( ): def isIndexPage() = file.isFile && (file.getName == "index.md" || file.getName == "index.html") - def resolveToHtml(ctx: StaticSiteContext): ResolvedPage = resolveInner(RenderingContext(settings, ctx.layouts)) - private[site] def resolveInner(ctx: RenderingContext): ResolvedPage = if (ctx.resolving.contains(file.getAbsolutePath)) throw new RuntimeException(s"Cycle in templates involving $file: ${ctx.resolving}") @@ -64,19 +62,19 @@ case class TemplateFile( val layoutTemplate = layout.map(name => ctx.layouts.getOrElse(name, throw new RuntimeException(s"No layouts named $name in ${ctx.layouts}"))) - def asJavaElement(k: String, v: Object): Object = v match + def asJavaElement(o: Object): Object = o match case m: Map[_, _] => m.transform { - case (k: String, v: Object) => asJavaElement(k, v) + case (k: String, v: Object) => asJavaElement(v) }.asJava - case l: List[_] => l.asJava + case l: List[_] => l.map(x => asJavaElement(x.asInstanceOf[Object])).asJava case other => other // Library requires mutable maps.. - val mutableProperties = HMap(ctx.properties.transform(asJavaElement).asJava) + val mutableProperties = JHashMap(ctx.properties.transform((_, v) => asJavaElement(v)).asJava) val rendered = Template.parse(this.rawCode).render(mutableProperties) // We want to render markdown only if next template is html val code = if (isHtml || layoutTemplate.exists(!_.isHtml)) rendered else - val parser: Parser = Parser.builder().build() + val parser: Parser = Parser.builder(defaultMarkdownOptions).build() HtmlRenderer.builder(defaultMarkdownOptions).build().render(parser.parse(rendered)) layoutTemplate match diff --git a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala index e49244678e44..d33fac7752c0 100644 --- a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala @@ -1,6 +1,6 @@ -package dotty.dokka.tasty +package dotty.dokka +package tasty -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model._ import collection.JavaConverters._ import dotty.dokka._ @@ -47,7 +47,4 @@ trait BasicSupport: def getAnnotations(): List[Annotation] = sym.annots.filterNot(_.symbol.packageName.startsWith("scala.annotation.internal")).map(parseAnnotation).reverse - private val emptyDRI = DRI.Companion.getTopLevel - - diff --git a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala index 6de72f1fb18d..8a25aa7a86a2 100644 --- a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala @@ -1,7 +1,6 @@ package dotty.dokka.tasty import org.jetbrains.dokka.model.{TypeConstructor => DTypeConstructor, _} -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet import collection.JavaConverters._ diff --git a/scala3doc/src/dotty/dokka/tasty/PackageSupport.scala b/scala3doc/src/dotty/dokka/tasty/PackageSupport.scala index 6dd08843364f..5ebff3b556f0 100644 --- a/scala3doc/src/dotty/dokka/tasty/PackageSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/PackageSupport.scala @@ -2,7 +2,7 @@ package dotty.dokka package tasty import org.jetbrains.dokka.model._ -import org.jetbrains.dokka.links._ +import org.jetbrains.dokka.links.PointingToDeclaration import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.model.doc.DocumentationNode import dotty.dokka._ diff --git a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala index 67985b4626b9..4d88ed152470 100644 --- a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala +++ b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala @@ -6,7 +6,6 @@ import org.jetbrains.dokka.transformers.sources._ import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.model._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ import org.jetbrains.dokka.base.parsers._ import org.jetbrains.dokka.plugability.DokkaContext @@ -18,6 +17,9 @@ import org.jetbrains.dokka.model.properties.{WithExtraProperties} import quoted.QuoteContext import scala.tasty.inspector.TastyInspector import dotty.dokka.model.api.withNewMembers +import dotty.dokka.tasty.comments.MemberLookup +import dotty.dokka.tasty.comments.QueryParser +import scala.util.Try /** Responsible for collectively inspecting all the Tasty files we're interested in. * @@ -101,6 +103,15 @@ trait DokkaBaseTastyInspector: def processCompilationUnit(using qctx: QuoteContext)(root: qctx.reflect.Tree): Unit = val parser = new TastyParser(qctx, this, config) + + def driFor(link: String): Option[DRI] = + val symOps = new SymOps[qctx.type](qctx) + import symOps._ + Try(QueryParser(link).readQuery()).toOption.flatMap(q => + MemberLookup.lookupOpt(q, None).map{ case (sym, _) => sym.dri} + ) + + config.staticSiteContext.foreach(_.memberLinkResolver = driFor) topLevels ++= parser.parseRootTree(root.asInstanceOf[parser.qctx.reflect.Tree]) def result(): List[DPackage] = diff --git a/scala3doc/src/dotty/dokka/tasty/comments/MemberLookup.scala b/scala3doc/src/dotty/dokka/tasty/comments/MemberLookup.scala index f07a5350debd..0b3acbb025be 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/MemberLookup.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/MemberLookup.scala @@ -12,47 +12,52 @@ trait MemberLookup { def lookupOpt(using QuoteContext)( query: Query, ownerOpt: Option[qctx.reflect.Symbol], - ): Option[(qctx.reflect.Symbol, String)] = { - import qctx.reflect._ - - def nearestClass(sym: Symbol): Symbol = - if sym.isClassDef then sym else nearestClass(sym.owner) - - def nearestPackage(sym: Symbol): Symbol = - if sym.flags.is(Flags.Package) then sym else nearestPackage(sym.owner) - - def nearestMembered(sym: Symbol): Symbol = - if sym.isClassDef || sym.flags.is(Flags.Package) then sym else nearestMembered(sym.owner) - - val res = - ownerOpt match { - case Some(owner) => - val nearest = nearestMembered(owner) - val nearestCls = nearestClass(owner) - val nearestPkg = nearestPackage(owner) - query match { - case Query.StrictMemberId(id) => localLookup(id, nearest).map(_ -> id) - case Query.Id(id) => - (localLookup(id, nearest) orElse localLookup(id, nearestPkg)).map(_ -> id) - case Query.QualifiedId(Query.Qual.This, _, rest) => - downwardLookup(rest.asList, nearestCls).map(_ -> rest.join) - case Query.QualifiedId(Query.Qual.Package, _, rest) => - downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join) - case Query.QualifiedId(Query.Qual.Id(id), _, rest) if id == nearestCls.name => - downwardLookup(rest.asList, nearestCls).map(_ -> rest.join) - case Query.QualifiedId(Query.Qual.Id(id), _, rest) if id == nearestPkg.name => - downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join) - case query: Query.QualifiedId => downwardLookup(query.asList, defn.RootPackage).map(_ -> query.join) - } - - case None => - downwardLookup(query.asList, defn.RootPackage).map(_ -> query.join) - } - - // println(s"looked up `$query` in ${owner.show}[${owner.flags.show}] as ${res.map(_.show)}") - - res - } + ): Option[(qctx.reflect.Symbol, String)] = + try + import qctx.reflect._ + + def nearestClass(sym: Symbol): Symbol = + if sym.isClassDef then sym else nearestClass(sym.owner) + + def nearestPackage(sym: Symbol): Symbol = + if sym.flags.is(Flags.Package) then sym else nearestPackage(sym.owner) + + def nearestMembered(sym: Symbol): Symbol = + if sym.isClassDef || sym.flags.is(Flags.Package) then sym else nearestMembered(sym.owner) + + val res = + ownerOpt match { + case Some(owner) => + val nearest = nearestMembered(owner) + val nearestCls = nearestClass(owner) + val nearestPkg = nearestPackage(owner) + query match { + case Query.StrictMemberId(id) => localLookup(id, nearest).map(_ -> id) + case Query.Id(id) => + (localLookup(id, nearest) orElse localLookup(id, nearestPkg)).map(_ -> id) + case Query.QualifiedId(Query.Qual.This, _, rest) => + downwardLookup(rest.asList, nearestCls).map(_ -> rest.join) + case Query.QualifiedId(Query.Qual.Package, _, rest) => + downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join) + case Query.QualifiedId(Query.Qual.Id(id), _, rest) if id == nearestCls.name => + downwardLookup(rest.asList, nearestCls).map(_ -> rest.join) + case Query.QualifiedId(Query.Qual.Id(id), _, rest) if id == nearestPkg.name => + downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join) + case query: Query.QualifiedId => downwardLookup(query.asList, defn.RootPackage).map(_ -> query.join) + } + + case None => + downwardLookup(query.asList, defn.RootPackage).map(_ -> query.join) + } + + // println(s"looked up `$query` in ${owner.show}[${owner.flags.show}] as ${res.map(_.show)}") + + res + catch + case e: Exception => + // TODO (https://github.com/lampepfl/scala3doc/issues/238): proper reporting + println(s"[WARN] Unable to find a link for ${query} ${ownerOpt.fold("")(o => "in " + o.name)}") + None private def hackMembersOf(using QuoteContext)(rsym: qctx.reflect.Symbol) = { import qctx.reflect._ diff --git a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala index cac96da63409..676fcd49d079 100644 --- a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala @@ -5,7 +5,6 @@ import org.jetbrains.dokka.model._ import collection.JavaConverters import collection.JavaConverters._ import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.properties._ import dotty.dokka.model._ diff --git a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala index 2dd864ad7bc4..0382fb609955 100644 --- a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala @@ -4,7 +4,6 @@ import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer import org.jetbrains.dokka.model._ import collection.JavaConverters._ import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.properties._ import dotty.dokka.model._ diff --git a/scala3doc/src/dotty/dokka/translators/FilterAttributes.scala b/scala3doc/src/dotty/dokka/translators/FilterAttributes.scala index fbef51408e4b..ced94b5a3f96 100644 --- a/scala3doc/src/dotty/dokka/translators/FilterAttributes.scala +++ b/scala3doc/src/dotty/dokka/translators/FilterAttributes.scala @@ -12,7 +12,6 @@ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet import org.jetbrains.dokka.base.resolvers.anchors._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.doc._ import dotty.dokka.model.api._ diff --git a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala index e15296aa88a8..a555d678491c 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala @@ -13,7 +13,6 @@ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet import org.jetbrains.dokka.base.resolvers.anchors._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.doc._ import dotty.dokka.model.api.{Kind => _, Link => SLink, _} diff --git a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala index 6f2fabeaf713..38981dd5dc1a 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala @@ -16,9 +16,7 @@ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet import org.jetbrains.dokka.base.resolvers.anchors._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.model.doc._ -import org.jetbrains.dokka.links.DRIKt.getParent import dotty.dokka.model.api._ import dotty.dokka.model.api.Kind import dotty.dokka.model.api.Link diff --git a/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala b/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala index 35911cbd1e18..6a6e3b1f0c1a 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala @@ -12,7 +12,6 @@ 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 org.jetbrains.dokka.links.DRI import dotty.dokka.model.api.{Kind, _} diff --git a/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala b/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala index f091e6a1f118..c6e9fab30846 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala @@ -2,7 +2,6 @@ package dotty.dokka import org.jetbrains.dokka.base.signatures._ import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder -import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model._ import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages._ diff --git a/scala3doc/src/dotty/dokka/utils.scala b/scala3doc/src/dotty/dokka/utils.scala index 31a6afae752a..5763eddde4a4 100644 --- a/scala3doc/src/dotty/dokka/utils.scala +++ b/scala3doc/src/dotty/dokka/utils.scala @@ -4,7 +4,6 @@ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.base.signatures._ import org.jetbrains.dokka.model._ import org.jetbrains.dokka.pages._ -import org.jetbrains.dokka.links._ import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.utilities.DokkaLogger diff --git a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala index 0c680aa0f012..226d4e6ae394 100644 --- a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala +++ b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala @@ -2,7 +2,6 @@ package dotty.dokka import dotty.dokka.model._ import org.jetbrains.dokka.model._ -import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.pages._ import dotty.dokka.model.api.Kind import HTML._ diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 277a030eb7eb..ada77dc0d100 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -16,7 +16,6 @@ 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 @@ -24,6 +23,8 @@ import dotty.dokka.site.StaticPageNode import dotty.dokka.site.PartiallyRenderedContent import scala.util.Try import org.jetbrains.dokka.base.renderers.html.SearchbarDataInstaller +import org.jsoup.Jsoup +import java.nio.file.Paths class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet], locationProvider: LocationProvider): def link(dri: DRI): Option[String] = Option(locationProvider.resolve(dri, sourceSetRestriciton, pageContext)) @@ -41,7 +42,7 @@ class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[Dis def renderElement(e: String | (String, DRI) | Link) = renderElementWith(e) -class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { +class ScalaHtmlRenderer(ctx: DokkaContext, args: Args) extends HtmlRenderer(ctx) { // TODO #239 val hackScalaSearchbarDataInstaller: SearchbarDataInstaller = { @@ -252,12 +253,15 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { page.getContent match case prc: PartiallyRenderedContent => def processLocalLink(str: String): String = + Try(URL(str)).map(_ => str).getOrElse{ // TODO (https://github.com/lampepfl/scala3doc/issues/238) error handling - prc.context.driForLink(prc.template, str).toOption - .flatMap(dri => Option(getLocationProvider.resolve(dri, sourceSets, page))) - .getOrElse(str) + prc.context.driForLink(prc.template.templateFile, str) + .flatMap(dri => Option(getLocationProvider.resolve(dri, sourceSets, page))) + .getOrElse(str) + } - withHtml(context, prc.procsesHtml(url => Try(URL(url)).fold(_ => processLocalLink(url), _ => url))) + val html = prc.procsesHtml(processLocalLink, resolveLink(page)) + withHtml(context, html) case content => build(content, context, page, /*sourceSetRestriction=*/null) @@ -269,6 +273,12 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { case _ => (page.getName, false) + val projectLogo = + args.projectLogo.map { path => + val fileName = Paths.get(path).getFileName() + span(img(src := resolveRoot(page, s"project-logo/$fileName"))) + }.toSeq + html( head( meta(charset := "utf-8"), @@ -281,7 +291,13 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { if noFrame then raw(buildWithKotlinx(kotlinxContent)) else div(id := "container")( div(id := "leftColumn")( - div(id := "logo"), + div(id := "logo")( + projectLogo, + span( + div(cls:="projectName")(args.name), + args.projectVersion.map(v => div(cls:="projectVersion")(v)).toList + ) + ), div(id := "paneSearch"), nav(id := "sideMenu"), ), @@ -300,9 +316,9 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { raw(" Back to top") ) ), - span(cls := "pull-right")( - raw("Generated by "), - a(href := "https://github.com/lampepfl/scala3doc")("Scala3doc") + raw("Generated by "), + a(href := "https://github.com/lampepfl/dotty/tree/master/scala3doc")( + img(src := resolveRoot(page, "images/scala3doc_logo.svg"), alt := "Scala3doc", cls := "scala3doc_logo") ) ) ) @@ -315,6 +331,9 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { private def resolveRoot(page: PageNode, path: String) = getLocationProvider.pathToRoot(page) + path + private def resolveLink(page: PageNode)(url: String): String = + if URI(url).isAbsolute then url else resolveRoot(page, url) + private def linkResources(page: PageNode, resources: Iterable[String]): Iterable[AppliedTag] = def fileExtension(url: String): String = val param = url.indexOf('?') @@ -322,13 +341,10 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { val point = url.lastIndexOf('.', end) url.substring(point+1, end) - def resolveLink(url: String): String = - if URI(url).isAbsolute then url else resolveRoot(page, url) - for res <- resources yield fileExtension(res) match - case "css" => link(rel := "stylesheet", href := resolveLink(res)) - case "js" => script(`type` := "text/javascript", src := resolveLink(res), defer := "true") + case "css" => link(rel := "stylesheet", href := resolveLink(page)(res)) + case "js" => script(`type` := "text/javascript", src := resolveLink(page)(res), defer := "true") case _ => raw(res) private def buildWithKotlinx(node: ContentNode, pageContext: ContentPage, sourceSetRestriction: JSet[DisplaySourceSet]): String = diff --git a/scala3doc/src/dotty/renderers/html.scala b/scala3doc/src/dotty/renderers/html.scala index f19b0ac288d9..b5cd9668fa5f 100644 --- a/scala3doc/src/dotty/renderers/html.scala +++ b/scala3doc/src/dotty/renderers/html.scala @@ -76,6 +76,7 @@ object HTML: val title = Tag("title") val body = Tag("body") val nav = Tag("nav") + val img = Tag("img") val cls = Attr("class") val href = Attr("href") @@ -90,6 +91,7 @@ object HTML: val name = Attr("name") val content = Attr("content") val testId = Attr("data-test-id") + val alt = Attr("alt") def raw(content: String): AppliedTag = AppliedTag(content) diff --git a/scala3doc/test/dotty/dokka/ScaladocTest.scala b/scala3doc/test/dotty/dokka/ScaladocTest.scala index b657af779a50..d54c3c132d44 100644 --- a/scala3doc/test/dotty/dokka/ScaladocTest.scala +++ b/scala3doc/test/dotty/dokka/ScaladocTest.scala @@ -26,7 +26,7 @@ abstract class ScaladocTest(val name: String): classpath = System.getProperty("java.class.path"), None, output = getTempDir().getRoot, - projectVersion = "1.0", + projectVersion = Some("1.0"), projectTitle = None, projectLogo = None, defaultSyntax = None, diff --git a/scala3doc/test/dotty/dokka/SignatureTestCases.scala b/scala3doc/test/dotty/dokka/SignatureTestCases.scala index eb8f8731d691..299737b4e82a 100644 --- a/scala3doc/test/dotty/dokka/SignatureTestCases.scala +++ b/scala3doc/test/dotty/dokka/SignatureTestCases.scala @@ -58,3 +58,5 @@ class InheritedMembers extends SignatureTest("inheritedMembers2", SignatureTest. sourceFiles = List("inheritedMembers1", "inheritedMembers2")) class ComplexNames extends SignatureTest("complexNames", Seq("def")) + +class WrongDocumentationLinks extends SignatureTest("links", Seq("def")) \ No newline at end of file diff --git a/scala3doc/test/dotty/dokka/SourceLinksTests.scala b/scala3doc/test/dotty/dokka/SourceLinksTests.scala index f54493d583cf..9b72b9053f36 100644 --- a/scala3doc/test/dotty/dokka/SourceLinksTests.scala +++ b/scala3doc/test/dotty/dokka/SourceLinksTests.scala @@ -56,6 +56,7 @@ class SourceLinkTest: } class SourceLinksTest: + // TODO (https://github.com/lampepfl/scala3doc/issues/240): configure source root val projectRoot = Paths.get("").toAbsolutePath() val edit: Operation = "edit" // union types need explicit singletons diff --git a/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala b/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala index 7a66810eae36..97682256e2aa 100644 --- a/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala +++ b/scala3doc/test/dotty/dokka/site/TemplateFileTests.scala @@ -133,9 +133,9 @@ class TemplateFileTests: val expected = - """

Test page

+ """

Test page

|

Hello world!!

- |

Test page end

+ |

Test page end

|
""".stripMargin testContent( @@ -203,7 +203,9 @@ class TemplateFileTests: """# Hello {{ msg }}!""", ext = "md" ) { t => - assertEquals("

Hello there!

", t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim()) + assertEquals( + """

Hello there!

""", + t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim()) } @Test @@ -212,7 +214,8 @@ class TemplateFileTests: """# Hello {{ msg }}!""", ext = "md" ) { t => - assertEquals("

Hello there!

", t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim()) + assertEquals("""

Hello there!

""", + t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim()) } @Test