Skip to content

Commit

Permalink
sample plugin for extra docs
Browse files Browse the repository at this point in the history
  • Loading branch information
atyrin committed Apr 18, 2023
1 parent 047a3bc commit c0a2661
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 2 deletions.
2 changes: 1 addition & 1 deletion core/src/main/kotlin/pages/Pages.kt
@@ -1,7 +1,7 @@
package org.jetbrains.dokka.pages

interface MultimoduleRootPage : ContentPage

interface CustomRootPage : ContentPage
interface ModulePage : ContentPage, WithDocumentables

interface PackagePage : ContentPage, WithDocumentables
Expand Down
31 changes: 31 additions & 0 deletions plugins/auxiliary-docs/README.md
@@ -0,0 +1,31 @@
# Auxiliary documentation

1. Add the plugin to classpath

```kotlin
// build.gradle.kts
buildscript {
dependencies {
classpath("org.jetbrains.dokka:auxiliary-docs:$dokka_version")
}
}
```

2. Add the plugin to dependencies

```kotlin
dependencies {
dokkaHtmlPlugin("org.jetbrains.dokka:auxiliary-docs:$dokka_version")
}
```

3. Configure md files location

```kotlin

tasks.dokkaHtml.configure {
pluginConfiguration<org.jetbrains.dokka.auxiliarydocs.AuxiliaryDocsPlugin, org.jetbrains.dokka.auxiliarydocs.AuxiliaryConfiguration> {
docs = setOf(File("readme.md"))
}
}
```
Empty file.
28 changes: 28 additions & 0 deletions plugins/auxiliary-docs/build.gradle.kts
@@ -0,0 +1,28 @@
import org.jetbrains.registerDokkaArtifactPublication

plugins {
id("org.jetbrains.conventions.kotlin-jvm")
id("org.jetbrains.conventions.maven-publish")
}

dependencies {
compileOnly(projects.core)
// implementation(kotlin("reflect"))
implementation(projects.plugins.base)
implementation(projects.plugins.templating)

// testImplementation(libs.jsoup)
// testImplementation(projects.plugins.base.baseTestUtils)
// testImplementation(projects.core.contentMatcherTestUtils)
// testImplementation(kotlin("test-junit"))
// testImplementation(projects.kotlinAnalysis)
//
// testImplementation(projects.testUtils)
// testImplementation(projects.core.testApi)
// testImplementation(platform(libs.junit.bom))
// testImplementation(libs.junit.jupiter)
}

registerDokkaArtifactPublication("auxiliaryDocs") {
artifactId = "auxiliary-docs"
}
12 changes: 12 additions & 0 deletions plugins/auxiliary-docs/src/main/kotlin/AuxiliaryConfiguration.kt
@@ -0,0 +1,12 @@
package org.jetbrains.dokka.auxiliaryDocs

import org.jetbrains.dokka.plugability.ConfigurableBlock
import java.io.File

data class AuxiliaryConfiguration(
var docs: Set<File> = defaultDocs,
) : ConfigurableBlock {
companion object {
val defaultDocs: Set<File> = emptySet()
}
}
119 changes: 119 additions & 0 deletions plugins/auxiliary-docs/src/main/kotlin/AuxiliaryDocPageCreator.kt
@@ -0,0 +1,119 @@
package org.jetbrains.dokka.auxiliaryDocs

import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.parsers.MarkdownParser
import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.doc.DocumentationNode
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.querySingle
import org.jetbrains.dokka.transformers.pages.CreationContext
import java.io.File


class AuxiliaryDocPageCreator(
private val context: DokkaContext,
) {
private val commentsConverter by lazy { context.plugin<DokkaBase>().querySingle { commentsToContentConverter } }
private val signatureProvider by lazy { context.plugin<DokkaBase>().querySingle { signatureProvider } }

fun root(creationContext: AuxiliaryDocPageContext): RootPageNode {
return AuxiliaryRootPageNode(
name = creationContext.page,
dri = setOf(DRI(packageName = AUX_PACKAGE_PLACEHOLDER, classNames = creationContext.page)),
content = renderContent(creationContext)
)
}

fun page(creationContext: AuxiliaryDocPageContext): PageNode {
return AuxiliaryPageNode(
name = creationContext.page,
dri = setOf(DRI(packageName = AUX_PACKAGE_PLACEHOLDER, classNames = creationContext.page)),
content = renderContent(creationContext)
)
}

private fun renderContent(creationContext: AuxiliaryDocPageContext): ContentGroup {
val sourceSetData = emptySet<DokkaSourceSet>()
val builder = PageContentBuilder(commentsConverter, signatureProvider, context.logger)
return builder.contentFor(
dri = DRI(packageName = AUX_PACKAGE_PLACEHOLDER, classNames = creationContext.page),
kind = ContentKind.Cover,
sourceSets = sourceSetData
) {
getMarkdownContent(creationContext.configuration.docs).takeIf { it.isNotEmpty() }?.let { nodes ->
group(kind = ContentKind.Cover) {
nodes.forEach { node ->
group {
node.children.forEach { comment(it.root) }
}
}
}
}
}
}

private fun getMarkdownContent(files: Set<File>): List<DocumentationNode> =
files.map { MarkdownParser({ null }, it.absolutePath).parse(it.readText()) }


companion object {
const val AUX_PACKAGE_PLACEHOLDER = ".ext"
}
}


