aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2020-10-07 19:36:49 +0200
committerBłażej Kardyś <bkardys@virtuslab.com>2020-11-27 03:15:02 +0100
commitd41965ca3cb180bc82bd5dd6fa747d2b83381b31 (patch)
tree5660f17f21fdf5871c8239f4d96c4521545292d1
parentdc179bf9a649d925e7e64dbcaf52a2187416a1d5 (diff)
downloaddokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.tar.gz
dokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.tar.bz2
dokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.zip
Implement immediate temaplates resolution
-rw-r--r--core/src/main/kotlin/configuration.kt2
-rw-r--r--core/src/main/kotlin/defaultConfiguration.kt1
-rw-r--r--core/src/main/kotlin/generation/SingleModuleGeneration.kt90
-rw-r--r--core/src/main/kotlin/pages/PageNodes.kt10
-rw-r--r--plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt1
-rw-r--r--plugins/all-module-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt4
-rw-r--r--plugins/all-module-page/src/main/kotlin/templates/PathToRootSubstitutor.kt6
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt12
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt23
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/Tags.kt22
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/command/consumers/ImmediateResolutionTagConsumer.kt31
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/command/consumers/PathToRootConsumer.kt22
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/command/consumers/ResolveLinkConsumer.kt30
-rw-r--r--plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt12
-rw-r--r--plugins/base/src/main/kotlin/templating/Command.kt6
-rw-r--r--plugins/base/src/main/kotlin/templating/ImmediateHtmlCommandConsumer.kt14
-rw-r--r--plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt2
-rw-r--r--plugins/base/src/main/resources/dokka/styles/style.css2
-rw-r--r--runners/cli/src/main/kotlin/cli/main.kt5
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTaskPartial.kt50
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt8
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)