diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2020-09-22 17:29:21 +0200 |
---|---|---|
committer | Błażej Kardyś <bkardys@virtuslab.com> | 2020-11-27 03:15:02 +0100 |
commit | 80b6d1824960205e1c1d57c0c51e913d3c2360db (patch) | |
tree | ea40c9c6dc0bd24999312cfa49a01c43cb746926 | |
parent | c8a83153a88fe6f5b50b6f459295421f90a21583 (diff) | |
download | dokka-80b6d1824960205e1c1d57c0c51e913d3c2360db.tar.gz dokka-80b6d1824960205e1c1d57c0c51e913d3c2360db.tar.bz2 dokka-80b6d1824960205e1c1d57c0c51e913d3c2360db.zip |
Add templating commands to the html format
18 files changed, 215 insertions, 21 deletions
diff --git a/core/src/main/kotlin/configuration.kt b/core/src/main/kotlin/configuration.kt index 15525152..fa3bd6c1 100644 --- a/core/src/main/kotlin/configuration.kt +++ b/core/src/main/kotlin/configuration.kt @@ -140,6 +140,7 @@ interface DokkaConfiguration : Serializable { interface DokkaModuleDescription : Serializable { val name: String val relativePathToOutputDirectory: File + val sourceOutputDirectory: File val includes: Set<File> } diff --git a/core/src/main/kotlin/defaultConfiguration.kt b/core/src/main/kotlin/defaultConfiguration.kt index 240fb110..c4dc1d00 100644 --- a/core/src/main/kotlin/defaultConfiguration.kt +++ b/core/src/main/kotlin/defaultConfiguration.kt @@ -52,6 +52,7 @@ data class DokkaModuleDescriptionImpl( override val name: String, override val relativePathToOutputDirectory: File, override val includes: Set<File>, + override val sourceOutputDirectory: File ) : DokkaConfiguration.DokkaModuleDescription data class SourceLinkDefinitionImpl( diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt index 6e1038d9..2998578b 100644 --- a/core/src/main/kotlin/links/DRI.kt +++ b/core/src/main/kotlin/links/DRI.kt @@ -1,5 +1,8 @@ package org.jetbrains.dokka.links +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS + /** * [DRI] stands for DokkaResourceIdentifier */ @@ -78,6 +81,7 @@ object StarProjection : TypeReference() { override fun toString() = "*" } +@JsonTypeInfo(use = CLASS) sealed class DriTarget { override fun toString(): String = this.javaClass.simpleName diff --git a/plugins/all-module-page/build.gradle.kts b/plugins/all-module-page/build.gradle.kts index dc5e0a6a..a0c5a5ed 100644 --- a/plugins/all-module-page/build.gradle.kts +++ b/plugins/all-module-page/build.gradle.kts @@ -6,4 +6,9 @@ registerDokkaArtifactPublication("dokkaAllModulesPage") { dependencies { implementation(project(":plugins:base")) + + val coroutines_version: String by project + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") + + implementation("org.jsoup:jsoup:1.12.1") }
\ No newline at end of file diff --git a/plugins/all-module-page/src/main/kotlin/AllModulesPageGeneration.kt b/plugins/all-module-page/src/main/kotlin/AllModulesPageGeneration.kt index 815cf160..f654514a 100644 --- a/plugins/all-module-page/src/main/kotlin/AllModulesPageGeneration.kt +++ b/plugins/all-module-page/src/main/kotlin/AllModulesPageGeneration.kt @@ -5,6 +5,8 @@ import org.jetbrains.dokka.Timer import org.jetbrains.dokka.generation.Generation import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle class AllModulesPageGeneration(private val context: DokkaContext) : Generation { override fun Timer.generate() { @@ -16,6 +18,9 @@ class AllModulesPageGeneration(private val context: DokkaContext) : Generation { report("Rendering") render(transformedPages) + + report("Processing submodules") + context.plugin<AllModulesPagePlugin>().querySingle { templateProcessor }.process() } override val generationName = "index page for project" diff --git a/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt b/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt index 163f13ab..584de32f 100644 --- a/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt +++ b/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt @@ -1,10 +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.base.DokkaBase import org.jetbrains.dokka.plugability.DokkaPlugin class AllModulesPagePlugin : DokkaPlugin() { + val templateProcessor by extensionPoint<TemplateProcessor>() + val allModulePageCreators by extending { (CoreExtensions.allModulePageCreator providing ::MultimodulePageCreator) @@ -22,4 +27,8 @@ class AllModulesPagePlugin : DokkaPlugin() { providing ::AllModulesPageGeneration override CoreExtensions.singleGeneration) } + + val defaultTemplateProcessor by extending { + templateProcessor providing { DefaultTemplateProcessor(it, DirectiveBasedTemplateProcessingStrategy(it)) } + } }
\ 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 new file mode 100644 index 00000000..7786ca18 --- /dev/null +++ b/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt @@ -0,0 +1,49 @@ +package org.jetbrains.dokka.allModulesPage.templates + +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +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.plugability.DokkaContext +import org.jsoup.Jsoup +import org.jsoup.nodes.Attributes +import org.jsoup.nodes.Element +import org.jsoup.parser.Tag +import java.io.File +import java.nio.file.Files + +class DirectiveBasedTemplateProcessingStrategy(context: DokkaContext) : TemplateProcessingStrategy { + override suspend fun process(input: File, output: File): Unit = coroutineScope { + if (input.extension == "html") { + launch { + val document = withContext(IO) { Jsoup.parse(input, "UTF-8") } + document.outputSettings().indentAmount(0).prettyPrint(false) + document.select("dokka-template-command").forEach { + val command = parseJson<Command>(it.attr("data")) + if (command is ResolveLinkCommand) { + resolveLink(it, command) + } + } + withContext(IO) { Files.writeString(output.toPath(), document.outerHtml()) } + } + } else { + launch(IO) { + Files.copy(input.toPath(), output.toPath()) + } + } + } + + private fun resolveLink(it: Element, command: ResolveLinkCommand) { + val attributes = Attributes().apply { + put("href", "") // TODO: resolve + } + val children = it.childNodes().toList() + val element = Element(Tag.valueOf("a"), "", attributes).apply { + children.forEach { ch -> appendChild(ch) } + } + it.replaceWith(element) + } +}
\ 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 new file mode 100644 index 00000000..4c247d94 --- /dev/null +++ b/plugins/all-module-page/src/main/kotlin/templates/TemplateProcessor.kt @@ -0,0 +1,41 @@ +package org.jetbrains.dokka.allModulesPage.templates + +import kotlinx.coroutines.* +import org.jetbrains.dokka.plugability.DokkaContext +import java.io.File +import java.nio.file.Files +import java.nio.file.Path + +interface TemplateProcessor { + fun process() +} + +interface TemplateProcessingStrategy { + suspend fun process(input: File, output: File) +} + +class DefaultTemplateProcessor( + private val context: DokkaContext, + private val strategy: TemplateProcessingStrategy +): TemplateProcessor { + override fun process() = runBlocking(Dispatchers.Default) { + context.configuration.modules.forEach { + launch { + it.sourceOutputDirectory.visit(context.configuration.outputDir.resolve(it.relativePathToOutputDirectory)) + } + } + } + + private suspend fun File.visit(target: File): Unit = coroutineScope { + val source = this@visit + if (source.isDirectory) { + target.mkdir() + source.list()?.forEach { + launch { source.resolve(it).visit(target.resolve(it)) } + } + } else { + strategy.process(source, target) + } + } +} + diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index de7e1e5e..afda6f24 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -14,6 +14,7 @@ import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.templating.Command import org.jetbrains.dokka.base.transformers.documentables.* import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter @@ -37,6 +38,7 @@ class DokkaBase : DokkaPlugin() { val htmlPreprocessors by extensionPoint<PageTransformer>() val kotlinAnalysis by extensionPoint<KotlinAnalysis>() val tabSortingStrategy by extensionPoint<TabSortingStrategy>() + val templatingCommand by extensionPoint<Class<out Command>>() val descriptorToDocumentableTranslator by extending { CoreExtensions.sourceToDocumentableTranslator providing ::DefaultDescriptorToDocumentableTranslator diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index 9fbecad3..661e1e58 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.ResolveLinkCommand import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DisplaySourceSet import org.jetbrains.dokka.model.properties.PropertyContainer @@ -657,8 +658,7 @@ open class HtmlRenderer( buildLink(address) { buildText(node.children, pageContext, sourceSetRestriction) } - } ?: span { - attributes["data-unresolved-link"] = node.address.toString().htmlEscape() + } ?: templateCommand(ResolveLinkCommand(node.address)) { buildText(node.children, pageContext, sourceSetRestriction) } diff --git a/plugins/base/src/main/kotlin/renderers/html/Tags.kt b/plugins/base/src/main/kotlin/renderers/html/Tags.kt index a3951eff..67eed686 100644 --- a/plugins/base/src/main/kotlin/renderers/html/Tags.kt +++ b/plugins/base/src/main/kotlin/renderers/html/Tags.kt @@ -1,12 +1,21 @@ package org.jetbrains.dokka.base.renderers.html import kotlinx.html.* +import org.jetbrains.dokka.base.templating.Command +import org.jetbrains.dokka.base.templating.toJsonString @HtmlTagMarker -fun FlowOrPhrasingContent.wbr(classes : String? = null, block : WBR.() -> Unit = {}) : Unit = WBR(attributesMapOf("class", classes), consumer).visit(block) +fun FlowOrPhrasingContent.wbr(classes: String? = null, block: WBR.() -> Unit = {}): Unit = + WBR(attributesMapOf("class", classes), consumer).visit(block) @Suppress("unused") -open class WBR(initialAttributes : Map<String, String>, override val consumer : TagConsumer<*>) : HTMLTag("wbr", consumer, initialAttributes, null, true, false), - HtmlBlockInlineTag { +open class WBR(initialAttributes: Map<String, String>, consumer: TagConsumer<*>) : + HTMLTag("wbr", consumer, initialAttributes, namespace = null, inlineTag = true, emptyTag = false), + HtmlBlockInlineTag -}
\ No newline at end of file +fun FlowOrPhrasingContent.templateCommand(data: Command, block: TemplateCommand.() -> Unit = {}):Unit = + TemplateCommand(attributesMapOf("data", toJsonString(data)), consumer).visit(block) + +class TemplateCommand(initialAttributes: Map<String, String>, consumer: TagConsumer<*>) : + HTMLTag("dokka-template-command", consumer, initialAttributes, namespace = null, inlineTag = true, emptyTag = false), + CommonAttributeGroupFacadeFlowInteractivePhrasingContent diff --git a/plugins/base/src/main/kotlin/templating/Command.kt b/plugins/base/src/main/kotlin/templating/Command.kt new file mode 100644 index 00000000..e352ba32 --- /dev/null +++ b/plugins/base/src/main/kotlin/templating/Command.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.base.templating + +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonSubTypes.Type +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS + +@JsonTypeInfo(use= CLASS) +interface Command diff --git a/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt b/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt new file mode 100644 index 00000000..83b7e0d8 --- /dev/null +++ b/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt @@ -0,0 +1,6 @@ +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/base/src/main/kotlin/templating/jsonMapperForPlugins.kt b/plugins/base/src/main/kotlin/templating/jsonMapperForPlugins.kt new file mode 100644 index 00000000..9b656309 --- /dev/null +++ b/plugins/base/src/main/kotlin/templating/jsonMapperForPlugins.kt @@ -0,0 +1,56 @@ +package org.jetbrains.dokka.base.templating + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +import com.fasterxml.jackson.databind.type.TypeFactory +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.jetbrains.dokka.base.DokkaBase +import java.io.File + +// THIS IS COPIED FROM BASE SINCE IT NEEDS TO BE INSTANTIATED ON THE SAME CLASS LOADER AS PLUGINS + +private val objectMapper = run { + val module = SimpleModule().apply { + addSerializer(FileSerializer) + } + jacksonObjectMapper() + .apply { + typeFactory = PluginTypeFactory() + } + .registerModule(module) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) +} + +@PublishedApi +internal class TypeReference<T> private constructor( + internal val jackson: com.fasterxml.jackson.core.type.TypeReference<T> +) { + companion object { + internal inline operator fun <reified T> invoke(): TypeReference<T> = TypeReference(jacksonTypeRef()) + } +} + +fun toJsonString(value: Any): String = objectMapper.writeValueAsString(value) + +inline fun <reified T : Any> parseJson(json: String): T = parseJson(json, TypeReference()) + + +@PublishedApi +internal fun <T : Any> parseJson(json: String, typeReference: TypeReference<T>): T = + objectMapper.readValue(json, typeReference.jackson) + + +private object FileSerializer : StdScalarSerializer<File>(File::class.java) { + override fun serialize(value: File, g: JsonGenerator, provider: SerializerProvider) { + g.writeString(value.path) + } +} + +private class PluginTypeFactory: TypeFactory(null) { + override fun findClass(className: String): Class<out Any>? = + Class.forName(className, true, DokkaBase::class.java.classLoader) ?: super.findClass(className) +}
\ No newline at end of file diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTask.kt index c782197e..b431cf98 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTask.kt @@ -8,9 +8,7 @@ import org.jetbrains.dokka.DokkaBootstrap import org.jetbrains.dokka.DokkaBootstrapImpl import kotlin.reflect.KClass -abstract class AbstractDokkaParentTask( - bootstrapClass: KClass<out DokkaBootstrap> = DokkaBootstrapImpl::class -) : AbstractDokkaTask(bootstrapClass) { +abstract class AbstractDokkaParentTask : AbstractDokkaTask() { @get:Internal internal var childDokkaTaskPaths: Set<String> = emptySet() diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt index 77b4b2e1..d2506f7a 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt @@ -22,9 +22,7 @@ import java.util.function.BiConsumer import kotlin.reflect.KClass import kotlin.reflect.full.createInstance -abstract class AbstractDokkaTask( - private val bootstrapClass: KClass<out DokkaBootstrap> = DokkaBootstrap::class -) : DefaultTask() { +abstract class AbstractDokkaTask : DefaultTask() { @Input val moduleName: Property<String> = project.objects.safeProperty<String>() @@ -81,7 +79,7 @@ abstract class AbstractDokkaTask( @TaskAction internal open fun generateDocumentation() { - DokkaBootstrap(runtime, bootstrapClass).apply { + DokkaBootstrap(runtime, DokkaBootstrapImpl::class).apply { configure(buildDokkaConfiguration().toJsonString(), createProxyLogger()) generate() } diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultiModuleTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultiModuleTask.kt index 74f17215..4139a0cd 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultiModuleTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultiModuleTask.kt @@ -2,12 +2,12 @@ package org.jetbrains.dokka.gradle import org.gradle.api.internal.tasks.TaskDependencyInternal import org.gradle.api.provider.Property -import org.gradle.api.tasks.* -import org.jetbrains.dokka.DokkaBootstrapImpl +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputDirectories import org.jetbrains.dokka.DokkaConfigurationImpl import org.jetbrains.dokka.DokkaModuleDescriptionImpl -import org.jetbrains.dokka.DokkaMultimoduleBootstrapImpl -import org.jetbrains.dokka.PluginConfigurationImpl import java.io.File @Suppress("unused") // Shall provide source compatibility if possible @@ -16,7 +16,7 @@ typealias DokkaMultimoduleTask = DokkaMultiModuleTask private typealias TaskPath = String -abstract class DokkaMultiModuleTask : AbstractDokkaParentTask(DokkaBootstrapImpl::class) { +abstract class DokkaMultiModuleTask : AbstractDokkaParentTask() { @Internal val fileLayout: Property<DokkaMultiModuleFileLayout> = project.objects.safeProperty<DokkaMultiModuleFileLayout>() @@ -43,7 +43,7 @@ abstract class DokkaMultiModuleTask : AbstractDokkaParentTask(DokkaBootstrapImpl override fun generateDocumentation() { checkChildDokkaTasksIsNotEmpty() - copyChildOutputDirectories() +// copyChildOutputDirectories() super.generateDocumentation() } @@ -59,7 +59,8 @@ abstract class DokkaMultiModuleTask : AbstractDokkaParentTask(DokkaBootstrapImpl DokkaModuleDescriptionImpl( name = dokkaTask.moduleName.getSafe(), relativePathToOutputDirectory = targetChildOutputDirectory(dokkaTask).relativeTo(outputDirectory.getSafe()), - includes = childDokkaTaskIncludes[dokkaTask.path].orEmpty() + includes = childDokkaTaskIncludes[dokkaTask.path].orEmpty(), + sourceOutputDirectory = dokkaTask.outputDirectory.getSafe() ) } ) diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt index 9944bacb..caee0d92 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt @@ -6,7 +6,7 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Nested import org.jetbrains.dokka.* -abstract class DokkaTask : AbstractDokkaTask(DokkaBootstrapImpl::class) { +abstract class DokkaTask : AbstractDokkaTask() { @get:Internal val dokkaSourceSets: NamedDomainObjectContainer<GradleDokkaSourceSetBuilder> = |