class AuxiliaryPageNode(
override val name: String,
override val dri: Set<DRI>,
override val content: ContentNode,
override val embeddedResources: List<String> = emptyList(),
override val children: List<PageNode> = emptyList(),
) : ContentPage {

override fun modified(
name: String,
content: ContentNode,
dri: Set<DRI>,
embeddedResources: List<String>,
children: List<PageNode>,
): ContentPage {
return AuxiliaryPageNode(name, dri, content, embeddedResources, children)
}

override fun modified(name: String, children: List<PageNode>): PageNode {
return AuxiliaryPageNode(name, dri, content, embeddedResources, children)
}
}

class AuxiliaryRootPageNode(
override val name: String = "Home",
override val dri: Set<DRI>,
override val content: ContentNode,
override val embeddedResources: List<String> = emptyList(),
override val children: List<PageNode> = emptyList(),
) : RootPageNode(forceTopLevelName = true), CustomRootPage {


override fun modified(name: String, children: List<PageNode>): RootPageNode {
return AuxiliaryRootPageNode(name, dri, content, embeddedResources, children)
}

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

private infix fun <T> List<T>.shallowEq(other: List<T>) =
this === other || (this.size == other.size && (this zip other).all { (a, b) -> a === b })

data class AuxiliaryDocPageContext(val page: String, val configuration: AuxiliaryConfiguration) : CreationContext
54 changes: 54 additions & 0 deletions plugins/auxiliary-docs/src/main/kotlin/AuxiliaryDocsPlugin.kt
@@ -0,0 +1,54 @@
package org.jetbrains.dokka.auxiliaryDocs

import org.jetbrains.dokka.CoreExtensions
import org.jetbrains.dokka.base.templating.parseJson
import org.jetbrains.dokka.pages.PageNode
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.DokkaPlugin
import org.jetbrains.dokka.plugability.DokkaPluginApiPreview
import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement
import org.jetbrains.dokka.transformers.pages.PageTransformer

class AuxiliaryDocsPlugin : DokkaPlugin() {
val transformer by extending {
CoreExtensions.pageTransformer providing {
AuxDocsTransformer(it)
}
}

@OptIn(DokkaPluginApiPreview::class)
override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement =
PluginApiPreviewAcknowledgement
}

class AuxDocsTransformer(private val context: DokkaContext) : PageTransformer {
override fun invoke(input: RootPageNode): RootPageNode {
val conf = pluginConfiguration()

val creator = AuxiliaryDocPageCreator(context)
val homePage = creator.root(AuxiliaryDocPageContext("Home", conf))

return homePage.modified(
children = creator.samplePages(conf).toList() + input
)
}

private fun AuxiliaryDocPageCreator.samplePages(conf: AuxiliaryConfiguration): Set<PageNode> {
//todo
val quickStart = page(AuxiliaryDocPageContext("Quick Start", conf))
val faqStart = page(AuxiliaryDocPageContext("F.A.Q", conf))

return setOf(quickStart, faqStart)
}

// todo: handle empty configuration
private fun pluginConfiguration() =
parseJson<AuxiliaryConfiguration>(
context.configuration.pluginsConfiguration
.first { it.fqPluginName == "org.jetbrains.dokka.auxiliaryDocs.AuxiliaryDocsPlugin" }.values
)
}



@@ -0,0 +1 @@
org.jetbrains.dokka.auxiliaryDocs.AuxiliaryDocsPlugin
Expand Up @@ -12,7 +12,8 @@ import org.jetbrains.dokka.pages.*

abstract class NavigationDataProvider {
open fun navigableChildren(input: RootPageNode): NavigationNode = input.withDescendants()
.first { it is ModulePage || it is MultimoduleRootPage }.let { visit(it as ContentPage) }
.first { it is ModulePage || it is MultimoduleRootPage || it is CustomRootPage }
.let { visit(it as ContentPage) }

open fun visit(page: ContentPage): NavigationNode =
NavigationNode(
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Expand Up @@ -74,6 +74,7 @@ include(
":plugins:templating",
":plugins:versioning",
":plugins:android-documentation",
":plugins:auxiliary-docs",

":plugins:mathjax",
":plugins:gfm",
Expand Down

0 comments on commit c0a2661

Please sign in to comment.