aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorKamil Doległo <kamilok1965@interia.pl>2020-09-24 14:16:13 +0200
committerBłażej Kardyś <bkardys@virtuslab.com>2020-11-27 03:15:02 +0100
commitdc179bf9a649d925e7e64dbcaf52a2187416a1d5 (patch)
treec86de1bd76fae760e21380b5e99767ec45c82f32 /plugins
parent52f64c664573567259f8f678d32924f86b4f147c (diff)
downloaddokka-dc179bf9a649d925e7e64dbcaf52a2187416a1d5.tar.gz
dokka-dc179bf9a649d925e7e64dbcaf52a2187416a1d5.tar.bz2
dokka-dc179bf9a649d925e7e64dbcaf52a2187416a1d5.zip
Implement basic link resolution
Diffstat (limited to 'plugins')
-rw-r--r--plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt10
-rw-r--r--plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt110
-rw-r--r--plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt16
-rw-r--r--plugins/all-module-page/src/main/kotlin/templates/Substitutor.kt7
-rw-r--r--plugins/all-module-page/src/main/kotlin/templates/TemplateProcessor.kt8
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt5
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/Tags.kt2
-rw-r--r--plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt4
-rw-r--r--plugins/base/src/main/kotlin/templating/Command.kt4
-rw-r--r--plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt3
10 files changed, 154 insertions, 15 deletions
diff --git a/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt b/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt
index 584de32f..f1ed8c1e 100644
--- a/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt
+++ b/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt
@@ -1,15 +1,15 @@
package org.jetbrains.dokka.allModulesPage
import org.jetbrains.dokka.CoreExtensions
-import org.jetbrains.dokka.allModulesPage.templates.DefaultTemplateProcessor
-import org.jetbrains.dokka.allModulesPage.templates.DirectiveBasedTemplateProcessingStrategy
-import org.jetbrains.dokka.allModulesPage.templates.TemplateProcessor
+import org.jetbrains.dokka.allModulesPage.templates.*
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.plugability.DokkaPlugin
class AllModulesPagePlugin : DokkaPlugin() {
val templateProcessor by extensionPoint<TemplateProcessor>()
+ val substitutor by extensionPoint<Substitutor>()
+
val allModulePageCreators by extending {
(CoreExtensions.allModulePageCreator
providing ::MultimodulePageCreator)
@@ -31,4 +31,8 @@ class AllModulesPagePlugin : DokkaPlugin() {
val defaultTemplateProcessor by extending {
templateProcessor providing { DefaultTemplateProcessor(it, DirectiveBasedTemplateProcessingStrategy(it)) }
}
+
+ val pathToRootSubstitutor by extending {
+ substitutor providing ::PathToRootSubstitutor
+ }
} \ No newline at end of file
diff --git a/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt b/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt
index 705e6678..d50cbc32 100644
--- a/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt
+++ b/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt
@@ -4,22 +4,27 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import org.jetbrains.dokka.base.templating.AddToNavigationCommand
-import org.jetbrains.dokka.base.templating.Command
-import org.jetbrains.dokka.base.templating.ResolveLinkCommand
-import org.jetbrains.dokka.base.templating.parseJson
+import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation
+import org.jetbrains.dokka.base.resolvers.shared.PackageList
+import org.jetbrains.dokka.base.templating.*
import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
import org.jsoup.Jsoup
-import org.jsoup.nodes.Attributes
-import org.jsoup.nodes.Element
+import org.jsoup.nodes.*
import org.jsoup.parser.Tag
import java.io.File
+import java.net.URL
import java.nio.file.Files
import java.util.concurrent.ConcurrentHashMap
class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext) : TemplateProcessingStrategy {
private val navigationFragments = ConcurrentHashMap<String, Element>()
+ private val substitutors = context.plugin<AllModulesPagePlugin>().query { substitutor }
+
override suspend fun process(input: File, output: File): Unit = coroutineScope {
if (input.extension == "html") {
launch {
@@ -28,8 +33,9 @@ class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext
document.select("dokka-template-command").forEach {
val command = parseJson<Command>(it.attr("data"))
when (command) {
- is ResolveLinkCommand -> resolveLink(it, command)
+ is ResolveLinkCommand -> resolveLink(it, command, output)
is AddToNavigationCommand -> navigationFragments[command.moduleName] = it
+ is SubstitutionCommand -> substitute(it, TemplatingContext(input, output, it, command))
else -> context.logger.warn("Unknown templating command $command")
}
}
@@ -42,6 +48,42 @@ class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext
}
}
+ private fun substitute(element: Element, commandContext: TemplatingContext<SubstitutionCommand>) {
+ val regex = commandContext.command.pattern.toRegex()
+ element.children().forEach { it.traverseToSubstitute(regex, commandContext) }
+
+ val childrenCopy = element.children().toList()
+ val position = element.elementSiblingIndex()
+ val parent = element.parent()
+ element.remove()
+
+ parent.insertChildren(position, childrenCopy)
+ }
+
+ private fun Node.traverseToSubstitute(regex: Regex, commandContext: TemplatingContext<SubstitutionCommand>) {
+ when (this) {
+ is TextNode -> replaceWith(TextNode(wholeText.substitute(regex, commandContext)))
+ is DataNode -> replaceWith(DataNode(wholeData.substitute(regex, commandContext)))
+ is Element -> {
+ attributes().forEach { attr(it.key, it.value.substitute(regex, commandContext)) }
+ childNodes().forEach { it.traverseToSubstitute(regex, commandContext) }
+ }
+ }
+ }
+
+ private fun String.substitute(regex: Regex, commandContext: TemplatingContext<SubstitutionCommand>) = buildString {
+ var lastOffset = 0
+ regex.findAll(this@substitute).forEach { match ->
+ append(this@substitute, lastOffset, match.range.first)
+ append(findSubstitution(commandContext, match))
+ lastOffset = match.range.last + 1
+ }
+ append(this@substitute, lastOffset, this@substitute.length)
+ }
+
+ private fun findSubstitution(commandContext: TemplatingContext<SubstitutionCommand>, match: MatchResult): String =
+ substitutors.asSequence().mapNotNull { it.trySubstitute(commandContext, match) }.firstOrNull() ?: match.value
+
override suspend fun finish(output: File) {
val attributes = Attributes().apply {
put("class", "sideMenu")
@@ -76,9 +118,57 @@ class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext
}
}
- private fun resolveLink(it: Element, command: ResolveLinkCommand) {
+ private fun resolveLink(it: Element, command: ResolveLinkCommand, fileContext: File) {
+ val elpFactory = context.plugin<DokkaBase>().query { externalLocationProviderFactory }
+
+ val packageLists =
+ context.configuration.modules.map { it.sourceOutputDirectory.resolve(it.relativePathToOutputDirectory) }
+ .map { module ->
+ module to PackageList.load(
+ URL("file:" + module.resolve("package-list").path),
+ 8,
+ true
+ )
+ }.toMap()
+
+ val externalDocumentations =
+ packageLists.map { (module, pckgList) ->
+ ExternalDocumentation(
+ URL("file:/${module.name}/${module.name}"),
+ pckgList!!
+ )
+ }
+
+ val elps = elpFactory
+ .flatMap { externalDocumentations.map { ed -> it.getExternalLocationProvider(ed) } }
+ .filterNotNull()
+
+ val absoluteLink = elps.mapNotNull { it.resolve(command.dri) }.firstOrNull()
+ if (absoluteLink == null) {
+ val children = it.childNodes().toList()
+ val attributes = Attributes().apply {
+ put("data-unresolved-link", command.dri.toString())
+ }
+ val element = Element(Tag.valueOf("span"), "", attributes).apply {
+ children.forEach { ch -> appendChild(ch) }
+ }
+ it.replaceWith(element)
+ return
+ }
+
+ val modulePath = context.configuration.outputDir.absolutePath.split("/")
+ val contextPath = fileContext.absolutePath.split("/")
+ val commonPathElements = modulePath.zip(contextPath)
+ .takeWhile { (a, b) -> a == b }.count()
+
+ // -1 here to not drop the last directory
+ val link =
+ (List(contextPath.size - commonPathElements - 1) { ".." } + modulePath.drop(commonPathElements)).joinToString(
+ "/"
+ ) + absoluteLink.removePrefix("file:")
+
val attributes = Attributes().apply {
- put("href", "") // TODO: resolve
+ put("href", link) // TODO: resolve
}
val children = it.childNodes().toList()
val element = Element(Tag.valueOf("a"), "", attributes).apply {
@@ -86,4 +176,4 @@ class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext
}
it.replaceWith(element)
}
-} \ No newline at end of file
+}
diff --git a/plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt b/plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt
new file mode 100644
index 00000000..992ee7ed
--- /dev/null
+++ b/plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt
@@ -0,0 +1,16 @@
+package org.jetbrains.dokka.allModulesPage.templates
+
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand
+import org.jetbrains.dokka.base.templating.SubstitutionCommand
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.query
+import java.nio.file.Path
+
+class PathToRootSubstitutor(private val dokkaContext: DokkaContext) : Substitutor {
+ override fun trySubstitute(context: TemplatingContext<SubstitutionCommand>, match: MatchResult): String? =
+ if (context.command is PathToRootSubstitutionCommand) {
+ context.output.toPath().parent.relativize(dokkaContext.configuration.outputDir.toPath()).toString() + "/"
+ } else null
+
+} \ No newline at end of file
diff --git a/plugins/all-module-page/src/main/kotlin/templates/Substitutor.kt b/plugins/all-module-page/src/main/kotlin/templates/Substitutor.kt
new file mode 100644
index 00000000..98f1d88e
--- /dev/null
+++ b/plugins/all-module-page/src/main/kotlin/templates/Substitutor.kt
@@ -0,0 +1,7 @@
+package org.jetbrains.dokka.allModulesPage.templates
+
+import org.jetbrains.dokka.base.templating.SubstitutionCommand
+
+fun interface Substitutor {
+ fun trySubstitute(context: TemplatingContext<SubstitutionCommand>, match: MatchResult): String?
+} \ No newline at end of file
diff --git a/plugins/all-module-page/src/main/kotlin/templates/TemplateProcessor.kt b/plugins/all-module-page/src/main/kotlin/templates/TemplateProcessor.kt
index cd144046..eafc3669 100644
--- a/plugins/all-module-page/src/main/kotlin/templates/TemplateProcessor.kt
+++ b/plugins/all-module-page/src/main/kotlin/templates/TemplateProcessor.kt
@@ -1,7 +1,9 @@
package org.jetbrains.dokka.allModulesPage.templates
import kotlinx.coroutines.*
+import org.jetbrains.dokka.base.templating.Command
import org.jetbrains.dokka.plugability.DokkaContext
+import org.jsoup.nodes.Element
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
@@ -44,3 +46,9 @@ class DefaultTemplateProcessor(
}
}
+data class TemplatingContext<out T: Command>(
+ val input: File,
+ val output: File,
+ val element: Element,
+ val command: T,
+) \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
index 16f60a83..d344dafe 100644
--- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -13,6 +13,7 @@ import org.jetbrains.dokka.base.renderers.isImage
import org.jetbrains.dokka.base.renderers.pageId
import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
import org.jetbrains.dokka.base.resolvers.local.DokkaBaseLocationProvider
+import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand
import org.jetbrains.dokka.base.templating.ResolveLinkCommand
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.DisplaySourceSet
@@ -736,7 +737,9 @@ open class HtmlRenderer(
else -> unsafe { +it }
}
}
- script { unsafe { +"""var pathToRoot = "${locationProvider.pathToRoot(page)}";""" } }
+ templateCommand(PathToRootSubstitutionCommand("###")) {
+ script { unsafe { +"""var pathToRoot = "###";""" } }
+ }
}
body {
div {
diff --git a/plugins/base/src/main/kotlin/renderers/html/Tags.kt b/plugins/base/src/main/kotlin/renderers/html/Tags.kt
index 3db49ebf..59d711e9 100644
--- a/plugins/base/src/main/kotlin/renderers/html/Tags.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/Tags.kt
@@ -13,7 +13,7 @@ open class WBR(initialAttributes: Map<String, String>, consumer: TagConsumer<*>)
HTMLTag("wbr", consumer, initialAttributes, namespace = null, inlineTag = true, emptyTag = false),
HtmlBlockInlineTag
-fun FlowOrPhrasingContent.templateCommand(data: Command, block: TemplateCommand.() -> Unit = {}): Unit =
+fun FlowOrPhrasingOrMetaDataContent.templateCommand(data: Command, block: TemplateCommand.() -> Unit = {}): Unit =
TemplateCommand(attributesMapOf("data", toJsonString(data)), consumer).visit(block)
fun <T> TagConsumer<T>.templateCommand(data: Command, block: TemplateCommand.() -> Unit = {}): T =
diff --git a/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt b/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt
index c1b76a3b..8d3b7521 100644
--- a/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt
+++ b/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt
@@ -1,6 +1,7 @@
package org.jetbrains.dokka.base.resolvers.shared
import org.jetbrains.dokka.base.renderers.PackageListService
+import java.io.File
import java.net.URL
data class PackageList(
@@ -14,6 +15,9 @@ data class PackageList(
if (offlineMode && url.protocol.toLowerCase() != "file")
return null
+ if (!File(url.file).isFile)
+ return null
+
val packageListStream = url.readContent()
val (params, packages) = packageListStream
diff --git a/plugins/base/src/main/kotlin/templating/Command.kt b/plugins/base/src/main/kotlin/templating/Command.kt
index e352ba32..0b998dee 100644
--- a/plugins/base/src/main/kotlin/templating/Command.kt
+++ b/plugins/base/src/main/kotlin/templating/Command.kt
@@ -7,3 +7,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS
@JsonTypeInfo(use= CLASS)
interface Command
+
+abstract class SubstitutionCommand: Command {
+ abstract val pattern: String
+}
diff --git a/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt b/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt
new file mode 100644
index 00000000..03f091c3
--- /dev/null
+++ b/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt
@@ -0,0 +1,3 @@
+package org.jetbrains.dokka.base.templating
+
+data class PathToRootSubstitutionCommand(override val pattern: String): SubstitutionCommand() \ No newline at end of file