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 /plugins/base/src/main | |
parent | dc179bf9a649d925e7e64dbcaf52a2187416a1d5 (diff) | |
download | dokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.tar.gz dokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.tar.bz2 dokka-d41965ca3cb180bc82bd5dd6fa747d2b83381b31.zip |
Implement immediate temaplates resolution
Diffstat (limited to 'plugins/base/src/main')
11 files changed, 153 insertions, 23 deletions
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); } |