diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2020-10-07 19:36:49 +0200 |
---|---|---|
committer | Błażej Kardyś <bkardys@virtuslab.com> | 2020-11-27 03:15:02 +0100 |
commit | d41965ca3cb180bc82bd5dd6fa747d2b83381b31 (patch) | |
tree | 5660f17f21fdf5871c8239f4d96c4521545292d1 | |
parent | dc179bf9a649d925e7e64dbcaf52a2187416a1d5 (diff) | |
download | dokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.tar.gz dokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.tar.bz2 dokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.zip |
Implement immediate temaplates resolution
21 files changed, 261 insertions, 92 deletions
diff --git a/core/src/main/kotlin/configuration.kt b/core/src/main/kotlin/configuration.kt index fa3bd6c1..a3989ebc 100644 --- a/core/src/main/kotlin/configuration.kt +++ b/core/src/main/kotlin/configuration.kt @@ -16,6 +16,7 @@ object DokkaDefaults { val cacheRoot: File? = null const val offlineMode: Boolean = false const val failOnWarning: Boolean = false + const val delayTemplateSubstitution: Boolean = false const val includeNonPublic: Boolean = false const val reportUndocumented: Boolean = false @@ -96,6 +97,7 @@ interface DokkaConfiguration : Serializable { val modules: List<DokkaModuleDescription> val pluginsClasspath: List<File> val pluginsConfiguration: List<PluginConfiguration> + val delayTemplateSubstitution: Boolean enum class SerializationFormat : Serializable { JSON, XML diff --git a/core/src/main/kotlin/defaultConfiguration.kt b/core/src/main/kotlin/defaultConfiguration.kt index c4dc1d00..869a99ed 100644 --- a/core/src/main/kotlin/defaultConfiguration.kt +++ b/core/src/main/kotlin/defaultConfiguration.kt @@ -15,6 +15,7 @@ data class DokkaConfigurationImpl( override val pluginsConfiguration: List<PluginConfigurationImpl> = DokkaDefaults.pluginsConfiguration, override val modules: List<DokkaModuleDescriptionImpl> = emptyList(), override val failOnWarning: Boolean = DokkaDefaults.failOnWarning, + override val delayTemplateSubstitution: Boolean = false, ) : DokkaConfiguration data class PluginConfigurationImpl( diff --git a/core/src/main/kotlin/generation/SingleModuleGeneration.kt b/core/src/main/kotlin/generation/SingleModuleGeneration.kt index 59514632..2eedc561 100644 --- a/core/src/main/kotlin/generation/SingleModuleGeneration.kt +++ b/core/src/main/kotlin/generation/SingleModuleGeneration.kt @@ -1,8 +1,12 @@ + package org.jetbrains.dokka.generation import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import org.jetbrains.dokka.* +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaException +import org.jetbrains.dokka.Timer import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext @@ -10,85 +14,59 @@ import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTransla import org.jetbrains.dokka.utilities.parallelMap import org.jetbrains.dokka.utilities.report -class SingleModuleGeneration(private val context: DokkaContext): Generation { +class SingleModuleGeneration(private val context: DokkaContext) : Generation { override fun Timer.generate() { report("Validity check") validityCheck(context) report("Creating documentation models") - val modulesFromPlatforms = createDocumentationModels(context) + val modulesFromPlatforms = createDocumentationModels() report("Transforming documentation model before merging") - val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms, context) + val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms) report("Merging documentation models") - val documentationModel = mergeDocumentationModels(transformedDocumentationBeforeMerge, context) + val documentationModel = mergeDocumentationModels(transformedDocumentationBeforeMerge) report("Transforming documentation model after merging") - val transformedDocumentation = transformDocumentationModelAfterMerge(documentationModel, context) + val transformedDocumentation = transformDocumentationModelAfterMerge(documentationModel) report("Creating pages") - val pages = createPages(transformedDocumentation, context) + val pages = createPages(transformedDocumentation) report("Transforming pages") - val transformedPages = transformPages(pages, context) + val transformedPages = transformPages(pages) report("Rendering") - render(transformedPages, context) + render(transformedPages) - reportAfterRendering(context) + reportAfterRendering() } - override val generationName: String - get() = TODO("Not yet implemented") + override val generationName = " documentation for ${context.configuration.moduleName}" - fun createDocumentationModels( - context: DokkaContext - ) = runBlocking(Dispatchers.Default) { + fun createDocumentationModels() = runBlocking(Dispatchers.Default) { context.configuration.sourceSets.parallelMap { sourceSet -> translateSources(sourceSet, context) }.flatten() .also { modules -> if (modules.isEmpty()) exitGenerationGracefully("Nothing to document") } } - fun transformDocumentationModelBeforeMerge( - modulesFromPlatforms: List<DModule>, - context: DokkaContext - ) = context[CoreExtensions.preMergeDocumentableTransformer].fold(modulesFromPlatforms) { acc, t -> t(acc) } - - fun mergeDocumentationModels( - modulesFromPlatforms: List<DModule>, - context: DokkaContext - ) = context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms) - - fun transformDocumentationModelAfterMerge( - documentationModel: DModule, - context: DokkaContext - ) = context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) } - - fun createPages( - transformedDocumentation: DModule, - context: DokkaContext - ) = context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation) - - fun createAllModulePage( - context: DokkaContext - ) = context.single(CoreExtensions.allModulePageCreator).invoke() - - fun transformPages( - pages: RootPageNode, - context: DokkaContext - ) = context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) } - - fun transformAllModulesPage( - pages: RootPageNode, - context: DokkaContext - ) = context[CoreExtensions.allModulePageTransformer].fold(pages) { acc, t -> t(acc) } - - fun render( - transformedPages: RootPageNode, - context: DokkaContext - ) { - val renderer = context.single(CoreExtensions.renderer) - renderer.render(transformedPages) + fun transformDocumentationModelBeforeMerge(modulesFromPlatforms: List<DModule>) = + context[CoreExtensions.preMergeDocumentableTransformer].fold(modulesFromPlatforms) { acc, t -> t(acc) } + + fun mergeDocumentationModels(modulesFromPlatforms: List<DModule>) = + context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms) + + fun transformDocumentationModelAfterMerge(documentationModel: DModule) = + context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) } + + fun createPages(transformedDocumentation: DModule) = + context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation) + + fun transformPages(pages: RootPageNode) = + context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) } + + fun render(transformedPages: RootPageNode) { + context.single(CoreExtensions.renderer).render(transformedPages) } fun validityCheck(context: DokkaContext) { @@ -100,7 +78,7 @@ class SingleModuleGeneration(private val context: DokkaContext): Generation { ) } - fun reportAfterRendering(context: DokkaContext) { + fun reportAfterRendering() { context.unusedPoints.takeIf { it.isNotEmpty() }?.also { context.logger.info("Unused extension points found: ${it.joinToString(", ")}") } diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt index a91887b4..94ec8258 100644 --- a/core/src/main/kotlin/pages/PageNodes.kt +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -30,7 +30,7 @@ interface ContentPage : PageNode { ): ContentPage } -abstract class RootPageNode : PageNode { +abstract class RootPageNode(val forceTopLevelName: Boolean = false) : PageNode { val parentMap: Map<PageNode, PageNode> by lazy { IdentityHashMap<PageNode, PageNode>().apply { fun process(parent: PageNode) { @@ -157,18 +157,18 @@ class MemberPageNode( class MultimoduleRootPageNode( - override val name: String, override val dri: Set<DRI>, override val content: ContentNode, override val embeddedResources: List<String> = emptyList() -) : RootPageNode(), MultimoduleRootPage { +) : RootPageNode(forceTopLevelName = true), MultimoduleRootPage { + override val name = "" override val children: List<PageNode> = emptyList() override val documentable: Documentable? = null override fun modified(name: String, children: List<PageNode>): RootPageNode = - MultimoduleRootPageNode(name, dri, content, embeddedResources) + MultimoduleRootPageNode(dri, content, embeddedResources) override fun modified( name: String, @@ -178,7 +178,7 @@ class MultimoduleRootPageNode( children: List<PageNode> ) = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this - else MultimoduleRootPageNode(name, dri, content, embeddedResources) + else MultimoduleRootPageNode(dri, content, embeddedResources) } inline fun <reified T : PageNode> PageNode.children() = children.filterIsInstance<T>() diff --git a/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt b/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt index 3ad3e0ce..c3806b00 100644 --- a/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt +++ b/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt @@ -64,7 +64,6 @@ class MultimodulePageCreator( } } return MultimoduleRootPageNode( - "Modules", setOf(DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = "allModules")), contentNode ) 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 d50cbc32..e609397b 100644 --- a/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt +++ b/plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt @@ -156,8 +156,8 @@ class DirectiveBasedTemplateProcessingStrategy(private val context: DokkaContext return } - val modulePath = context.configuration.outputDir.absolutePath.split("/") - val contextPath = fileContext.absolutePath.split("/") + 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() diff --git a/plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt b/plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt index 992ee7ed..5056b724 100644 --- a/plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt +++ b/plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt @@ -1,16 +1,14 @@ 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 +import java.io.File 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() + "/" + context.output.toPath().parent.relativize(dokkaContext.configuration.outputDir.toPath()).toString().split(File.separator).joinToString(separator = "/", postfix = "/") { it } } else null }
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 8289447c..fce5c399 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -6,6 +6,8 @@ import org.jetbrains.dokka.CoreExtensions import org.jetbrains.dokka.analysis.KotlinAnalysis import org.jetbrains.dokka.base.renderers.* import org.jetbrains.dokka.base.renderers.html.* +import org.jetbrains.dokka.base.renderers.html.command.consumers.PathToRootConsumer +import org.jetbrains.dokka.base.renderers.html.command.consumers.ResolveLinkConsumer import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProviderFactory import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProviderFactory import org.jetbrains.dokka.base.resolvers.external.javadoc.JavadocExternalLocationProviderFactory @@ -15,6 +17,7 @@ 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.templating.ImmediateHtmlCommandConsumer 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 @@ -38,7 +41,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 immediateHtmlCommandConsumer by extensionPoint<ImmediateHtmlCommandConsumer>() val descriptorToDocumentableTranslator by extending { CoreExtensions.sourceToDocumentableTranslator providing ::DefaultDescriptorToDocumentableTranslator @@ -206,6 +209,13 @@ class DokkaBase : DokkaPlugin() { htmlPreprocessors providing ::SourcesetDependencyAppender order { after(rootCreator) } } + val resolveLinkConsumer by extending { + immediateHtmlCommandConsumer with ResolveLinkConsumer + } + + val pathToRootConsumer by extending { + immediateHtmlCommandConsumer with PathToRootConsumer + } val baseSearchbarDataInstaller by extending { htmlPreprocessors providing ::SearchbarDataInstaller order { after(sourceLinksTransformer) } } diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index d344dafe..ba5329bf 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -9,6 +9,7 @@ import org.jetbrains.dokka.DokkaSourceSetID import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.renderers.DefaultRenderer import org.jetbrains.dokka.base.renderers.TabSortingStrategy +import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer import org.jetbrains.dokka.base.renderers.isImage import org.jetbrains.dokka.base.renderers.pageId import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint @@ -45,6 +46,10 @@ open class HtmlRenderer( private val tabSortingStrategy = context.plugin<DokkaBase>().querySingle { tabSortingStrategy } + private fun <R> TagConsumer<R>.prepareForTemplates() = + if (context.configuration.delayTemplateSubstitution || this is ImmediateResolutionTagConsumer) this + else ImmediateResolutionTagConsumer(this, context) + private fun <T : ContentNode> sortTabs(strategy: TabSortingStrategy, tabs: Collection<T>): List<T> { val sorted = strategy.sort(tabs) if (sorted.size != tabs.size) @@ -208,7 +213,7 @@ open class HtmlRenderer( ): List<Pair<DisplaySourceSet, String>> { var counter = 0 return nodes.toList().map { (sourceSet, elements) -> - sourceSet to createHTML(prettyPrint = false).div { + sourceSet to createHTML(prettyPrint = false).prepareForTemplates().div { elements.forEach { buildContentNode(it, pageContext, sourceSet.toSet()) } @@ -221,7 +226,7 @@ open class HtmlRenderer( sourceSet.sourceSetIDs.all.flatMap { sourceSetDependencyMap[it].orEmpty() } .any { sourceSetId -> sourceSetId in sourceSets.sourceSetIDs } }.map { - it to createHTML(prettyPrint = false).div(classes = "content sourceset-depenent-content") { + it to createHTML(prettyPrint = false).prepareForTemplates().div(classes = "content sourceset-depenent-content") { if (counter++ == 0) attributes["data-active"] = "" attributes["data-togglable"] = it.sourceSetIDs.merged.toString() unsafe { @@ -236,13 +241,13 @@ open class HtmlRenderer( val distinct = node.groupDivergentInstances(pageContext, { instance, contentPage, sourceSet -> - createHTML(prettyPrint = false).div { + createHTML(prettyPrint = false).prepareForTemplates().div { instance.before?.let { before -> buildContentNode(before, pageContext, sourceSet) } }.stripDiv() }, { instance, contentPage, sourceSet -> - createHTML(prettyPrint = false).div { + createHTML(prettyPrint = false).prepareForTemplates().div { instance.after?.let { after -> buildContentNode(after, pageContext, sourceSet) } @@ -253,7 +258,7 @@ open class HtmlRenderer( val groupedDivergent = it.value.groupBy { it.second } consumer.onTagContentUnsafe { - +createHTML().div("divergent-group") { + +createHTML().prepareForTemplates().div("divergent-group") { attributes["data-filterable-current"] = groupedDivergent.keys.joinToString(" ") { it.sourceSetIDs.merged.toString() } @@ -268,11 +273,11 @@ open class HtmlRenderer( val content = contentsForSourceSetDependent(divergentForPlatformDependent, pageContext) consumer.onTagContentUnsafe { - +createHTML().div("with-platform-tags") { + +createHTML().prepareForTemplates().div("with-platform-tags") { consumer.onTagContentUnsafe { +it.key.first } consumer.onTagContentUnsafe { - +createHTML().span("pull-right") { + +createHTML().prepareForTemplates().span("pull-right") { if ((distinct.size > 1 && groupedDivergent.size == 1) || groupedDivergent.size == 1 || content.size == 1) { if (node.sourceSets.size != 1) { createPlatformTags(node, setOf(content.first().first)) @@ -716,7 +721,7 @@ open class HtmlRenderer( private fun resolveLink(link: String, page: PageNode): String = if (URI(link).isAbsolute) link else page.root(link) open fun buildHtml(page: PageNode, resources: List<String>, content: FlowContent.() -> Unit) = - createHTML().html { + createHTML().prepareForTemplates().html { head { meta(name = "viewport", content = "width=device-width, initial-scale=1", charset = "UTF-8") title(page.name) @@ -737,7 +742,7 @@ open class HtmlRenderer( else -> unsafe { +it } } } - templateCommand(PathToRootSubstitutionCommand("###")) { + templateCommand(PathToRootSubstitutionCommand("###", default = locationProvider.pathToRoot(page))) { script { unsafe { +"""var pathToRoot = "###";""" } } } } diff --git a/plugins/base/src/main/kotlin/renderers/html/Tags.kt b/plugins/base/src/main/kotlin/renderers/html/Tags.kt index 59d711e9..0da16a1a 100644 --- a/plugins/base/src/main/kotlin/renderers/html/Tags.kt +++ b/plugins/base/src/main/kotlin/renderers/html/Tags.kt @@ -1,9 +1,13 @@ package org.jetbrains.dokka.base.renderers.html import kotlinx.html.* +import kotlinx.html.stream.createHTML +import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer import org.jetbrains.dokka.base.templating.Command import org.jetbrains.dokka.base.templating.toJsonString +typealias TemplateBlock = TemplateCommand.() -> Unit + @HtmlTagMarker fun FlowOrPhrasingContent.wbr(classes: String? = null, block: WBR.() -> Unit = {}): Unit = WBR(attributesMapOf("class", classes), consumer).visit(block) @@ -13,11 +17,16 @@ open class WBR(initialAttributes: Map<String, String>, consumer: TagConsumer<*>) HTMLTag("wbr", consumer, initialAttributes, namespace = null, inlineTag = true, emptyTag = false), HtmlBlockInlineTag -fun FlowOrPhrasingOrMetaDataContent.templateCommand(data: Command, block: TemplateCommand.() -> Unit = {}): Unit = - TemplateCommand(attributesMapOf("data", toJsonString(data)), consumer).visit(block) +fun FlowOrPhrasingOrMetaDataContent.templateCommand(data: Command, block: TemplateBlock = {}): Unit = + (consumer as? ImmediateResolutionTagConsumer)?.processCommand(data, block) + ?: TemplateCommand(attributesMapOf("data", toJsonString(data)), consumer).visit(block) + +fun <T> TagConsumer<T>.templateCommand(data: Command, block: TemplateBlock = {}): T = + (this as? ImmediateResolutionTagConsumer)?.processCommandAndFinalize(data, block) + ?: TemplateCommand(attributesMapOf("data", toJsonString(data)), this).visitAndFinalize(this, block) -fun <T> TagConsumer<T>.templateCommand(data: Command, block: TemplateCommand.() -> Unit = {}): T = - TemplateCommand(attributesMapOf("data", toJsonString(data)), this).visitAndFinalize(this, block) +fun templateCommandFor(data: Command, consumer: TagConsumer<*>) = + TemplateCommand(attributesMapOf("data", toJsonString(data)), consumer) class TemplateCommand(initialAttributes: Map<String, String>, consumer: TagConsumer<*>) : HTMLTag( @@ -29,3 +38,8 @@ class TemplateCommand(initialAttributes: Map<String, String>, consumer: TagConsu emptyTag = false ), CommonAttributeGroupFacadeFlowInteractivePhrasingContent + +// This hack is outrageous. I hate it but I cannot find any other way around `kotlinx.html` type system. +fun TemplateBlock.buildAsInnerHtml(): String = createHTML(prettyPrint = false).run { + TemplateCommand(emptyMap, this).visitAndFinalize(this, this@buildAsInnerHtml).substringAfter(">").substringBeforeLast("<") +} diff --git a/plugins/base/src/main/kotlin/renderers/html/command/consumers/ImmediateResolutionTagConsumer.kt b/plugins/base/src/main/kotlin/renderers/html/command/consumers/ImmediateResolutionTagConsumer.kt new file mode 100644 index 00000000..68f6dc81 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/html/command/consumers/ImmediateResolutionTagConsumer.kt @@ -0,0 +1,31 @@ +package org.jetbrains.dokka.base.renderers.html.command.consumers + +import kotlinx.html.TagConsumer +import kotlinx.html.visit +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.html.TemplateBlock +import org.jetbrains.dokka.base.renderers.html.templateCommand +import org.jetbrains.dokka.base.renderers.html.templateCommandFor +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 + +class ImmediateResolutionTagConsumer<out R>( + private val downstream: TagConsumer<R>, + private val context: DokkaContext +): TagConsumer<R> by downstream { + fun processCommand(command: Command, block: TemplateBlock) { + context.plugin<DokkaBase>().query { immediateHtmlCommandConsumer } + .find { it.canProcess(command) } + ?.processCommand(command, block, this) + ?: run { templateCommandFor(command, downstream).visit(block) } + } + + fun processCommandAndFinalize(command: Command, block: TemplateBlock): R = + context.plugin<DokkaBase>().query { immediateHtmlCommandConsumer } + .find { it.canProcess(command) } + ?.processCommandAndFinalize(command, block, this) + ?: downstream.templateCommand(command, block) +} + diff --git a/plugins/base/src/main/kotlin/renderers/html/command/consumers/PathToRootConsumer.kt b/plugins/base/src/main/kotlin/renderers/html/command/consumers/PathToRootConsumer.kt new file mode 100644 index 00000000..b4649af6 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/html/command/consumers/PathToRootConsumer.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.base.renderers.html.command.consumers + +import org.jetbrains.dokka.base.renderers.html.TemplateBlock +import org.jetbrains.dokka.base.renderers.html.buildAsInnerHtml +import org.jetbrains.dokka.base.templating.Command +import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer +import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand + +object PathToRootConsumer: ImmediateHtmlCommandConsumer { + override fun canProcess(command: Command) = command is PathToRootSubstitutionCommand + + override fun <R> processCommand(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>) { + command as PathToRootSubstitutionCommand + tagConsumer.onTagContentUnsafe { +block.buildAsInnerHtml().replace(command.pattern, command.default) } + } + + override fun <R> processCommandAndFinalize(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>): R { + processCommand(command, block, tagConsumer) + return tagConsumer.finalize() + } + +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/html/command/consumers/ResolveLinkConsumer.kt b/plugins/base/src/main/kotlin/renderers/html/command/consumers/ResolveLinkConsumer.kt new file mode 100644 index 00000000..1c42c3d5 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/html/command/consumers/ResolveLinkConsumer.kt @@ -0,0 +1,30 @@ +package org.jetbrains.dokka.base.renderers.html.command.consumers + +import kotlinx.html.SPAN +import kotlinx.html.span +import kotlinx.html.unsafe +import kotlinx.html.visit +import org.jetbrains.dokka.base.renderers.html.TemplateBlock +import org.jetbrains.dokka.base.renderers.html.buildAsInnerHtml +import org.jetbrains.dokka.base.templating.Command +import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer +import org.jetbrains.dokka.base.templating.ResolveLinkCommand +import org.jetbrains.dokka.utilities.htmlEscape + +object ResolveLinkConsumer: ImmediateHtmlCommandConsumer { + override fun canProcess(command: Command) = command is ResolveLinkCommand + + override fun <R> processCommand(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>) { + command as ResolveLinkCommand + SPAN(mapOf("data-unresolved-link" to command.dri.toString().htmlEscape()), tagConsumer).visit { + unsafe { block.buildAsInnerHtml() } + } + } + + override fun <R> processCommandAndFinalize(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>): R { + command as ResolveLinkCommand + return tagConsumer.span { + attributes["data-unresolved-link"] = command.dri.toString().htmlEscape() + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt index 030197a5..437a1c58 100644 --- a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt @@ -20,9 +20,15 @@ open class DokkaLocationProvider( protected open val pathsIndex: Map<PageNode, List<String>> = IdentityHashMap<PageNode, List<String>>().apply { fun registerPath(page: PageNode, prefix: List<String>) { - val newPrefix = prefix + page.pathName - put(page, newPrefix) - page.children.forEach { registerPath(it, newPrefix) } + if (page is RootPageNode && page.forceTopLevelName) { + put(page, prefix + PAGE_WITH_CHILDREN_SUFFIX) + page.children.forEach { registerPath(it, prefix) } + } else { + val newPrefix = prefix + page.pathName + put(page, newPrefix) + page.children.forEach { registerPath(it, newPrefix) } + } + } put(pageGraphRoot, emptyList()) pageGraphRoot.children.forEach { registerPath(it, emptyList()) } diff --git a/plugins/base/src/main/kotlin/templating/Command.kt b/plugins/base/src/main/kotlin/templating/Command.kt index 0b998dee..5a81144c 100644 --- a/plugins/base/src/main/kotlin/templating/Command.kt +++ b/plugins/base/src/main/kotlin/templating/Command.kt @@ -1,13 +1,11 @@ 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) +@JsonTypeInfo(use = CLASS) interface Command -abstract class SubstitutionCommand: Command { +abstract class SubstitutionCommand : Command { abstract val pattern: String } diff --git a/plugins/base/src/main/kotlin/templating/ImmediateHtmlCommandConsumer.kt b/plugins/base/src/main/kotlin/templating/ImmediateHtmlCommandConsumer.kt new file mode 100644 index 00000000..a2a3b31e --- /dev/null +++ b/plugins/base/src/main/kotlin/templating/ImmediateHtmlCommandConsumer.kt @@ -0,0 +1,14 @@ +package org.jetbrains.dokka.base.templating + +import org.jetbrains.dokka.base.renderers.html.TemplateBlock +import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer +import org.jetbrains.dokka.plugability.DokkaContext + +interface ImmediateHtmlCommandConsumer { + fun canProcess(command:Command): Boolean + + fun <R> processCommand(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>) + + fun <R> processCommandAndFinalize(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>): R +} + diff --git a/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt b/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt index 03f091c3..f307db86 100644 --- a/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt +++ b/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt @@ -1,3 +1,3 @@ package org.jetbrains.dokka.base.templating -data class PathToRootSubstitutionCommand(override val pattern: String): SubstitutionCommand()
\ No newline at end of file +data class PathToRootSubstitutionCommand(override val pattern: String, val default: String): SubstitutionCommand()
\ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css index 9e843fe1..5390b6d7 100644 --- a/plugins/base/src/main/resources/dokka/styles/style.css +++ b/plugins/base/src/main/resources/dokka/styles/style.css @@ -418,7 +418,7 @@ code.paragraph { box-sizing: border-box; } -.sideMenuPart .hidden > .overview .navButtonContent::before { +.sideMenuPart.hidden > .overview .navButtonContent::before { transform: rotate(0deg); } diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt index 681ebfa6..9cfee73b 100644 --- a/runners/cli/src/main/kotlin/cli/main.kt +++ b/runners/cli/src/main/kotlin/cli/main.kt @@ -64,6 +64,11 @@ class GlobalArguments(args: Array<String>) : DokkaConfiguration { "Throw an exception if the generation exited with warnings" ).default(DokkaDefaults.failOnWarning) + override val delayTemplateSubstitution by parser.option( + ArgType.Boolean, + description = "Delay substitution of some elements (usefull for incremental builds of multimoule projects)" + ).default(DokkaDefaults.delayTemplateSubstitution) + val globalPackageOptions by parser.option( ArgType.String, description = "List of package source sets in format \"prefix,-deprecated,-privateApi,+warnUndocumented,+suppress;...\" " diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTaskPartial.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTaskPartial.kt new file mode 100644 index 00000000..0f509d3b --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTaskPartial.kt @@ -0,0 +1,50 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.internal.plugins.DslObject +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.build + +abstract class DokkaTaskPartial : AbstractDokkaTask() { + + @get:Internal + val dokkaSourceSets: NamedDomainObjectContainer<GradleDokkaSourceSetBuilder> = + project.container(GradleDokkaSourceSetBuilder::class.java, gradleDokkaSourceSetBuilderFactory()) + .also { container -> + DslObject(this).extensions.add("dokkaSourceSets", container) + project.kotlinOrNull?.sourceSets?.all { kotlinSourceSet -> + container.register(kotlinSourceSet.name) { dokkaSourceSet -> + dokkaSourceSet.configureWithKotlinSourceSet(kotlinSourceSet) + } + } + } + + /** + * Only contains source sets that are marked with `isDocumented`. + * Non documented source sets are not relevant for Gradle's UP-TO-DATE mechanism, as well + * as task dependency graph. + */ + @get:Nested + protected val unsuppressedSourceSets: List<GradleDokkaSourceSetBuilder> + get() = dokkaSourceSets + .toList() + .also(::checkSourceSetDependencies) + .filterNot { it.suppress.getSafe() } + + override fun buildDokkaConfiguration(): DokkaConfigurationImpl { + return DokkaConfigurationImpl( + moduleName = moduleName.getSafe(), + moduleVersion = moduleVersion.orNull, + outputDir = outputDirectory.getSafe(), + cacheRoot = cacheRoot.getSafe(), + offlineMode = offlineMode.getSafe(), + failOnWarning = failOnWarning.getSafe(), + sourceSets = unsuppressedSourceSets.build(), + pluginsConfiguration = buildPluginsConfiguration(), + pluginsClasspath = plugins.resolve().toList(), + delayTemplateSubstitution = true + ) + } +}
\ No newline at end of file diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt index 1cda5c39..99e6e7e3 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt @@ -47,6 +47,12 @@ open class DokkaPlugin : Plugin<Project> { configuration() } + if (project.parent != null) { + project.tasks.register<DokkaTaskPartial>("${name}Partial") { + configuration() + } + } + if (project.subprojects.isNotEmpty()) { if (multiModuleTaskSupported) { val multiModuleName = "${name}MultiModule" @@ -54,7 +60,7 @@ open class DokkaPlugin : Plugin<Project> { project.maybeCreateDokkaRuntimeConfiguration(multiModuleName) project.tasks.register<DokkaMultiModuleTask>(multiModuleName) { - addSubprojectChildTasks(name) + addSubprojectChildTasks("${name}Partial") configuration() description = "Runs all subprojects '$name' tasks and generates module navigation page" plugins.dependencies.add(project.dokkaArtifacts.allModulesPage) |