From 076a5f421c5e4621539efd814be612f43fef33f5 Mon Sep 17 00:00:00 2001 From: Błażej Kardyś Date: Mon, 16 Nov 2020 19:46:31 +0100 Subject: Adding inter-module link resolving template strategy for Gfm --- .../src/main/kotlin/AllModulesPagePlugin.kt | 13 +++++- .../src/main/kotlin/MultimoduleLocationProvider.kt | 7 ++- .../src/main/kotlin/MultimodulePageCreator.kt | 10 ++-- .../templates/DirectiveBasedTemplateProcessing.kt | 54 +++------------------- .../kotlin/templates/ExternalModuleLinkResolver.kt | 49 ++++++++++++++++++++ .../FallbackTemplateProcessingStrategy.kt | 18 ++++++++ .../src/main/kotlin/templates/TemplateProcessor.kt | 14 ++++-- plugins/base/src/main/kotlin/DokkaBase.kt | 3 +- .../src/main/kotlin/renderers/DefaultRenderer.kt | 2 +- .../main/kotlin/templating/ResolveLinkCommand.kt | 1 - plugins/gfm/build.gradle.kts | 1 + plugins/gfm/gfm-all-module-page/build.gradle.kts | 3 ++ .../gfm/allModulesPage/GfmAllModulesPagaPlugin.kt | 8 ++++ .../GfmMultimoduleLocationProvider.kt | 30 ++---------- .../GfmTemplateProcessingStrategy.kt | 45 ++++++++++++++++++ .../org/jetbrains/dokka/gfm/gfmTemplating.kt | 19 ++++++++ .../dokka/gfm/renderer/CommonmarkRenderer.kt | 15 +++++- 17 files changed, 199 insertions(+), 93 deletions(-) create mode 100644 plugins/all-module-page/src/main/kotlin/templates/ExternalModuleLinkResolver.kt create mode 100644 plugins/all-module-page/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt create mode 100644 plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmTemplateProcessingStrategy.kt create mode 100644 plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/gfmTemplating.kt (limited to 'plugins') diff --git a/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt b/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt index a65c0c58..bcc43043 100644 --- a/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt +++ b/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt @@ -10,6 +10,7 @@ import org.jetbrains.dokka.transformers.pages.PageTransformer class AllModulesPagePlugin : DokkaPlugin() { val templateProcessor by extensionPoint() + val templateProcessingStrategy by extensionPoint() val allModulePageCreator by extensionPoint() val allModulePageTransformer by extensionPoint() @@ -33,7 +34,17 @@ class AllModulesPagePlugin : DokkaPlugin() { } val defaultTemplateProcessor by extending { - templateProcessor providing { DefaultTemplateProcessor(it, DirectiveBasedTemplateProcessingStrategy(it)) } + templateProcessor providing ::DefaultTemplateProcessor + } + + val directiveBasedHtmlTemplateProcessingStrategy by extending { + templateProcessingStrategy providing ::DirectiveBasedHtmlTemplateProcessingStrategy order { + before(fallbackProcessingStrategy) + } + } + + val fallbackProcessingStrategy by extending { + templateProcessingStrategy providing ::FallbackTemplateProcessingStrategy } val pathToRootSubstitutor by extending { diff --git a/plugins/all-module-page/src/main/kotlin/MultimoduleLocationProvider.kt b/plugins/all-module-page/src/main/kotlin/MultimoduleLocationProvider.kt index 1e7fb60c..cb1eca7b 100644 --- a/plugins/all-module-page/src/main/kotlin/MultimoduleLocationProvider.kt +++ b/plugins/all-module-page/src/main/kotlin/MultimoduleLocationProvider.kt @@ -10,17 +10,16 @@ import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext -class MultimoduleLocationProvider(private val root: RootPageNode, context: DokkaContext) : DokkaBaseLocationProvider(root, context, ".html") { - - private val defaultLocationProvider = DokkaLocationProvider(root, context) +open class MultimoduleLocationProvider(private val root: RootPageNode, val context: DokkaContext, private val fileExtension: String = ".html") : DokkaBaseLocationProvider(root, context, fileExtension) { + protected open val defaultLocationProvider = DokkaLocationProvider(root, context) val paths = context.configuration.modules.map { it.name to it.relativePathToOutputDirectory }.toMap() override fun resolve(dri: DRI, sourceSets: Set, context: PageNode?) = dri.takeIf { it.packageName == MULTIMODULE_PACKAGE_PLACEHOLDER }?.classNames?.let { paths[it] }?.let { - "$it/${identifierToFilename(dri.classNames.orEmpty())}/index.html" + "$it/${identifierToFilename(dri.classNames.orEmpty())}/index$fileExtension" } ?: defaultLocationProvider.resolve(dri, sourceSets, context) override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean) = diff --git a/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt b/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt index 46c8bf82..123dee2b 100644 --- a/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt +++ b/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt @@ -18,6 +18,7 @@ import org.jetbrains.dokka.model.doc.P import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.pages.PageCreator import org.jetbrains.dokka.utilities.DokkaLogger @@ -27,14 +28,11 @@ class MultimodulePageCreator( ) : PageCreator { private val logger: DokkaLogger = context.logger + private val commentsConverter by lazy { context.plugin().querySingle { commentsToContentConverter } } + private val signatureProvider by lazy { context.plugin().querySingle { signatureProvider } } + override fun invoke(): RootPageNode { val modules = context.configuration.modules - - val commentsConverter = context.plugin(DokkaBase::class)?.querySingle { commentsToContentConverter } - val signatureProvider = context.plugin(DokkaBase::class)?.querySingle { signatureProvider } - if (commentsConverter == null || signatureProvider == null) - throw IllegalStateException("Both comments converter and signature provider must not be null") - val sourceSetData = emptySet() val builder = PageContentBuilder(commentsConverter, signatureProvider, context.logger) val contentNode = builder.contentFor( 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 92f39779..feba2d4e 100644 --- a/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt +++ b/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt @@ -5,9 +5,6 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext 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 @@ -16,16 +13,16 @@ import org.jsoup.Jsoup 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 { +class DirectiveBasedHtmlTemplateProcessingStrategy(private val context: DokkaContext) : TemplateProcessingStrategy { private val navigationFragments = ConcurrentHashMap() private val substitutors = context.plugin().query { substitutor } + private val externalModuleLinkResolver = ExternalModuleLinkResolver(context) - override suspend fun process(input: File, output: File): Unit = coroutineScope { + override suspend fun process(input: File, output: File): Boolean = coroutineScope { if (input.extension == "html") { launch { val document = withContext(IO) { Jsoup.parse(input, "UTF-8") } @@ -40,11 +37,8 @@ class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext } withContext(IO) { Files.write(output.toPath(), listOf(document.outerHtml())) } } - } else { - launch(IO) { - Files.copy(input.toPath(), output.toPath()) - } - } + true + } else false } private fun substitute(element: Element, commandContext: TemplatingContext) { @@ -118,32 +112,9 @@ class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext } private fun resolveLink(it: Element, command: ResolveLinkCommand, fileContext: File) { - val elpFactory = context.plugin().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 link = externalModuleLinkResolver.resolve(command.dri, fileContext) + if (link == null) { val children = it.childNodes().toList() val attributes = Attributes().apply { put("data-unresolved-link", command.dri.toString()) @@ -155,17 +126,6 @@ class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext return } - val modulePath = context.configuration.outputDir.absolutePath.split(File.separator) - val contextPath = fileContext.absolutePath.split(File.separator) - 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", link) // TODO: resolve } diff --git a/plugins/all-module-page/src/main/kotlin/templates/ExternalModuleLinkResolver.kt b/plugins/all-module-page/src/main/kotlin/templates/ExternalModuleLinkResolver.kt new file mode 100644 index 00000000..5a3d3d33 --- /dev/null +++ b/plugins/all-module-page/src/main/kotlin/templates/ExternalModuleLinkResolver.kt @@ -0,0 +1,49 @@ +package org.jetbrains.dokka.allModulesPage.templates + +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.links.DRI +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query +import java.net.URL +import java.io.File + +class ExternalModuleLinkResolver(val context: DokkaContext) { + private val elpFactory = context.plugin().query { externalLocationProviderFactory } + private val externalDocumentations by lazy(::setupExternalDocumentations) + private val elps by lazy { + elpFactory.flatMap { externalDocumentations.map { ed -> it.getExternalLocationProvider(ed) } }.filterNotNull() + } + + private fun setupExternalDocumentations(): List { + 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() + return packageLists.map { (module, pckgList) -> + ExternalDocumentation( + URL("file:/${module.name}/${module.name}"), + pckgList!! + ) + } + } + + fun resolve(dri: DRI, fileContext: File): String? { + val absoluteLink = elps.mapNotNull { it.resolve(dri) }.firstOrNull() ?: return null + val modulePath = context.configuration.outputDir.absolutePath.split(File.separator) + val contextPath = fileContext.absolutePath.split(File.separator) + val commonPathElements = modulePath.zip(contextPath) + .takeWhile { (a, b) -> a == b }.count() + + return (List(contextPath.size - commonPathElements - 1) { ".." } + modulePath.drop(commonPathElements)).joinToString( + "/" + ) + absoluteLink.removePrefix("file:") + } +} \ No newline at end of file diff --git a/plugins/all-module-page/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt b/plugins/all-module-page/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt new file mode 100644 index 00000000..9b5251ac --- /dev/null +++ b/plugins/all-module-page/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt @@ -0,0 +1,18 @@ +package org.jetbrains.dokka.allModulesPage.templates + +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import org.jetbrains.dokka.plugability.DokkaContext +import java.io.File +import java.nio.file.Files + +class FallbackTemplateProcessingStrategy(dokkaContext: DokkaContext) : TemplateProcessingStrategy { + + override suspend fun process(input: File, output: File): Boolean = coroutineScope { + launch(IO) { + Files.copy(input.toPath(), output.toPath()) + } + true + } +} \ 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 eafc3669..18d63df0 100644 --- a/plugins/all-module-page/src/main/kotlin/templates/TemplateProcessor.kt +++ b/plugins/all-module-page/src/main/kotlin/templates/TemplateProcessor.kt @@ -1,8 +1,11 @@ package org.jetbrains.dokka.allModulesPage.templates import kotlinx.coroutines.* +import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin import org.jetbrains.dokka.base.templating.Command import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query import org.jsoup.nodes.Element import java.io.File import java.nio.file.Files @@ -14,14 +17,16 @@ interface TemplateProcessor { } interface TemplateProcessingStrategy { - suspend fun process(input: File, output: File) + suspend fun process(input: File, output: File): Boolean suspend fun finish(output: File) {} } class DefaultTemplateProcessor( private val context: DokkaContext, - private val strategy: TemplateProcessingStrategy ): TemplateProcessor { + + private val strategies: List = context.plugin().query { templateProcessingStrategy } + override fun process() = runBlocking(Dispatchers.Default) { coroutineScope { context.configuration.modules.forEach { @@ -30,7 +35,8 @@ class DefaultTemplateProcessor( } } } - strategy.finish(context.configuration.outputDir) + strategies.map { it.finish(context.configuration.outputDir) } + Unit } private suspend fun File.visit(target: File): Unit = coroutineScope { @@ -41,7 +47,7 @@ class DefaultTemplateProcessor( launch { source.resolve(it).visit(target.resolve(it)) } } } else { - strategy.process(source, target) + strategies.asSequence().first { it.process(source, target) } } } } diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index ac070ab4..c4dacdcd 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -32,7 +32,8 @@ import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer import org.jetbrains.dokka.transformers.pages.PageTransformer -class DokkaBase : DokkaPlugin() { +class +DokkaBase : DokkaPlugin() { val preMergeDocumentableTransformer by extensionPoint() val pageMergerStrategy by extensionPoint() diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt index 182ecbc6..d76ba597 100644 --- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt @@ -104,7 +104,7 @@ abstract class DefaultRenderer( pageContext: ContentPage, sourceSetRestriction: Set? = null ) { - if (sourceSetRestriction == null || node.sourceSets.any { it in sourceSetRestriction }) { + if (sourceSetRestriction.isNullOrEmpty() || node.sourceSets.any { it in sourceSetRestriction }) { when (node) { is ContentText -> buildText(node) is ContentHeader -> buildHeader(node, pageContext, sourceSetRestriction) diff --git a/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt b/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt index 83b7e0d8..1cc3e773 100644 --- a/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt +++ b/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt @@ -1,6 +1,5 @@ package org.jetbrains.dokka.base.templating -import com.fasterxml.jackson.annotation.JsonTypeInfo import org.jetbrains.dokka.links.DRI class ResolveLinkCommand(val dri: DRI): Command diff --git a/plugins/gfm/build.gradle.kts b/plugins/gfm/build.gradle.kts index 33917cdf..6400dba1 100644 --- a/plugins/gfm/build.gradle.kts +++ b/plugins/gfm/build.gradle.kts @@ -4,6 +4,7 @@ dependencies { implementation(project(":plugins:base")) testImplementation(project(":plugins:base")) testImplementation(project(":plugins:base:base-test-utils")) + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.1") } registerDokkaArtifactPublication("gfmPlugin") { diff --git a/plugins/gfm/gfm-all-module-page/build.gradle.kts b/plugins/gfm/gfm-all-module-page/build.gradle.kts index 66deb291..85f83587 100644 --- a/plugins/gfm/gfm-all-module-page/build.gradle.kts +++ b/plugins/gfm/gfm-all-module-page/build.gradle.kts @@ -4,6 +4,9 @@ dependencies { implementation(project(":plugins:base")) implementation(project(":plugins:gfm")) implementation(project(":plugins:all-module-page")) + + val coroutines_version: String by project + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") } registerDokkaArtifactPublication("dokkaGfmAllModulePage") { diff --git a/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmAllModulesPagaPlugin.kt b/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmAllModulesPagaPlugin.kt index 8052a762..eb5e7e45 100644 --- a/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmAllModulesPagaPlugin.kt +++ b/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmAllModulesPagaPlugin.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.gfm.allModulesPage import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin +import org.jetbrains.dokka.allModulesPage.templates.DefaultTemplateProcessor import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.gfm.GfmPlugin import org.jetbrains.dokka.plugability.DokkaPlugin @@ -17,4 +18,11 @@ class GfmAllModulesPagePlugin : DokkaPlugin() { (dokkaBase.locationProviderFactory providing GfmMultimoduleLocationProvider::Factory override listOf(allModulesPagePlugin.multimoduleLocationProvider, gfmPlugin.locationProvider)) } + + val defaultTemplateProcessor by extending { + (allModulesPagePlugin.templateProcessingStrategy + providing ::GfmTemplateProcessingStrategy + override allModulesPagePlugin.directiveBasedHtmlTemplateProcessingStrategy + order { before(allModulesPagePlugin.fallbackProcessingStrategy) }) + } } \ No newline at end of file diff --git a/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmMultimoduleLocationProvider.kt b/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmMultimoduleLocationProvider.kt index d49be426..9f2ee140 100644 --- a/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmMultimoduleLocationProvider.kt +++ b/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmMultimoduleLocationProvider.kt @@ -1,38 +1,14 @@ package org.jetbrains.dokka.gfm.allModulesPage -import org.jetbrains.dokka.base.resolvers.local.DokkaBaseLocationProvider -import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider +import org.jetbrains.dokka.allModulesPage.MultimoduleLocationProvider import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory import org.jetbrains.dokka.gfm.location.MarkdownLocationProvider -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext -class GfmMultimoduleLocationProvider(private val root: RootPageNode, context: DokkaContext) : DokkaBaseLocationProvider(root, context, ".md") { +class GfmMultimoduleLocationProvider(root: RootPageNode, context: DokkaContext) : MultimoduleLocationProvider(root, context, ".md") { - private val defaultLocationProvider = MarkdownLocationProvider(root, context) - - val paths = context.configuration.modules.map { - it.name to it.relativePathToOutputDirectory - }.toMap() - - override fun resolve(dri: DRI, sourceSets: Set, context: PageNode?) = - dri.takeIf { it.packageName == MULTIMODULE_PACKAGE_PLACEHOLDER }?.classNames?.let { paths[it] }?.let { - "$it/${DokkaLocationProvider.identifierToFilename(dri.classNames.orEmpty())}/index.md" - } ?: defaultLocationProvider.resolve(dri, sourceSets, context) - - override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean) = - defaultLocationProvider.resolve(node, context, skipExtension) - - override fun pathToRoot(from: PageNode): String = defaultLocationProvider.pathToRoot(from) - - override fun ancestors(node: PageNode): List = listOf(root) - - companion object { - const val MULTIMODULE_PACKAGE_PLACEHOLDER = ".ext" - } + override val defaultLocationProvider = MarkdownLocationProvider(root, context) class Factory(private val context: DokkaContext): LocationProviderFactory { override fun getLocationProvider(pageNode: RootPageNode) = diff --git a/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmTemplateProcessingStrategy.kt b/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmTemplateProcessingStrategy.kt new file mode 100644 index 00000000..37226d1e --- /dev/null +++ b/plugins/gfm/gfm-all-module-page/src/main/kotlin/org/jetbrains/dokka/gfm/allModulesPage/GfmTemplateProcessingStrategy.kt @@ -0,0 +1,45 @@ +package org.jetbrains.dokka.gfm.allModulesPage + +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import org.jetbrains.dokka.allModulesPage.templates.ExternalModuleLinkResolver +import org.jetbrains.dokka.allModulesPage.templates.TemplateProcessingStrategy +import org.jetbrains.dokka.base.templating.parseJson +import org.jetbrains.dokka.gfm.GfmCommand +import org.jetbrains.dokka.gfm.GfmCommand.Companion.templateCommandRegex +import org.jetbrains.dokka.gfm.ResolveLinkGfmCommand +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.plugability.DokkaContext +import java.io.File + +class GfmTemplateProcessingStrategy(val context: DokkaContext) : TemplateProcessingStrategy { + + private val externalModuleLinkResolver = ExternalModuleLinkResolver(context) + + override suspend fun process(input: File, output: File): Boolean = coroutineScope { + if (input.extension == "md") { + launch(IO) { + val reader = input.bufferedReader() + val writer = output.bufferedWriter() + do { + val line = reader.readLine() + if (line != null) { + writer.write(line.replace(templateCommandRegex) { + when (val command = parseJson(it.groupValues.last())) { + is ResolveLinkGfmCommand -> resolveLink(output, command.dri) + } + }) + writer.newLine() + } + } while (line != null) + reader.close() + writer.close() + } + true + } else false + } + + private fun resolveLink(fileContext: File, dri: DRI): String = + externalModuleLinkResolver.resolve(dri, fileContext) ?: "" +} \ No newline at end of file diff --git a/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/gfmTemplating.kt b/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/gfmTemplating.kt new file mode 100644 index 00000000..1473ceee --- /dev/null +++ b/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/gfmTemplating.kt @@ -0,0 +1,19 @@ +package org.jetbrains.dokka.gfm + +import org.jetbrains.dokka.base.templating.toJsonString +import org.jetbrains.dokka.links.DRI +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS + +@JsonTypeInfo(use = CLASS) +sealed class GfmCommand { + companion object { + private const val delimiter = "\u1680" + fun templateCommand(command: GfmCommand): String = "$delimiter GfmCommand ${toJsonString(command)}$delimiter" + val templateCommandRegex: Regex = Regex("$delimiter GfmCommand ([^$delimiter ]*)$delimiter") + } +} + +class ResolveLinkGfmCommand(val dri: DRI): GfmCommand() + + diff --git a/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/renderer/CommonmarkRenderer.kt b/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/renderer/CommonmarkRenderer.kt index 0655ce20..25b24f0c 100644 --- a/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/renderer/CommonmarkRenderer.kt +++ b/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/renderer/CommonmarkRenderer.kt @@ -3,7 +3,9 @@ package org.jetbrains.dokka.gfm.renderer import org.jetbrains.dokka.DokkaException import org.jetbrains.dokka.base.renderers.DefaultRenderer import org.jetbrains.dokka.base.renderers.isImage +import org.jetbrains.dokka.gfm.GfmCommand.Companion.templateCommand import org.jetbrains.dokka.gfm.GfmPlugin +import org.jetbrains.dokka.gfm.ResolveLinkGfmCommand import org.jetbrains.dokka.model.DisplaySourceSet import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext @@ -80,6 +82,17 @@ open class CommonmarkRenderer( } } + override fun StringBuilder.buildDRILink( + node: ContentDRILink, + pageContext: ContentPage, + sourceSetRestriction: Set? + ) { + val address = locationProvider.resolve(node.address, node.sourceSets, pageContext) + buildLink(address ?: templateCommand(ResolveLinkGfmCommand(node.address))) { + buildText(node.children, pageContext, sourceSetRestriction) + } + } + override fun StringBuilder.buildNewLine() { append(" \n") } @@ -118,7 +131,7 @@ open class CommonmarkRenderer( } override fun StringBuilder.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage) { - if(node.isImage()){ + if (node.isImage()) { append("!") } append("[${node.altText}](${node.address})") -- cgit