diff options
Diffstat (limited to 'plugins/all-module-page/src/main/kotlin')
5 files changed, 138 insertions, 13 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 |