Skip to content

Commit

Permalink
Add All Types page generation (#3267)
Browse files Browse the repository at this point in the history
* Hide All Types page under a system property
* Show documentation if it exists selecting most relevant
* Take the minimum SinceKotlin version if they differ
* Extract internal configuration properties to one place
  • Loading branch information
whyoleg authored Nov 24, 2023
1 parent d9d287b commit 17ad866
Show file tree
Hide file tree
Showing 20 changed files with 927 additions and 67 deletions.
1 change: 1 addition & 0 deletions dokka-subprojects/core/api/dokka-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -3807,6 +3807,7 @@ public final class org/jetbrains/dokka/pages/ContentHeader : org/jetbrains/dokka
}

public final class org/jetbrains/dokka/pages/ContentKind : java/lang/Enum, org/jetbrains/dokka/pages/Kind {
public static final field AllTypes Lorg/jetbrains/dokka/pages/ContentKind;
public static final field Annotations Lorg/jetbrains/dokka/pages/ContentKind;
public static final field BriefComment Lorg/jetbrains/dokka/pages/ContentKind;
public static final field Classlikes Lorg/jetbrains/dokka/pages/ContentKind;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ public enum class ContentKind : Kind {
*/
Symbol,

Comment, Constructors, Functions, Parameters, Properties, Classlikes, Packages, Sample, Main, BriefComment,
Comment, Constructors, Functions, Parameters, Properties, Classlikes, Packages, AllTypes, Sample, Main, BriefComment,
Empty, Source, TypeAliases, Cover, Inheritors, SourceSetDependentHint, Extensions, Annotations,

/**
Expand All @@ -352,6 +352,7 @@ public enum class ContentKind : Kind {
Properties,
Classlikes,
Packages,
AllTypes,
Source,
TypeAliases,
Inheritors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public class DokkaBase : DokkaPlugin() {

public val sinceKotlinTransformer: Extension<DocumentableTransformer, *, *> by extending {
CoreExtensions.documentableTransformer providing ::SinceKotlinTransformer applyIf {
SinceKotlinTransformer.shouldDisplaySinceKotlin()
DokkaBaseInternalConfiguration.sinceKotlinRenderingEnabled
} order {
before(extensionsExtractor)
}
Expand Down Expand Up @@ -159,7 +159,7 @@ public class DokkaBase : DokkaPlugin() {

public val sinceKotlinTagContentProvider: Extension<CustomTagContentProvider, *, *> by extending {
customTagContentProvider with SinceKotlinTagContentProvider applyIf {
SinceKotlinTransformer.shouldDisplaySinceKotlin()
DokkaBaseInternalConfiguration.sinceKotlinRenderingEnabled
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package org.jetbrains.dokka.base

// revisit in scope of https://github.com/Kotlin/dokka/issues/2776
internal object DokkaBaseInternalConfiguration {
const val SHOULD_DISPLAY_ALL_TYPES_PAGE_SYS_PROP = "dokka.shouldDisplayAllTypesPage"
const val SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP = "dokka.shouldDisplaySinceKotlin"

var allTypesPageEnabled: Boolean = false
private set
var sinceKotlinRenderingEnabled: Boolean = false
private set

init {
reinitialize()
}

// should be private, internal is only for usage in tests
internal fun reinitialize() {
allTypesPageEnabled = getBooleanProperty(SHOULD_DISPLAY_ALL_TYPES_PAGE_SYS_PROP)
sinceKotlinRenderingEnabled = getBooleanProperty(SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP)
}

private fun getBooleanProperty(propertyName: String): Boolean {
return System.getProperty(propertyName) in setOf("1", "true")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package org.jetbrains.dokka.base.pages

import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.pages.ContentNode
import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.pages.PageNode

/**
* This page is internal because it's an stdlib-specific feature,
* which is not intended for public use or customization.
*
* For more details, see https://github.com/Kotlin/dokka/issues/2887
*/
internal class AllTypesPageNode(
override val content: ContentNode,
override val embeddedResources: List<String> = listOf()
) : ContentPage {
override val dri: Set<DRI> = setOf(DRI)
override val name: String = "All Types"
override val children: List<PageNode> get() = emptyList()

override fun modified(name: String, children: List<PageNode>): AllTypesPageNode =
modified(name = name, content = this.content, dri = dri, children = children)

override fun modified(
name: String,
content: ContentNode,
dri: Set<DRI>,
embeddedResources: List<String>,
children: List<PageNode>
): AllTypesPageNode =
if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this
else AllTypesPageNode(content, embeddedResources)

companion object {
val DRI: DRI = DRI(packageName = ".alltypes")
}
}

// copy-pasted from dokka-core, not sure why it was needed in the first place
private infix fun <T> List<T>.shallowEq(other: List<T>) =
this === other || (this.size == other.size && (this zip other).all { (a, b) -> a === b })
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
import org.jetbrains.dokka.base.resolvers.local.DokkaBaseLocationProvider
import org.jetbrains.dokka.base.templating.*
import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions
import org.jetbrains.dokka.base.pages.AllTypesPageNode
import org.jetbrains.dokka.base.translators.documentables.shouldDocumentConstructors
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.*
Expand Down Expand Up @@ -474,7 +475,10 @@ public open class HtmlRenderer(
?.let {
when (pageContext) {
is MultimoduleRootPage -> buildRowForMultiModule(node, it, pageContext, sourceSetRestriction)
is ModulePage -> buildRowForModule(node, it, pageContext, sourceSetRestriction)

is ModulePage,
is AllTypesPageNode -> buildRowForPlatformTaggedBrief(node, it, pageContext, sourceSetRestriction)

else -> buildRowForContent(node, it, pageContext, sourceSetRestriction)
}
}
Expand All @@ -497,7 +501,12 @@ public open class HtmlRenderer(
}
}

private fun FlowContent.buildRowForModule(
/**
* Builds a row with support for filtering and showing platform bubble and brief content.
*
* Used for rendering packages in [ModulePage] and types in [AllTypesPageNode]
*/
private fun FlowContent.buildRowForPlatformTaggedBrief(
contextNode: ContentGroup,
toRender: List<ContentNode>,
pageContext: ContentPage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.querySingle
import org.jetbrains.dokka.analysis.kotlin.internal.DocumentableLanguage
import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
import org.jetbrains.dokka.base.DokkaBaseInternalConfiguration
import org.jetbrains.dokka.base.pages.AllTypesPageNode

public abstract class NavigationDataProvider(
dokkaContext: DokkaContext
Expand Down Expand Up @@ -105,23 +107,47 @@ public abstract class NavigationDataProvider(
}

private val navigationNodeOrder: Comparator<NavigationNode> =
compareBy(canonicalAlphabeticalOrder) { it.name }
compareBy(canonicalAlphabeticalOrder, NavigationNode::name)

private val navigationModuleNodeOrder: Comparator<NavigationNode> =
when (DokkaBaseInternalConfiguration.allTypesPageEnabled) {
false -> navigationNodeOrder
// put `All Types` page at the bottom of the navigation page
true -> Comparator { a, b ->
when {
a === b -> 0
a.dri == AllTypesPageNode.DRI -> 1
b.dri == AllTypesPageNode.DRI -> -1
else -> navigationNodeOrder.compare(a, b)
}
}
}

private fun ContentPage.navigableChildren() =
if (this is ClasslikePage) {
private fun ContentPage.navigableChildren() = when (this) {
is ClasslikePage -> {
this.navigableChildren()
} else {
}

is ModulePage -> {
children
.filterIsInstance<ContentPage>()
.map { visit(it) }
.map(::visit)
.sortedWith(navigationModuleNodeOrder)
}

else -> {
children
.filterIsInstance<ContentPage>()
.map(::visit)
.sortedWith(navigationNodeOrder)
}
}

private fun ClasslikePage.navigableChildren(): List<NavigationNode> {
// Classlikes should only have other classlikes as navigable children
val navigableChildren = children
.filterIsInstance<ClasslikePage>()
.map { visit(it) }
.map(::visit)

val isEnumPage = documentables.any { it is DEnum }
return if (isEnumPage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package org.jetbrains.dokka.base.resolvers.local

import org.jetbrains.dokka.base.renderers.sourceSets
import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
import org.jetbrains.dokka.base.pages.AllTypesPageNode
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.PointingToDeclaration
import org.jetbrains.dokka.model.*
Expand All @@ -25,6 +26,9 @@ public open class DokkaLocationProvider(
if (page is RootPageNode && page.forceTopLevelName) {
put(page, prefix + PAGE_WITH_CHILDREN_SUFFIX)
page.children.forEach { registerPath(it, prefix) }
} else if (page is AllTypesPageNode) {
put(page, prefix + ALL_TYPES_PAGE_PATH)
page.children.forEach { registerPath(it, prefix) }
} else {
val newPrefix = prefix + page.pathName
put(page, if (page is ModulePageNode) prefix else newPrefix)
Expand Down Expand Up @@ -159,7 +163,12 @@ public open class DokkaLocationProvider(
protected data class PageWithKind(val page: ContentPage, val kind: Kind)

public companion object {
public val reservedFilenames: Set<String> = setOf("index", "con", "aux", "lst", "prn", "nul", "eof", "inp", "out")
private const val ALL_TYPES_PAGE_PATH: String = "all-types"

public val reservedFilenames: Set<String> = setOf(
"index", "con", "aux", "lst", "prn", "nul", "eof", "inp", "out",
ALL_TYPES_PAGE_PATH
)

//Taken from: https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names
internal val reservedCharacters = setOf('|', '>', '<', '*', ':', '"', '?', '%')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,60 @@ public class SinceKotlinVersion(str: String) : Comparable<SinceKotlinVersion> {
}

override fun toString(): String = parts.joinToString(".")

internal companion object {
internal const val SINCE_KOTLIN_TAG_NAME = "Since Kotlin"

private val minVersionOfPlatform = mapOf(
Platform.common to SinceKotlinVersion("1.0"),
Platform.jvm to SinceKotlinVersion("1.0"),
Platform.js to SinceKotlinVersion("1.1"),
Platform.native to SinceKotlinVersion("1.3"),
Platform.wasm to SinceKotlinVersion("1.8"),
)

fun minVersionOfPlatform(platform: Platform): SinceKotlinVersion {
return minVersionOfPlatform[platform]
?: throw IllegalStateException("No value for platform: $platform")
}

/**
* Should be in sync with [extractSinceKotlinVersionFromCustomTag]
*/
fun createCustomTagFromSinceKotlinVersion(
version: SinceKotlinVersion?,
platform: Platform
): CustomTagWrapper {
val sinceKotlinVersion = version?: minVersionOfPlatform(platform)
return CustomTagWrapper(
CustomDocTag(
children = listOf(Text(sinceKotlinVersion.toString())),
name = MARKDOWN_ELEMENT_FILE_NAME
),
SINCE_KOTLIN_TAG_NAME
)
}

/**
* Should be in sync with [createCustomTagFromSinceKotlinVersion]
*/
fun extractSinceKotlinVersionFromCustomTag(
tagWrapper: CustomTagWrapper,
platform: Platform
): SinceKotlinVersion {
val customTag = tagWrapper.root as? CustomDocTag
val sinceKotlinVersionText = customTag?.children?.firstOrNull() as? Text
val sinceKotlinVersion = sinceKotlinVersionText?.body?.let(::SinceKotlinVersion)
return sinceKotlinVersion ?: minVersionOfPlatform(platform)
}

}
}

public class SinceKotlinTransformer(
public val context: DokkaContext
) : DocumentableTransformer {

private val minSinceKotlinVersionOfPlatform = mapOf(
Platform.common to SinceKotlinVersion("1.0"),
Platform.jvm to SinceKotlinVersion("1.0"),
Platform.js to SinceKotlinVersion("1.1"),
Platform.native to SinceKotlinVersion("1.3"),
Platform.wasm to SinceKotlinVersion("1.8"),
)

override fun invoke(original: DModule, context: DokkaContext): DModule = original.transform() as DModule

private fun <T : Documentable> T.transform(parent: SourceSetDependent<SinceKotlinVersion>? = null): Documentable {
Expand Down Expand Up @@ -132,8 +172,7 @@ public class SinceKotlinTransformer(
?.params?.let { it["version"] as? StringValue }?.value
?.let { SinceKotlinVersion(it) }

val minSinceKotlin = minSinceKotlinVersionOfPlatform[sourceSet.analysisPlatform]
?: throw IllegalStateException("No value for platform: ${sourceSet.analysisPlatform}")
val minSinceKotlin = SinceKotlinVersion.minVersionOfPlatform(sourceSet.analysisPlatform)

return annotatedVersion?.takeIf { version -> version >= minSinceKotlin } ?: minSinceKotlin
}
Expand All @@ -153,34 +192,17 @@ public class SinceKotlinTransformer(
private fun Documentable.appendSinceKotlin(versions: SourceSetDependent<SinceKotlinVersion>) =
sourceSets.fold(documentation) { acc, sourceSet ->

val version = versions[sourceSet]

val sinceKotlinCustomTag = CustomTagWrapper(
CustomDocTag(
listOf(
Text(
version.toString()
)
),
name = MARKDOWN_ELEMENT_FILE_NAME
),
"Since Kotlin"
val sinceKotlinCustomTag = SinceKotlinVersion.createCustomTagFromSinceKotlinVersion(
version = versions[sourceSet],
platform = sourceSet.analysisPlatform
)
if (acc[sourceSet] == null)
acc + (sourceSet to DocumentationNode(listOf(sinceKotlinCustomTag)))
else
acc.mapValues {
if (it.key == sourceSet) it.value.copy(
it.value.children + listOf(
sinceKotlinCustomTag
)
it.value.children + listOf(sinceKotlinCustomTag)
) else it.value
}
}

internal companion object {
internal const val SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP = "dokka.shouldDisplaySinceKotlin"
internal fun shouldDisplaySinceKotlin() =
System.getProperty(SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP) in listOf("true", "1")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
package org.jetbrains.dokka.base.transformers.pages.tags

import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinVersion
import org.jetbrains.dokka.base.translators.documentables.KDOC_TAG_HEADER_LEVEL
import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder
import org.jetbrains.dokka.model.doc.CustomTagWrapper
import org.jetbrains.dokka.pages.TextStyle

public object SinceKotlinTagContentProvider : CustomTagContentProvider {

private const val SINCE_KOTLIN_TAG_NAME = "Since Kotlin"

override fun isApplicable(customTag: CustomTagWrapper): Boolean = customTag.name == SINCE_KOTLIN_TAG_NAME
override fun isApplicable(customTag: CustomTagWrapper): Boolean =
customTag.name == SinceKotlinVersion.SINCE_KOTLIN_TAG_NAME

override fun DocumentableContentBuilder.contentForDescription(
sourceSet: DokkaConfiguration.DokkaSourceSet,
Expand Down
Loading

0 comments on commit 17ad866

Please sign in to comment.